Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(commonjs): dynamic require root check was broken in some cases #1461

Merged
merged 6 commits into from May 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 9 additions & 7 deletions packages/commonjs/src/index.js
Expand Up @@ -112,6 +112,7 @@ export default function commonjs(options = {}) {
let requireResolver;

function transformAndCheckExports(code, id) {
const normalizedId = normalizePathSlashes(id);
const { isEsModule, hasDefaultExport, hasNamedExports, ast } = analyzeTopLevelStatements(
this.parse,
code,
Expand All @@ -127,7 +128,7 @@ export default function commonjs(options = {}) {
}

if (
!dynamicRequireModules.has(normalizePathSlashes(id)) &&
!dynamicRequireModules.has(normalizedId) &&
(!(hasCjsKeywords(code, ignoreGlobal) || requireResolver.isRequiredId(id)) ||
(isEsModule && !options.transformMixedEsModules))
) {
Expand All @@ -136,18 +137,19 @@ export default function commonjs(options = {}) {
}

const needsRequireWrapper =
!isEsModule &&
(dynamicRequireModules.has(normalizePathSlashes(id)) || strictRequiresFilter(id));
!isEsModule && (dynamicRequireModules.has(normalizedId) || strictRequiresFilter(id));

const checkDynamicRequire = (position) => {
if (id.indexOf(dynamicRequireRoot) !== 0) {
const normalizedRequireRoot = normalizePathSlashes(dynamicRequireRoot);

if (normalizedId.indexOf(normalizedRequireRoot) !== 0) {
this.error(
{
code: 'DYNAMIC_REQUIRE_OUTSIDE_ROOT',
id,
normalizedId,
dynamicRequireRoot,
message: `"${id}" contains dynamic require statements but it is not within the current dynamicRequireRoot "${dynamicRequireRoot}". You should set dynamicRequireRoot to "${dirname(
id
message: `"${normalizedId}" contains dynamic require statements but it is not within the current dynamicRequireRoot "${normalizedRequireRoot}". You should set dynamicRequireRoot to "${dirname(
normalizedId
)}" or one of its parent directories.`
},
position
Expand Down
5 changes: 5 additions & 0 deletions packages/commonjs/test/helpers/util.js
Expand Up @@ -7,6 +7,10 @@ function commonjs(options) {
return commonjsPlugin(options);
}

function normalizePathSlashes(path) {
return path.replace(/\\/g, '/');
}

function requireWithContext(code, context) {
const module = { exports: {} };
const contextWithExports = { ...context, module, exports: module.exports };
Expand Down Expand Up @@ -90,5 +94,6 @@ module.exports = {
executeBundle,
getCodeFromBundle,
getCodeMapFromBundle,
normalizePathSlashes,
runCodeSplitTest
};
66 changes: 62 additions & 4 deletions packages/commonjs/test/test.js
Expand Up @@ -13,7 +13,12 @@ const { testBundle } = require('../../../util/test');

const { peerDependencies } = require('../package.json');

const { commonjs, executeBundle, getCodeFromBundle } = require('./helpers/util.js');
const {
commonjs,
executeBundle,
getCodeFromBundle,
normalizePathSlashes
} = require('./helpers/util.js');

install();
test.beforeEach(() => process.chdir(__dirname));
Expand Down Expand Up @@ -715,9 +720,16 @@ test('throws when there is a dynamic require from outside dynamicRequireRoot', a
}

const cwd = process.cwd();
const id = path.join(cwd, 'fixtures/samples/dynamic-require-outside-root/main.js');
const dynamicRequireRoot = path.join(cwd, 'fixtures/samples/dynamic-require-outside-root/nested');
const minimalDynamicRequireRoot = path.join(cwd, 'fixtures/samples/dynamic-require-outside-root');
const id = normalizePathSlashes(
path.join(cwd, 'fixtures/samples/dynamic-require-outside-root/main.js')
);
const dynamicRequireRoot = normalizePathSlashes(
path.join(cwd, 'fixtures/samples/dynamic-require-outside-root/nested')
);
const minimalDynamicRequireRoot = normalizePathSlashes(
path.join(cwd, 'fixtures/samples/dynamic-require-outside-root')
);

t.like(error, {
message: `"${id}" contains dynamic require statements but it is not within the current dynamicRequireRoot "${dynamicRequireRoot}". You should set dynamicRequireRoot to "${minimalDynamicRequireRoot}" or one of its parent directories.`,
pluginCode: 'DYNAMIC_REQUIRE_OUTSIDE_ROOT',
Expand All @@ -726,6 +738,52 @@ test('throws when there is a dynamic require from outside dynamicRequireRoot', a
});
});

test('does not throw when a dynamic require uses different slashes than dynamicRequireRoot', async (t) => {
let error = null;
try {
await rollup({
input: 'fixtures/samples/dynamic-require-outside-root/main.js',
plugins: [
commonjs({
dynamicRequireRoot: 'fixtures\\samples\\dynamic-require-outside-root',
dynamicRequireTargets: [
'fixtures\\samples\\dynamic-require-outside-root\\nested\\target.js'
]
})
]
});
} catch (err) {
error = err;
}

t.is(error, null);
});

// On Windows, avoid a false error about a module not being in the dynamic require root due to
// incoherent slashes/backslashes in the paths.
if (os.platform() === 'win32') {
test('correctly asserts dynamicRequireRoot on Windows', async (t) => {
let error = null;
try {
await rollup({
input: 'fixtures/samples/dynamic-require-outside-root/main.js',
plugins: [
commonjs({
dynamicRequireRoot: 'fixtures/samples/dynamic-require-outside-root',
dynamicRequireTargets: [
'fixtures/samples/dynamic-require-outside-root/nested/target.js'
]
})
]
});
} catch (err) {
error = err;
}

t.is(error, null);
});
}

test('does not transform typeof exports for mixed modules', async (t) => {
const bundle = await rollup({
input: 'fixtures/samples/mixed-module-typeof-exports/main.js',
Expand Down