Skip to content

Commit 7c7b4e4

Browse files
committed
feat(commonjs): Generate simpler code when replacing module.exports
1 parent 775aa6a commit 7c7b4e4

File tree

31 files changed

+458
-704
lines changed

31 files changed

+458
-704
lines changed

packages/commonjs/src/generate-exports.js

+85-62
Original file line numberDiff line numberDiff line change
@@ -21,81 +21,104 @@ export function rewriteExportsAndGetExportsBlock(
2121
code,
2222
uses,
2323
HELPERS_NAME,
24-
id
24+
id,
25+
replacesModuleExports
2526
) {
26-
const exportDeclarations = [
27-
`export { exports as __moduleExports } from ${JSON.stringify(wrapId(id, MODULE_SUFFIX))}`
28-
];
29-
const moduleExportsPropertyAssignments = [];
27+
const exportDeclarations = [];
3028

31-
if (wrapped) {
32-
if (defineCompiledEsmExpressions.length > 0 || code.indexOf('__esModule') >= 0) {
33-
// eslint-disable-next-line no-param-reassign
34-
uses.commonjsHelpers = true;
35-
exportDeclarations.push(
36-
`export default /*@__PURE__*/${HELPERS_NAME}.getDefaultExportFromCjs(${moduleName}.exports);`
37-
);
29+
// TODO Lukas maybe introduce an export mode with
30+
// - 'replace'
31+
// - 'wrapped'
32+
// - 'module'
33+
// - 'exports'
34+
// then consider extracting parts that "getExportDeclarations"
35+
if (replacesModuleExports) {
36+
if (topLevelModuleExportsAssignments.length > 0) {
37+
for (const { left } of topLevelModuleExportsAssignments) {
38+
magicString.overwrite(left.start, left.end, `var ${moduleName}`);
39+
}
3840
} else {
39-
exportDeclarations.push(`export default ${moduleName}.exports;`);
41+
exportDeclarations.unshift(`var ${moduleName} = {};`);
4042
}
43+
exportDeclarations.push(
44+
`export { ${moduleName} as __moduleExports };`,
45+
`export { ${moduleName} as default };`
46+
);
4147
} else {
42-
let deconflictedDefaultExportName;
48+
exportDeclarations.push(
49+
`export { exports as __moduleExports } from ${JSON.stringify(wrapId(id, MODULE_SUFFIX))}`
50+
);
51+
if (wrapped) {
52+
if (defineCompiledEsmExpressions.length > 0 || code.indexOf('__esModule') >= 0) {
53+
// eslint-disable-next-line no-param-reassign
54+
uses.commonjsHelpers = true;
55+
exportDeclarations.push(
56+
`export default /*@__PURE__*/${HELPERS_NAME}.getDefaultExportFromCjs(${moduleName}.exports);`
57+
);
58+
} else {
59+
exportDeclarations.push(`export default ${moduleName}.exports;`);
60+
}
61+
} else {
62+
let deconflictedDefaultExportName;
4363

44-
// Collect and rewrite module.exports assignments
45-
for (const { left } of topLevelModuleExportsAssignments) {
46-
magicString.overwrite(left.start, left.end, `${moduleName}.exports`);
47-
}
64+
// TODO Lukas if we have an assignment to module.exports and assignments to exports.foo or usages of exports we need to wrap
65+
// TODO Lukas remove?
66+
// Collect and rewrite module.exports assignments
67+
for (const { left } of topLevelModuleExportsAssignments) {
68+
magicString.overwrite(left.start, left.end, `${moduleName}.exports`);
69+
}
4870

49-
// Collect and rewrite named exports
50-
for (const [exportName, node] of topLevelExportsAssignmentsByName) {
51-
const deconflicted = deconflict(exportName);
52-
magicString.overwrite(node.start, node.left.end, `var ${deconflicted}`);
71+
// Collect and rewrite named exports
72+
for (const [exportName, node] of topLevelExportsAssignmentsByName) {
73+
const deconflicted = deconflict(exportName);
74+
magicString.overwrite(node.start, node.left.end, `var ${deconflicted}`);
5375

54-
if (exportName === 'default') {
55-
deconflictedDefaultExportName = deconflicted;
56-
} else {
57-
exportDeclarations.push(
58-
exportName === deconflicted
59-
? `export { ${exportName} };`
60-
: `export { ${deconflicted} as ${exportName} };`
76+
if (exportName === 'default') {
77+
deconflictedDefaultExportName = deconflicted;
78+
} else {
79+
exportDeclarations.push(
80+
exportName === deconflicted
81+
? `export { ${exportName} };`
82+
: `export { ${deconflicted} as ${exportName} };`
83+
);
84+
}
85+
86+
magicString.appendLeft(
87+
code[node.end] === ';' ? node.end + 1 : node.end,
88+
`\n${moduleName}.exports.${exportName} = ${deconflicted};`
6189
);
6290
}
6391

64-
magicString.appendLeft(
65-
code[node.end] === ';' ? node.end + 1 : node.end,
66-
`\n${moduleName}.exports.${exportName} = ${deconflicted};`
67-
);
68-
}
69-
70-
// Collect and rewrite exports.__esModule assignments
71-
let isRestorableCompiledEsm = false;
72-
for (const expression of defineCompiledEsmExpressions) {
73-
isRestorableCompiledEsm = true;
74-
const moduleExportsExpression =
75-
expression.type === 'CallExpression' ? expression.arguments[0] : expression.left.object;
76-
magicString.overwrite(
77-
moduleExportsExpression.start,
78-
moduleExportsExpression.end,
79-
`${moduleName}.exports`
80-
);
81-
}
92+
// Collect and rewrite exports.__esModule assignments
93+
let isRestorableCompiledEsm = false;
94+
for (const expression of defineCompiledEsmExpressions) {
95+
isRestorableCompiledEsm = true;
96+
const moduleExportsExpression =
97+
expression.type === 'CallExpression' ? expression.arguments[0] : expression.left.object;
98+
magicString.overwrite(
99+
moduleExportsExpression.start,
100+
moduleExportsExpression.end,
101+
`${moduleName}.exports`
102+
);
103+
}
82104

83-
if (isRestorableCompiledEsm) {
84-
exportDeclarations.push(
85-
deconflictedDefaultExportName
86-
? `export {${deconflictedDefaultExportName} as default};`
87-
: `export default ${moduleName}.exports;`
88-
);
89-
} else if (deconflictedDefaultExportName && code.indexOf('__esModule') >= 0) {
90-
// eslint-disable-next-line no-param-reassign
91-
uses.commonjsHelpers = true;
92-
exportDeclarations.push(
93-
`export default /*@__PURE__*/${HELPERS_NAME}.getDefaultExportFromCjs(${moduleName}.exports);`
94-
);
95-
} else {
96-
exportDeclarations.push(`export default ${moduleName}.exports;`);
105+
if (isRestorableCompiledEsm) {
106+
exportDeclarations.push(
107+
deconflictedDefaultExportName
108+
? `export {${deconflictedDefaultExportName} as default};`
109+
: `export default ${moduleName}.exports;`
110+
);
111+
} else if (deconflictedDefaultExportName && code.indexOf('__esModule') >= 0) {
112+
// eslint-disable-next-line no-param-reassign
113+
uses.commonjsHelpers = true;
114+
exportDeclarations.push(
115+
`export default /*@__PURE__*/${HELPERS_NAME}.getDefaultExportFromCjs(${moduleName}.exports);`
116+
);
117+
} else {
118+
exportDeclarations.push(`export default ${moduleName}.exports;`);
119+
}
97120
}
98121
}
99122

100-
return `\n\n${exportDeclarations.concat(moduleExportsPropertyAssignments).join('\n')}`;
123+
return `\n\n${exportDeclarations.join('\n')}`;
101124
}

packages/commonjs/src/generate-imports.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ export function getRequireHandlers() {
124124
helpersNameIfUsed,
125125
dynamicRegisterSources,
126126
moduleName,
127-
id
127+
id,
128+
replacesModuleExports
128129
) {
129130
const removedDeclarators = getDeclaratorsReplacedByImportsAndSetImportNames(
130131
topLevelRequireDeclarators,
@@ -141,9 +142,15 @@ export function getRequireHandlers() {
141142
? [`import * as ${helpersNameIfUsed} from "${HELPERS_ID}";`]
142143
: []
143144
)
144-
.concat([
145-
`import { __module as ${moduleName} } from ${JSON.stringify(wrapId(id, MODULE_SUFFIX))}`
146-
])
145+
.concat(
146+
replacesModuleExports
147+
? []
148+
: [
149+
`import { __module as ${moduleName} } from ${JSON.stringify(
150+
wrapId(id, MODULE_SUFFIX)
151+
)}`
152+
]
153+
)
147154
.concat(
148155
// dynamic registers first, as the may be required in the other modules
149156
[...dynamicRegisterSources].map(

packages/commonjs/src/index.js

+6-11
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
PROXY_SUFFIX,
2525
unwrapId
2626
} from './helpers';
27-
import { setIsCjsPromise } from './is-cjs';
27+
import { setCommonJSMetaPromise } from './is-cjs';
2828
import { hasCjsKeywords } from './parse';
2929
import {
3030
getDynamicJsonProxy,
@@ -141,14 +141,12 @@ export default function commonjs(options = {}) {
141141

142142
// TODO Lukas in Rollup, ensure synthetic namespace is only rendered when needed
143143
// TODO Lukas
144+
// - Use foo?exports instead of foo?module if there are no assignments to module.exports
144145
// - Only wrap if
145146
// - there is an assignment to module.exports (also check destructuring) or
146147
// - unchecked usages of module or
147148
// - direct eassignment to exports (also check destructuring)
148-
// - Use foo?exports instead of foo?module if there are no assignments to module.exports
149149
// (also check destructring)
150-
// - Do not use foo?module and do not wrap if there are only direct top-level module.exports
151-
// assignments and no exports property assignments
152150
load(id) {
153151
if (id === HELPERS_ID) {
154152
return getHelpersModule(isDynamicRequireModulesEnabled);
@@ -234,14 +232,11 @@ export default function commonjs(options = {}) {
234232
},
235233

236234
moduleParsed({ id, meta: { commonjs: commonjsMeta } }) {
237-
if (commonjsMeta) {
238-
const isCjs = commonjsMeta.isCommonJS;
239-
if (isCjs != null) {
240-
setIsCjsPromise(id, isCjs);
241-
return;
242-
}
235+
if (commonjsMeta && commonjsMeta.isCommonJS != null) {
236+
setCommonJSMetaPromise(id, commonjsMeta);
237+
return;
243238
}
244-
setIsCjsPromise(id, null);
239+
setCommonJSMetaPromise(id, null);
245240
}
246241
};
247242
}

packages/commonjs/src/is-cjs.js

+14-14
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
1-
const isCjsPromises = new Map();
1+
const commonJSMetaPromises = new Map();
22

3-
export function getIsCjsPromise(id) {
4-
let isCjsPromise = isCjsPromises.get(id);
5-
if (isCjsPromise) return isCjsPromise.promise;
3+
export function getCommonJSMetaPromise(id) {
4+
let commonJSMetaPromise = commonJSMetaPromises.get(id);
5+
if (commonJSMetaPromise) return commonJSMetaPromise.promise;
66

77
const promise = new Promise((resolve) => {
8-
isCjsPromise = {
8+
commonJSMetaPromise = {
99
resolve,
1010
promise: null
1111
};
12-
isCjsPromises.set(id, isCjsPromise);
12+
commonJSMetaPromises.set(id, commonJSMetaPromise);
1313
});
14-
isCjsPromise.promise = promise;
14+
commonJSMetaPromise.promise = promise;
1515

1616
return promise;
1717
}
1818

19-
export function setIsCjsPromise(id, resolution) {
20-
const isCjsPromise = isCjsPromises.get(id);
21-
if (isCjsPromise) {
22-
if (isCjsPromise.resolve) {
23-
isCjsPromise.resolve(resolution);
24-
isCjsPromise.resolve = null;
19+
export function setCommonJSMetaPromise(id, commonjsMeta) {
20+
const commonJSMetaPromise = commonJSMetaPromises.get(id);
21+
if (commonJSMetaPromise) {
22+
if (commonJSMetaPromise.resolve) {
23+
commonJSMetaPromise.resolve(commonjsMeta);
24+
commonJSMetaPromise.resolve = null;
2525
}
2626
} else {
27-
isCjsPromises.set(id, { promise: Promise.resolve(resolution), resolve: null });
27+
commonJSMetaPromises.set(id, { promise: Promise.resolve(commonjsMeta), resolve: null });
2828
}
2929
}

packages/commonjs/src/proxies.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { readFileSync } from 'fs';
22

3-
import { DYNAMIC_JSON_PREFIX, MODULE_SUFFIX, HELPERS_ID, wrapId } from './helpers';
4-
import { getIsCjsPromise } from './is-cjs';
3+
import { DYNAMIC_JSON_PREFIX, HELPERS_ID } from './helpers';
4+
import { getCommonJSMetaPromise } from './is-cjs';
55
import { getName, getVirtualPathForDynamicRequirePath, normalizePathSlashes } from './utils';
66

77
// e.g. id === "commonjsHelpers?commonjsRegister"
@@ -49,13 +49,13 @@ export async function getStaticRequireProxy(
4949
esModulesWithNamedExports
5050
) {
5151
const name = getName(id);
52-
const isCjs = await getIsCjsPromise(id);
53-
if (isCjs) {
54-
return `export { exports as default } from ${JSON.stringify(wrapId(id, MODULE_SUFFIX))};`;
55-
} else if (isCjs === null) {
52+
const commonjsMeta = await getCommonJSMetaPromise(id);
53+
if (commonjsMeta && commonjsMeta.isCommonJS) {
54+
return `export { __moduleExports as default } from ${JSON.stringify(id)};`;
55+
} else if (commonjsMeta === null) {
5656
return getUnknownRequireProxy(id, requireReturnsDefault);
5757
} else if (!requireReturnsDefault) {
58-
return `import {getAugmentedNamespace} from "${HELPERS_ID}"; import * as ${name} from ${JSON.stringify(
58+
return `import { getAugmentedNamespace } from "${HELPERS_ID}"; import * as ${name} from ${JSON.stringify(
5959
id
6060
)}; export default /*@__PURE__*/getAugmentedNamespace(${name});`;
6161
} else if (
@@ -66,5 +66,5 @@ export async function getStaticRequireProxy(
6666
) {
6767
return `import * as ${name} from ${JSON.stringify(id)}; export default ${name};`;
6868
}
69-
return `export {default} from ${JSON.stringify(id)};`;
69+
return `export { default } from ${JSON.stringify(id)};`;
7070
}

packages/commonjs/src/transform-commonjs.js

+20-12
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,24 @@ export default function transformCommonjs(
349349
magicString.remove(0, commentEnd).trim();
350350
}
351351

352+
// TODO Lukas test all cases
353+
const replacesModuleExports =
354+
!shouldWrap &&
355+
topLevelExportsAssignmentsByName.size === 0 &&
356+
defineCompiledEsmExpressions.length === 0;
357+
358+
const importBlock = rewriteRequireExpressionsAndGetImportBlock(
359+
magicString,
360+
topLevelDeclarations,
361+
topLevelRequireDeclarators,
362+
reassignedNames,
363+
uses.commonjsHelpers && HELPERS_NAME,
364+
dynamicRegisterSources,
365+
moduleName,
366+
id,
367+
replacesModuleExports
368+
);
369+
352370
const exportBlock = isEsModule
353371
? ''
354372
: rewriteExportsAndGetExportsBlock(
@@ -362,20 +380,10 @@ export default function transformCommonjs(
362380
code,
363381
uses,
364382
HELPERS_NAME,
365-
id
383+
id,
384+
replacesModuleExports
366385
);
367386

368-
const importBlock = rewriteRequireExpressionsAndGetImportBlock(
369-
magicString,
370-
topLevelDeclarations,
371-
topLevelRequireDeclarators,
372-
reassignedNames,
373-
uses.commonjsHelpers && HELPERS_NAME,
374-
dynamicRegisterSources,
375-
moduleName,
376-
id
377-
);
378-
379387
if (shouldWrap) {
380388
wrapCode(magicString, uses, moduleName, HELPERS_NAME, virtualDynamicRequirePath);
381389
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { __module as input } from "\u0000fixtures/form/async-function/input.js?commonjs-module"
2-
3-
input.exports = async function () {
1+
var input = async function () {
42
// TODO
53
};
64

7-
export { exports as __moduleExports } from "\u0000fixtures/form/async-function/input.js?commonjs-module"
8-
export default input.exports;
5+
export { input as __moduleExports };
6+
export { input as default };
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { __module as input } from "\u0000fixtures/form/constant-template-literal/input.js?commonjs-module"
21
import "\u0000tape?commonjs-require";
32
import foo from "\u0000tape?commonjs-proxy";
43

54
console.log(foo);
65

7-
export { exports as __moduleExports } from "\u0000fixtures/form/constant-template-literal/input.js?commonjs-module"
8-
export default input.exports;
6+
var input = {};
7+
export { input as __moduleExports };
8+
export { input as default };

0 commit comments

Comments
 (0)