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

[v3.0] Keep dynamic imports in CommonJS output #4647

Merged
merged 4 commits into from Oct 7, 2022
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
1 change: 1 addition & 0 deletions cli/help.md
Expand Up @@ -29,6 +29,7 @@ Basic options:
--chunkFileNames <pattern> Name pattern for emitted secondary chunks
--compact Minify wrapper code
--context <variable> Specify top-level `this` value
--no-dynamicImportInCjs Write external dynamic CommonJS imports as require
--entryFileNames <pattern> Name pattern for emitted entry chunks
--environment <values> Settings passed to config file (see example)
--no-esModule Do not add __esModule property
Expand Down
1 change: 1 addition & 0 deletions docs/01-command-line-reference.md
Expand Up @@ -360,6 +360,7 @@ Many options have command line equivalents. In those cases, any arguments passed
--chunkFileNames <pattern> Name pattern for emitted secondary chunks
--compact Minify wrapper code
--context <variable> Specify top-level `this` value
--no-dynamicImportInCjs Write external dynamic CommonJS imports as require
--entryFileNames <pattern> Name pattern for emitted entry chunks
--environment <values> Settings passed to config file (see example)
--no-esModule Do not add __esModule property
Expand Down
46 changes: 46 additions & 0 deletions docs/999-big-list-of-options.md
Expand Up @@ -466,6 +466,52 @@ Type: `boolean`<br> CLI: `--compact`/`--no-compact`<br> Default: `false`

This will minify the wrapper code generated by rollup. Note that this does not affect code written by the user. This option is useful when bundling pre-minified code.

#### output.dynamicImportInCjs

Type: `boolean`<br> CLI: `--dynamicImportInCjs`/`--no-dynamicImportInCjs`<br> Default: `true`

While CommonJS output originally supported only `require(…)` to import dependencies, recent Node versions also started to support `import(…)`, which is the only way to import ES modules from CommonJS files. If this option is `true`, which is the default, Rollup will keep external dynamic imports as `import(…)` expressions in CommonJS output. Set this to `false` to rewrite dynamic imports using `require(…)` syntax.

```js
// input
import('external').then(console.log);

// cjs output with dynamicImportInCjs: true or not set
import('external').then(console.log);

// cjs output with dynamicImportInCjs: false
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(
n,
k,
d.get
? d
: {
enumerable: true,
get: function () {
return e[k];
}
}
);
}
});
}
n.default = e;
return Object.freeze(n);
}

Promise.resolve()
.then(function () {
return /*#__PURE__*/ _interopNamespaceDefault(require('external'));
})
.then(console.log);
```

#### output.entryFileNames

Type: `string | ((chunkInfo: ChunkInfo) => string)`<br> CLI: `--entryFileNames <pattern>`<br> Default: `"[name].js"`
Expand Down
9 changes: 8 additions & 1 deletion src/ast/nodes/ImportExpression.ts
@@ -1,6 +1,6 @@
import type MagicString from 'magic-string';
import ExternalModule from '../../ExternalModule';
import type Module from '../../Module';
import Module from '../../Module';
import type { GetInterop, NormalizedOutputOptions } from '../../rollup/types';
import type { PluginDriver } from '../../utils/PluginDriver';
import type { GenerateCodeSnippets } from '../../utils/generateCodeSnippets';
Expand Down Expand Up @@ -134,6 +134,7 @@ export default class ImportExpression extends NodeBase {
{
compact,
dynamicImportFunction,
dynamicImportInCjs,
format,
generatedCode: { arrowFunctions },
interop
Expand All @@ -156,6 +157,12 @@ export default class ImportExpression extends NodeBase {
const hasDynamicTarget = !this.resolution || typeof this.resolution === 'string';
switch (format) {
case 'cjs': {
if (
dynamicImportInCjs &&
(!resolution || typeof resolution === 'string' || resolution instanceof ExternalModule)
) {
return { helper: null, mechanism: null };
}
const helper = getInteropHelper(resolution, exportMode, interop);
let left = `require(`;
let right = `)`;
Expand Down
2 changes: 2 additions & 0 deletions src/rollup/types.d.ts
Expand Up @@ -613,6 +613,7 @@ export interface OutputOptions {
dir?: string;
/** @deprecated Use the "renderDynamicImport" plugin hook instead. */
dynamicImportFunction?: string;
dynamicImportInCjs?: boolean;
entryFileNames?: string | ((chunkInfo: PreRenderedChunk) => string);
esModule?: boolean | 'if-default-prop';
exports?: 'default' | 'named' | 'none' | 'auto';
Expand Down Expand Up @@ -663,6 +664,7 @@ export interface NormalizedOutputOptions {
dir: string | undefined;
/** @deprecated Use the "renderDynamicImport" plugin hook instead. */
dynamicImportFunction: string | undefined;
dynamicImportInCjs: boolean;
entryFileNames: string | ((chunkInfo: PreRenderedChunk) => string);
esModule: boolean | 'if-default-prop';
exports: 'default' | 'named' | 'none' | 'auto';
Expand Down
1 change: 1 addition & 0 deletions src/utils/options/mergeOptions.ts
Expand Up @@ -231,6 +231,7 @@ function mergeOutputOptions(
compact: getOption('compact'),
dir: getOption('dir'),
dynamicImportFunction: getOption('dynamicImportFunction'),
dynamicImportInCjs: getOption('dynamicImportInCjs'),
entryFileNames: getOption('entryFileNames'),
esModule: getOption('esModule'),
exports: getOption('exports'),
Expand Down
1 change: 1 addition & 0 deletions src/utils/options/normalizeOutputOptions.ts
Expand Up @@ -43,6 +43,7 @@ export function normalizeOutputOptions(
compact,
dir: getDir(config, file),
dynamicImportFunction: getDynamicImportFunction(config, inputOptions, format),
dynamicImportInCjs: config.dynamicImportInCjs ?? true,
entryFileNames: getEntryFileNames(config, unsetOptions),
esModule: config.esModule ?? 'if-default-prop',
exports: getExports(config, unsetOptions),
Expand Down
@@ -1,22 +1,5 @@
'use strict';

function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}

Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require(
import /* () should not break */ (
/* webpackChunkName: "chunk-name" */
'./foo.js'/*suffix*/)); });
'./foo.js'/*suffix*/);
@@ -1,20 +1,3 @@
'use strict';

function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}

Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require('./foo.js')); }).then(result => console.log(result));
import('./foo.js').then(result => console.log(result));
@@ -1,22 +1,5 @@
'use strict';

function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}

var dep = 'dep';

(function (t) { return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require(t)); }); })(dep);
import(dep);
@@ -1,20 +1,3 @@
'use strict';

function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}

Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require('./foo.js')); });
import('./foo.js');
Expand Up @@ -2,23 +2,6 @@

var dep = require('./generated-dep.js');

function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}

Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require('./ext\'ernal')); });
import('./ext\'ernal');

console.log('main', dep.value);
19 changes: 1 addition & 18 deletions test/chunking-form/samples/nested-chunks/_expected/cjs/main1.js
Expand Up @@ -2,24 +2,7 @@

var dep = require('./generated-dep.js');

function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}

console.log('main1', dep.value);

Promise.resolve().then(function () { return require('./generated-dynamic.js'); }).then(result => console.log(result));
Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require('./external.js')); }).then(result => console.log(result));
import('./external.js').then(result => console.log(result));
Expand Up @@ -2,24 +2,7 @@

var dep = require('../generated-dep.js');

function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}

console.log('main2', dep.value);

Promise.resolve().then(function () { return require('../generated-dynamic.js'); }).then(result => console.log(result));
Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require('../external.js')); }).then(result => console.log(result));
import('../external.js').then(result => console.log(result));
Expand Up @@ -5,29 +5,12 @@ require('to-indirect-relative-external');
require('direct-absolute-external');
require('to-indirect-absolute-external');

function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}

// nested
Promise.resolve().then(function () { return existing; });
Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require('./direct-relative-external')); });
Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require('to-indirect-relative-external')); });
Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require('direct-absolute-external')); });
Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require('to-indirect-absolute-external')); });
import('./direct-relative-external');
import('to-indirect-relative-external');
import('direct-absolute-external');
import('to-indirect-absolute-external');

const value = 'existing';
console.log('existing');
Expand All @@ -39,12 +22,12 @@ var existing = /*#__PURE__*/Object.freeze({

//main
Promise.resolve().then(function () { return existing; });
Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require('./direct-relative-external')); });
Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require('to-indirect-relative-external')); });
Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require('direct-absolute-external')); });
Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require('to-indirect-absolute-external')); });
import('./direct-relative-external');
import('to-indirect-relative-external');
import('direct-absolute-external');
import('to-indirect-absolute-external');

(function (t) { return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require(t)); }); })('dynamic-direct-external' + unknown);
Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require('to-dynamic-indirect-external')); });
import('dynamic-direct-external' + unknown);
import('to-dynamic-indirect-external');
Promise.resolve().then(function () { return existing; });
(function (t) { return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespaceDefault(require(t)); }); })('my' + 'replacement');
import('my' + 'replacement');
Expand Up @@ -5,7 +5,8 @@ module.exports = {
output: {
globals: { external: 'external' },
name: 'bundle',
interop: 'auto'
interop: 'auto',
dynamicImportInCjs: false
}
}
};
3 changes: 2 additions & 1 deletion test/form/samples/dynamic-import-this-arrow/_config.js
Expand Up @@ -6,7 +6,8 @@ module.exports = {
external: ['input', 'output'],
output: {
generatedCode: { arrowFunctions: true },
name: 'bundle'
name: 'bundle',
dynamicImportInCjs: false
}
}
};
3 changes: 2 additions & 1 deletion test/form/samples/dynamic-import-this-function/_config.js
Expand Up @@ -6,7 +6,8 @@ module.exports = {
external: ['input', 'output'],
output: {
generatedCode: { arrowFunctions: false },
name: 'bundle'
name: 'bundle',
dynamicImportInCjs: false
}
}
};