diff --git a/packages/commonjs/README.md b/packages/commonjs/README.md index 09e82a8e7..715f7e778 100644 --- a/packages/commonjs/README.md +++ b/packages/commonjs/README.md @@ -44,13 +44,30 @@ Then call `rollup` either via the [CLI](https://www.rollupjs.org/guide/en/#comma ## Options +### `strictRequires` + +Type: `"auto" | boolean | "debug" | string[]`
+Default: `"auto"` + +By default, this plugin will try to hoist `require` statements as imports to the top of each file. While this works well for many code bases and allows for very efficient ESM output, it does not perfectly capture CommonJS semantics as the order of side effects like log statements may change. But it is especially problematic when there are circular `require` calls between CommonJS modules as those often rely on the lazy execution of nested `require` calls. + +Setting this option to `true` will wrap all CommonJS files in functions which are executed when they are required for the first time, preserving NodeJS semantics. Note that this can have an impact on the size and performance of the generated code. + +The default value of `"auto"` will only wrap CommonJS files when they are part of a CommonJS dependency cycle, e.g. an index file that is required by many of its dependencies. All other CommonJS files are hoisted. This is the recommended setting for most code bases. + +`false` will entirely prevent wrapping and hoist all files. This may still work depending on the nature of cyclic dependencies but will often cause problems. + +You can also provide a [minimatch pattern](https://github.com/isaacs/minimatch), or array of patterns, to only specify a subset of files which should be wrapped in functions for proper `require` semantics. + +`"debug"` works like `"auto"` but after bundling, it will display a warning containing a list of ids that have been wrapped which can be used as minimatch pattern for fine-tuning. + ### `dynamicRequireTargets` Type: `string | string[]`
Default: `[]` Some modules contain dynamic `require` calls, or require modules that contain circular dependencies, which are not handled well by static imports. -Including those modules as `dynamicRequireTargets` will simulate a CommonJS (NodeJS-like) environment for them with support for dynamic and circular dependencies. +Including those modules as `dynamicRequireTargets` will simulate a CommonJS (NodeJS-like) environment for them with support for dynamic dependencies. It also enables `strictRequires` for those modules, see above. _Note: In extreme cases, this feature may result in some paths being rendered as absolute in the final bundle. The plugin tries to avoid exposing paths from the local machine, but if you are `dynamicRequirePaths` with paths that are far away from your project's folder, that may require replacing strings like `"/Users/John/Desktop/foo-project/"` -> `"/"`._ @@ -113,17 +130,6 @@ Default: `false` Instructs the plugin whether to enable mixed module transformations. This is useful in scenarios with modules that contain a mix of ES `import` statements and CommonJS `require` expressions. Set to `true` if `require` calls should be transformed to imports in mixed modules, or `false` if the `require` expressions should survive the transformation. The latter can be important if the code contains environment detection, or you are coding for an environment with special treatment for `require` calls such as [ElectronJS](https://www.electronjs.org/). See also the "ignore" option. -### `strictRequireSemantic` - -Type: `boolean | string | string[]`
-Default: `false` - -By default, this plugin will try to hoist all `require` statements as imports to the top of each file. While this works well for many code bases and allows for very efficient ESM output, it does not perfectly capture CommonJS semantics. This is especially problematic when there are circular `require` calls between CommonJS modules as those often rely on the lazy execution of nested `require` calls. - -Setting this option to `true` will wrap all CommonJS files in functions which are executed when they are required for the first time, preserving NodeJS semantics. Note that this can have a small impact on the size and performance of the generated code. - -You can also provide a [minimatch pattern](https://github.com/isaacs/minimatch), or array of patterns, to only specify a subset of files which should be wrapped in functions for proper `require` semantics. - ### `ignore` Type: `string[] | ((id: string) => boolean)`
diff --git a/packages/commonjs/src/generate-imports.js b/packages/commonjs/src/generate-imports.js index 22a59027d..e90152d36 100644 --- a/packages/commonjs/src/generate-imports.js +++ b/packages/commonjs/src/generate-imports.js @@ -112,7 +112,7 @@ export function getRequireHandlers() { id, exportMode, resolveRequireSourcesAndGetMeta, - usesRequireWrapper, + needsRequireWrapper, isEsModule, usesRequire ) { @@ -135,13 +135,16 @@ export function getRequireHandlers() { ); } const requiresBySource = collectSources(requireExpressions); - const requireTargets = await resolveRequireSourcesAndGetMeta( + const { requireTargets, usesRequireWrapper } = await resolveRequireSourcesAndGetMeta( id, - usesRequireWrapper ? IS_WRAPPED_COMMONJS : !isEsModule, + needsRequireWrapper ? IS_WRAPPED_COMMONJS : !isEsModule, Object.keys(requiresBySource) ); processRequireExpressions(imports, requireTargets, requiresBySource, magicString); - return imports.length ? `${imports.join('\n')}\n\n` : ''; + return { + importBlock: imports.length ? `${imports.join('\n')}\n\n` : '', + usesRequireWrapper + }; } return { diff --git a/packages/commonjs/src/index.js b/packages/commonjs/src/index.js index 5fa50e419..b3e277e0e 100644 --- a/packages/commonjs/src/index.js +++ b/packages/commonjs/src/index.js @@ -1,4 +1,4 @@ -import { extname } from 'path'; +import { extname, relative } from 'path'; import { createFilter } from '@rollup/pluginutils'; import getCommonDir from 'commondir'; @@ -27,7 +27,7 @@ import getResolveId from './resolve-id'; import { getResolveRequireSourcesAndGetMeta } from './resolve-require-sources'; import validateRollupVersion from './rollup-version'; import transformCommonjs from './transform-commonjs'; -import { getName, normalizePathSlashes } from './utils'; +import { getName, getStrictRequiresFilter, normalizePathSlashes } from './utils'; export default function commonjs(options = {}) { const { @@ -39,12 +39,7 @@ export default function commonjs(options = {}) { } = options; const extensions = options.extensions || ['.js']; const filter = createFilter(options.include, options.exclude); - const strictRequireSemanticFilter = - options.strictRequireSemantic === true - ? () => true - : !options.strictRequireSemantic - ? () => false - : createFilter(options.strictRequireSemantic); + const { strictRequiresFilter, detectCycles } = getStrictRequiresFilter(options); const getRequireReturnsDefault = typeof requireReturnsDefaultOption === 'function' @@ -65,7 +60,10 @@ export default function commonjs(options = {}) { : () => typeof defaultIsModuleExportsOption === 'boolean' ? defaultIsModuleExportsOption : 'auto'; - const resolveRequireSourcesAndGetMeta = getResolveRequireSourcesAndGetMeta(extensions); + const { resolveRequireSourcesAndGetMeta, getWrappedIds } = getResolveRequireSourcesAndGetMeta( + extensions, + detectCycles + ); const dynamicRequireModuleSet = getDynamicRequireModuleSet(options.dynamicRequireTargets); const isDynamicRequireModulesEnabled = dynamicRequireModuleSet.size > 0; const commonDir = isDynamicRequireModulesEnabled @@ -122,9 +120,9 @@ export default function commonjs(options = {}) { return { meta: { commonjs: { isCommonJS: false } } }; } - const usesRequireWrapper = + const needsRequireWrapper = !isEsModule && - (dynamicRequireModuleSet.has(normalizePathSlashes(id)) || strictRequireSemanticFilter(id)); + (dynamicRequireModuleSet.has(normalizePathSlashes(id)) || strictRequiresFilter(id)); return transformCommonjs( this.parse, @@ -141,7 +139,7 @@ export default function commonjs(options = {}) { commonDir, ast, getDefaultIsModuleExports(id), - usesRequireWrapper, + needsRequireWrapper, resolveRequireSourcesAndGetMeta(this) ); } @@ -158,6 +156,28 @@ export default function commonjs(options = {}) { } }, + buildEnd() { + if (options.strictRequires === 'debug') { + const wrappedIds = getWrappedIds(); + if (wrappedIds.length) { + this.warn({ + code: 'WRAPPED_IDS', + ids: wrappedIds, + message: `The commonjs plugin automatically wrapped the following files:\n[\n${wrappedIds + .map((id) => `\t${JSON.stringify(relative(process.cwd(), id))}`) + .join(',\n')}\n]` + }); + } else { + // TODO Lukas test + this.warn({ + code: 'WRAPPED_IDS', + ids: wrappedIds, + message: 'The commonjs plugin did not wrap any files.' + }); + } + } + }, + resolveId, load(id) { diff --git a/packages/commonjs/src/resolve-require-sources.js b/packages/commonjs/src/resolve-require-sources.js index 5be46a0c9..ce3727288 100644 --- a/packages/commonjs/src/resolve-require-sources.js +++ b/packages/commonjs/src/resolve-require-sources.js @@ -1,54 +1,80 @@ import { EXTERNAL_SUFFIX, IS_WRAPPED_COMMONJS, PROXY_SUFFIX, wrapId } from './helpers'; import { resolveExtensions } from './resolve-id'; -// TODO Lukas auto-detect circular dependencies -// * only return once all dependencies have been analyzed -// * wait for this.load dependencies unless they already have an entry in knownCjsModuleTypes to avoid deadlocks -// as those have already started being processed -// * only analyze cycles if we do not have an explicit config -export function getResolveRequireSourcesAndGetMeta(extensions) { +export function getResolveRequireSourcesAndGetMeta(extensions, detectCycles) { const knownCjsModuleTypes = Object.create(null); - return (rollupContext) => (id, isParentCommonJS, sources) => { - knownCjsModuleTypes[id] = isParentCommonJS; - return Promise.all( - sources.map(async (source) => { - // Never analyze or proxy internal modules - if (source.startsWith('\0')) { - return { source, id: source, isCommonJS: false }; - } - const resolved = - (await rollupContext.resolve(source, id, { - skipSelf: true, - custom: { - 'node-resolve': { isRequire: true } + const dependentModules = Object.create(null); + const getDependentModules = (id) => + dependentModules[id] || (dependentModules[id] = Object.create(null)); + + return { + getWrappedIds: () => + Object.keys(knownCjsModuleTypes).filter( + (id) => knownCjsModuleTypes[id] === IS_WRAPPED_COMMONJS + ), + resolveRequireSourcesAndGetMeta: (rollupContext) => async (id, isParentCommonJS, sources) => { + knownCjsModuleTypes[id] = isParentCommonJS; + const requireTargets = await Promise.all( + sources.map(async (source) => { + // Never analyze or proxy internal modules + if (source.startsWith('\0')) { + return { id: source, allowProxy: false }; + } + const resolved = + (await rollupContext.resolve(source, id, { + skipSelf: true, + custom: { + 'node-resolve': { isRequire: true } + } + })) || resolveExtensions(source, id, extensions); + if (!resolved) { + return { id: wrapId(source, EXTERNAL_SUFFIX), allowProxy: false }; + } + const childId = resolved.id; + if (resolved.external) { + return { id: wrapId(childId, EXTERNAL_SUFFIX), allowProxy: false }; + } + const parentDependentModules = getDependentModules(id); + const childDependentModules = getDependentModules(childId); + childDependentModules[id] = true; + for (const dependentId of Object.keys(parentDependentModules)) { + childDependentModules[dependentId] = true; + } + if (parentDependentModules[childId]) { + // If we depend on one of our dependencies, we have a cycle. Then all modules that + // we depend on that also depend on the same module are part of a cycle as well. + if (detectCycles && isParentCommonJS) { + knownCjsModuleTypes[id] = IS_WRAPPED_COMMONJS; + knownCjsModuleTypes[childId] = IS_WRAPPED_COMMONJS; + for (const dependentId of Object.keys(parentDependentModules)) { + if (getDependentModules(dependentId)[childId]) { + knownCjsModuleTypes[dependentId] = IS_WRAPPED_COMMONJS; + } + } } - })) || resolveExtensions(source, id, extensions); - if (!resolved) { - return { source, id: wrapId(source, EXTERNAL_SUFFIX), isCommonJS: false }; - } - if (resolved.external) { - return { source, id: wrapId(resolved.id, EXTERNAL_SUFFIX), isCommonJS: false }; - } - if (resolved.id in knownCjsModuleTypes) { + } else { + // This makes sure the current transform handler waits for all direct dependencies to be + // loaded and transformed and therefore for all transitive CommonJS dependencies to be + // loaded as well so that all cycles have been found and knownCjsModuleTypes is reliable. + await rollupContext.load(resolved); + } + return { id: childId, allowProxy: true }; + }) + ); + return { + requireTargets: requireTargets.map(({ id: dependencyId, allowProxy }, index) => { + const isCommonJS = knownCjsModuleTypes[dependencyId]; return { - source, + source: sources[index], id: - knownCjsModuleTypes[resolved.id] === IS_WRAPPED_COMMONJS - ? resolved.id - : wrapId(resolved.id, PROXY_SUFFIX), - isCommonJS: knownCjsModuleTypes[resolved.id] + allowProxy && isCommonJS !== IS_WRAPPED_COMMONJS + ? wrapId(dependencyId, PROXY_SUFFIX) + : dependencyId, + isCommonJS }; - } - const { - meta: { commonjs: commonjsMeta } - } = await rollupContext.load(resolved); - const isCommonJS = commonjsMeta && commonjsMeta.isCommonJS; - return { - source, - id: isCommonJS === IS_WRAPPED_COMMONJS ? resolved.id : wrapId(resolved.id, PROXY_SUFFIX), - isCommonJS - }; - }) - ); + }), + usesRequireWrapper: knownCjsModuleTypes[id] === IS_WRAPPED_COMMONJS + }; + } }; } diff --git a/packages/commonjs/src/transform-commonjs.js b/packages/commonjs/src/transform-commonjs.js index 3716e6f35..fed2690d6 100644 --- a/packages/commonjs/src/transform-commonjs.js +++ b/packages/commonjs/src/transform-commonjs.js @@ -50,7 +50,7 @@ export default async function transformCommonjs( commonDir, astCache, defaultIsModuleExports, - usesRequireWrapper, + needsRequireWrapper, resolveRequireSourcesAndGetMeta ) { const ast = astCache || tryParse(parse, code, id); @@ -227,9 +227,10 @@ export default async function transformCommonjs( let shouldRemoveRequireStatement = false; if (currentTryBlockEnd !== null) { - ({ canConvertRequire, shouldRemoveRequireStatement } = - getIgnoreTryCatchRequireStatementMode(node.arguments[0].value)); - + const ignoreTryCatchRequire = getIgnoreTryCatchRequireStatementMode( + node.arguments[0].value + ); + ({ canConvertRequire, shouldRemoveRequireStatement } = ignoreTryCatchRequire); if (shouldRemoveRequireStatement) { hasRemovedRequire = true; } @@ -447,7 +448,7 @@ export default async function transformCommonjs( ? 'exports' : 'module'; - const importBlock = await rewriteRequireExpressionsAndGetImportBlock( + const { importBlock, usesRequireWrapper } = await rewriteRequireExpressionsAndGetImportBlock( magicString, topLevelDeclarations, topLevelRequireDeclarators, @@ -459,7 +460,7 @@ export default async function transformCommonjs( id, exportMode, resolveRequireSourcesAndGetMeta, - usesRequireWrapper, + needsRequireWrapper, isEsModule, uses.require ); @@ -490,17 +491,15 @@ export default async function transformCommonjs( } if (usesRequireWrapper) { - magicString - .trim() - .indent('\t') - .prepend( - `var ${isRequiredName}; + magicString.trim().indent('\t'); + magicString.prepend( + `var ${isRequiredName}; function ${requireName} () { \tif (${isRequiredName}) return ${exportsName}; \t${isRequiredName} = 1; ` - ).append(` + ).append(` \treturn ${exportsName}; }`); if (exportMode === 'replace') { diff --git a/packages/commonjs/src/utils.js b/packages/commonjs/src/utils.js index b7dc9cda1..d66765c93 100644 --- a/packages/commonjs/src/utils.js +++ b/packages/commonjs/src/utils.js @@ -2,7 +2,7 @@ import { basename, dirname, extname } from 'path'; -import { makeLegalIdentifier } from '@rollup/pluginutils'; +import { createFilter, makeLegalIdentifier } from '@rollup/pluginutils'; export function deconflict(scopes, globals, identifier) { let i = 1; @@ -34,6 +34,7 @@ export function normalizePathSlashes(path) { return path.replace(/\\/g, '/'); } +// TODO Lukas get rid of this? const VIRTUAL_PATH_BASE = '/$$rollup_base$$'; export const getVirtualPathForDynamicRequirePath = (path, commonDir) => { const normalizedPath = normalizePathSlashes(path); @@ -45,3 +46,26 @@ export const getVirtualPathForDynamicRequirePath = (path, commonDir) => { export function capitalize(name) { return name[0].toUpperCase() + name.slice(1); } + +export function getStrictRequiresFilter({ strictRequires }) { + switch (strictRequires) { + case true: + return { strictRequiresFilter: () => true, detectCycles: false }; + // eslint-disable-next-line no-undefined + case undefined: + case 'auto': + case 'debug': + case null: + return { strictRequiresFilter: () => false, detectCycles: true }; + case false: + return { strictRequiresFilter: () => false, detectCycles: false }; + default: + if (typeof strictRequires === 'string' || Array.isArray(strictRequires)) { + return { + strictRequiresFilter: createFilter(strictRequires), + detectCycles: false + }; + } + throw new Error('Unexpected value for "strictRequires" option.'); + } +} diff --git a/packages/commonjs/test/fixtures/form/constant-template-literal/output.js b/packages/commonjs/test/fixtures/form/constant-template-literal/output.js index 787983734..08f8bd02d 100644 --- a/packages/commonjs/test/fixtures/form/constant-template-literal/output.js +++ b/packages/commonjs/test/fixtures/form/constant-template-literal/output.js @@ -1,7 +1,7 @@ import * as commonjsHelpers from "_commonjsHelpers.js"; import { commonjsRequire as commonjsRequire } from "_commonjs-dynamic-modules"; import { __exports as input } from "\u0000fixtures/form/constant-template-literal/input.js?commonjs-exports" -import require$$0 from "\u0000/Users/lukastaegert/Github/rollup-plugins/packages/commonjs/test/fixtures/form/constant-template-literal/tape.js?commonjs-proxy"; +import require$$0 from "\u0000CWD/fixtures/form/constant-template-literal/tape.js?commonjs-proxy"; var foo = require$$0; console.log(foo); diff --git a/packages/commonjs/test/fixtures/form/ignore-ids-function/output.js b/packages/commonjs/test/fixtures/form/ignore-ids-function/output.js index 5fc418cd4..de90c6826 100644 --- a/packages/commonjs/test/fixtures/form/ignore-ids-function/output.js +++ b/packages/commonjs/test/fixtures/form/ignore-ids-function/output.js @@ -1,7 +1,7 @@ import * as commonjsHelpers from "_commonjsHelpers.js"; import { commonjsRequire as commonjsRequire } from "_commonjs-dynamic-modules"; import { __exports as input } from "\u0000fixtures/form/ignore-ids-function/input.js?commonjs-exports" -import require$$0 from "\u0000/Users/lukastaegert/Github/rollup-plugins/packages/commonjs/test/fixtures/form/ignore-ids-function/bar.js?commonjs-proxy"; +import require$$0 from "\u0000CWD/fixtures/form/ignore-ids-function/bar.js?commonjs-proxy"; var foo = require( 'foo' ); var bar = require$$0; diff --git a/packages/commonjs/test/fixtures/form/ignore-ids/output.js b/packages/commonjs/test/fixtures/form/ignore-ids/output.js index 83b9d1e9f..e37256ccb 100644 --- a/packages/commonjs/test/fixtures/form/ignore-ids/output.js +++ b/packages/commonjs/test/fixtures/form/ignore-ids/output.js @@ -1,7 +1,7 @@ import * as commonjsHelpers from "_commonjsHelpers.js"; import { commonjsRequire as commonjsRequire } from "_commonjs-dynamic-modules"; import { __exports as input } from "\u0000fixtures/form/ignore-ids/input.js?commonjs-exports" -import require$$0 from "\u0000/Users/lukastaegert/Github/rollup-plugins/packages/commonjs/test/fixtures/form/ignore-ids/bar.js?commonjs-proxy"; +import require$$0 from "\u0000CWD/fixtures/form/ignore-ids/bar.js?commonjs-proxy"; var foo = require( 'foo' ); var bar = require$$0; diff --git a/packages/commonjs/test/fixtures/form/multi-entry-module-exports/output1.js b/packages/commonjs/test/fixtures/form/multi-entry-module-exports/output1.js index cd261710a..5bf32420e 100644 --- a/packages/commonjs/test/fixtures/form/multi-entry-module-exports/output1.js +++ b/packages/commonjs/test/fixtures/form/multi-entry-module-exports/output1.js @@ -1,6 +1,6 @@ import * as commonjsHelpers from "_commonjsHelpers.js"; import { commonjsRequire as commonjsRequire } from "_commonjs-dynamic-modules"; -import require$$0 from "\u0000/Users/lukastaegert/Github/rollup-plugins/packages/commonjs/test/fixtures/form/multi-entry-module-exports/input2.js?commonjs-proxy"; +import require$$0 from "\u0000CWD/fixtures/form/multi-entry-module-exports/input2.js?commonjs-proxy"; const t2 = require$$0; diff --git a/packages/commonjs/test/fixtures/form/multiple-var-declarations-b/output.js b/packages/commonjs/test/fixtures/form/multiple-var-declarations-b/output.js index 0186bc7eb..05bb39f86 100644 --- a/packages/commonjs/test/fixtures/form/multiple-var-declarations-b/output.js +++ b/packages/commonjs/test/fixtures/form/multiple-var-declarations-b/output.js @@ -1,7 +1,7 @@ import * as commonjsHelpers from "_commonjsHelpers.js"; import { commonjsRequire as commonjsRequire } from "_commonjs-dynamic-modules"; import { __exports as input } from "\u0000fixtures/form/multiple-var-declarations-b/input.js?commonjs-exports" -import require$$0 from "\u0000/Users/lukastaegert/Github/rollup-plugins/packages/commonjs/test/fixtures/form/multiple-var-declarations-b/a.js?commonjs-proxy"; +import require$$0 from "\u0000CWD/fixtures/form/multiple-var-declarations-b/a.js?commonjs-proxy"; var a = require$$0 , b = 42; diff --git a/packages/commonjs/test/fixtures/form/multiple-var-declarations-c/output.js b/packages/commonjs/test/fixtures/form/multiple-var-declarations-c/output.js index 4101b47e8..74dc40f2e 100644 --- a/packages/commonjs/test/fixtures/form/multiple-var-declarations-c/output.js +++ b/packages/commonjs/test/fixtures/form/multiple-var-declarations-c/output.js @@ -1,7 +1,7 @@ import * as commonjsHelpers from "_commonjsHelpers.js"; import { commonjsRequire as commonjsRequire } from "_commonjs-dynamic-modules"; import { __exports as input } from "\u0000fixtures/form/multiple-var-declarations-c/input.js?commonjs-exports" -import require$$0 from "\u0000/Users/lukastaegert/Github/rollup-plugins/packages/commonjs/test/fixtures/form/multiple-var-declarations-c/b.js?commonjs-proxy"; +import require$$0 from "\u0000CWD/fixtures/form/multiple-var-declarations-c/b.js?commonjs-proxy"; var a = 'a' , b = require$$0 diff --git a/packages/commonjs/test/fixtures/form/multiple-var-declarations/output.js b/packages/commonjs/test/fixtures/form/multiple-var-declarations/output.js index 60c5118b9..38a4a54aa 100644 --- a/packages/commonjs/test/fixtures/form/multiple-var-declarations/output.js +++ b/packages/commonjs/test/fixtures/form/multiple-var-declarations/output.js @@ -1,8 +1,8 @@ import * as commonjsHelpers from "_commonjsHelpers.js"; import { commonjsRequire as commonjsRequire } from "_commonjs-dynamic-modules"; import { __exports as input } from "\u0000fixtures/form/multiple-var-declarations/input.js?commonjs-exports" -import require$$0 from "\u0000/Users/lukastaegert/Github/rollup-plugins/packages/commonjs/test/fixtures/form/multiple-var-declarations/a.js?commonjs-proxy"; -import require$$1 from "\u0000/Users/lukastaegert/Github/rollup-plugins/packages/commonjs/test/fixtures/form/multiple-var-declarations/b.js?commonjs-proxy"; +import require$$0 from "\u0000CWD/fixtures/form/multiple-var-declarations/a.js?commonjs-proxy"; +import require$$1 from "\u0000CWD/fixtures/form/multiple-var-declarations/b.js?commonjs-proxy"; var a = require$$0() , b = require$$1; diff --git a/packages/commonjs/test/fixtures/form/no-exports-entry/output.js b/packages/commonjs/test/fixtures/form/no-exports-entry/output.js index 3e83089b2..b10040f7b 100644 --- a/packages/commonjs/test/fixtures/form/no-exports-entry/output.js +++ b/packages/commonjs/test/fixtures/form/no-exports-entry/output.js @@ -1,7 +1,7 @@ import * as commonjsHelpers from "_commonjsHelpers.js"; import { commonjsRequire as commonjsRequire } from "_commonjs-dynamic-modules"; import { __exports as input_1 } from "\u0000fixtures/form/no-exports-entry/input.js?commonjs-exports" -import require$$0 from "\u0000/Users/lukastaegert/Github/rollup-plugins/packages/commonjs/test/fixtures/form/no-exports-entry/dummy.js?commonjs-proxy"; +import require$$0 from "\u0000CWD/fixtures/form/no-exports-entry/dummy.js?commonjs-proxy"; var dummy = require$$0; diff --git a/packages/commonjs/test/fixtures/form/require-collision/output.js b/packages/commonjs/test/fixtures/form/require-collision/output.js index 22eb3c645..d760dbf85 100644 --- a/packages/commonjs/test/fixtures/form/require-collision/output.js +++ b/packages/commonjs/test/fixtures/form/require-collision/output.js @@ -1,7 +1,7 @@ import * as commonjsHelpers from "_commonjsHelpers.js"; import { commonjsRequire as commonjsRequire } from "_commonjs-dynamic-modules"; import { __exports as input } from "\u0000fixtures/form/require-collision/input.js?commonjs-exports" -import require$$1 from "\u0000/Users/lukastaegert/Github/rollup-plugins/packages/commonjs/test/fixtures/form/require-collision/foo.js?commonjs-proxy"; +import require$$1 from "\u0000CWD/fixtures/form/require-collision/foo.js?commonjs-proxy"; (function() { var foo = require$$1; diff --git a/packages/commonjs/test/fixtures/form/unambiguous-with-default-export/output.js b/packages/commonjs/test/fixtures/form/unambiguous-with-default-export/output.js index 6c0116507..51bfef8ef 100644 --- a/packages/commonjs/test/fixtures/form/unambiguous-with-default-export/output.js +++ b/packages/commonjs/test/fixtures/form/unambiguous-with-default-export/output.js @@ -1,6 +1,6 @@ import * as commonjsHelpers from "_commonjsHelpers.js"; import { commonjsRequire as commonjsRequire } from "_commonjs-dynamic-modules"; import { __exports as input } from "\u0000fixtures/form/unambiguous-with-default-export/input.js?commonjs-exports" -import "\u0000/Users/lukastaegert/Github/rollup-plugins/packages/commonjs/test/fixtures/form/unambiguous-with-default-export/foo.js?commonjs-proxy"; +import "\u0000CWD/fixtures/form/unambiguous-with-default-export/foo.js?commonjs-proxy"; export default {}; diff --git a/packages/commonjs/test/fixtures/form/unambiguous-with-import/output.js b/packages/commonjs/test/fixtures/form/unambiguous-with-import/output.js index b1dd5178e..8b277a597 100644 --- a/packages/commonjs/test/fixtures/form/unambiguous-with-import/output.js +++ b/packages/commonjs/test/fixtures/form/unambiguous-with-import/output.js @@ -1,6 +1,6 @@ import * as commonjsHelpers from "_commonjsHelpers.js"; import { commonjsRequire as commonjsRequire } from "_commonjs-dynamic-modules"; import { __exports as input } from "\u0000fixtures/form/unambiguous-with-import/input.js?commonjs-exports" -import "\u0000/Users/lukastaegert/Github/rollup-plugins/packages/commonjs/test/fixtures/form/unambiguous-with-import/foo.js?commonjs-proxy"; +import "\u0000CWD/fixtures/form/unambiguous-with-import/foo.js?commonjs-proxy"; import './bar.js'; diff --git a/packages/commonjs/test/fixtures/form/unambiguous-with-named-export/output.js b/packages/commonjs/test/fixtures/form/unambiguous-with-named-export/output.js index d7428f35e..c84e44011 100644 --- a/packages/commonjs/test/fixtures/form/unambiguous-with-named-export/output.js +++ b/packages/commonjs/test/fixtures/form/unambiguous-with-named-export/output.js @@ -1,6 +1,6 @@ import * as commonjsHelpers from "_commonjsHelpers.js"; import { commonjsRequire as commonjsRequire } from "_commonjs-dynamic-modules"; import { __exports as input } from "\u0000fixtures/form/unambiguous-with-named-export/input.js?commonjs-exports" -import "\u0000/Users/lukastaegert/Github/rollup-plugins/packages/commonjs/test/fixtures/form/unambiguous-with-named-export/foo.js?commonjs-proxy"; +import "\u0000CWD/fixtures/form/unambiguous-with-named-export/foo.js?commonjs-proxy"; export {}; diff --git a/packages/commonjs/test/fixtures/function/circular-dependencies-wrapped/_config.js b/packages/commonjs/test/fixtures/function/circular-dependencies-wrapped/_config.js index 0b7a53026..cda7e0f2f 100644 --- a/packages/commonjs/test/fixtures/function/circular-dependencies-wrapped/_config.js +++ b/packages/commonjs/test/fixtures/function/circular-dependencies-wrapped/_config.js @@ -9,5 +9,8 @@ module.exports = { output: { exports: 'named' } + }, + pluginOptions: { + strictRequires: false } }; diff --git a/packages/commonjs/test/fixtures/function/circular-dependencies/_config.js b/packages/commonjs/test/fixtures/function/circular-dependencies/_config.js index 78fe11373..c561742f0 100644 --- a/packages/commonjs/test/fixtures/function/circular-dependencies/_config.js +++ b/packages/commonjs/test/fixtures/function/circular-dependencies/_config.js @@ -9,5 +9,8 @@ module.exports = { output: { exports: 'named' } + }, + pluginOptions: { + strictRequires: false } }; diff --git a/packages/commonjs/test/fixtures/function/dynamic-require-es-mixed-helpers/_config.js b/packages/commonjs/test/fixtures/function/dynamic-require-es-mixed-helpers/_config.js index df33c9e84..d9b26bed7 100755 --- a/packages/commonjs/test/fixtures/function/dynamic-require-es-mixed-helpers/_config.js +++ b/packages/commonjs/test/fixtures/function/dynamic-require-es-mixed-helpers/_config.js @@ -1,7 +1,7 @@ module.exports = { description: 'supports strict require semantic in mixed modules', pluginOptions: { - strictRequireSemantic: true, + strictRequires: true, transformMixedEsModules: true } }; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-auto/_config.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-auto/_config.js new file mode 100644 index 000000000..8249fa044 --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-auto/_config.js @@ -0,0 +1,7 @@ +module.exports = { + description: + 'automatically detects cycles and switches those modules to strict semantics for "auto"', + pluginOptions: { + strictRequires: 'auto' + } +}; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-auto/a-imports-b.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-auto/a-imports-b.js new file mode 100644 index 000000000..f3e51c119 --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-auto/a-imports-b.js @@ -0,0 +1,2 @@ +exports.a = 'a'; +t.is(require('./b-imports-c').a, 'a'); diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-auto/b-imports-c.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-auto/b-imports-c.js new file mode 100644 index 000000000..53d3bc879 --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-auto/b-imports-c.js @@ -0,0 +1 @@ +exports.a = require('./c-imports-a.js').a; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-auto/c-imports-a.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-auto/c-imports-a.js new file mode 100644 index 000000000..dc755f527 --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-auto/c-imports-a.js @@ -0,0 +1 @@ +exports.a = require('./a-imports-b').a; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-auto/main.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-auto/main.js new file mode 100644 index 000000000..7aa9154de --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-auto/main.js @@ -0,0 +1 @@ +require('./a-imports-b'); diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-circular/_config.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-circular/_config.js index 073d40526..04efeeef1 100644 --- a/packages/commonjs/test/fixtures/function/strict-require-semantic-circular/_config.js +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-circular/_config.js @@ -1,6 +1,6 @@ module.exports = { description: 'handles circular dependencies with strict require semantic', pluginOptions: { - strictRequireSemantic: true + strictRequires: true } }; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-cycle-detection/_config.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-cycle-detection/_config.js new file mode 100644 index 000000000..52f7e94ef --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-cycle-detection/_config.js @@ -0,0 +1,3 @@ +module.exports = { + description: 'automatically detects cycles and switches those modules to strict semantics' +}; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-cycle-detection/a-imports-b.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-cycle-detection/a-imports-b.js new file mode 100644 index 000000000..f3e51c119 --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-cycle-detection/a-imports-b.js @@ -0,0 +1,2 @@ +exports.a = 'a'; +t.is(require('./b-imports-c').a, 'a'); diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-cycle-detection/b-imports-c.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-cycle-detection/b-imports-c.js new file mode 100644 index 000000000..53d3bc879 --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-cycle-detection/b-imports-c.js @@ -0,0 +1 @@ +exports.a = require('./c-imports-a.js').a; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-cycle-detection/c-imports-a.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-cycle-detection/c-imports-a.js new file mode 100644 index 000000000..dc755f527 --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-cycle-detection/c-imports-a.js @@ -0,0 +1 @@ +exports.a = require('./a-imports-b').a; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-cycle-detection/main.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-cycle-detection/main.js new file mode 100644 index 000000000..7aa9154de --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-cycle-detection/main.js @@ -0,0 +1 @@ +require('./a-imports-b'); diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-debug-none/_config.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-debug-none/_config.js new file mode 100644 index 000000000..d570e0882 --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-debug-none/_config.js @@ -0,0 +1,23 @@ +const assert = require('assert'); + +const warnings = []; + +module.exports = { + description: 'has correct debug output when there are no cycles', + pluginOptions: { + strictRequires: 'debug' + }, + options: { + onwarn(warning) { + if (warning.pluginCode !== 'WRAPPED_IDS') { + throw new Error(`Unexpected warning ${warning.code}: ${warning.message}`); + } + warnings.push(warning); + } + }, + exports() { + assert.strictEqual(warnings.length, 1); + assert.deepStrictEqual(warnings[0].ids, []); + assert.strictEqual(warnings[0].message, 'The commonjs plugin did not wrap any files.'); + } +}; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-debug-none/main.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-debug-none/main.js new file mode 100644 index 000000000..cb1c2c01e --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-debug-none/main.js @@ -0,0 +1 @@ +module.exports = 'bar'; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-debug/_config.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-debug/_config.js new file mode 100644 index 000000000..1845e2eb9 --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-debug/_config.js @@ -0,0 +1,29 @@ +const assert = require('assert'); +const path = require('path'); + +const warnings = []; + +module.exports = { + description: + 'automatically detects cycles and switches those modules to strict semantics for "debug"', + pluginOptions: { + strictRequires: 'debug' + }, + options: { + onwarn(warning) { + if (warning.code === 'CIRCULAR_DEPENDENCY') return; + if (warning.pluginCode !== 'WRAPPED_IDS') { + throw new Error(`Unexpected warning ${warning.code}: ${warning.message}`); + } + warnings.push(warning); + } + }, + exports() { + assert.strictEqual(warnings.length, 1); + assert.deepStrictEqual(warnings[0].ids, [ + path.join(__dirname, 'a-imports-b.js'), + path.join(__dirname, 'b-imports-c.js'), + path.join(__dirname, 'c-imports-a.js') + ]); + } +}; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-debug/a-imports-b.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-debug/a-imports-b.js new file mode 100644 index 000000000..f3e51c119 --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-debug/a-imports-b.js @@ -0,0 +1,2 @@ +exports.a = 'a'; +t.is(require('./b-imports-c').a, 'a'); diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-debug/b-imports-c.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-debug/b-imports-c.js new file mode 100644 index 000000000..53d3bc879 --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-debug/b-imports-c.js @@ -0,0 +1 @@ +exports.a = require('./c-imports-a.js').a; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-debug/c-imports-a.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-debug/c-imports-a.js new file mode 100644 index 000000000..dc755f527 --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-debug/c-imports-a.js @@ -0,0 +1 @@ +exports.a = require('./a-imports-b').a; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-debug/main.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-debug/main.js new file mode 100644 index 000000000..7aa9154de --- /dev/null +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-debug/main.js @@ -0,0 +1 @@ +require('./a-imports-b'); diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-entry/_config.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-entry/_config.js index 0ba3d13b4..757ac2cab 100644 --- a/packages/commonjs/test/fixtures/function/strict-require-semantic-entry/_config.js +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-entry/_config.js @@ -12,7 +12,7 @@ module.exports = { } }, pluginOptions: { - strictRequireSemantic: ['fixtures/function/strict-require-semantic-entry/main.js'] + strictRequires: ['fixtures/function/strict-require-semantic-entry/main.js'] }, exports(exports) { assert.deepStrictEqual(exports, { foo: 'foo' }); diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-exportmode-exports/_config.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-exportmode-exports/_config.js index dde0e4242..683e0b747 100644 --- a/packages/commonjs/test/fixtures/function/strict-require-semantic-exportmode-exports/_config.js +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-exportmode-exports/_config.js @@ -1,6 +1,6 @@ module.exports = { description: 'supports using function wrappers for modules for export mode "exports"', pluginOptions: { - strictRequireSemantic: ['fixtures/function/strict-require-semantic-exportmode-exports/*E*.js'] + strictRequires: ['fixtures/function/strict-require-semantic-exportmode-exports/*E*.js'] } }; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-exportmode-module/_config.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-exportmode-module/_config.js index e8dd75b4f..dda61ff86 100644 --- a/packages/commonjs/test/fixtures/function/strict-require-semantic-exportmode-module/_config.js +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-exportmode-module/_config.js @@ -1,6 +1,6 @@ module.exports = { description: 'supports using function wrappers for modules for export mode "module"', pluginOptions: { - strictRequireSemantic: ['fixtures/function/strict-require-semantic-exportmode-module/*E*.js'] + strictRequires: ['fixtures/function/strict-require-semantic-exportmode-module/*E*.js'] } }; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-exportmode-replace/_config.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-exportmode-replace/_config.js index 6f9c03a62..4f1cd0f78 100644 --- a/packages/commonjs/test/fixtures/function/strict-require-semantic-exportmode-replace/_config.js +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-exportmode-replace/_config.js @@ -1,6 +1,6 @@ module.exports = { description: 'supports using function wrappers for modules for export mode "replace"', pluginOptions: { - strictRequireSemantic: ['fixtures/function/strict-require-semantic-exportmode-replace/*E*.js'] + strictRequires: ['fixtures/function/strict-require-semantic-exportmode-replace/*E*.js'] } }; diff --git a/packages/commonjs/test/fixtures/function/strict-require-semantic-from-esm/_config.js b/packages/commonjs/test/fixtures/function/strict-require-semantic-from-esm/_config.js index 56fdd8541..9ba78e6a5 100644 --- a/packages/commonjs/test/fixtures/function/strict-require-semantic-from-esm/_config.js +++ b/packages/commonjs/test/fixtures/function/strict-require-semantic-from-esm/_config.js @@ -1,6 +1,6 @@ module.exports = { description: 'handles importing wrapped modules from ESM', pluginOptions: { - strictRequireSemantic: ['fixtures/function/strict-require-semantic-from-esm/strict.js'] + strictRequires: ['fixtures/function/strict-require-semantic-from-esm/strict.js'] } }; diff --git a/packages/commonjs/test/form.js b/packages/commonjs/test/form.js index d551cc43b..3dcc25740 100644 --- a/packages/commonjs/test/form.js +++ b/packages/commonjs/test/form.js @@ -24,77 +24,84 @@ const transformContext = { load: ({ id }) => Promise.resolve({ id, meta: {} }) }; -fs.readdirSync('./fixtures/form').forEach((dir) => { - let config; - - try { - config = require(`./fixtures/form/${dir}/_config.js`); - } catch (err) { - config = {}; - } +// Do not run on Windows as we have full path names in the output +if (path.sep === '/') { + fs.readdirSync('./fixtures/form').forEach((dir) => { + let config; + + try { + config = require(`./fixtures/form/${dir}/_config.js`); + } catch (err) { + config = {}; + } - const inputEntries = []; + const inputEntries = []; - if (typeof config.multi === 'object') { - for (const [key, entry] of Object.entries(config.multi)) { - inputEntries.push([key, `fixtures/form/${dir}/${entry}`]); + if (typeof config.multi === 'object') { + for (const [key, entry] of Object.entries(config.multi)) { + inputEntries.push([key, `fixtures/form/${dir}/${entry}`]); + } + } else { + inputEntries.push(['output', `fixtures/form/${dir}/input.js`]); } - } else { - inputEntries.push(['output', `fixtures/form/${dir}/input.js`]); - } - - (config.solo ? test.only : test)(dir, (t) => - Promise.all( - inputEntries.map(async ([outputName, id]) => { - const { transform } = commonjs(config.options); - - transformContext.getModuleInfo = (moduleId) => { - return { - isEntry: config.entry && moduleId === id, - importers: - config.importers && config.importers[outputName] - ? config.importers[outputName].map((x) => `fixtures/form/${dir}/${x}`) - : [] + + (config.solo ? test.only : test)(dir, (t) => + Promise.all( + inputEntries.map(async ([outputName, id]) => { + const { transform } = commonjs(config.options); + + transformContext.getModuleInfo = (moduleId) => { + return { + isEntry: config.entry && moduleId === id, + importers: + config.importers && config.importers[outputName] + ? config.importers[outputName].map((x) => `fixtures/form/${dir}/${x}`) + : [] + }; }; - }; - transformContext.error = (base, props) => { - let error = base; - if (!(base instanceof Error)) error = Object.assign(new Error(base.message), base); - if (props) Object.assign(error, props); - throw error; - }; - - const input = fs.readFileSync(id, 'utf-8'); - - let outputFile = `fixtures/form/${dir}/${outputName}`; - if (fs.existsSync(`${outputFile}.${process.platform}.js`)) { - outputFile += `.${process.platform}.js`; - } else { - outputFile += '.js'; - } - - const expected = fs.readFileSync(outputFile, 'utf-8').trim(); - // eslint-disable-next-line no-await-in-loop - const transformed = await transform.call(transformContext, input, id); - const actual = (transformed ? transformed.code : input).trim().replace(/\0/g, '_'); - - // uncomment to update snapshots - // fs.writeFileSync(outputFile, `${actual}\n`); - - // trim whitespace from line endings, - // this will benefit issues like `form/try-catch-remove` where whitespace is left in the line, - // and testing on windows (\r\n) - t.is( - actual - .split('\n') - .map((x) => x.trimEnd()) - .join('\n'), - expected - .split('\n') - .map((x) => x.trimEnd()) - .join('\n') - ); - }) - ) - ); -}); + transformContext.error = (base, props) => { + let error = base; + if (!(base instanceof Error)) error = Object.assign(new Error(base.message), base); + if (props) Object.assign(error, props); + throw error; + }; + + const input = fs.readFileSync(id, 'utf-8'); + + let outputFile = `fixtures/form/${dir}/${outputName}`; + if (fs.existsSync(`${outputFile}.${process.platform}.js`)) { + outputFile += `.${process.platform}.js`; + } else { + outputFile += '.js'; + } + + const expected = fs.readFileSync(outputFile, 'utf-8').trim(); + // eslint-disable-next-line no-await-in-loop + const transformed = await transform.call(transformContext, input, id); + let actual = (transformed ? transformed.code : input).trim().replace(/\0/g, '_'); + const cwd = process.cwd(); + while (actual.indexOf(cwd) >= 0) { + actual = actual.replace(process.cwd(), 'CWD'); + } + + // uncomment to update snapshots + // fs.writeFileSync(outputFile, `${actual}\n`); + + // trim whitespace from line endings, + // this will benefit issues like `form/try-catch-remove` where whitespace is left in the line, + // and testing on windows (\r\n) + t.is( + actual + .split('\n') + .map((x) => x.trimEnd()) + .join('\n'), + expected + .split('\n') + .map((x) => x.trimEnd()) + .join('\n') + ); + }) + ) + ); + }); +} diff --git a/packages/commonjs/test/snapshots/function.js.md b/packages/commonjs/test/snapshots/function.js.md index 5715c0ae2..c9311e099 100644 --- a/packages/commonjs/test/snapshots/function.js.md +++ b/packages/commonjs/test/snapshots/function.js.md @@ -5442,6 +5442,55 @@ Generated by [AVA](https://avajs.dev). `, } +## strict-require-semantic-auto + +> Snapshot 1 + + { + 'main.js': `'use strict';␊ + ␊ + var main = {};␊ + ␊ + var aImportsB = {};␊ + ␊ + var bImportsC = {};␊ + ␊ + var cImportsA = {};␊ + ␊ + var hasRequiredCImportsA;␊ + ␊ + function requireCImportsA () {␊ + if (hasRequiredCImportsA) return cImportsA;␊ + hasRequiredCImportsA = 1;␊ + cImportsA.a = requireAImportsB().a;␊ + return cImportsA;␊ + }␊ + ␊ + var hasRequiredBImportsC;␊ + ␊ + function requireBImportsC () {␊ + if (hasRequiredBImportsC) return bImportsC;␊ + hasRequiredBImportsC = 1;␊ + bImportsC.a = requireCImportsA().a;␊ + return bImportsC;␊ + }␊ + ␊ + var hasRequiredAImportsB;␊ + ␊ + function requireAImportsB () {␊ + if (hasRequiredAImportsB) return aImportsB;␊ + hasRequiredAImportsB = 1;␊ + aImportsB.a = 'a';␊ + t.is(requireBImportsC().a, 'a');␊ + return aImportsB;␊ + }␊ + ␊ + requireAImportsB();␊ + ␊ + module.exports = main;␊ + `, + } + ## strict-require-semantic-circular > Snapshot 1 @@ -5478,6 +5527,117 @@ Generated by [AVA](https://avajs.dev). `, } +## strict-require-semantic-cycle-detection + +> Snapshot 1 + + { + 'main.js': `'use strict';␊ + ␊ + var main = {};␊ + ␊ + var aImportsB = {};␊ + ␊ + var bImportsC = {};␊ + ␊ + var cImportsA = {};␊ + ␊ + var hasRequiredCImportsA;␊ + ␊ + function requireCImportsA () {␊ + if (hasRequiredCImportsA) return cImportsA;␊ + hasRequiredCImportsA = 1;␊ + cImportsA.a = requireAImportsB().a;␊ + return cImportsA;␊ + }␊ + ␊ + var hasRequiredBImportsC;␊ + ␊ + function requireBImportsC () {␊ + if (hasRequiredBImportsC) return bImportsC;␊ + hasRequiredBImportsC = 1;␊ + bImportsC.a = requireCImportsA().a;␊ + return bImportsC;␊ + }␊ + ␊ + var hasRequiredAImportsB;␊ + ␊ + function requireAImportsB () {␊ + if (hasRequiredAImportsB) return aImportsB;␊ + hasRequiredAImportsB = 1;␊ + aImportsB.a = 'a';␊ + t.is(requireBImportsC().a, 'a');␊ + return aImportsB;␊ + }␊ + ␊ + requireAImportsB();␊ + ␊ + module.exports = main;␊ + `, + } + +## strict-require-semantic-debug + +> Snapshot 1 + + { + 'main.js': `'use strict';␊ + ␊ + var main = {};␊ + ␊ + var aImportsB = {};␊ + ␊ + var bImportsC = {};␊ + ␊ + var cImportsA = {};␊ + ␊ + var hasRequiredCImportsA;␊ + ␊ + function requireCImportsA () {␊ + if (hasRequiredCImportsA) return cImportsA;␊ + hasRequiredCImportsA = 1;␊ + cImportsA.a = requireAImportsB().a;␊ + return cImportsA;␊ + }␊ + ␊ + var hasRequiredBImportsC;␊ + ␊ + function requireBImportsC () {␊ + if (hasRequiredBImportsC) return bImportsC;␊ + hasRequiredBImportsC = 1;␊ + bImportsC.a = requireCImportsA().a;␊ + return bImportsC;␊ + }␊ + ␊ + var hasRequiredAImportsB;␊ + ␊ + function requireAImportsB () {␊ + if (hasRequiredAImportsB) return aImportsB;␊ + hasRequiredAImportsB = 1;␊ + aImportsB.a = 'a';␊ + t.is(requireBImportsC().a, 'a');␊ + return aImportsB;␊ + }␊ + ␊ + requireAImportsB();␊ + ␊ + module.exports = main;␊ + `, + } + +## strict-require-semantic-debug-none + +> Snapshot 1 + + { + 'main.js': `'use strict';␊ + ␊ + var main = 'bar';␊ + ␊ + module.exports = main;␊ + `, + } + ## strict-require-semantic-entry > Snapshot 1 @@ -6578,32 +6738,3 @@ Generated by [AVA](https://avajs.dev). module.exports = main;␊ `, } - -## require-mixed-module - -> Snapshot 1 - - { - 'main.js': `'use strict';␊ - ␊ - var main = {};␊ - ␊ - var other = 'foo';␊ - ␊ - const foo = other;␊ - ␊ - var dep$1 = 'default';␊ - ␊ - var dep$2 = /*#__PURE__*/Object.freeze({␊ - __proto__: null,␊ - foo: foo,␊ - 'default': dep$1␊ - });␊ - ␊ - const dep = dep$2;␊ - ␊ - t.deepEqual(dep, { default: 'default', ns: { default: 'bar', foo: 'foo' } });␊ - ␊ - module.exports = main;␊ - `, - } diff --git a/packages/commonjs/test/snapshots/function.js.snap b/packages/commonjs/test/snapshots/function.js.snap index ab054b8b0..71ddc9153 100644 Binary files a/packages/commonjs/test/snapshots/function.js.snap and b/packages/commonjs/test/snapshots/function.js.snap differ diff --git a/packages/commonjs/test/types.ts b/packages/commonjs/test/types.ts index 8c010a0d2..d91cebfba 100644 --- a/packages/commonjs/test/types.ts +++ b/packages/commonjs/test/types.ts @@ -20,7 +20,8 @@ const config: RollupOptions = { sourceMap: false, transformMixedEsModules: false, ignore: ['conditional-runtime-dependency'], - dynamicRequireTargets: ['node_modules/logform/*.js'] + dynamicRequireTargets: ['node_modules/logform/*.js'], + strictRequires: ['node_modules/foo/*.js'] }) ] }; diff --git a/packages/commonjs/types/index.d.ts b/packages/commonjs/types/index.d.ts index 24c0d8633..3337ed6ee 100644 --- a/packages/commonjs/types/index.d.ts +++ b/packages/commonjs/types/index.d.ts @@ -8,10 +8,10 @@ interface RollupCommonJSOptions { /** * A minimatch pattern, or array of patterns, which specifies the files in * the build the plugin should operate on. By default, all files with - * extension `".cjs"` or those in `extensions` are included, but you can narrow - * this list by only including specific files. These files will be analyzed - * and transpiled if either the analysis does not find ES module specific - * statements or `transformMixedEsModules` is `true`. + * extension `".cjs"` or those in `extensions` are included, but you can + * narrow this list by only including specific files. These files will be + * analyzed and transpiled if either the analysis does not find ES module + * specific statements or `transformMixedEsModules` is `true`. * @default undefined */ include?: FilterPattern; @@ -66,23 +66,38 @@ interface RollupCommonJSOptions { */ transformMixedEsModules?: boolean; /** - * By default, this plugin will try to hoist all `require` statements as - * imports to the top of each file. While this works well for many code bases - * and allows for very efficient ESM output, it does not perfectly capture - * CommonJS semantics. This is especially problematic when there are circular - * `require` calls between CommonJS modules as those often rely on the lazy - * execution of nested `require` calls. + * By default, this plugin will try to hoist `require` statements as imports + * to the top of each file. While this works well for many code bases and + * allows for very efficient ESM output, it does not perfectly capture + * CommonJS semantics as the order of side effects like log statements may + * change. But it is especially problematic when there are circular `require` + * calls between CommonJS modules as those often rely on the lazy execution of + * nested `require` calls. * * Setting this option to `true` will wrap all CommonJS files in functions * which are executed when they are required for the first time, preserving - * NodeJS semantics. Note that this can have a small impact on the size and + * NodeJS semantics. Note that this can have an impact on the size and * performance of the generated code. * + * The default value of `"auto"` will only wrap CommonJS files when they are + * part of a CommonJS dependency cycle, e.g. an index file that is required by + * many of its dependencies. All other CommonJS files are hoisted. This is the + * recommended setting for most code bases. + * + * `false` will entirely prevent wrapping and hoist all files. This may still + * work depending on the nature of cyclic dependencies but will often cause + * problems. + * * You can also provide a minimatch pattern, or array of patterns, to only * specify a subset of files which should be wrapped in functions for proper * `require` semantics. + * + * `"debug"` works like `"auto"` but after bundling, it will display a warning + * containing a list of ids that have been wrapped which can be used as + * minimatch pattern for fine-tuning. + * @default "auto" */ - strictRequireSemantic?: boolean | FilterPattern; + strictRequires?: boolean | FilterPattern; /** * Sometimes you have to leave require statements unconverted. Pass an array * containing the IDs or a `id => boolean` function. @@ -93,14 +108,16 @@ interface RollupCommonJSOptions { * In most cases, where `require` calls are inside a `try-catch` clause, * they should be left unconverted as it requires an optional dependency * that may or may not be installed beside the rolled up package. - * Due to the conversion of `require` to a static `import` - the call is hoisted - * to the top of the file, outside of the `try-catch` clause. + * Due to the conversion of `require` to a static `import` - the call is + * hoisted to the top of the file, outside of the `try-catch` clause. * * - `true`: All `require` calls inside a `try` will be left unconverted. - * - `false`: All `require` calls inside a `try` will be converted as if the `try-catch` clause is not there. + * - `false`: All `require` calls inside a `try` will be converted as if the + * `try-catch` clause is not there. * - `remove`: Remove all `require` calls from inside any `try` block. * - `string[]`: Pass an array containing the IDs to left unconverted. - * - `((id: string) => boolean|'remove')`: Pass a function that control individual IDs. + * - `((id: string) => boolean|'remove')`: Pass a function that control + * individual IDs. * * @default false */ @@ -191,8 +208,8 @@ interface RollupCommonJSOptions { * Some modules contain dynamic `require` calls, or require modules that * contain circular dependencies, which are not handled well by static * imports. Including those modules as `dynamicRequireTargets` will simulate a - * CommonJS (NodeJS-like) environment for them with support for dynamic and - * circular dependencies. + * CommonJS (NodeJS-like) environment for them with support for dynamic + * dependencies. It also enables `strictRequires` for those modules. * * Note: In extreme cases, this feature may result in some paths being * rendered as absolute in the final bundle. The plugin tries to avoid