Skip to content

Commit

Permalink
fix(commonjs): support CJS modules re-exporting transpiled ESM modules (
Browse files Browse the repository at this point in the history
#1165)

* fix(commonjs): attach correct plugin meta-data to commonjs modules

* feat(commonjs): reimplement dynamic import handling

BREAKING CHANGES: requires Node 12
No longer supports require.cache

* feat(commonjs): add strictRequires option to wrap modules

* feat(commonjs): automatically wrap cyclic modules

* feat(commonjs): Infer type for unidentified modules

* feat(commonjs): make namespace callable when requiring ESM with function default

* fix(commonjs): handle relative external imports

* feat(commonjs): limit ignoreTryCatch to external requires

* feat(commonjs): auto-detect conditional requires

* feat(commonjs): add dynamicRequireRoot option

* feat(commonjs): throw for dynamic requires from outside the configured root

* refactor(commonjs): deconflict helpers only once globals are known

* feat(commonjs): expose plugin version

* fix(commonjs): do not transform "typeof exports" for mixed modules

resolves #1014

* fix(commonjs): inject module name into dynamic require function

* fix(commonjs): validate node-resolve peer version

* fix(commonjs): use correct version and add package exports

* fix(commonjs): proxy all entries to not break legacy polyfill plugins

* fix(commonjs): add heuristic to deoptimize requires after calling imported function

* fix(commonjs): make sure type changes of esm imports are tracked

BREAKING CHANGES: Requires at least rollup@2.68.0

* fix(commonjs): handle external dependencies when using the cache

* fix(commonjs): Do not change semantics when removing requires in if statements

* fix(commonjs): Allow to dynamically require an empty file

* Add a test to illustrate problematic re-exported module

This is exhibited for example in Next.js, see https://github.com/vercel/next.js/blob/5feb400aff8e7b8968174b4e339b98ce48412180/packages/next/link.js#L1 which doesn't specify __esModule but re-exports a module that does.

See https://www.runpkg.com/?next@12.1.4/link.js and https://www.runpkg.com/?next@12.1.4/dist/client/link.js for the compiled example.

* fix(commonjs): support CJS modules re-exporting ESM modules

* fix(commonjs): Warn when plugins do not pass options to resolveId

* Update snapshots post-merge

* Update tests

* Update snapshot

* Remove unnecessary fixtures

* Remove comment given other tests do exactly the same

See:
- https://github.com/rollup/plugins/blob/d637611a79a2701c359f5f2f6ffb49978070da38/packages/commonjs/test/fixtures/function/transpiled-esm-entry-named/main.js#L3
- https://github.com/rollup/plugins/blob/d637611a79a2701c359f5f2f6ffb49978070da38/packages/commonjs/test/fixtures/function/transpiled-esm-namespace-named/main.js#L5

* Update snapshots

Co-authored-by: Lukas Taegert-Atkinson <lukas.taegert-atkinson@tngtech.com>
  • Loading branch information
fwouts and lukastaegert committed Apr 24, 2022
1 parent 6277f08 commit 1e66132
Show file tree
Hide file tree
Showing 29 changed files with 462 additions and 132 deletions.
8 changes: 6 additions & 2 deletions packages/commonjs/src/transform-commonjs.js
Expand Up @@ -71,6 +71,7 @@ export default async function transformCommonjs(
let programDepth = 0;
let currentTryBlockEnd = null;
let shouldWrap = false;
let reexports = false;

const globals = new Set();
// A conditionalNode is a node for which execution is not guaranteed. If such a node is a require
Expand Down Expand Up @@ -151,8 +152,9 @@ export default async function transformCommonjs(
if (hasDefineEsmProperty(node.right)) {
shouldWrap = true;
}
} else if (defaultIsModuleExports === false) {
} else if (isRequireExpression(node.right, scope)) {
shouldWrap = true;
reexports = true;
}
}
} else if (exportName === KEY_COMPILED_ESM) {
Expand Down Expand Up @@ -444,7 +446,9 @@ export default async function transformCommonjs(
shouldWrap = !isEsModule && (shouldWrap || (uses.exports && moduleExportsAssignments.length > 0));
const detectWrappedDefault =
shouldWrap &&
(topLevelDefineCompiledEsmExpressions.length > 0 || code.indexOf('__esModule') >= 0);
(reexports ||
topLevelDefineCompiledEsmExpressions.length > 0 ||
code.indexOf('__esModule') >= 0);

if (
!(
Expand Down
@@ -0,0 +1,3 @@
module.exports = {
description: 'creates the correct exports from CJS module re-exporting a transpiled ES module',
};
@@ -0,0 +1,2 @@
Object.defineProperty(exports, '__esModule', { value: true });
exports.default = 'default';
@@ -0,0 +1,3 @@
import dep from './proxy';

t.is(dep, 'default');
@@ -0,0 +1 @@
module.exports = require('./dep');
@@ -0,0 +1,3 @@
module.exports = {
description: 'creates the correct exports from CJS module re-exporting a transpiled ES module',
};
@@ -0,0 +1,2 @@
Object.defineProperty(exports, '__esModule', { value: true });
exports.default = 'default';
@@ -0,0 +1,3 @@
import * as entry from './proxy';

t.deepEqual(entry, { default: 'default' });
@@ -0,0 +1 @@
module.exports = require('./entry');
@@ -0,0 +1,3 @@
module.exports = {
description: 'creates the correct exports from CJS module re-exporting a transpiled ES module',
};
@@ -0,0 +1,3 @@
Object.defineProperty(exports, '__esModule', { value: true });
exports.default = 'default';
exports.named = 'named';
@@ -0,0 +1,3 @@
import * as entry from './proxy';

t.deepEqual(entry, { default: 'default', named: 'named' });
@@ -0,0 +1 @@
module.exports = require('./entry');
@@ -0,0 +1,3 @@
module.exports = {
description: 'creates the correct exports from CJS module re-exporting a transpiled ES module',
};
@@ -0,0 +1,2 @@
Object.defineProperty(exports, '__esModule', { value: true });
exports.named = 'named';
@@ -0,0 +1,8 @@
import * as entry from './proxy';

t.deepEqual(entry, {
default: {
named: 'named',
},
named: 'named'
});
@@ -0,0 +1 @@
module.exports = require('./entry');
@@ -0,0 +1,3 @@
module.exports = {
description: 'creates the correct exports from CJS module re-exporting a transpiled ES module',
};
@@ -0,0 +1,3 @@
Object.defineProperty(exports, '__esModule', { value: true });
exports.named = 'named';
exports.default = 'default';
@@ -0,0 +1,4 @@
import dep, { named } from './proxy';

t.is(dep, 'default');
t.is(named, 'named');
@@ -0,0 +1 @@
module.exports = require('./dep');
@@ -0,0 +1,3 @@
module.exports = {
description: 'creates the correct exports from CJS module re-exporting a transpiled ES module',
};
@@ -0,0 +1,2 @@
Object.defineProperty(exports, '__esModule', { value: true });
exports.named = 'named';
@@ -0,0 +1,3 @@
import { named } from './proxy';

t.is(named, 'named');
@@ -0,0 +1 @@
module.exports = require('./dep');

0 comments on commit 1e66132

Please sign in to comment.