@knighted/module 1.0.0-rc.5 → 1.0.0-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -3
- package/dist/cjs/format.cjs +212 -11
- package/dist/cjs/formatters/identifier.cjs +3 -2
- package/dist/cjs/formatters/memberExpression.cjs +27 -5
- package/dist/cjs/memberExpression.d.cts +1 -1
- package/dist/cjs/module.cjs +1 -0
- package/dist/cjs/types.d.cts +10 -2
- package/dist/cjs/utils/exports.cjs +33 -5
- package/dist/format.js +212 -11
- package/dist/formatters/identifier.js +3 -2
- package/dist/formatters/memberExpression.js +27 -5
- package/dist/memberExpression.d.cts +1 -1
- package/dist/memberExpression.d.ts +1 -1
- package/dist/module.js +1 -0
- package/dist/src/formatters/identifier.d.ts +2 -1
- package/dist/src/formatters/memberExpression.d.ts +1 -1
- package/dist/src/types.d.ts +10 -2
- package/dist/types.d.cts +10 -2
- package/dist/types.d.ts +10 -2
- package/dist/utils/exports.js +33 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,7 +99,7 @@ invoked directly by node
|
|
|
99
99
|
type ModuleOptions = {
|
|
100
100
|
target: 'module' | 'commonjs'
|
|
101
101
|
sourceType?: 'auto' | 'module' | 'commonjs'
|
|
102
|
-
transformSyntax?: boolean
|
|
102
|
+
transformSyntax?: boolean | 'globals-only'
|
|
103
103
|
liveBindings?: 'strict' | 'loose' | 'off'
|
|
104
104
|
appendJsExtension?: 'off' | 'relative-only' | 'all'
|
|
105
105
|
appendDirectoryIndex?: string | false
|
|
@@ -127,7 +127,7 @@ type ModuleOptions = {
|
|
|
127
127
|
Behavior notes (defaults in parentheses)
|
|
128
128
|
|
|
129
129
|
- `target` (`commonjs`): output module system.
|
|
130
|
-
- `transformSyntax` (true): enable/disable the ESM↔CJS lowering pass.
|
|
130
|
+
- `transformSyntax` (true): enable/disable the ESM↔CJS lowering pass; set to `'globals-only'` to rewrite module globals (`import.meta.*`, `__dirname`, `__filename`, `require.main` shims) while leaving import/export syntax untouched. In `'globals-only'`, no helpers are injected (e.g., `__requireResolve`), `require.resolve` rewrites to `import.meta.resolve`, and `idiomaticExports` is skipped. See [globals-only](#globals-only-scope).
|
|
131
131
|
- `liveBindings` (`strict`): getter-based live bindings, or snapshot (`loose`/`off`).
|
|
132
132
|
- `appendJsExtension` (`relative-only` when targeting ESM): append `.js` to relative specifiers; never touches bare specifiers.
|
|
133
133
|
- `appendDirectoryIndex` (`index.js`): when a relative specifier ends with a slash, append this index filename (set `false` to disable).
|
|
@@ -151,6 +151,13 @@ See [docs/esm-to-cjs.md](docs/esm-to-cjs.md) for deeper notes on live bindings,
|
|
|
151
151
|
> [!NOTE]
|
|
152
152
|
> Known limitations: `with` and unshadowed `eval` are rejected when raising CJS to ESM because the rewrite would be unsound; bare specifiers are not rewritten—only relative specifiers participate in `rewriteSpecifier`.
|
|
153
153
|
|
|
154
|
+
### Globals-only scope
|
|
155
|
+
|
|
156
|
+
- Rewrites module globals (`import.meta.*`, `__dirname`, `__filename`, `require.main` shims) for the target side.
|
|
157
|
+
- Optional specifier rewrites still run (`rewriteSpecifier`, `appendJsExtension`, `appendDirectoryIndex`).
|
|
158
|
+
- Leaves imports/exports and interop untouched (no export bag, no idiomaticExports, no live-binding synthesis, no helpers like `__requireResolve`).
|
|
159
|
+
- CJS→ESM: `require.resolve` maps to `import.meta.resolve` (URL return, ESM resolver) and may differ from CJS resolution. ESM→CJS: `import.meta` maps to CJS globals; no import lowering.
|
|
160
|
+
|
|
154
161
|
### Diagnostics callback example
|
|
155
162
|
|
|
156
163
|
Pass a `diagnostics` callback to surface CJS→ESM edge cases (mixed `module.exports`/`exports`, top-level `return`, legacy `require.cache`/`require.extensions`, live-binding reassignments, string-literal export names):
|
|
@@ -183,7 +190,7 @@ console.log(diagnostics)
|
|
|
183
190
|
|
|
184
191
|
## Pre-`tsc` transforms for TypeScript diagnostics
|
|
185
192
|
|
|
186
|
-
TypeScript reports asymmetric module-global errors (e.g., `import.meta` in CJS, `__dirname` in ESM) as tracked in [microsoft/TypeScript#58658](https://github.com/microsoft/TypeScript/issues/58658). You can mitigate this by running `@knighted/module` **before** `tsc` so the checker sees already-rewritten sources.
|
|
193
|
+
TypeScript reports asymmetric module-global errors (e.g., `import.meta` in CJS, `__dirname` in ESM) as tracked in [microsoft/TypeScript#58658](https://github.com/microsoft/TypeScript/issues/58658). You can mitigate this by running `@knighted/module` **before** `tsc` so the checker sees already-rewritten sources. For a specifier + globals-only pass that leaves import/export syntax for `tsc`, set `transformSyntax: 'globals-only'`.
|
|
187
194
|
|
|
188
195
|
Minimal flow:
|
|
189
196
|
|
package/dist/cjs/format.cjs
CHANGED
|
@@ -16,6 +16,36 @@ var _identifier2 = require("#helpers/identifier.js");
|
|
|
16
16
|
var _walk = require("#walk");
|
|
17
17
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
18
18
|
const isValidIdent = name => /^[$A-Z_a-z][$\w]*$/.test(name);
|
|
19
|
+
const expressionHasRequireCall = (node, shadowed) => {
|
|
20
|
+
let found = false;
|
|
21
|
+
const walkNode = n => {
|
|
22
|
+
if (!n || found) return;
|
|
23
|
+
if (n.type === 'CallExpression' && n.callee?.type === 'Identifier' && n.callee.name === 'require' && !shadowed.has('require')) {
|
|
24
|
+
found = true;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (n.type === 'CallExpression' && n.callee?.type === 'MemberExpression' && n.callee.object?.type === 'Identifier' && n.callee.object.name === 'require' && !shadowed.has('require')) {
|
|
28
|
+
found = true;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const keys = Object.keys(n);
|
|
32
|
+
for (const key of keys) {
|
|
33
|
+
const value = n[key];
|
|
34
|
+
if (!value) continue;
|
|
35
|
+
if (Array.isArray(value)) {
|
|
36
|
+
for (const item of value) {
|
|
37
|
+
if (item && typeof item === 'object') walkNode(item);
|
|
38
|
+
if (found) return;
|
|
39
|
+
}
|
|
40
|
+
} else if (value && typeof value === 'object') {
|
|
41
|
+
walkNode(value);
|
|
42
|
+
if (found) return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
walkNode(node);
|
|
47
|
+
return found;
|
|
48
|
+
};
|
|
19
49
|
const exportAssignment = (name, expr, live) => {
|
|
20
50
|
const prop = isValidIdent(name) ? `.${name}` : `[${JSON.stringify(name)}]`;
|
|
21
51
|
if (live === 'strict') {
|
|
@@ -397,14 +427,20 @@ const format = async (src, ast, opts) => {
|
|
|
397
427
|
loc
|
|
398
428
|
});
|
|
399
429
|
};
|
|
430
|
+
const transformMode = opts.transformSyntax;
|
|
431
|
+
const fullTransform = transformMode === true;
|
|
400
432
|
const moduleIdentifiers = await (0, _identifiers.collectModuleIdentifiers)(ast.program);
|
|
401
433
|
const shadowedBindings = new Set([...moduleIdentifiers.entries()].filter(([, meta]) => meta.declare.length > 0).map(([name]) => name));
|
|
402
|
-
if (opts.target === 'module' &&
|
|
434
|
+
if (opts.target === 'module' && fullTransform) {
|
|
403
435
|
if (shadowedBindings.has('module') || shadowedBindings.has('exports')) {
|
|
404
436
|
throw new Error('Cannot transform to ESM: module or exports is shadowed in module scope.');
|
|
405
437
|
}
|
|
406
438
|
}
|
|
407
439
|
const exportTable = opts.target === 'module' ? await (0, _exports.collectCjsExports)(ast.program) : null;
|
|
440
|
+
const idiomaticMode = opts.target === 'module' && fullTransform ? opts.idiomaticExports ?? 'safe' : 'off';
|
|
441
|
+
let useExportsBag = fullTransform;
|
|
442
|
+
let idiomaticPlan = null;
|
|
443
|
+
let idiomaticFallbackReason;
|
|
408
444
|
if (opts.target === 'module' && exportTable) {
|
|
409
445
|
const hasExportsVia = [...exportTable.values()].some(entry => entry.via.has('exports'));
|
|
410
446
|
const hasModuleExportsVia = [...exportTable.values()].some(entry => entry.via.has('module.exports'));
|
|
@@ -416,15 +452,163 @@ const format = async (src, ast, opts) => {
|
|
|
416
452
|
end: firstExports?.end ?? 0
|
|
417
453
|
});
|
|
418
454
|
}
|
|
455
|
+
const reservedExports = new Set(['await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'implements', 'import', 'in', 'instanceof', 'interface', 'let', 'new', 'null', 'package', 'private', 'protected', 'public', 'return', 'static', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield']);
|
|
456
|
+
const isValidExportName = name => /^[$A-Z_a-z][$\w]*$/.test(name) && !reservedExports.has(name);
|
|
457
|
+
const isAllowedRhs = node => {
|
|
458
|
+
return node.type === 'Identifier' || node.type === 'Literal' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression' || node.type === 'ClassExpression';
|
|
459
|
+
};
|
|
460
|
+
const buildIdiomaticPlan = () => {
|
|
461
|
+
if (idiomaticMode === 'off') return {
|
|
462
|
+
ok: false,
|
|
463
|
+
reason: 'disabled'
|
|
464
|
+
};
|
|
465
|
+
const entries = [...exportTable.values()];
|
|
466
|
+
if (!entries.length) return {
|
|
467
|
+
ok: false,
|
|
468
|
+
reason: 'no-exports'
|
|
469
|
+
};
|
|
470
|
+
if (exportTable.hasUnsupportedExportWrite) {
|
|
471
|
+
return {
|
|
472
|
+
ok: false,
|
|
473
|
+
reason: 'unsupported-left'
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
const viaSet = new Set();
|
|
477
|
+
for (const entry of entries) {
|
|
478
|
+
entry.via.forEach(v => viaSet.add(v));
|
|
479
|
+
if (entry.hasGetter) return {
|
|
480
|
+
ok: false,
|
|
481
|
+
reason: 'getter-present'
|
|
482
|
+
};
|
|
483
|
+
if (entry.reassignments.length) return {
|
|
484
|
+
ok: false,
|
|
485
|
+
reason: 'reassignment'
|
|
486
|
+
};
|
|
487
|
+
if (entry.hasNonTopLevelWrite) return {
|
|
488
|
+
ok: false,
|
|
489
|
+
reason: 'non-top-level'
|
|
490
|
+
};
|
|
491
|
+
if (entry.writes.length !== 1) return {
|
|
492
|
+
ok: false,
|
|
493
|
+
reason: 'multiple-writes'
|
|
494
|
+
};
|
|
495
|
+
if (!isValidExportName(entry.key)) return {
|
|
496
|
+
ok: false,
|
|
497
|
+
reason: 'non-identifier-key'
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
if (viaSet.size > 1) return {
|
|
501
|
+
ok: false,
|
|
502
|
+
reason: 'mixed-exports'
|
|
503
|
+
};
|
|
504
|
+
const replacements = [];
|
|
505
|
+
const exportsOut = [];
|
|
506
|
+
const seen = new Set();
|
|
507
|
+
const requireShadowed = shadowedBindings;
|
|
508
|
+
const rhsSourceFor = node => {
|
|
509
|
+
const raw = code.slice(node.start, node.end);
|
|
510
|
+
return raw.replace(/\b__dirname\b/g, 'import.meta.dirname').replace(/\b__filename\b/g, 'import.meta.filename');
|
|
511
|
+
};
|
|
512
|
+
for (const entry of entries) {
|
|
513
|
+
const write = entry.writes[0];
|
|
514
|
+
if (write.type !== 'AssignmentExpression') {
|
|
515
|
+
return {
|
|
516
|
+
ok: false,
|
|
517
|
+
reason: 'unsupported-write-kind'
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
const left = write.left;
|
|
521
|
+
if (left.type !== 'MemberExpression' || left.computed || left.property.type !== 'Identifier') {
|
|
522
|
+
return {
|
|
523
|
+
ok: false,
|
|
524
|
+
reason: 'unsupported-left'
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
const base = left.object;
|
|
528
|
+
const propName = left.property.name;
|
|
529
|
+
const baseIsExports = base.type === 'Identifier' && base.name === 'exports';
|
|
530
|
+
const baseIsModuleExports = base.type === 'MemberExpression' && base.object.type === 'Identifier' && base.object.name === 'module' && base.property.type === 'Identifier' && base.property.name === 'exports';
|
|
531
|
+
if (!baseIsExports && !baseIsModuleExports) {
|
|
532
|
+
return {
|
|
533
|
+
ok: false,
|
|
534
|
+
reason: 'unsupported-base'
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
const rhs = write.right;
|
|
538
|
+
if (!isAllowedRhs(rhs)) return {
|
|
539
|
+
ok: false,
|
|
540
|
+
reason: 'unsupported-rhs'
|
|
541
|
+
};
|
|
542
|
+
if (expressionHasRequireCall(rhs, requireShadowed)) {
|
|
543
|
+
return {
|
|
544
|
+
ok: false,
|
|
545
|
+
reason: 'rhs-require'
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
const rhsSrc = rhsSourceFor(rhs);
|
|
549
|
+
if (propName === 'exports' && baseIsModuleExports) {
|
|
550
|
+
// module.exports = ... handles default
|
|
551
|
+
if (seen.has('default')) return {
|
|
552
|
+
ok: false,
|
|
553
|
+
reason: 'duplicate-default'
|
|
554
|
+
};
|
|
555
|
+
seen.add('default');
|
|
556
|
+
exportsOut.push(`export default ${rhsSrc};`);
|
|
557
|
+
} else {
|
|
558
|
+
if (seen.has(propName)) return {
|
|
559
|
+
ok: false,
|
|
560
|
+
reason: 'duplicate-key'
|
|
561
|
+
};
|
|
562
|
+
seen.add(propName);
|
|
563
|
+
if (rhs.type === 'Identifier') {
|
|
564
|
+
const rhsId = rhsSourceFor(rhs);
|
|
565
|
+
if (rhsId === rhs.name) {
|
|
566
|
+
exportsOut.push(`export { ${rhsId} as ${propName} };`);
|
|
567
|
+
} else {
|
|
568
|
+
exportsOut.push(`export const ${propName} = ${rhsId};`);
|
|
569
|
+
}
|
|
570
|
+
} else {
|
|
571
|
+
exportsOut.push(`export const ${propName} = ${rhsSrc};`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
replacements.push({
|
|
575
|
+
start: write.start,
|
|
576
|
+
end: write.end
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
if (!seen.size) return {
|
|
580
|
+
ok: false,
|
|
581
|
+
reason: 'no-seen'
|
|
582
|
+
};
|
|
583
|
+
return {
|
|
584
|
+
ok: true,
|
|
585
|
+
plan: {
|
|
586
|
+
replacements,
|
|
587
|
+
exports: exportsOut
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
};
|
|
591
|
+
if (idiomaticMode !== 'off') {
|
|
592
|
+
const res = buildIdiomaticPlan();
|
|
593
|
+
if (res.ok && res.plan) {
|
|
594
|
+
useExportsBag = false;
|
|
595
|
+
idiomaticPlan = res.plan;
|
|
596
|
+
} else if (res.reason) {
|
|
597
|
+
idiomaticFallbackReason = res.reason;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
419
600
|
}
|
|
420
|
-
const shouldCheckTopLevelAwait = opts.target === 'commonjs' &&
|
|
601
|
+
const shouldCheckTopLevelAwait = opts.target === 'commonjs' && fullTransform;
|
|
421
602
|
const containsTopLevelAwait = shouldCheckTopLevelAwait ? hasTopLevelAwait(ast.program) : false;
|
|
603
|
+
if (idiomaticFallbackReason && idiomaticMode !== 'off') {
|
|
604
|
+
warnOnce('idiomatic-exports-fallback', `Idiomatic exports disabled for this file: ${idiomaticFallbackReason}. Falling back to helper exports.`);
|
|
605
|
+
}
|
|
422
606
|
const requireMainStrategy = opts.requireMainStrategy ?? 'import-meta-main';
|
|
423
607
|
let requireMainNeedsRealpath = false;
|
|
424
608
|
let needsRequireResolveHelper = false;
|
|
425
609
|
const nestedRequireStrategy = opts.nestedRequireStrategy ?? 'create-require';
|
|
426
|
-
const shouldLowerCjs = opts.target === 'commonjs' &&
|
|
427
|
-
const shouldRaiseEsm = opts.target === 'module' &&
|
|
610
|
+
const shouldLowerCjs = opts.target === 'commonjs' && fullTransform;
|
|
611
|
+
const shouldRaiseEsm = opts.target === 'module' && fullTransform;
|
|
428
612
|
let hoistedImports = [];
|
|
429
613
|
let hoistedStatements = [];
|
|
430
614
|
let pendingRequireTransforms = [];
|
|
@@ -574,7 +758,7 @@ const format = async (src, ast, opts) => {
|
|
|
574
758
|
onDiagnostic: (codeId, message, loc) => {
|
|
575
759
|
if (shouldRaiseEsm) warnOnce(codeId, message, loc);
|
|
576
760
|
}
|
|
577
|
-
});
|
|
761
|
+
}, useExportsBag, fullTransform);
|
|
578
762
|
}
|
|
579
763
|
if (shouldRaiseEsm && node.type === 'ThisExpression') {
|
|
580
764
|
const bindsThis = ancestor => {
|
|
@@ -594,7 +778,8 @@ const format = async (src, ast, opts) => {
|
|
|
594
778
|
code,
|
|
595
779
|
opts,
|
|
596
780
|
meta: exportsMeta,
|
|
597
|
-
shadowed: shadowedBindings
|
|
781
|
+
shadowed: shadowedBindings,
|
|
782
|
+
useExportsBag
|
|
598
783
|
});
|
|
599
784
|
}
|
|
600
785
|
}
|
|
@@ -604,6 +789,20 @@ const format = async (src, ast, opts) => {
|
|
|
604
789
|
code.overwrite(t.start, t.end, t.code);
|
|
605
790
|
}
|
|
606
791
|
}
|
|
792
|
+
if (!useExportsBag && idiomaticPlan) {
|
|
793
|
+
if (idiomaticPlan.exports.length === idiomaticPlan.replacements.length) {
|
|
794
|
+
idiomaticPlan.replacements.forEach((rep, idx) => {
|
|
795
|
+
code.overwrite(rep.start, rep.end, idiomaticPlan.exports[idx]);
|
|
796
|
+
});
|
|
797
|
+
} else {
|
|
798
|
+
for (const rep of idiomaticPlan.replacements) {
|
|
799
|
+
code.overwrite(rep.start, rep.end, ';');
|
|
800
|
+
}
|
|
801
|
+
if (idiomaticPlan.exports.length) {
|
|
802
|
+
code.append(`\n${idiomaticPlan.exports.join('\n')}\n`);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
607
806
|
if (shouldLowerCjs) {
|
|
608
807
|
const {
|
|
609
808
|
importTransforms,
|
|
@@ -623,7 +822,7 @@ const format = async (src, ast, opts) => {
|
|
|
623
822
|
code.prepend(`${interopHelper}exports.__esModule = true;\n`);
|
|
624
823
|
}
|
|
625
824
|
}
|
|
626
|
-
if (opts.target === 'module' &&
|
|
825
|
+
if (useExportsBag && opts.target === 'module' && fullTransform && exportTable) {
|
|
627
826
|
const isValidExportName = name => /^[$A-Z_a-z][$\w]*$/.test(name);
|
|
628
827
|
const asExportName = name => isValidExportName(name) ? name : JSON.stringify(name);
|
|
629
828
|
const accessProp = name => isValidExportName(name) ? `${_exports.exportsRename}.${name}` : `${_exports.exportsRename}[${JSON.stringify(name)}]`;
|
|
@@ -683,7 +882,7 @@ const format = async (src, ast, opts) => {
|
|
|
683
882
|
code.append(`\n${lines.join('\n')}\n`);
|
|
684
883
|
}
|
|
685
884
|
}
|
|
686
|
-
if (shouldRaiseEsm &&
|
|
885
|
+
if (shouldRaiseEsm && fullTransform) {
|
|
687
886
|
const importPrelude = [];
|
|
688
887
|
if (needsCreateRequire || needsRequireResolveHelper) {
|
|
689
888
|
importPrelude.push('import { createRequire } from "node:module";\n');
|
|
@@ -714,12 +913,14 @@ const format = async (src, ast, opts) => {
|
|
|
714
913
|
const resolved = req.resolve(id, parent);
|
|
715
914
|
return resolved.startsWith("file://") ? fileURLToPath(resolved) : resolved;
|
|
716
915
|
};\n` : '';
|
|
717
|
-
const
|
|
718
|
-
|
|
916
|
+
const exportsBagInit = useExportsBag ? `let ${_exports.exportsRename} = {};
|
|
917
|
+
` : '';
|
|
918
|
+
const modulePrelude = '';
|
|
919
|
+
const prelude = `${importPrelude.join('')}${importPrelude.length ? '\n' : ''}${setupPrelude.join('')}${setupPrelude.length ? '\n' : ''}${requireInit}${requireResolveInit}${exportsBagInit}${modulePrelude}void import.meta.filename;
|
|
719
920
|
`;
|
|
720
921
|
code.prepend(prelude);
|
|
721
922
|
}
|
|
722
|
-
if (opts.target === 'commonjs' &&
|
|
923
|
+
if (opts.target === 'commonjs' && fullTransform && containsTopLevelAwait) {
|
|
723
924
|
const body = code.toString();
|
|
724
925
|
if (opts.topLevelAwait === 'wrap') {
|
|
725
926
|
const tlaPromise = `const __tla = (async () => {\n${body}\nreturn module.exports;\n})();\n`;
|
|
@@ -12,7 +12,8 @@ const identifier = ({
|
|
|
12
12
|
code,
|
|
13
13
|
opts,
|
|
14
14
|
meta,
|
|
15
|
-
shadowed
|
|
15
|
+
shadowed,
|
|
16
|
+
useExportsBag = true
|
|
16
17
|
}) => {
|
|
17
18
|
if (opts.target === 'module') {
|
|
18
19
|
const {
|
|
@@ -33,7 +34,7 @@ const identifier = ({
|
|
|
33
34
|
case 'exports':
|
|
34
35
|
{
|
|
35
36
|
const parent = ancestors[ancestors.length - 2];
|
|
36
|
-
if (opts.transformSyntax) {
|
|
37
|
+
if (opts.transformSyntax && useExportsBag) {
|
|
37
38
|
if (parent.type === 'AssignmentExpression' && parent.left === node) {
|
|
38
39
|
// The code is reassigning `exports` to something else.
|
|
39
40
|
|
|
@@ -5,15 +5,33 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.memberExpression = void 0;
|
|
7
7
|
var _exports = require("#utils/exports.js");
|
|
8
|
-
const memberExpression = (node, parent, src, options, shadowed, extras) => {
|
|
8
|
+
const memberExpression = (node, parent, src, options, shadowed, extras, useExportsBag = true, rewriteExports = true) => {
|
|
9
9
|
if (options.target === 'module') {
|
|
10
|
-
if (
|
|
11
|
-
|
|
10
|
+
if (rewriteExports && !useExportsBag) {
|
|
11
|
+
if (parent?.type === 'MemberExpression' && parent.object === node && parent.property.type === 'Identifier') {
|
|
12
|
+
const baseIsExportsIdent = node.object.type === 'Identifier' && node.object.name === 'exports';
|
|
13
|
+
const baseIsModuleExports = node.object.type === 'Identifier' && node.object.name === 'module' && node.property.type === 'Identifier' && node.property.name === 'exports';
|
|
14
|
+
if (baseIsExportsIdent || baseIsModuleExports) {
|
|
15
|
+
src.update(parent.start, parent.end, parent.property.name);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (node.object.type === 'Identifier' && node.property.type === 'Identifier' && node.object.name === 'module' && node.property.name === 'exports') {
|
|
20
|
+
src.update(node.start, node.end, 'undefined');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
12
23
|
}
|
|
13
|
-
if (node.object.type === 'Identifier' && node.
|
|
14
|
-
src.update(node.start, node.end, _exports.exportsRename);
|
|
24
|
+
if (rewriteExports && (node.object.type === 'Identifier' && shadowed?.has(node.object.name) || node.property.type === 'Identifier' && shadowed?.has(node.property.name))) {
|
|
15
25
|
return;
|
|
16
26
|
}
|
|
27
|
+
if (rewriteExports) {
|
|
28
|
+
if (node.object.type === 'Identifier' && node.property.type === 'Identifier' && node.object.name === 'module' && node.property.name === 'exports') {
|
|
29
|
+
if (useExportsBag) {
|
|
30
|
+
src.update(node.start, node.end, _exports.exportsRename);
|
|
31
|
+
}
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
17
35
|
if (node.object.type === 'Identifier' && node.property.type === 'Identifier' && node.object.name === 'require') {
|
|
18
36
|
const {
|
|
19
37
|
start,
|
|
@@ -32,6 +50,10 @@ const memberExpression = (node, parent, src, options, shadowed, extras) => {
|
|
|
32
50
|
src.update(start, end, 'import.meta.main');
|
|
33
51
|
break;
|
|
34
52
|
case 'resolve':
|
|
53
|
+
if (options.transformSyntax !== true) {
|
|
54
|
+
src.update(start, end, 'import.meta.resolve');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
35
57
|
extras?.onRequireResolve?.();
|
|
36
58
|
src.update(start, end, extras?.requireResolveName ?? 'import.meta.resolve');
|
|
37
59
|
break;
|
|
@@ -9,5 +9,5 @@ type MemberExpressionExtras = {
|
|
|
9
9
|
end: number;
|
|
10
10
|
}) => void;
|
|
11
11
|
};
|
|
12
|
-
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions, shadowed?: Set<string>, extras?: MemberExpressionExtras) => void;
|
|
12
|
+
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions, shadowed?: Set<string>, extras?: MemberExpressionExtras, useExportsBag?: boolean, rewriteExports?: boolean) => void;
|
|
13
13
|
export {};
|
package/dist/cjs/module.cjs
CHANGED
package/dist/cjs/types.d.cts
CHANGED
|
@@ -6,8 +6,13 @@ export type ModuleOptions = {
|
|
|
6
6
|
target: 'module' | 'commonjs';
|
|
7
7
|
/** Explicit source type; auto infers from file extension. */
|
|
8
8
|
sourceType?: 'auto' | 'module' | 'commonjs';
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Enable syntax transforms beyond parsing.
|
|
11
|
+
* - true: full CJS↔ESM lowering/raising
|
|
12
|
+
* - 'globals-only': rewrite module-global differences (import.meta, __dirname/filename, require.main shims) while leaving import/export shapes untouched
|
|
13
|
+
* - false/undefined: no syntax transforms
|
|
14
|
+
*/
|
|
15
|
+
transformSyntax?: boolean | 'globals-only';
|
|
11
16
|
/** How to emit live bindings for ESM exports. */
|
|
12
17
|
liveBindings?: 'strict' | 'loose' | 'off';
|
|
13
18
|
/** Rewrite import specifiers (e.g. add extensions). */
|
|
@@ -32,6 +37,8 @@ export type ModuleOptions = {
|
|
|
32
37
|
nestedRequireStrategy?: 'create-require' | 'dynamic-import';
|
|
33
38
|
/** Default interop style for CommonJS default imports. */
|
|
34
39
|
cjsDefault?: 'module-exports' | 'auto' | 'none';
|
|
40
|
+
/** Emit idiomatic exports when raising CJS to ESM. */
|
|
41
|
+
idiomaticExports?: 'off' | 'safe' | 'aggressive';
|
|
35
42
|
/** Handling for top-level await constructs. */
|
|
36
43
|
topLevelAwait?: 'error' | 'wrap' | 'preserve';
|
|
37
44
|
/** Optional diagnostics sink for warnings/errors emitted during transform. */
|
|
@@ -67,6 +74,7 @@ export type CjsExport = {
|
|
|
67
74
|
via: Set<'exports' | 'module.exports'>;
|
|
68
75
|
reassignments: SpannedNode[];
|
|
69
76
|
hasGetter?: boolean;
|
|
77
|
+
hasNonTopLevelWrite?: boolean;
|
|
70
78
|
};
|
|
71
79
|
export type IdentMeta = {
|
|
72
80
|
declare: SpannedNode[];
|
|
@@ -69,6 +69,12 @@ const collectCjsExports = async ast => {
|
|
|
69
69
|
const localToExport = new Map();
|
|
70
70
|
const aliases = new Map();
|
|
71
71
|
const literals = new Map();
|
|
72
|
+
let hasUnsupportedExportWrite = false;
|
|
73
|
+
const isTopLevelWrite = ancestors => {
|
|
74
|
+
const parent = ancestors[ancestors.length - 2];
|
|
75
|
+
const grandparent = ancestors[ancestors.length - 3];
|
|
76
|
+
return grandparent?.type === 'Program' && (parent?.type === 'ExpressionStatement' || parent?.type === 'VariableDeclaration');
|
|
77
|
+
};
|
|
72
78
|
const addExport = (ref, node, rhs, options) => {
|
|
73
79
|
const entry = exportsMap.get(ref.key) ?? {
|
|
74
80
|
key: ref.key,
|
|
@@ -81,6 +87,9 @@ const collectCjsExports = async ast => {
|
|
|
81
87
|
if (options?.hasGetter) {
|
|
82
88
|
entry.hasGetter = true;
|
|
83
89
|
}
|
|
90
|
+
if (options?.topLevel === false) {
|
|
91
|
+
entry.hasNonTopLevelWrite = true;
|
|
92
|
+
}
|
|
84
93
|
if (rhs) {
|
|
85
94
|
entry.fromIdentifier ??= rhs.name;
|
|
86
95
|
const set = localToExport.get(rhs.name) ?? new Set();
|
|
@@ -110,9 +119,15 @@ const collectCjsExports = async ast => {
|
|
|
110
119
|
const target = resolveExportTarget(node.left, aliases, literals, ancestors);
|
|
111
120
|
if (target) {
|
|
112
121
|
const rhsIdent = node.right.type === 'Identifier' ? node.right : undefined;
|
|
113
|
-
|
|
122
|
+
const topLevel = isTopLevelWrite(ancestors);
|
|
123
|
+
addExport(target, node, rhsIdent, {
|
|
124
|
+
topLevel
|
|
125
|
+
});
|
|
114
126
|
return;
|
|
115
127
|
}
|
|
128
|
+
if (node.left.type === 'MemberExpression' && resolveBase(node.left.object, aliases, ancestors)) {
|
|
129
|
+
hasUnsupportedExportWrite = true;
|
|
130
|
+
}
|
|
116
131
|
if (node.left.type === 'Identifier') {
|
|
117
132
|
const keys = localToExport.get(node.left.name);
|
|
118
133
|
if (keys) {
|
|
@@ -129,7 +144,12 @@ const collectCjsExports = async ast => {
|
|
|
129
144
|
const findExportRefs = pattern => {
|
|
130
145
|
if (pattern.type === 'MemberExpression') {
|
|
131
146
|
const ref = resolveExportTarget(pattern, aliases, literals, ancestors);
|
|
132
|
-
if (ref)
|
|
147
|
+
if (ref) {
|
|
148
|
+
const topLevel = isTopLevelWrite(ancestors);
|
|
149
|
+
addExport(ref, node, undefined, {
|
|
150
|
+
topLevel
|
|
151
|
+
});
|
|
152
|
+
}
|
|
133
153
|
return;
|
|
134
154
|
}
|
|
135
155
|
if (pattern.type === 'ObjectPattern') {
|
|
@@ -171,10 +191,13 @@ const collectCjsExports = async ast => {
|
|
|
171
191
|
if (prop.value.type === 'Identifier') {
|
|
172
192
|
rhsIdent = prop.value;
|
|
173
193
|
}
|
|
194
|
+
const topLevel = isTopLevelWrite(ancestors);
|
|
174
195
|
addExport({
|
|
175
196
|
key: keyName,
|
|
176
197
|
via: ref
|
|
177
|
-
}, node, rhsIdent
|
|
198
|
+
}, node, rhsIdent, {
|
|
199
|
+
topLevel
|
|
200
|
+
});
|
|
178
201
|
}
|
|
179
202
|
}
|
|
180
203
|
}
|
|
@@ -203,11 +226,13 @@ const collectCjsExports = async ast => {
|
|
|
203
226
|
// Setter-only doesn’t create a readable export; ignore beyond marking write
|
|
204
227
|
}
|
|
205
228
|
}
|
|
229
|
+
const topLevel = isTopLevelWrite(ancestors);
|
|
206
230
|
addExport({
|
|
207
231
|
key: keyName,
|
|
208
232
|
via: target
|
|
209
233
|
}, node, rhsIdent, {
|
|
210
|
-
hasGetter
|
|
234
|
+
hasGetter,
|
|
235
|
+
topLevel
|
|
211
236
|
});
|
|
212
237
|
}
|
|
213
238
|
|
|
@@ -234,17 +259,20 @@ const collectCjsExports = async ast => {
|
|
|
234
259
|
hasGetter = true;
|
|
235
260
|
}
|
|
236
261
|
}
|
|
262
|
+
const topLevel = isTopLevelWrite(ancestors);
|
|
237
263
|
addExport({
|
|
238
264
|
key: keyName,
|
|
239
265
|
via: target
|
|
240
266
|
}, node, rhsIdent, {
|
|
241
|
-
hasGetter
|
|
267
|
+
hasGetter,
|
|
268
|
+
topLevel
|
|
242
269
|
});
|
|
243
270
|
}
|
|
244
271
|
}
|
|
245
272
|
}
|
|
246
273
|
}
|
|
247
274
|
});
|
|
275
|
+
exportsMap.hasUnsupportedExportWrite = hasUnsupportedExportWrite;
|
|
248
276
|
return exportsMap;
|
|
249
277
|
};
|
|
250
278
|
exports.collectCjsExports = collectCjsExports;
|
package/dist/format.js
CHANGED
|
@@ -9,6 +9,36 @@ import { collectModuleIdentifiers } from '#utils/identifiers.js';
|
|
|
9
9
|
import { isIdentifierName } from '#helpers/identifier.js';
|
|
10
10
|
import { ancestorWalk } from '#walk';
|
|
11
11
|
const isValidIdent = name => /^[$A-Z_a-z][$\w]*$/.test(name);
|
|
12
|
+
const expressionHasRequireCall = (node, shadowed) => {
|
|
13
|
+
let found = false;
|
|
14
|
+
const walkNode = n => {
|
|
15
|
+
if (!n || found) return;
|
|
16
|
+
if (n.type === 'CallExpression' && n.callee?.type === 'Identifier' && n.callee.name === 'require' && !shadowed.has('require')) {
|
|
17
|
+
found = true;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (n.type === 'CallExpression' && n.callee?.type === 'MemberExpression' && n.callee.object?.type === 'Identifier' && n.callee.object.name === 'require' && !shadowed.has('require')) {
|
|
21
|
+
found = true;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const keys = Object.keys(n);
|
|
25
|
+
for (const key of keys) {
|
|
26
|
+
const value = n[key];
|
|
27
|
+
if (!value) continue;
|
|
28
|
+
if (Array.isArray(value)) {
|
|
29
|
+
for (const item of value) {
|
|
30
|
+
if (item && typeof item === 'object') walkNode(item);
|
|
31
|
+
if (found) return;
|
|
32
|
+
}
|
|
33
|
+
} else if (value && typeof value === 'object') {
|
|
34
|
+
walkNode(value);
|
|
35
|
+
if (found) return;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
walkNode(node);
|
|
40
|
+
return found;
|
|
41
|
+
};
|
|
12
42
|
const exportAssignment = (name, expr, live) => {
|
|
13
43
|
const prop = isValidIdent(name) ? `.${name}` : `[${JSON.stringify(name)}]`;
|
|
14
44
|
if (live === 'strict') {
|
|
@@ -390,14 +420,20 @@ const format = async (src, ast, opts) => {
|
|
|
390
420
|
loc
|
|
391
421
|
});
|
|
392
422
|
};
|
|
423
|
+
const transformMode = opts.transformSyntax;
|
|
424
|
+
const fullTransform = transformMode === true;
|
|
393
425
|
const moduleIdentifiers = await collectModuleIdentifiers(ast.program);
|
|
394
426
|
const shadowedBindings = new Set([...moduleIdentifiers.entries()].filter(([, meta]) => meta.declare.length > 0).map(([name]) => name));
|
|
395
|
-
if (opts.target === 'module' &&
|
|
427
|
+
if (opts.target === 'module' && fullTransform) {
|
|
396
428
|
if (shadowedBindings.has('module') || shadowedBindings.has('exports')) {
|
|
397
429
|
throw new Error('Cannot transform to ESM: module or exports is shadowed in module scope.');
|
|
398
430
|
}
|
|
399
431
|
}
|
|
400
432
|
const exportTable = opts.target === 'module' ? await collectCjsExports(ast.program) : null;
|
|
433
|
+
const idiomaticMode = opts.target === 'module' && fullTransform ? opts.idiomaticExports ?? 'safe' : 'off';
|
|
434
|
+
let useExportsBag = fullTransform;
|
|
435
|
+
let idiomaticPlan = null;
|
|
436
|
+
let idiomaticFallbackReason;
|
|
401
437
|
if (opts.target === 'module' && exportTable) {
|
|
402
438
|
const hasExportsVia = [...exportTable.values()].some(entry => entry.via.has('exports'));
|
|
403
439
|
const hasModuleExportsVia = [...exportTable.values()].some(entry => entry.via.has('module.exports'));
|
|
@@ -409,15 +445,163 @@ const format = async (src, ast, opts) => {
|
|
|
409
445
|
end: firstExports?.end ?? 0
|
|
410
446
|
});
|
|
411
447
|
}
|
|
448
|
+
const reservedExports = new Set(['await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'implements', 'import', 'in', 'instanceof', 'interface', 'let', 'new', 'null', 'package', 'private', 'protected', 'public', 'return', 'static', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield']);
|
|
449
|
+
const isValidExportName = name => /^[$A-Z_a-z][$\w]*$/.test(name) && !reservedExports.has(name);
|
|
450
|
+
const isAllowedRhs = node => {
|
|
451
|
+
return node.type === 'Identifier' || node.type === 'Literal' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression' || node.type === 'ClassExpression';
|
|
452
|
+
};
|
|
453
|
+
const buildIdiomaticPlan = () => {
|
|
454
|
+
if (idiomaticMode === 'off') return {
|
|
455
|
+
ok: false,
|
|
456
|
+
reason: 'disabled'
|
|
457
|
+
};
|
|
458
|
+
const entries = [...exportTable.values()];
|
|
459
|
+
if (!entries.length) return {
|
|
460
|
+
ok: false,
|
|
461
|
+
reason: 'no-exports'
|
|
462
|
+
};
|
|
463
|
+
if (exportTable.hasUnsupportedExportWrite) {
|
|
464
|
+
return {
|
|
465
|
+
ok: false,
|
|
466
|
+
reason: 'unsupported-left'
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
const viaSet = new Set();
|
|
470
|
+
for (const entry of entries) {
|
|
471
|
+
entry.via.forEach(v => viaSet.add(v));
|
|
472
|
+
if (entry.hasGetter) return {
|
|
473
|
+
ok: false,
|
|
474
|
+
reason: 'getter-present'
|
|
475
|
+
};
|
|
476
|
+
if (entry.reassignments.length) return {
|
|
477
|
+
ok: false,
|
|
478
|
+
reason: 'reassignment'
|
|
479
|
+
};
|
|
480
|
+
if (entry.hasNonTopLevelWrite) return {
|
|
481
|
+
ok: false,
|
|
482
|
+
reason: 'non-top-level'
|
|
483
|
+
};
|
|
484
|
+
if (entry.writes.length !== 1) return {
|
|
485
|
+
ok: false,
|
|
486
|
+
reason: 'multiple-writes'
|
|
487
|
+
};
|
|
488
|
+
if (!isValidExportName(entry.key)) return {
|
|
489
|
+
ok: false,
|
|
490
|
+
reason: 'non-identifier-key'
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
if (viaSet.size > 1) return {
|
|
494
|
+
ok: false,
|
|
495
|
+
reason: 'mixed-exports'
|
|
496
|
+
};
|
|
497
|
+
const replacements = [];
|
|
498
|
+
const exportsOut = [];
|
|
499
|
+
const seen = new Set();
|
|
500
|
+
const requireShadowed = shadowedBindings;
|
|
501
|
+
const rhsSourceFor = node => {
|
|
502
|
+
const raw = code.slice(node.start, node.end);
|
|
503
|
+
return raw.replace(/\b__dirname\b/g, 'import.meta.dirname').replace(/\b__filename\b/g, 'import.meta.filename');
|
|
504
|
+
};
|
|
505
|
+
for (const entry of entries) {
|
|
506
|
+
const write = entry.writes[0];
|
|
507
|
+
if (write.type !== 'AssignmentExpression') {
|
|
508
|
+
return {
|
|
509
|
+
ok: false,
|
|
510
|
+
reason: 'unsupported-write-kind'
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
const left = write.left;
|
|
514
|
+
if (left.type !== 'MemberExpression' || left.computed || left.property.type !== 'Identifier') {
|
|
515
|
+
return {
|
|
516
|
+
ok: false,
|
|
517
|
+
reason: 'unsupported-left'
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
const base = left.object;
|
|
521
|
+
const propName = left.property.name;
|
|
522
|
+
const baseIsExports = base.type === 'Identifier' && base.name === 'exports';
|
|
523
|
+
const baseIsModuleExports = base.type === 'MemberExpression' && base.object.type === 'Identifier' && base.object.name === 'module' && base.property.type === 'Identifier' && base.property.name === 'exports';
|
|
524
|
+
if (!baseIsExports && !baseIsModuleExports) {
|
|
525
|
+
return {
|
|
526
|
+
ok: false,
|
|
527
|
+
reason: 'unsupported-base'
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
const rhs = write.right;
|
|
531
|
+
if (!isAllowedRhs(rhs)) return {
|
|
532
|
+
ok: false,
|
|
533
|
+
reason: 'unsupported-rhs'
|
|
534
|
+
};
|
|
535
|
+
if (expressionHasRequireCall(rhs, requireShadowed)) {
|
|
536
|
+
return {
|
|
537
|
+
ok: false,
|
|
538
|
+
reason: 'rhs-require'
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
const rhsSrc = rhsSourceFor(rhs);
|
|
542
|
+
if (propName === 'exports' && baseIsModuleExports) {
|
|
543
|
+
// module.exports = ... handles default
|
|
544
|
+
if (seen.has('default')) return {
|
|
545
|
+
ok: false,
|
|
546
|
+
reason: 'duplicate-default'
|
|
547
|
+
};
|
|
548
|
+
seen.add('default');
|
|
549
|
+
exportsOut.push(`export default ${rhsSrc};`);
|
|
550
|
+
} else {
|
|
551
|
+
if (seen.has(propName)) return {
|
|
552
|
+
ok: false,
|
|
553
|
+
reason: 'duplicate-key'
|
|
554
|
+
};
|
|
555
|
+
seen.add(propName);
|
|
556
|
+
if (rhs.type === 'Identifier') {
|
|
557
|
+
const rhsId = rhsSourceFor(rhs);
|
|
558
|
+
if (rhsId === rhs.name) {
|
|
559
|
+
exportsOut.push(`export { ${rhsId} as ${propName} };`);
|
|
560
|
+
} else {
|
|
561
|
+
exportsOut.push(`export const ${propName} = ${rhsId};`);
|
|
562
|
+
}
|
|
563
|
+
} else {
|
|
564
|
+
exportsOut.push(`export const ${propName} = ${rhsSrc};`);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
replacements.push({
|
|
568
|
+
start: write.start,
|
|
569
|
+
end: write.end
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
if (!seen.size) return {
|
|
573
|
+
ok: false,
|
|
574
|
+
reason: 'no-seen'
|
|
575
|
+
};
|
|
576
|
+
return {
|
|
577
|
+
ok: true,
|
|
578
|
+
plan: {
|
|
579
|
+
replacements,
|
|
580
|
+
exports: exportsOut
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
};
|
|
584
|
+
if (idiomaticMode !== 'off') {
|
|
585
|
+
const res = buildIdiomaticPlan();
|
|
586
|
+
if (res.ok && res.plan) {
|
|
587
|
+
useExportsBag = false;
|
|
588
|
+
idiomaticPlan = res.plan;
|
|
589
|
+
} else if (res.reason) {
|
|
590
|
+
idiomaticFallbackReason = res.reason;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
412
593
|
}
|
|
413
|
-
const shouldCheckTopLevelAwait = opts.target === 'commonjs' &&
|
|
594
|
+
const shouldCheckTopLevelAwait = opts.target === 'commonjs' && fullTransform;
|
|
414
595
|
const containsTopLevelAwait = shouldCheckTopLevelAwait ? hasTopLevelAwait(ast.program) : false;
|
|
596
|
+
if (idiomaticFallbackReason && idiomaticMode !== 'off') {
|
|
597
|
+
warnOnce('idiomatic-exports-fallback', `Idiomatic exports disabled for this file: ${idiomaticFallbackReason}. Falling back to helper exports.`);
|
|
598
|
+
}
|
|
415
599
|
const requireMainStrategy = opts.requireMainStrategy ?? 'import-meta-main';
|
|
416
600
|
let requireMainNeedsRealpath = false;
|
|
417
601
|
let needsRequireResolveHelper = false;
|
|
418
602
|
const nestedRequireStrategy = opts.nestedRequireStrategy ?? 'create-require';
|
|
419
|
-
const shouldLowerCjs = opts.target === 'commonjs' &&
|
|
420
|
-
const shouldRaiseEsm = opts.target === 'module' &&
|
|
603
|
+
const shouldLowerCjs = opts.target === 'commonjs' && fullTransform;
|
|
604
|
+
const shouldRaiseEsm = opts.target === 'module' && fullTransform;
|
|
421
605
|
let hoistedImports = [];
|
|
422
606
|
let hoistedStatements = [];
|
|
423
607
|
let pendingRequireTransforms = [];
|
|
@@ -567,7 +751,7 @@ const format = async (src, ast, opts) => {
|
|
|
567
751
|
onDiagnostic: (codeId, message, loc) => {
|
|
568
752
|
if (shouldRaiseEsm) warnOnce(codeId, message, loc);
|
|
569
753
|
}
|
|
570
|
-
});
|
|
754
|
+
}, useExportsBag, fullTransform);
|
|
571
755
|
}
|
|
572
756
|
if (shouldRaiseEsm && node.type === 'ThisExpression') {
|
|
573
757
|
const bindsThis = ancestor => {
|
|
@@ -587,7 +771,8 @@ const format = async (src, ast, opts) => {
|
|
|
587
771
|
code,
|
|
588
772
|
opts,
|
|
589
773
|
meta: exportsMeta,
|
|
590
|
-
shadowed: shadowedBindings
|
|
774
|
+
shadowed: shadowedBindings,
|
|
775
|
+
useExportsBag
|
|
591
776
|
});
|
|
592
777
|
}
|
|
593
778
|
}
|
|
@@ -597,6 +782,20 @@ const format = async (src, ast, opts) => {
|
|
|
597
782
|
code.overwrite(t.start, t.end, t.code);
|
|
598
783
|
}
|
|
599
784
|
}
|
|
785
|
+
if (!useExportsBag && idiomaticPlan) {
|
|
786
|
+
if (idiomaticPlan.exports.length === idiomaticPlan.replacements.length) {
|
|
787
|
+
idiomaticPlan.replacements.forEach((rep, idx) => {
|
|
788
|
+
code.overwrite(rep.start, rep.end, idiomaticPlan.exports[idx]);
|
|
789
|
+
});
|
|
790
|
+
} else {
|
|
791
|
+
for (const rep of idiomaticPlan.replacements) {
|
|
792
|
+
code.overwrite(rep.start, rep.end, ';');
|
|
793
|
+
}
|
|
794
|
+
if (idiomaticPlan.exports.length) {
|
|
795
|
+
code.append(`\n${idiomaticPlan.exports.join('\n')}\n`);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
600
799
|
if (shouldLowerCjs) {
|
|
601
800
|
const {
|
|
602
801
|
importTransforms,
|
|
@@ -616,7 +815,7 @@ const format = async (src, ast, opts) => {
|
|
|
616
815
|
code.prepend(`${interopHelper}exports.__esModule = true;\n`);
|
|
617
816
|
}
|
|
618
817
|
}
|
|
619
|
-
if (opts.target === 'module' &&
|
|
818
|
+
if (useExportsBag && opts.target === 'module' && fullTransform && exportTable) {
|
|
620
819
|
const isValidExportName = name => /^[$A-Z_a-z][$\w]*$/.test(name);
|
|
621
820
|
const asExportName = name => isValidExportName(name) ? name : JSON.stringify(name);
|
|
622
821
|
const accessProp = name => isValidExportName(name) ? `${exportsRename}.${name}` : `${exportsRename}[${JSON.stringify(name)}]`;
|
|
@@ -676,7 +875,7 @@ const format = async (src, ast, opts) => {
|
|
|
676
875
|
code.append(`\n${lines.join('\n')}\n`);
|
|
677
876
|
}
|
|
678
877
|
}
|
|
679
|
-
if (shouldRaiseEsm &&
|
|
878
|
+
if (shouldRaiseEsm && fullTransform) {
|
|
680
879
|
const importPrelude = [];
|
|
681
880
|
if (needsCreateRequire || needsRequireResolveHelper) {
|
|
682
881
|
importPrelude.push('import { createRequire } from "node:module";\n');
|
|
@@ -707,12 +906,14 @@ const format = async (src, ast, opts) => {
|
|
|
707
906
|
const resolved = req.resolve(id, parent);
|
|
708
907
|
return resolved.startsWith("file://") ? fileURLToPath(resolved) : resolved;
|
|
709
908
|
};\n` : '';
|
|
710
|
-
const
|
|
711
|
-
|
|
909
|
+
const exportsBagInit = useExportsBag ? `let ${exportsRename} = {};
|
|
910
|
+
` : '';
|
|
911
|
+
const modulePrelude = '';
|
|
912
|
+
const prelude = `${importPrelude.join('')}${importPrelude.length ? '\n' : ''}${setupPrelude.join('')}${setupPrelude.length ? '\n' : ''}${requireInit}${requireResolveInit}${exportsBagInit}${modulePrelude}void import.meta.filename;
|
|
712
913
|
`;
|
|
713
914
|
code.prepend(prelude);
|
|
714
915
|
}
|
|
715
|
-
if (opts.target === 'commonjs' &&
|
|
916
|
+
if (opts.target === 'commonjs' && fullTransform && containsTopLevelAwait) {
|
|
716
917
|
const body = code.toString();
|
|
717
918
|
if (opts.topLevelAwait === 'wrap') {
|
|
718
919
|
const tlaPromise = `const __tla = (async () => {\n${body}\nreturn module.exports;\n})();\n`;
|
|
@@ -6,7 +6,8 @@ export const identifier = ({
|
|
|
6
6
|
code,
|
|
7
7
|
opts,
|
|
8
8
|
meta,
|
|
9
|
-
shadowed
|
|
9
|
+
shadowed,
|
|
10
|
+
useExportsBag = true
|
|
10
11
|
}) => {
|
|
11
12
|
if (opts.target === 'module') {
|
|
12
13
|
const {
|
|
@@ -27,7 +28,7 @@ export const identifier = ({
|
|
|
27
28
|
case 'exports':
|
|
28
29
|
{
|
|
29
30
|
const parent = ancestors[ancestors.length - 2];
|
|
30
|
-
if (opts.transformSyntax) {
|
|
31
|
+
if (opts.transformSyntax && useExportsBag) {
|
|
31
32
|
if (parent.type === 'AssignmentExpression' && parent.left === node) {
|
|
32
33
|
// The code is reassigning `exports` to something else.
|
|
33
34
|
|
|
@@ -1,13 +1,31 @@
|
|
|
1
1
|
import { exportsRename } from '#utils/exports.js';
|
|
2
|
-
export const memberExpression = (node, parent, src, options, shadowed, extras) => {
|
|
2
|
+
export const memberExpression = (node, parent, src, options, shadowed, extras, useExportsBag = true, rewriteExports = true) => {
|
|
3
3
|
if (options.target === 'module') {
|
|
4
|
-
if (
|
|
5
|
-
|
|
4
|
+
if (rewriteExports && !useExportsBag) {
|
|
5
|
+
if (parent?.type === 'MemberExpression' && parent.object === node && parent.property.type === 'Identifier') {
|
|
6
|
+
const baseIsExportsIdent = node.object.type === 'Identifier' && node.object.name === 'exports';
|
|
7
|
+
const baseIsModuleExports = node.object.type === 'Identifier' && node.object.name === 'module' && node.property.type === 'Identifier' && node.property.name === 'exports';
|
|
8
|
+
if (baseIsExportsIdent || baseIsModuleExports) {
|
|
9
|
+
src.update(parent.start, parent.end, parent.property.name);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
if (node.object.type === 'Identifier' && node.property.type === 'Identifier' && node.object.name === 'module' && node.property.name === 'exports') {
|
|
14
|
+
src.update(node.start, node.end, 'undefined');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
6
17
|
}
|
|
7
|
-
if (node.object.type === 'Identifier' && node.
|
|
8
|
-
src.update(node.start, node.end, exportsRename);
|
|
18
|
+
if (rewriteExports && (node.object.type === 'Identifier' && shadowed?.has(node.object.name) || node.property.type === 'Identifier' && shadowed?.has(node.property.name))) {
|
|
9
19
|
return;
|
|
10
20
|
}
|
|
21
|
+
if (rewriteExports) {
|
|
22
|
+
if (node.object.type === 'Identifier' && node.property.type === 'Identifier' && node.object.name === 'module' && node.property.name === 'exports') {
|
|
23
|
+
if (useExportsBag) {
|
|
24
|
+
src.update(node.start, node.end, exportsRename);
|
|
25
|
+
}
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
11
29
|
if (node.object.type === 'Identifier' && node.property.type === 'Identifier' && node.object.name === 'require') {
|
|
12
30
|
const {
|
|
13
31
|
start,
|
|
@@ -26,6 +44,10 @@ export const memberExpression = (node, parent, src, options, shadowed, extras) =
|
|
|
26
44
|
src.update(start, end, 'import.meta.main');
|
|
27
45
|
break;
|
|
28
46
|
case 'resolve':
|
|
47
|
+
if (options.transformSyntax !== true) {
|
|
48
|
+
src.update(start, end, 'import.meta.resolve');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
29
51
|
extras?.onRequireResolve?.();
|
|
30
52
|
src.update(start, end, extras?.requireResolveName ?? 'import.meta.resolve');
|
|
31
53
|
break;
|
|
@@ -9,5 +9,5 @@ type MemberExpressionExtras = {
|
|
|
9
9
|
end: number;
|
|
10
10
|
}) => void;
|
|
11
11
|
};
|
|
12
|
-
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions, shadowed?: Set<string>, extras?: MemberExpressionExtras) => void;
|
|
12
|
+
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions, shadowed?: Set<string>, extras?: MemberExpressionExtras, useExportsBag?: boolean, rewriteExports?: boolean) => void;
|
|
13
13
|
export {};
|
|
@@ -9,5 +9,5 @@ type MemberExpressionExtras = {
|
|
|
9
9
|
end: number;
|
|
10
10
|
}) => void;
|
|
11
11
|
};
|
|
12
|
-
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions, shadowed?: Set<string>, extras?: MemberExpressionExtras) => void;
|
|
12
|
+
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions, shadowed?: Set<string>, extras?: MemberExpressionExtras, useExportsBag?: boolean, rewriteExports?: boolean) => void;
|
|
13
13
|
export {};
|
package/dist/module.js
CHANGED
|
@@ -8,6 +8,7 @@ type IdentifierArg = {
|
|
|
8
8
|
opts: FormatterOptions;
|
|
9
9
|
meta: ExportsMeta;
|
|
10
10
|
shadowed?: Set<string>;
|
|
11
|
+
useExportsBag?: boolean;
|
|
11
12
|
};
|
|
12
|
-
export declare const identifier: ({ node, ancestors, code, opts, meta, shadowed, }: IdentifierArg) => void;
|
|
13
|
+
export declare const identifier: ({ node, ancestors, code, opts, meta, shadowed, useExportsBag, }: IdentifierArg) => void;
|
|
13
14
|
export {};
|
|
@@ -9,5 +9,5 @@ type MemberExpressionExtras = {
|
|
|
9
9
|
end: number;
|
|
10
10
|
}) => void;
|
|
11
11
|
};
|
|
12
|
-
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions, shadowed?: Set<string>, extras?: MemberExpressionExtras) => void;
|
|
12
|
+
export declare const memberExpression: (node: MemberExpression, parent: Node | null, src: MagicString, options: FormatterOptions, shadowed?: Set<string>, extras?: MemberExpressionExtras, useExportsBag?: boolean, rewriteExports?: boolean) => void;
|
|
13
13
|
export {};
|
package/dist/src/types.d.ts
CHANGED
|
@@ -6,8 +6,13 @@ export type ModuleOptions = {
|
|
|
6
6
|
target: 'module' | 'commonjs';
|
|
7
7
|
/** Explicit source type; auto infers from file extension. */
|
|
8
8
|
sourceType?: 'auto' | 'module' | 'commonjs';
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Enable syntax transforms beyond parsing.
|
|
11
|
+
* - true: full CJS↔ESM lowering/raising
|
|
12
|
+
* - 'globals-only': rewrite module-global differences (import.meta, __dirname/filename, require.main shims) while leaving import/export shapes untouched
|
|
13
|
+
* - false/undefined: no syntax transforms
|
|
14
|
+
*/
|
|
15
|
+
transformSyntax?: boolean | 'globals-only';
|
|
11
16
|
/** How to emit live bindings for ESM exports. */
|
|
12
17
|
liveBindings?: 'strict' | 'loose' | 'off';
|
|
13
18
|
/** Rewrite import specifiers (e.g. add extensions). */
|
|
@@ -32,6 +37,8 @@ export type ModuleOptions = {
|
|
|
32
37
|
nestedRequireStrategy?: 'create-require' | 'dynamic-import';
|
|
33
38
|
/** Default interop style for CommonJS default imports. */
|
|
34
39
|
cjsDefault?: 'module-exports' | 'auto' | 'none';
|
|
40
|
+
/** Emit idiomatic exports when raising CJS to ESM. */
|
|
41
|
+
idiomaticExports?: 'off' | 'safe' | 'aggressive';
|
|
35
42
|
/** Handling for top-level await constructs. */
|
|
36
43
|
topLevelAwait?: 'error' | 'wrap' | 'preserve';
|
|
37
44
|
/** Optional diagnostics sink for warnings/errors emitted during transform. */
|
|
@@ -67,6 +74,7 @@ export type CjsExport = {
|
|
|
67
74
|
via: Set<'exports' | 'module.exports'>;
|
|
68
75
|
reassignments: SpannedNode[];
|
|
69
76
|
hasGetter?: boolean;
|
|
77
|
+
hasNonTopLevelWrite?: boolean;
|
|
70
78
|
};
|
|
71
79
|
export type IdentMeta = {
|
|
72
80
|
declare: SpannedNode[];
|
package/dist/types.d.cts
CHANGED
|
@@ -6,8 +6,13 @@ export type ModuleOptions = {
|
|
|
6
6
|
target: 'module' | 'commonjs';
|
|
7
7
|
/** Explicit source type; auto infers from file extension. */
|
|
8
8
|
sourceType?: 'auto' | 'module' | 'commonjs';
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Enable syntax transforms beyond parsing.
|
|
11
|
+
* - true: full CJS↔ESM lowering/raising
|
|
12
|
+
* - 'globals-only': rewrite module-global differences (import.meta, __dirname/filename, require.main shims) while leaving import/export shapes untouched
|
|
13
|
+
* - false/undefined: no syntax transforms
|
|
14
|
+
*/
|
|
15
|
+
transformSyntax?: boolean | 'globals-only';
|
|
11
16
|
/** How to emit live bindings for ESM exports. */
|
|
12
17
|
liveBindings?: 'strict' | 'loose' | 'off';
|
|
13
18
|
/** Rewrite import specifiers (e.g. add extensions). */
|
|
@@ -32,6 +37,8 @@ export type ModuleOptions = {
|
|
|
32
37
|
nestedRequireStrategy?: 'create-require' | 'dynamic-import';
|
|
33
38
|
/** Default interop style for CommonJS default imports. */
|
|
34
39
|
cjsDefault?: 'module-exports' | 'auto' | 'none';
|
|
40
|
+
/** Emit idiomatic exports when raising CJS to ESM. */
|
|
41
|
+
idiomaticExports?: 'off' | 'safe' | 'aggressive';
|
|
35
42
|
/** Handling for top-level await constructs. */
|
|
36
43
|
topLevelAwait?: 'error' | 'wrap' | 'preserve';
|
|
37
44
|
/** Optional diagnostics sink for warnings/errors emitted during transform. */
|
|
@@ -67,6 +74,7 @@ export type CjsExport = {
|
|
|
67
74
|
via: Set<'exports' | 'module.exports'>;
|
|
68
75
|
reassignments: SpannedNode[];
|
|
69
76
|
hasGetter?: boolean;
|
|
77
|
+
hasNonTopLevelWrite?: boolean;
|
|
70
78
|
};
|
|
71
79
|
export type IdentMeta = {
|
|
72
80
|
declare: SpannedNode[];
|
package/dist/types.d.ts
CHANGED
|
@@ -6,8 +6,13 @@ export type ModuleOptions = {
|
|
|
6
6
|
target: 'module' | 'commonjs';
|
|
7
7
|
/** Explicit source type; auto infers from file extension. */
|
|
8
8
|
sourceType?: 'auto' | 'module' | 'commonjs';
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Enable syntax transforms beyond parsing.
|
|
11
|
+
* - true: full CJS↔ESM lowering/raising
|
|
12
|
+
* - 'globals-only': rewrite module-global differences (import.meta, __dirname/filename, require.main shims) while leaving import/export shapes untouched
|
|
13
|
+
* - false/undefined: no syntax transforms
|
|
14
|
+
*/
|
|
15
|
+
transformSyntax?: boolean | 'globals-only';
|
|
11
16
|
/** How to emit live bindings for ESM exports. */
|
|
12
17
|
liveBindings?: 'strict' | 'loose' | 'off';
|
|
13
18
|
/** Rewrite import specifiers (e.g. add extensions). */
|
|
@@ -32,6 +37,8 @@ export type ModuleOptions = {
|
|
|
32
37
|
nestedRequireStrategy?: 'create-require' | 'dynamic-import';
|
|
33
38
|
/** Default interop style for CommonJS default imports. */
|
|
34
39
|
cjsDefault?: 'module-exports' | 'auto' | 'none';
|
|
40
|
+
/** Emit idiomatic exports when raising CJS to ESM. */
|
|
41
|
+
idiomaticExports?: 'off' | 'safe' | 'aggressive';
|
|
35
42
|
/** Handling for top-level await constructs. */
|
|
36
43
|
topLevelAwait?: 'error' | 'wrap' | 'preserve';
|
|
37
44
|
/** Optional diagnostics sink for warnings/errors emitted during transform. */
|
|
@@ -67,6 +74,7 @@ export type CjsExport = {
|
|
|
67
74
|
via: Set<'exports' | 'module.exports'>;
|
|
68
75
|
reassignments: SpannedNode[];
|
|
69
76
|
hasGetter?: boolean;
|
|
77
|
+
hasNonTopLevelWrite?: boolean;
|
|
70
78
|
};
|
|
71
79
|
export type IdentMeta = {
|
|
72
80
|
declare: SpannedNode[];
|
package/dist/utils/exports.js
CHANGED
|
@@ -63,6 +63,12 @@ const collectCjsExports = async ast => {
|
|
|
63
63
|
const localToExport = new Map();
|
|
64
64
|
const aliases = new Map();
|
|
65
65
|
const literals = new Map();
|
|
66
|
+
let hasUnsupportedExportWrite = false;
|
|
67
|
+
const isTopLevelWrite = ancestors => {
|
|
68
|
+
const parent = ancestors[ancestors.length - 2];
|
|
69
|
+
const grandparent = ancestors[ancestors.length - 3];
|
|
70
|
+
return grandparent?.type === 'Program' && (parent?.type === 'ExpressionStatement' || parent?.type === 'VariableDeclaration');
|
|
71
|
+
};
|
|
66
72
|
const addExport = (ref, node, rhs, options) => {
|
|
67
73
|
const entry = exportsMap.get(ref.key) ?? {
|
|
68
74
|
key: ref.key,
|
|
@@ -75,6 +81,9 @@ const collectCjsExports = async ast => {
|
|
|
75
81
|
if (options?.hasGetter) {
|
|
76
82
|
entry.hasGetter = true;
|
|
77
83
|
}
|
|
84
|
+
if (options?.topLevel === false) {
|
|
85
|
+
entry.hasNonTopLevelWrite = true;
|
|
86
|
+
}
|
|
78
87
|
if (rhs) {
|
|
79
88
|
entry.fromIdentifier ??= rhs.name;
|
|
80
89
|
const set = localToExport.get(rhs.name) ?? new Set();
|
|
@@ -104,9 +113,15 @@ const collectCjsExports = async ast => {
|
|
|
104
113
|
const target = resolveExportTarget(node.left, aliases, literals, ancestors);
|
|
105
114
|
if (target) {
|
|
106
115
|
const rhsIdent = node.right.type === 'Identifier' ? node.right : undefined;
|
|
107
|
-
|
|
116
|
+
const topLevel = isTopLevelWrite(ancestors);
|
|
117
|
+
addExport(target, node, rhsIdent, {
|
|
118
|
+
topLevel
|
|
119
|
+
});
|
|
108
120
|
return;
|
|
109
121
|
}
|
|
122
|
+
if (node.left.type === 'MemberExpression' && resolveBase(node.left.object, aliases, ancestors)) {
|
|
123
|
+
hasUnsupportedExportWrite = true;
|
|
124
|
+
}
|
|
110
125
|
if (node.left.type === 'Identifier') {
|
|
111
126
|
const keys = localToExport.get(node.left.name);
|
|
112
127
|
if (keys) {
|
|
@@ -123,7 +138,12 @@ const collectCjsExports = async ast => {
|
|
|
123
138
|
const findExportRefs = pattern => {
|
|
124
139
|
if (pattern.type === 'MemberExpression') {
|
|
125
140
|
const ref = resolveExportTarget(pattern, aliases, literals, ancestors);
|
|
126
|
-
if (ref)
|
|
141
|
+
if (ref) {
|
|
142
|
+
const topLevel = isTopLevelWrite(ancestors);
|
|
143
|
+
addExport(ref, node, undefined, {
|
|
144
|
+
topLevel
|
|
145
|
+
});
|
|
146
|
+
}
|
|
127
147
|
return;
|
|
128
148
|
}
|
|
129
149
|
if (pattern.type === 'ObjectPattern') {
|
|
@@ -165,10 +185,13 @@ const collectCjsExports = async ast => {
|
|
|
165
185
|
if (prop.value.type === 'Identifier') {
|
|
166
186
|
rhsIdent = prop.value;
|
|
167
187
|
}
|
|
188
|
+
const topLevel = isTopLevelWrite(ancestors);
|
|
168
189
|
addExport({
|
|
169
190
|
key: keyName,
|
|
170
191
|
via: ref
|
|
171
|
-
}, node, rhsIdent
|
|
192
|
+
}, node, rhsIdent, {
|
|
193
|
+
topLevel
|
|
194
|
+
});
|
|
172
195
|
}
|
|
173
196
|
}
|
|
174
197
|
}
|
|
@@ -197,11 +220,13 @@ const collectCjsExports = async ast => {
|
|
|
197
220
|
// Setter-only doesn’t create a readable export; ignore beyond marking write
|
|
198
221
|
}
|
|
199
222
|
}
|
|
223
|
+
const topLevel = isTopLevelWrite(ancestors);
|
|
200
224
|
addExport({
|
|
201
225
|
key: keyName,
|
|
202
226
|
via: target
|
|
203
227
|
}, node, rhsIdent, {
|
|
204
|
-
hasGetter
|
|
228
|
+
hasGetter,
|
|
229
|
+
topLevel
|
|
205
230
|
});
|
|
206
231
|
}
|
|
207
232
|
|
|
@@ -228,17 +253,20 @@ const collectCjsExports = async ast => {
|
|
|
228
253
|
hasGetter = true;
|
|
229
254
|
}
|
|
230
255
|
}
|
|
256
|
+
const topLevel = isTopLevelWrite(ancestors);
|
|
231
257
|
addExport({
|
|
232
258
|
key: keyName,
|
|
233
259
|
via: target
|
|
234
260
|
}, node, rhsIdent, {
|
|
235
|
-
hasGetter
|
|
261
|
+
hasGetter,
|
|
262
|
+
topLevel
|
|
236
263
|
});
|
|
237
264
|
}
|
|
238
265
|
}
|
|
239
266
|
}
|
|
240
267
|
}
|
|
241
268
|
});
|
|
269
|
+
exportsMap.hasUnsupportedExportWrite = hasUnsupportedExportWrite;
|
|
242
270
|
return exportsMap;
|
|
243
271
|
};
|
|
244
272
|
export { exportsRename, requireMainRgx, collectCjsExports };
|