Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into perf/create-filter
Browse files Browse the repository at this point in the history
  • Loading branch information
shellscape committed Sep 23, 2024
2 parents c90ba79 + 0af45c2 commit 023b547
Show file tree
Hide file tree
Showing 107 changed files with 9,860 additions and 5,938 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
id: pnpm-setup
run: |
corepack enable
corepack prepare pnpm@8 --activate
corepack prepare pnpm@latest --activate
pnpm config set script-shell "/usr/bin/bash"
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,5 @@
"test/"
]
},
"packageManager": "pnpm@8.15.8+sha512.d1a029e1a447ad90bc96cd58b0fad486d2993d531856396f7babf2d83eb1823bb83c5a3d0fc18f675b2d10321d49eb161fece36fe8134aa5823ecd215feed392"
"packageManager": "pnpm@9.4.0"
}
25 changes: 25 additions & 0 deletions packages/commonjs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
# @rollup/plugin-commonjs ChangeLog

## v28.0.0

_2024-09-23_

### Breaking Changes

- chore: switch to fdir for fewer dependencies (#1741)

## v27.0.0

_2024-09-23_

### Breaking Changes

- feat!: default strictRequires to true (#1639)
- fix!: replace top-level this with exports name (#1618)

## v26.0.3

_2024-09-23_

### Updates

- chore: revert #1618 (e98927b)

## v26.0.1

_2024-06-05_
Expand Down
32 changes: 28 additions & 4 deletions packages/commonjs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ When used together with the node-resolve plugin
### `strictRequires`

Type: `"auto" | boolean | "debug" | string[]`<br>
Default: `"auto"`
Default: `true`

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 initialisation order of required modules will be different. The resultant side effects can include log statements being emitted in a different order, and some code that is dependent on the initialisation order of polyfills in require statements may not work. 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.
Historically, this plugin tried 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 initialisation order of required modules will be different. The resultant side effects can include log statements being emitted in a different order, and some code that is dependent on the initialisation order of polyfills in require statements may not work. 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. This is the safest setting and should be used if the generated code does not work correctly with `"auto"`. Note that `strictRequires: true` can have a small impact on the size and performance of generated code, but less so if the code is minified.
The default value of `true` will wrap all CommonJS files in functions which are executed when they are required for the first time, preserving NodeJS semantics. This is the safest setting and should be used if the generated code does not work correctly with `"auto"`. Note that `strictRequires: true` can have a small impact on the size and performance of generated code, but less so if the code is minified.

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 some of its dependencies, or if they are only required in a potentially "conditional" way like from within an if-statement or a function. All other CommonJS files are hoisted. This is the recommended setting for most code bases. Note that the detection of conditional requires can be subject to race conditions if there are both conditional and unconditional requires of the same file, which in edge cases may result in inconsistencies between builds. If you think this is a problem for you, you can avoid this by using any value other than `"auto"` or `"debug"`.
Setting this option to `"auto"` will only wrap CommonJS files when they are part of a CommonJS dependency cycle, e.g. an index file that is required by some of its dependencies, or if they are only required in a potentially "conditional" way like from within an if-statement or a function. All other CommonJS files are hoisted. This is the recommended setting for most code bases. Note that the detection of conditional requires can be subject to race conditions if there are both conditional and unconditional requires of the same file, which in edge cases may result in inconsistencies between builds. If you think this is a problem for you, you can avoid this by using any value other than `"auto"` or `"debug"`.

`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.

Expand Down Expand Up @@ -386,6 +386,30 @@ For these situations, you can change Rollup's behaviour either globally or per m

To change this for individual modules, you can supply a function for `requireReturnsDefault` instead. This function will then be called once for each required ES module or external dependency with the corresponding id and allows you to return different values for different modules.

## Using CommonJS files as entry points

With this plugin, you can also use CommonJS files as entry points. This means, however, that when you are bundling to an ES module, your bundle will only have a default export. If you want named exports instead, you should use an ES module entry point instead that reexports from your CommonJS entry point, e.g.

```js
// main.cjs, the CommonJS entry
exports.foo = 'foo';
exports.bar = 'bar';

// main.mjs, the ES module entry
export { foo, bar } from './main.cjs';

// rollup.config.mjs
export default {
input: 'main.mjs',
output: {
format: 'es',
file: 'bundle.mjs'
}
};
```

When bundling to CommonJS, i.e `output.format === 'cjs'`, make sure that you do not set `output.exports` to `'named'`. The default value of `'auto'` will usually work, but you can also set it explicitly to `'default'`. That makes sure that Rollup assigns the default export that was generated for your CommonJS entry point to `module.exports`, and semantics do not change.

## Using with @rollup/plugin-node-resolve

Since most CommonJS packages you are importing are probably dependencies in `node_modules`, you may need to use [@rollup/plugin-node-resolve](https://github.com/rollup/plugins/tree/master/packages/node-resolve):
Expand Down
7 changes: 4 additions & 3 deletions packages/commonjs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rollup/plugin-commonjs",
"version": "26.0.1",
"version": "28.0.0",
"publishConfig": {
"access": "public"
},
Expand Down Expand Up @@ -65,9 +65,10 @@
"@rollup/pluginutils": "^5.0.1",
"commondir": "^1.0.1",
"estree-walker": "^2.0.2",
"glob": "^10.4.1",
"fdir": "^6.1.1",
"is-reference": "1.2.1",
"magic-string": "^0.30.3"
"magic-string": "^0.30.3",
"picomatch": "^2.3.1"
},
"devDependencies": {
"@rollup/plugin-json": "^5.0.0",
Expand Down
11 changes: 8 additions & 3 deletions packages/commonjs/src/dynamic-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { join, resolve, dirname } from 'path';

import getCommonDir from 'commondir';

import { glob } from 'glob';
import { fdir } from 'fdir';

import { getVirtualPathForDynamicRequirePath, normalizePathSlashes } from './utils';

Expand Down Expand Up @@ -41,8 +41,13 @@ export function getDynamicRequireModules(patterns, dynamicRequireRoot) {
isNegated
? dynamicRequireModules.delete(targetPath)
: dynamicRequireModules.set(targetPath, resolvedPath);
for (const path of glob
.sync(isNegated ? pattern.substr(1) : pattern)
// eslint-disable-next-line new-cap
for (const path of new fdir()
.withBasePath()
.withDirs()
.glob(isNegated ? pattern.substr(1) : pattern)
.crawl()
.sync()
.sort((a, b) => a.localeCompare(b, 'en'))) {
const resolvedPath = resolve(path);
const requirePath = normalizePathSlashes(resolvedPath);
Expand Down
8 changes: 6 additions & 2 deletions packages/commonjs/src/generate-imports.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ function processRequireExpressions(
magicString
) {
const generateRequireName = getGenerateRequireName();
for (const { source, id: resolvedId, isCommonJS } of requireTargets) {
for (const { source, id: resolvedId, isCommonJS, wrappedModuleSideEffects } of requireTargets) {
const requires = requiresBySource[source];
const name = generateRequireName(requires);
let usesRequired = false;
Expand All @@ -184,7 +184,11 @@ function processRequireExpressions(
} else if (canConvertRequire) {
needsImport = true;
if (isCommonJS === IS_WRAPPED_COMMONJS) {
magicString.overwrite(node.start, node.end, `${name}()`);
magicString.overwrite(
node.start,
node.end,
`${wrappedModuleSideEffects ? '' : '/*@__PURE__*/ '}${name}()`
);
} else if (usesReturnValue) {
usesRequired = true;
magicString.overwrite(node.start, node.end, name);
Expand Down
7 changes: 7 additions & 0 deletions packages/commonjs/src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ export const isWrappedId = (id, suffix) => id.endsWith(suffix);
export const wrapId = (id, suffix) => `\0${id}${suffix}`;
export const unwrapId = (wrappedId, suffix) => wrappedId.slice(1, -suffix.length);

// A proxy module when a module is required from non-wrapped CommonJS. Is different for ESM and CommonJS requires.
export const PROXY_SUFFIX = '?commonjs-proxy';
// Indicates that a required module is wrapped commonjs and needs special handling.
export const WRAPPED_SUFFIX = '?commonjs-wrapped';
// Indicates that a required module is external
export const EXTERNAL_SUFFIX = '?commonjs-external';
// A helper module that contains the exports object of a module
export const EXPORTS_SUFFIX = '?commonjs-exports';
// A helper module that contains the module object of a module, e.g. when module.exports is reassigned
export const MODULE_SUFFIX = '?commonjs-module';
// A special proxy for CommonJS entry points
export const ENTRY_SUFFIX = '?commonjs-entry';
// A proxy when wrapped ESM is required from CommonJS
export const ES_IMPORT_SUFFIX = '?commonjs-es-import';

export const DYNAMIC_MODULES_ID = '\0commonjs-dynamic-modules';
Expand Down
10 changes: 7 additions & 3 deletions packages/commonjs/src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { extname, relative, resolve, dirname } from 'path';
import { dirname, extname, relative, resolve } from 'path';

import { createFilter } from '@rollup/pluginutils';

Expand Down Expand Up @@ -239,7 +239,7 @@ export default function commonjs(options = {}) {
}
},

load(id) {
async load(id) {
if (id === HELPERS_ID) {
return getHelpersModule();
}
Expand Down Expand Up @@ -285,7 +285,11 @@ export default function commonjs(options = {}) {

if (isWrappedId(id, ES_IMPORT_SUFFIX)) {
const actualId = unwrapId(id, ES_IMPORT_SUFFIX);
return getEsImportProxy(actualId, getDefaultIsModuleExports(actualId));
return getEsImportProxy(
actualId,
getDefaultIsModuleExports(actualId),
(await this.load({ id: actualId })).moduleSideEffects
);
}

if (id === DYNAMIC_MODULES_ID) {
Expand Down
10 changes: 6 additions & 4 deletions packages/commonjs/src/proxies.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,26 +57,28 @@ export function getEntryProxy(id, defaultIsModuleExports, getModuleInfo, shebang
}
return shebang + code;
}
const result = getEsImportProxy(id, defaultIsModuleExports);
const result = getEsImportProxy(id, defaultIsModuleExports, true);
return {
...result,
code: shebang + result.code
};
}

export function getEsImportProxy(id, defaultIsModuleExports) {
export function getEsImportProxy(id, defaultIsModuleExports, moduleSideEffects) {
const name = getName(id);
const exportsName = `${name}Exports`;
const requireModule = `require${capitalize(name)}`;
let code =
`import { getDefaultExportFromCjs } from "${HELPERS_ID}";\n` +
`import { __require as ${requireModule} } from ${JSON.stringify(id)};\n` +
`var ${exportsName} = ${requireModule}();\n` +
`var ${exportsName} = ${moduleSideEffects ? '' : '/*@__PURE__*/ '}${requireModule}();\n` +
`export { ${exportsName} as __moduleExports };`;
if (defaultIsModuleExports === true) {
code += `\nexport { ${exportsName} as default };`;
} else if (defaultIsModuleExports === false) {
code += `\nexport default ${exportsName}.default;`;
} else {
code += `export default /*@__PURE__*/getDefaultExportFromCjs(${exportsName});`;
code += `\nexport default /*@__PURE__*/getDefaultExportFromCjs(${exportsName});`;
}
return {
code,
Expand Down
6 changes: 1 addition & 5 deletions packages/commonjs/src/resolve-id.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,7 @@ export default function getResolveId(extensions, isPossibleCjsId) {
// All logic below is specific to ES imports.
// Also, if we do not skip this logic for requires that are resolved while
// transforming a commonjs file, it can easily lead to deadlocks.
if (
customOptions &&
customOptions['node-resolve'] &&
customOptions['node-resolve'].isRequire
) {
if (customOptions?.['node-resolve']?.isRequire) {
return null;
}
const currentlyResolvingForParent = currentlyResolving.get(importer);
Expand Down
7 changes: 4 additions & 3 deletions packages/commonjs/src/resolve-require-sources.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,13 +192,14 @@ export function getRequireResolver(extensions, detectCyclesAndConditional, curre
// eslint-disable-next-line no-multi-assign
const isCommonJS = (parentMeta.isRequiredCommonJS[dependencyId] =
getTypeForFullyAnalyzedModule(dependencyId));
const isWrappedCommonJS = isCommonJS === IS_WRAPPED_COMMONJS;
fullyAnalyzedModules[dependencyId] = true;
return {
wrappedModuleSideEffects:
isWrappedCommonJS && rollupContext.getModuleInfo(dependencyId).moduleSideEffects,
source: sources[index].source,
id: allowProxy
? isCommonJS === IS_WRAPPED_COMMONJS
? wrapId(dependencyId, WRAPPED_SUFFIX)
: wrapId(dependencyId, PROXY_SUFFIX)
? wrapId(dependencyId, isWrappedCommonJS ? WRAPPED_SUFFIX : PROXY_SUFFIX)
: dependencyId,
isCommonJS
};
Expand Down
8 changes: 7 additions & 1 deletion packages/commonjs/src/transform-commonjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export default async function transformCommonjs(
const topLevelAssignments = new Set();
const topLevelDefineCompiledEsmExpressions = [];
const replacedGlobal = [];
const replacedThis = [];
const replacedDynamicRequires = [];
const importedVariables = new Set();
const indentExclusionRanges = [];
Expand Down Expand Up @@ -369,7 +370,7 @@ export default async function transformCommonjs(
if (lexicalDepth === 0 && !classBodyDepth) {
uses.global = true;
if (!ignoreGlobal) {
replacedGlobal.push(node);
replacedThis.push(node);
}
}
return;
Expand Down Expand Up @@ -444,6 +445,11 @@ export default async function transformCommonjs(
storeName: true
});
}
for (const node of replacedThis) {
magicString.overwrite(node.start, node.end, exportsName, {
storeName: true
});
}
for (const node of replacedDynamicRequires) {
magicString.overwrite(
node.start,
Expand Down
4 changes: 2 additions & 2 deletions packages/commonjs/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ export function capitalize(name) {

export function getStrictRequiresFilter({ strictRequires }) {
switch (strictRequires) {
case true:
return { strictRequiresFilter: () => true, detectCyclesAndConditional: false };
// eslint-disable-next-line no-undefined
case undefined:
case true:
return { strictRequiresFilter: () => true, detectCyclesAndConditional: false };
case 'auto':
case 'debug':
case null:
Expand Down
17 changes: 12 additions & 5 deletions packages/commonjs/test/fixtures/form/async-function/output.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import * as commonjsHelpers from "_commonjsHelpers.js";

var input = async function () {
// TODO
};
var input;
var hasRequiredInput;

export default /*@__PURE__*/commonjsHelpers.getDefaultExportFromCjs(input);
export { input as __moduleExports };
function requireInput () {
if (hasRequiredInput) return input;
hasRequiredInput = 1;
input = async function () {
// TODO
};
return input;
}

export { requireInput as __require };
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import * as commonjsHelpers from "_commonjsHelpers.js";
import { __exports as input } from "\u0000fixtures/form/compiled-esm-assign-exports/input.js?commonjs-exports";

input.__esModule = true;
var _default = input.default = 'x';
var foo = input.foo = 'foo';
var hasRequiredInput;

export { input as __moduleExports, foo, _default as default };
function requireInput () {
if (hasRequiredInput) return input;
hasRequiredInput = 1;
input.__esModule = true;
input.default = 'x';
input.foo = 'foo';
return input;
}

export { requireInput as __require };
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import * as commonjsHelpers from "_commonjsHelpers.js";
import { __exports as input } from "\u0000fixtures/form/compiled-esm-assign-module/input.js?commonjs-exports";

input.__esModule = true;
var _default = input.default = 'x';
var foo = input.foo = 'foo';
var hasRequiredInput;

export { input as __moduleExports, foo, _default as default };
function requireInput () {
if (hasRequiredInput) return input;
hasRequiredInput = 1;
input.__esModule = true;
input.default = 'x';
input.foo = 'foo';
return input;
}

export { requireInput as __require };
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import * as commonjsHelpers from "_commonjsHelpers.js";
import { __exports as input } from "\u0000fixtures/form/compiled-esm-deconflict/input.js?commonjs-exports";

Object.defineProperty(input, '__esModule', { value: true });
var foo_1 = input.foo = 'bar';
var hasRequiredInput;

const foo = 'also bar';
function requireInput () {
if (hasRequiredInput) return input;
hasRequiredInput = 1;
Object.defineProperty(input, '__esModule', { value: true });
input.foo = 'bar';

export { input as __moduleExports, foo_1 as foo, input as default };
const foo = 'also bar';
return input;
}

export { requireInput as __require };
Loading

0 comments on commit 023b547

Please sign in to comment.