Skip to content

Commit 3c00191

Browse files
committed
feat(commonjs): make namespace callable when requiring ESM with function default (#1038)
1 parent 2aa0ac9 commit 3c00191

File tree

34 files changed

+604
-278
lines changed

34 files changed

+604
-278
lines changed

packages/commonjs/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ export default {
4242

4343
Then call `rollup` either via the [CLI](https://www.rollupjs.org/guide/en/#command-line-reference) or the [API](https://www.rollupjs.org/guide/en/#javascript-api).
4444

45+
When used together with the node-resolve plugin
46+
4547
## Options
4648

4749
### `strictRequires`
@@ -378,7 +380,7 @@ export default {
378380
format: 'iife',
379381
name: 'MyModule'
380382
},
381-
plugins: [resolve(), commonjs()]
383+
plugins: [commonjs(), resolve()]
382384
};
383385
```
384386

packages/commonjs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
},
6161
"devDependencies": {
6262
"@rollup/plugin-json": "^4.1.0",
63-
"@rollup/plugin-node-resolve": "^8.4.0",
63+
"@rollup/plugin-node-resolve": "^13.0.6",
6464
"locate-character": "^2.0.5",
6565
"require-relative": "^0.8.7",
6666
"rollup": "^2.67.3",

packages/commonjs/src/dynamic-modules.js

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,63 @@
1+
import { existsSync, readFileSync, statSync } from 'fs';
2+
import { join, resolve } from 'path';
3+
4+
import glob from 'glob';
5+
16
import { getVirtualPathForDynamicRequirePath, normalizePathSlashes } from './utils';
27

8+
function getPackageEntryPoint(dirPath) {
9+
let entryPoint = 'index.js';
10+
11+
try {
12+
if (existsSync(join(dirPath, 'package.json'))) {
13+
entryPoint =
14+
JSON.parse(readFileSync(join(dirPath, 'package.json'), { encoding: 'utf8' })).main ||
15+
entryPoint;
16+
}
17+
} catch (ignored) {
18+
// ignored
19+
}
20+
21+
return entryPoint;
22+
}
23+
24+
function isDirectory(path) {
25+
try {
26+
if (statSync(path).isDirectory()) return true;
27+
} catch (ignored) {
28+
// Nothing to do here
29+
}
30+
return false;
31+
}
32+
33+
export function getDynamicRequireModules(patterns) {
34+
const dynamicRequireModules = new Map();
35+
for (const pattern of !patterns || Array.isArray(patterns) ? patterns || [] : [patterns]) {
36+
const isNegated = pattern.startsWith('!');
37+
const modifyMap = (targetPath, resolvedPath) =>
38+
isNegated
39+
? dynamicRequireModules.delete(targetPath)
40+
: dynamicRequireModules.set(targetPath, resolvedPath);
41+
for (const path of glob.sync(isNegated ? pattern.substr(1) : pattern)) {
42+
const resolvedPath = resolve(path);
43+
const requirePath = normalizePathSlashes(resolvedPath);
44+
if (isDirectory(resolvedPath)) {
45+
const modulePath = resolve(join(resolvedPath, getPackageEntryPoint(path)));
46+
modifyMap(requirePath, modulePath);
47+
modifyMap(normalizePathSlashes(modulePath), modulePath);
48+
} else {
49+
modifyMap(requirePath, resolvedPath);
50+
}
51+
}
52+
}
53+
return dynamicRequireModules;
54+
}
55+
356
const FAILED_REQUIRE_ERROR = `throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');`;
457

5-
export function getDynamicRequireModules(
58+
export function getDynamicModuleRegistry(
659
isDynamicRequireModulesEnabled,
7-
dynamicRequireModuleSet,
60+
dynamicRequireModules,
861
commonDir,
962
ignoreDynamicRequires
1063
) {
@@ -13,16 +66,15 @@ export function getDynamicRequireModules(
1366
${FAILED_REQUIRE_ERROR}
1467
}`;
1568
}
16-
const dynamicModuleIds = [...dynamicRequireModuleSet];
17-
const dynamicModuleImports = dynamicModuleIds
69+
const dynamicModuleImports = [...dynamicRequireModules.values()]
1870
.map(
1971
(id, index) =>
2072
`import ${
2173
id.endsWith('.json') ? `json${index}` : `{ __require as require${index} }`
2274
} from ${JSON.stringify(id)};`
2375
)
2476
.join('\n');
25-
const dynamicModuleProps = dynamicModuleIds
77+
const dynamicModuleProps = [...dynamicRequireModules.keys()]
2678
.map(
2779
(id, index) =>
2880
`\t\t${JSON.stringify(

packages/commonjs/src/dynamic-require-paths.js

Lines changed: 0 additions & 46 deletions
This file was deleted.

packages/commonjs/src/generate-imports.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,11 @@ export function getRequireStringArg(node) {
6868
: node.arguments[0].quasis[0].value.cooked;
6969
}
7070

71-
export function hasDynamicModuleForPath(source, id, dynamicRequireModuleSet) {
71+
export function hasDynamicModuleForPath(source, id, dynamicRequireModules) {
7272
if (!/^(?:\.{0,2}[/\\]|[A-Za-z]:[/\\])/.test(source)) {
7373
try {
7474
const resolvedPath = normalizePathSlashes(nodeResolveSync(source, { basedir: dirname(id) }));
75-
if (dynamicRequireModuleSet.has(resolvedPath)) {
75+
if (dynamicRequireModules.has(resolvedPath)) {
7676
return true;
7777
}
7878
} catch (ex) {
@@ -85,7 +85,7 @@ export function hasDynamicModuleForPath(source, id, dynamicRequireModuleSet) {
8585

8686
for (const attemptExt of ['', '.js', '.json']) {
8787
const resolvedPath = normalizePathSlashes(resolve(dirname(id), source + attemptExt));
88-
if (dynamicRequireModuleSet.has(resolvedPath)) {
88+
if (dynamicRequireModules.has(resolvedPath)) {
8989
return true;
9090
}
9191
}
@@ -119,6 +119,7 @@ export function getRequireHandlers() {
119119
const imports = [];
120120
imports.push(`import * as ${helpersName} from "${HELPERS_ID}";`);
121121
if (usesRequire) {
122+
// TODO Lukas check where to import it from or change to usesDynamicRequire
122123
imports.push(
123124
`import { commonjsRequire as ${dynamicRequireName} } from "${DYNAMIC_MODULES_ID}";`
124125
);

packages/commonjs/src/helpers.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export const wrapId = (id, suffix) => `\0${id}${suffix}`;
33
export const unwrapId = (wrappedId, suffix) => wrappedId.slice(1, -suffix.length);
44

55
export const PROXY_SUFFIX = '?commonjs-proxy';
6+
export const WRAPPED_SUFFIX = '?commonjs-wrapped';
67
export const EXTERNAL_SUFFIX = '?commonjs-external';
78
export const EXPORTS_SUFFIX = '?commonjs-exports';
89
export const MODULE_SUFFIX = '?commonjs-module';
@@ -33,8 +34,14 @@ export function getDefaultExportFromNamespaceIfNotNamed (n) {
3334
}
3435
3536
export function getAugmentedNamespace(n) {
36-
if (n.__esModule) return n;
37-
var a = Object.defineProperty({}, '__esModule', {value: true});
37+
var f = n.default;
38+
if (typeof f == "function") {
39+
var a = function () {
40+
return f.apply(this, arguments);
41+
};
42+
a.prototype = f.prototype;
43+
} else a = {};
44+
Object.defineProperty(a, '__esModule', {value: true});
3845
Object.keys(n).forEach(function (k) {
3946
var d = Object.getOwnPropertyDescriptor(n, k);
4047
Object.defineProperty(a, k, d.get ? d : {

packages/commonjs/src/index.js

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ import getCommonDir from 'commondir';
66
import { peerDependencies } from '../package.json';
77

88
import analyzeTopLevelStatements from './analyze-top-level-statements';
9-
import { getDynamicRequireModules } from './dynamic-modules';
9+
import { getDynamicModuleRegistry, getDynamicRequireModules } from './dynamic-modules';
1010

11-
import getDynamicRequireModuleSet from './dynamic-require-paths';
1211
import {
1312
DYNAMIC_MODULES_ID,
1413
ES_IMPORT_SUFFIX,
@@ -65,10 +64,11 @@ export default function commonjs(options = {}) {
6564
getWrappedIds,
6665
isRequiredId
6766
} = getResolveRequireSourcesAndGetMeta(extensions, detectCycles);
68-
const dynamicRequireModuleSet = getDynamicRequireModuleSet(options.dynamicRequireTargets);
69-
const isDynamicRequireModulesEnabled = dynamicRequireModuleSet.size > 0;
67+
const dynamicRequireModules = getDynamicRequireModules(options.dynamicRequireTargets);
68+
const isDynamicRequireModulesEnabled = dynamicRequireModules.size > 0;
69+
// TODO Lukas do we need the CWD?
7070
const commonDir = isDynamicRequireModulesEnabled
71-
? getCommonDir(null, Array.from(dynamicRequireModuleSet).concat(process.cwd()))
71+
? getCommonDir(null, Array.from(dynamicRequireModules.keys()).concat(process.cwd()))
7272
: null;
7373

7474
const esModulesWithDefaultExport = new Set();
@@ -115,7 +115,7 @@ export default function commonjs(options = {}) {
115115
}
116116

117117
if (
118-
!dynamicRequireModuleSet.has(normalizePathSlashes(id)) &&
118+
!dynamicRequireModules.has(normalizePathSlashes(id)) &&
119119
(!(hasCjsKeywords(code, ignoreGlobal) || isRequiredId(id)) ||
120120
(isEsModule && !options.transformMixedEsModules))
121121
) {
@@ -124,7 +124,7 @@ export default function commonjs(options = {}) {
124124

125125
const needsRequireWrapper =
126126
!isEsModule &&
127-
(dynamicRequireModuleSet.has(normalizePathSlashes(id)) || strictRequiresFilter(id));
127+
(dynamicRequireModules.has(normalizePathSlashes(id)) || strictRequiresFilter(id));
128128

129129
return transformCommonjs(
130130
this.parse,
@@ -137,7 +137,7 @@ export default function commonjs(options = {}) {
137137
getIgnoreTryCatchRequireStatementMode,
138138
sourceMap,
139139
isDynamicRequireModulesEnabled,
140-
dynamicRequireModuleSet,
140+
dynamicRequireModules,
141141
commonDir,
142142
ast,
143143
getDefaultIsModuleExports(id),
@@ -150,18 +150,19 @@ export default function commonjs(options = {}) {
150150
return {
151151
name: 'commonjs',
152152

153-
options(options) {
154-
// Always sort the node-resolve plugin after the commonjs plugin as otherwise CommonJS entries
155-
// will not work with strictRequires: true
156-
const { plugins } = options;
157-
if (Array.isArray(plugins)) {
158-
const cjsIndex = plugins.findIndex((plugin) => plugin.name === 'commonjs');
159-
const nodeResolveIndex = plugins.findIndex((plugin) => plugin.name === 'node-resolve');
160-
if (nodeResolveIndex >= 0 && nodeResolveIndex < cjsIndex) {
161-
plugins.splice(cjsIndex + 1, 0, plugins[nodeResolveIndex]);
162-
plugins.splice(nodeResolveIndex, 1);
163-
}
164-
}
153+
options(rawOptions) {
154+
// We inject the resolver in the beginning so that "catch-all-resolver" like node-resolver
155+
// do not prevent our plugin from resolving entry points ot proxies.
156+
const plugins = Array.isArray(rawOptions.plugins)
157+
? rawOptions.plugins
158+
: rawOptions.plugins
159+
? [rawOptions.plugins]
160+
: [];
161+
plugins.unshift({
162+
name: 'commonjs--resolver',
163+
resolveId
164+
});
165+
return { ...rawOptions, plugins };
165166
},
166167

167168
buildStart() {
@@ -185,7 +186,6 @@ export default function commonjs(options = {}) {
185186
.join(',\n')}\n]`
186187
});
187188
} else {
188-
// TODO Lukas test
189189
this.warn({
190190
code: 'WRAPPED_IDS',
191191
ids: wrappedIds,
@@ -195,8 +195,6 @@ export default function commonjs(options = {}) {
195195
}
196196
},
197197

198-
resolveId,
199-
200198
load(id) {
201199
if (id === HELPERS_ID) {
202200
return getHelpersModule();
@@ -232,9 +230,9 @@ export default function commonjs(options = {}) {
232230
}
233231

234232
if (id === DYNAMIC_MODULES_ID) {
235-
return getDynamicRequireModules(
233+
return getDynamicModuleRegistry(
236234
isDynamicRequireModulesEnabled,
237-
dynamicRequireModuleSet,
235+
dynamicRequireModules,
238236
commonDir,
239237
ignoreDynamicRequires
240238
);

0 commit comments

Comments
 (0)