@knighted/module 1.0.0-beta.0 → 1.0.0-beta.1
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/dist/cjs/exports.d.cts +6 -0
- package/dist/cjs/format.cjs +45 -12
- package/dist/cjs/format.d.cts +3 -2
- package/dist/cjs/formatters/assignmentExpression.cjs +1 -1
- package/dist/cjs/formatters/identifier.cjs +4 -4
- package/dist/cjs/formatters/memberExpression.cjs +2 -2
- package/dist/cjs/identifiers.d.cts +19 -0
- package/dist/cjs/lang.d.cts +4 -0
- package/dist/cjs/module.cjs +4 -4
- package/dist/cjs/types.d.cts +8 -0
- package/dist/cjs/url.d.cts +2 -0
- package/dist/cjs/utils/exports.cjs +227 -0
- package/dist/cjs/utils/identifiers.cjs +190 -0
- package/dist/cjs/utils/lang.cjs +25 -0
- package/dist/cjs/utils/url.cjs +16 -0
- package/dist/cjs/utils.cjs +71 -1
- package/dist/cjs/utils.d.cts +3 -2
- package/dist/exports.d.ts +6 -0
- package/dist/format.d.ts +3 -2
- package/dist/format.js +42 -9
- package/dist/formatters/assignmentExpression.js +1 -1
- package/dist/formatters/identifier.js +2 -2
- package/dist/formatters/memberExpression.js +1 -1
- package/dist/identifiers.d.ts +19 -0
- package/dist/lang.d.ts +4 -0
- package/dist/module.js +3 -3
- package/dist/src/format.d.ts +9 -0
- package/dist/src/module.d.ts +3 -0
- package/dist/src/parse.d.ts +2 -0
- package/dist/src/types.d.ts +43 -0
- package/dist/src/utils/exports.d.ts +6 -0
- package/dist/src/utils/identifiers.d.ts +19 -0
- package/dist/src/utils/lang.d.ts +4 -0
- package/dist/src/utils/url.d.ts +2 -0
- package/dist/src/utils.d.ts +26 -0
- package/dist/src/walk.d.ts +20 -0
- package/dist/types.d.ts +8 -0
- package/dist/url.d.ts +2 -0
- package/dist/utils/exports.js +221 -0
- package/dist/utils/identifiers.js +183 -0
- package/dist/utils/lang.js +20 -0
- package/dist/utils/url.js +10 -0
- package/dist/utils.d.ts +3 -2
- package/dist/utils.js +70 -1
- package/package.json +4 -4
- /package/dist/{formatters → src/formatters}/assignmentExpression.d.ts +0 -0
- /package/dist/{formatters → src/formatters}/expressionStatement.d.ts +0 -0
- /package/dist/{formatters → src/formatters}/identifier.d.ts +0 -0
- /package/dist/{formatters → src/formatters}/memberExpression.d.ts +0 -0
- /package/dist/{formatters → src/formatters}/metaProperty.d.ts +0 -0
- /package/dist/{helpers → src/helpers}/identifier.d.ts +0 -0
- /package/dist/{helpers → src/helpers}/scope.d.ts +0 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Node } from 'oxc-parser';
|
|
2
|
+
import type { CjsExport } from '../types.cjs';
|
|
3
|
+
declare const exportsRename = "__exports";
|
|
4
|
+
declare const requireMainRgx: RegExp;
|
|
5
|
+
declare const collectCjsExports: (ast: Node) => Promise<Map<string, CjsExport>>;
|
|
6
|
+
export { exportsRename, requireMainRgx, collectCjsExports };
|
package/dist/cjs/format.cjs
CHANGED
|
@@ -5,17 +5,20 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.format = void 0;
|
|
7
7
|
var _magicString = _interopRequireDefault(require("magic-string"));
|
|
8
|
-
var _identifier = require("
|
|
9
|
-
var _metaProperty = require("
|
|
10
|
-
var _memberExpression = require("
|
|
11
|
-
var _assignmentExpression = require("
|
|
12
|
-
var
|
|
13
|
-
var
|
|
14
|
-
var
|
|
8
|
+
var _identifier = require("#formatters/identifier.js");
|
|
9
|
+
var _metaProperty = require("#formatters/metaProperty.js");
|
|
10
|
+
var _memberExpression = require("#formatters/memberExpression.js");
|
|
11
|
+
var _assignmentExpression = require("#formatters/assignmentExpression.js");
|
|
12
|
+
var _url = require("#utils/url.js");
|
|
13
|
+
var _exports = require("#utils/exports.js");
|
|
14
|
+
var _identifiers = require("#utils/identifiers.js");
|
|
15
|
+
var _identifier2 = require("#helpers/identifier.js");
|
|
16
|
+
var _walk = require("#walk");
|
|
15
17
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
18
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
+
* Node added support for import.meta.main.
|
|
20
|
+
* Added in: v24.2.0, v22.18.0
|
|
21
|
+
* @see https://nodejs.org/api/esm.html#importmetamain
|
|
19
22
|
*/
|
|
20
23
|
const format = async (src, ast, opts) => {
|
|
21
24
|
const code = new _magicString.default(src);
|
|
@@ -25,14 +28,15 @@ const format = async (src, ast, opts) => {
|
|
|
25
28
|
hasDefaultExportBeenReassigned: false,
|
|
26
29
|
hasDefaultExportBeenAssigned: false
|
|
27
30
|
};
|
|
28
|
-
await (0,
|
|
31
|
+
const exportTable = opts.target === 'module' ? await (0, _exports.collectCjsExports)(ast.program) : null;
|
|
32
|
+
await (0, _identifiers.collectModuleIdentifiers)(ast.program);
|
|
29
33
|
if (opts.target === 'module' && opts.transformSyntax) {
|
|
30
34
|
/**
|
|
31
35
|
* Prepare ESM output by renaming `exports` to `__exports` and seeding an
|
|
32
36
|
* `import.meta.filename` touch so import.meta is present even when the
|
|
33
37
|
* original source never referenced it.
|
|
34
38
|
*/
|
|
35
|
-
code.prepend(`let ${
|
|
39
|
+
code.prepend(`let ${_exports.exportsRename} = {};
|
|
36
40
|
void import.meta.filename;
|
|
37
41
|
`);
|
|
38
42
|
}
|
|
@@ -53,7 +57,7 @@ void import.meta.filename;
|
|
|
53
57
|
*/
|
|
54
58
|
if (node.type === 'AssignmentExpression' && node.left.type === 'MemberExpression' && node.left.object.type === 'MetaProperty' && node.left.property.type === 'Identifier' && node.left.property.name === 'url') {
|
|
55
59
|
if (node.right.type === 'Literal' && typeof node.right.value === 'string') {
|
|
56
|
-
if (!(0,
|
|
60
|
+
if (!(0, _url.isValidUrl)(node.right.value)) {
|
|
57
61
|
const rhs = code.snip(node.right.start, node.right.end).toString();
|
|
58
62
|
const assignment = code.snip(node.start, node.end).toString();
|
|
59
63
|
code.update(node.start, node.end, `/* Invalid assignment: ${rhs} is not a URL. ${assignment} */`);
|
|
@@ -110,6 +114,35 @@ void import.meta.filename;
|
|
|
110
114
|
}
|
|
111
115
|
}
|
|
112
116
|
});
|
|
117
|
+
if (opts.target === 'module' && opts.transformSyntax && exportTable) {
|
|
118
|
+
const isValidExportName = name => /^[$A-Z_a-z][$\w]*$/.test(name);
|
|
119
|
+
const asExportName = name => isValidExportName(name) ? name : JSON.stringify(name);
|
|
120
|
+
const accessProp = name => isValidExportName(name) ? `${_exports.exportsRename}.${name}` : `${_exports.exportsRename}[${JSON.stringify(name)}]`;
|
|
121
|
+
const tempNameFor = name => {
|
|
122
|
+
const sanitized = name.replace(/[^$\w]/g, '_') || 'value';
|
|
123
|
+
const safe = /^[0-9]/.test(sanitized) ? `_${sanitized}` : sanitized;
|
|
124
|
+
return `__export_${safe}`;
|
|
125
|
+
};
|
|
126
|
+
const lines = [];
|
|
127
|
+
const defaultEntry = exportTable.get('default');
|
|
128
|
+
if (defaultEntry) {
|
|
129
|
+
const def = defaultEntry.fromIdentifier ?? _exports.exportsRename;
|
|
130
|
+
lines.push(`export default ${def};`);
|
|
131
|
+
}
|
|
132
|
+
for (const [key, entry] of exportTable) {
|
|
133
|
+
if (key === 'default') continue;
|
|
134
|
+
if (entry.fromIdentifier) {
|
|
135
|
+
lines.push(`export { ${entry.fromIdentifier} as ${asExportName(key)} };`);
|
|
136
|
+
} else {
|
|
137
|
+
const temp = tempNameFor(key);
|
|
138
|
+
lines.push(`const ${temp} = ${accessProp(key)};`);
|
|
139
|
+
lines.push(`export { ${temp} as ${asExportName(key)} };`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (lines.length) {
|
|
143
|
+
code.append(`\n${lines.join('\n')}\n`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
113
146
|
return code.toString();
|
|
114
147
|
};
|
|
115
148
|
exports.format = format;
|
package/dist/cjs/format.d.cts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { ParseResult } from 'oxc-parser';
|
|
2
2
|
import type { FormatterOptions } from './types.cjs';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Node added support for import.meta.main.
|
|
5
|
+
* Added in: v24.2.0, v22.18.0
|
|
6
|
+
* @see https://nodejs.org/api/esm.html#importmetamain
|
|
6
7
|
*/
|
|
7
8
|
declare const format: (src: string, ast: ParseResult, opts: FormatterOptions) => Promise<string>;
|
|
8
9
|
export { format };
|
|
@@ -4,8 +4,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.identifier = void 0;
|
|
7
|
-
var
|
|
8
|
-
var _identifier = require("
|
|
7
|
+
var _exports = require("#utils/exports.js");
|
|
8
|
+
var _identifier = require("#helpers/identifier.js");
|
|
9
9
|
const identifier = ({
|
|
10
10
|
node,
|
|
11
11
|
ancestors,
|
|
@@ -34,10 +34,10 @@ const identifier = ({
|
|
|
34
34
|
// The code is reassigning `exports` to something else.
|
|
35
35
|
|
|
36
36
|
meta.hasExportsBeenReassigned = true;
|
|
37
|
-
code.update(parent.left.start, parent.left.end,
|
|
37
|
+
code.update(parent.left.start, parent.left.end, _exports.exportsRename);
|
|
38
38
|
}
|
|
39
39
|
if (_identifier.identifier.isModuleScope(ancestors) && !_identifier.identifier.isFunctionExpressionId(ancestors) && !_identifier.identifier.isExportSpecifierAlias(ancestors) && !_identifier.identifier.isClassPropertyKey(ancestors) && !_identifier.identifier.isMethodDefinitionKey(ancestors) && !_identifier.identifier.isMemberKey(ancestors) && !_identifier.identifier.isPropertyKey(ancestors) && !_identifier.identifier.isIife(ancestors)) {
|
|
40
|
-
code.update(start, end,
|
|
40
|
+
code.update(start, end, _exports.exportsRename);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -4,11 +4,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.memberExpression = void 0;
|
|
7
|
-
var
|
|
7
|
+
var _exports = require("#utils/exports.js");
|
|
8
8
|
const memberExpression = (node, parent, src, options) => {
|
|
9
9
|
if (options.target === 'module') {
|
|
10
10
|
if (node.object.type === 'Identifier' && node.property.type === 'Identifier' && node.object.name === 'module' && node.property.name === 'exports') {
|
|
11
|
-
src.update(node.start, node.end,
|
|
11
|
+
src.update(node.start, node.end, _exports.exportsRename);
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
if (node.object.type === 'Identifier' && node.property.type === 'Identifier' && node.object.name === 'require') {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Node } from 'oxc-parser';
|
|
2
|
+
import type { IdentMeta, Scope } from '../types.cjs';
|
|
3
|
+
declare const collectScopeIdentifiers: (node: Node, scopes: Scope[]) => void;
|
|
4
|
+
/**
|
|
5
|
+
* Collects all module scope identifiers in the AST.
|
|
6
|
+
*
|
|
7
|
+
* Ignores identifiers that are in functions or classes.
|
|
8
|
+
* Ignores new scopes for StaticBlock nodes (can only reference static class members).
|
|
9
|
+
*
|
|
10
|
+
* Special case handling for these which create their own scopes,
|
|
11
|
+
* but are also valid module scope identifiers:
|
|
12
|
+
* - ClassDeclaration
|
|
13
|
+
* - FunctionDeclaration
|
|
14
|
+
*
|
|
15
|
+
* Special case handling for var inside BlockStatement
|
|
16
|
+
* which are also valid module scope identifiers.
|
|
17
|
+
*/
|
|
18
|
+
declare const collectModuleIdentifiers: (ast: Node, hoisting?: boolean) => Promise<Map<string, IdentMeta>>;
|
|
19
|
+
export { collectScopeIdentifiers, collectModuleIdentifiers };
|
package/dist/cjs/module.cjs
CHANGED
|
@@ -7,9 +7,9 @@ exports.transform = void 0;
|
|
|
7
7
|
var _nodePath = require("node:path");
|
|
8
8
|
var _promises = require("node:fs/promises");
|
|
9
9
|
var _specifier = require("@knighted/specifier");
|
|
10
|
-
var _parse = require("
|
|
11
|
-
var _format = require("
|
|
12
|
-
var
|
|
10
|
+
var _parse = require("#parse");
|
|
11
|
+
var _format = require("#format");
|
|
12
|
+
var _lang = require("#utils/lang.js");
|
|
13
13
|
const defaultOptions = {
|
|
14
14
|
target: 'commonjs',
|
|
15
15
|
sourceType: 'auto',
|
|
@@ -34,7 +34,7 @@ const transform = async (filename, options = defaultOptions) => {
|
|
|
34
34
|
const ast = (0, _parse.parse)(filename, code);
|
|
35
35
|
let source = await (0, _format.format)(code, ast, opts);
|
|
36
36
|
if (opts.rewriteSpecifier) {
|
|
37
|
-
const code = await _specifier.specifier.updateSrc(source, (0,
|
|
37
|
+
const code = await _specifier.specifier.updateSrc(source, (0, _lang.getLangFromExt)(filename), ({
|
|
38
38
|
value
|
|
39
39
|
}) => {
|
|
40
40
|
if (typeof opts.rewriteSpecifier === 'function') {
|
package/dist/cjs/types.d.cts
CHANGED
|
@@ -21,6 +21,14 @@ export type ExportsMeta = {
|
|
|
21
21
|
hasDefaultExportBeenAssigned: boolean;
|
|
22
22
|
defaultExportValue: unknown;
|
|
23
23
|
};
|
|
24
|
+
export type CjsExport = {
|
|
25
|
+
key: string;
|
|
26
|
+
writes: SpannedNode[];
|
|
27
|
+
fromIdentifier?: string;
|
|
28
|
+
via: Set<'exports' | 'module.exports'>;
|
|
29
|
+
reassignments: SpannedNode[];
|
|
30
|
+
hasGetter?: boolean;
|
|
31
|
+
};
|
|
24
32
|
export type IdentMeta = {
|
|
25
33
|
declare: SpannedNode[];
|
|
26
34
|
read: SpannedNode[];
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.requireMainRgx = exports.exportsRename = exports.collectCjsExports = void 0;
|
|
7
|
+
var _walk = require("#walk");
|
|
8
|
+
const exportsRename = exports.exportsRename = '__exports';
|
|
9
|
+
const requireMainRgx = exports.requireMainRgx = /(require\.main\s*===\s*module|module\s*===\s*require\.main)/g;
|
|
10
|
+
const literalPropName = (prop, literals) => {
|
|
11
|
+
if (prop.type === 'Identifier') return literals?.get(prop.name)?.toString() ?? prop.name;
|
|
12
|
+
if (prop.type === 'Literal' && (typeof prop.value === 'string' || typeof prop.value === 'number')) {
|
|
13
|
+
return String(prop.value);
|
|
14
|
+
}
|
|
15
|
+
if (prop.type === 'TemplateLiteral' && prop.expressions.length === 0 && prop.quasis.length === 1) {
|
|
16
|
+
return prop.quasis[0].value.cooked ?? prop.quasis[0].value.raw;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
};
|
|
20
|
+
const resolveExportTarget = (node, aliases, literals) => {
|
|
21
|
+
if (node.type === 'Identifier' && node.name === 'exports') {
|
|
22
|
+
return {
|
|
23
|
+
key: 'default',
|
|
24
|
+
via: 'exports'
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (node.type === 'MemberExpression' && node.object.type === 'Identifier' && node.object.name === 'module' && node.property.type === 'Identifier' && node.property.name === 'exports') {
|
|
28
|
+
return {
|
|
29
|
+
key: 'default',
|
|
30
|
+
via: 'module.exports'
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (node.type !== 'MemberExpression') return null;
|
|
34
|
+
const base = node.object;
|
|
35
|
+
const prop = node.property;
|
|
36
|
+
const key = literalPropName(prop, literals);
|
|
37
|
+
if (!key) return null;
|
|
38
|
+
const baseVia = resolveBase(base, aliases);
|
|
39
|
+
if (!baseVia) return null;
|
|
40
|
+
if (baseVia === 'module.exports' && key === 'exports') {
|
|
41
|
+
return {
|
|
42
|
+
key: 'default',
|
|
43
|
+
via: 'module.exports'
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
key,
|
|
48
|
+
via: baseVia
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
const resolveBase = (node, aliases) => {
|
|
52
|
+
if (node.type === 'Identifier') {
|
|
53
|
+
if (node.name === 'exports') return 'exports';
|
|
54
|
+
const alias = aliases.get(node.name);
|
|
55
|
+
if (alias) return alias;
|
|
56
|
+
}
|
|
57
|
+
if (node.type === 'MemberExpression' && node.object.type === 'Identifier' && node.object.name === 'module' && node.property.type === 'Identifier' && node.property.name === 'exports') {
|
|
58
|
+
return 'module.exports';
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
};
|
|
62
|
+
const collectCjsExports = async ast => {
|
|
63
|
+
const exportsMap = new Map();
|
|
64
|
+
const localToExport = new Map();
|
|
65
|
+
const aliases = new Map();
|
|
66
|
+
const literals = new Map();
|
|
67
|
+
const addExport = (ref, node, rhs, options) => {
|
|
68
|
+
const entry = exportsMap.get(ref.key) ?? {
|
|
69
|
+
key: ref.key,
|
|
70
|
+
writes: [],
|
|
71
|
+
via: new Set(),
|
|
72
|
+
reassignments: []
|
|
73
|
+
};
|
|
74
|
+
entry.via.add(ref.via);
|
|
75
|
+
entry.writes.push(node);
|
|
76
|
+
if (options?.hasGetter) {
|
|
77
|
+
entry.hasGetter = true;
|
|
78
|
+
}
|
|
79
|
+
if (rhs) {
|
|
80
|
+
entry.fromIdentifier ??= rhs.name;
|
|
81
|
+
const set = localToExport.get(rhs.name) ?? new Set();
|
|
82
|
+
set.add(ref.key);
|
|
83
|
+
localToExport.set(rhs.name, set);
|
|
84
|
+
}
|
|
85
|
+
exportsMap.set(ref.key, entry);
|
|
86
|
+
};
|
|
87
|
+
await (0, _walk.ancestorWalk)(ast, {
|
|
88
|
+
enter(node) {
|
|
89
|
+
if (node.type === 'VariableDeclarator' && node.id.type === 'Identifier' && node.init) {
|
|
90
|
+
const via = resolveBase(node.init, aliases);
|
|
91
|
+
if (via) {
|
|
92
|
+
aliases.set(node.id.name, via);
|
|
93
|
+
}
|
|
94
|
+
if (node.init.type === 'Literal' && (typeof node.init.value === 'string' || typeof node.init.value === 'number')) {
|
|
95
|
+
literals.set(node.id.name, node.init.value);
|
|
96
|
+
}
|
|
97
|
+
if (node.init.type === 'TemplateLiteral' && node.init.expressions.length === 0 && node.init.quasis.length === 1) {
|
|
98
|
+
const cooked = node.init.quasis[0].value.cooked ?? node.init.quasis[0].value.raw;
|
|
99
|
+
literals.set(node.id.name, cooked);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (node.type === 'AssignmentExpression') {
|
|
103
|
+
const target = resolveExportTarget(node.left, aliases, literals);
|
|
104
|
+
if (target) {
|
|
105
|
+
const rhsIdent = node.right.type === 'Identifier' ? node.right : undefined;
|
|
106
|
+
addExport(target, node, rhsIdent);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (node.left.type === 'Identifier') {
|
|
110
|
+
const keys = localToExport.get(node.left.name);
|
|
111
|
+
if (keys) {
|
|
112
|
+
keys.forEach(key => {
|
|
113
|
+
const entry = exportsMap.get(key);
|
|
114
|
+
if (entry) {
|
|
115
|
+
entry.reassignments.push(node);
|
|
116
|
+
exportsMap.set(key, entry);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (node.left.type === 'ObjectPattern') {
|
|
122
|
+
for (const prop of node.left.properties) {
|
|
123
|
+
if (prop.type === 'Property' && prop.value.type === 'MemberExpression') {
|
|
124
|
+
const ref = resolveExportTarget(prop.value, aliases, literals);
|
|
125
|
+
if (ref) {
|
|
126
|
+
addExport(ref, node);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (node.type === 'CallExpression') {
|
|
133
|
+
const callee = node.callee;
|
|
134
|
+
|
|
135
|
+
// Object.assign(exports, { foo: bar })
|
|
136
|
+
if (callee.type === 'MemberExpression' && callee.object.type === 'Identifier' && callee.object.name === 'Object' && callee.property.type === 'Identifier' && callee.property.name === 'assign' && node.arguments.length >= 2) {
|
|
137
|
+
const targetArg = node.arguments[0];
|
|
138
|
+
const ref = resolveBase(targetArg, aliases);
|
|
139
|
+
if (!ref) return;
|
|
140
|
+
for (let i = 1; i < node.arguments.length; i++) {
|
|
141
|
+
const arg = node.arguments[i];
|
|
142
|
+
if (arg.type === 'ObjectExpression') {
|
|
143
|
+
for (const prop of arg.properties) {
|
|
144
|
+
if (prop.type !== 'Property') continue;
|
|
145
|
+
const keyName = literalPropName(prop.key, literals);
|
|
146
|
+
if (!keyName) continue;
|
|
147
|
+
let rhsIdent;
|
|
148
|
+
if (prop.value.type === 'Identifier') {
|
|
149
|
+
rhsIdent = prop.value;
|
|
150
|
+
}
|
|
151
|
+
addExport({
|
|
152
|
+
key: keyName,
|
|
153
|
+
via: ref
|
|
154
|
+
}, node, rhsIdent);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Object.defineProperty(exports, 'foo', { value, get, set })
|
|
161
|
+
if (callee.type === 'MemberExpression' && callee.object.type === 'Identifier' && callee.object.name === 'Object' && callee.property.type === 'Identifier' && callee.property.name === 'defineProperty' && node.arguments.length >= 3) {
|
|
162
|
+
const target = resolveBase(node.arguments[0], aliases);
|
|
163
|
+
if (!target) return;
|
|
164
|
+
const keyName = literalPropName(node.arguments[1], literals);
|
|
165
|
+
if (!keyName) return;
|
|
166
|
+
const desc = node.arguments[2];
|
|
167
|
+
if (desc.type !== 'ObjectExpression') return;
|
|
168
|
+
let rhsIdent;
|
|
169
|
+
let hasGetter = false;
|
|
170
|
+
for (const prop of desc.properties) {
|
|
171
|
+
if (prop.type !== 'Property') continue;
|
|
172
|
+
if (prop.key.type !== 'Identifier') continue;
|
|
173
|
+
if (prop.key.name === 'value' && prop.value.type === 'Identifier') {
|
|
174
|
+
rhsIdent = prop.value;
|
|
175
|
+
}
|
|
176
|
+
if (prop.key.name === 'get' && prop.value.type === 'Identifier') {
|
|
177
|
+
hasGetter = true;
|
|
178
|
+
}
|
|
179
|
+
if (prop.key.name === 'set' && prop.value.type === 'Identifier') {
|
|
180
|
+
// Setter-only doesn’t create a readable export; ignore beyond marking write
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
addExport({
|
|
184
|
+
key: keyName,
|
|
185
|
+
via: target
|
|
186
|
+
}, node, rhsIdent, {
|
|
187
|
+
hasGetter
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Object.defineProperties(exports, { foo: { value: ... }, bar: { get: ... } })
|
|
192
|
+
if (callee.type === 'MemberExpression' && callee.object.type === 'Identifier' && callee.object.name === 'Object' && callee.property.type === 'Identifier' && callee.property.name === 'defineProperties' && node.arguments.length >= 2) {
|
|
193
|
+
const target = resolveBase(node.arguments[0], aliases);
|
|
194
|
+
if (!target) return;
|
|
195
|
+
const descMap = node.arguments[1];
|
|
196
|
+
if (descMap.type !== 'ObjectExpression') return;
|
|
197
|
+
for (const prop of descMap.properties) {
|
|
198
|
+
if (prop.type !== 'Property') continue;
|
|
199
|
+
const keyName = literalPropName(prop.key, literals);
|
|
200
|
+
if (!keyName) continue;
|
|
201
|
+
if (prop.value.type !== 'ObjectExpression') continue;
|
|
202
|
+
let rhsIdent;
|
|
203
|
+
let hasGetter = false;
|
|
204
|
+
for (const descProp of prop.value.properties) {
|
|
205
|
+
if (descProp.type !== 'Property') continue;
|
|
206
|
+
if (descProp.key.type !== 'Identifier') continue;
|
|
207
|
+
if (descProp.key.name === 'value' && descProp.value.type === 'Identifier') {
|
|
208
|
+
rhsIdent = descProp.value;
|
|
209
|
+
}
|
|
210
|
+
if (descProp.key.name === 'get' && descProp.value.type === 'Identifier') {
|
|
211
|
+
hasGetter = true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
addExport({
|
|
215
|
+
key: keyName,
|
|
216
|
+
via: target
|
|
217
|
+
}, node, rhsIdent, {
|
|
218
|
+
hasGetter
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
return exportsMap;
|
|
226
|
+
};
|
|
227
|
+
exports.collectCjsExports = collectCjsExports;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.collectScopeIdentifiers = exports.collectModuleIdentifiers = void 0;
|
|
7
|
+
var _walk = require("#walk");
|
|
8
|
+
var _scope = require("#helpers/scope.js");
|
|
9
|
+
var _identifier = require("#helpers/identifier.js");
|
|
10
|
+
const collectScopeIdentifiers = (node, scopes) => {
|
|
11
|
+
const {
|
|
12
|
+
type
|
|
13
|
+
} = node;
|
|
14
|
+
switch (type) {
|
|
15
|
+
case 'BlockStatement':
|
|
16
|
+
case 'ClassBody':
|
|
17
|
+
scopes.push({
|
|
18
|
+
node,
|
|
19
|
+
type: 'Block',
|
|
20
|
+
name: type,
|
|
21
|
+
idents: new Set()
|
|
22
|
+
});
|
|
23
|
+
break;
|
|
24
|
+
case 'FunctionDeclaration':
|
|
25
|
+
case 'FunctionExpression':
|
|
26
|
+
case 'ArrowFunctionExpression':
|
|
27
|
+
{
|
|
28
|
+
const name = node.id ? node.id.name : 'anonymous';
|
|
29
|
+
const scope = {
|
|
30
|
+
node,
|
|
31
|
+
name,
|
|
32
|
+
type: 'Function',
|
|
33
|
+
idents: new Set()
|
|
34
|
+
};
|
|
35
|
+
node.params.map(param => {
|
|
36
|
+
if (param.type === 'TSParameterProperty') {
|
|
37
|
+
return param.parameter;
|
|
38
|
+
}
|
|
39
|
+
if (param.type === 'RestElement') {
|
|
40
|
+
return param.argument;
|
|
41
|
+
}
|
|
42
|
+
if (param.type === 'AssignmentPattern') {
|
|
43
|
+
return param.left;
|
|
44
|
+
}
|
|
45
|
+
return param;
|
|
46
|
+
}).filter(_identifier.identifier.isNamed).forEach(param => {
|
|
47
|
+
scope.idents.add(param.name);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* If a FunctionExpression has an id, it is a named function expression.
|
|
52
|
+
* The function expression name shadows the module scope identifier, so
|
|
53
|
+
* we don't want to count reads of module identifers that have the same name.
|
|
54
|
+
* They also do not cause a SyntaxError if the function expression name is
|
|
55
|
+
* the same as a module scope identifier.
|
|
56
|
+
*
|
|
57
|
+
* TODO: Is this necessary for FunctionDeclaration?
|
|
58
|
+
*/
|
|
59
|
+
if (node.type === 'FunctionExpression' && node.id) {
|
|
60
|
+
scope.idents.add(node.id.name);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// First add the function to any previous scopes
|
|
64
|
+
if (scopes.length > 0) {
|
|
65
|
+
scopes[scopes.length - 1].idents.add(name);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Then add the function scope to the scopes stack
|
|
69
|
+
scopes.push(scope);
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
case 'ClassDeclaration':
|
|
73
|
+
{
|
|
74
|
+
const className = node.id ? node.id.name : 'anonymous';
|
|
75
|
+
|
|
76
|
+
// First add the class to any previous scopes
|
|
77
|
+
if (scopes.length > 0) {
|
|
78
|
+
scopes[scopes.length - 1].idents.add(className);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Then add the class to the scopes stack
|
|
82
|
+
scopes.push({
|
|
83
|
+
node,
|
|
84
|
+
name: className,
|
|
85
|
+
type: 'Class',
|
|
86
|
+
idents: new Set()
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
case 'ClassExpression':
|
|
91
|
+
{}
|
|
92
|
+
break;
|
|
93
|
+
case 'VariableDeclaration':
|
|
94
|
+
if (scopes.length > 0) {
|
|
95
|
+
const scope = scopes[scopes.length - 1];
|
|
96
|
+
node.declarations.forEach(decl => {
|
|
97
|
+
if (decl.type === 'VariableDeclarator' && decl.id.type === 'Identifier') {
|
|
98
|
+
scope.idents.add(decl.id.name);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Collects all module scope identifiers in the AST.
|
|
108
|
+
*
|
|
109
|
+
* Ignores identifiers that are in functions or classes.
|
|
110
|
+
* Ignores new scopes for StaticBlock nodes (can only reference static class members).
|
|
111
|
+
*
|
|
112
|
+
* Special case handling for these which create their own scopes,
|
|
113
|
+
* but are also valid module scope identifiers:
|
|
114
|
+
* - ClassDeclaration
|
|
115
|
+
* - FunctionDeclaration
|
|
116
|
+
*
|
|
117
|
+
* Special case handling for var inside BlockStatement
|
|
118
|
+
* which are also valid module scope identifiers.
|
|
119
|
+
*/
|
|
120
|
+
exports.collectScopeIdentifiers = collectScopeIdentifiers;
|
|
121
|
+
const collectModuleIdentifiers = async (ast, hoisting = true) => {
|
|
122
|
+
const identifiers = new Map();
|
|
123
|
+
const globalReads = new Map();
|
|
124
|
+
const scopes = [];
|
|
125
|
+
await (0, _walk.ancestorWalk)(ast, {
|
|
126
|
+
enter(node, ancestors) {
|
|
127
|
+
const {
|
|
128
|
+
type
|
|
129
|
+
} = node;
|
|
130
|
+
collectScopeIdentifiers(node, scopes);
|
|
131
|
+
|
|
132
|
+
// Add module scope identifiers to the registry map
|
|
133
|
+
|
|
134
|
+
if (type === 'Identifier') {
|
|
135
|
+
const {
|
|
136
|
+
name
|
|
137
|
+
} = node;
|
|
138
|
+
const meta = identifiers.get(name) ?? {
|
|
139
|
+
declare: [],
|
|
140
|
+
read: []
|
|
141
|
+
};
|
|
142
|
+
const isDeclaration = _identifier.identifier.isDeclaration(ancestors);
|
|
143
|
+
const inScope = scopes.some(scope => scope.idents.has(name) || scope.name === name);
|
|
144
|
+
if (hoisting && !_identifier.identifier.isDeclaration(ancestors) && !_identifier.identifier.isFunctionExpressionId(ancestors) && !_identifier.identifier.isExportSpecifierAlias(ancestors) && !_identifier.identifier.isClassPropertyKey(ancestors) && !_identifier.identifier.isMethodDefinitionKey(ancestors) && !_identifier.identifier.isMemberKey(ancestors) && !_identifier.identifier.isPropertyKey(ancestors) && !_identifier.identifier.isIife(ancestors) && !inScope) {
|
|
145
|
+
if (globalReads.has(name)) {
|
|
146
|
+
globalReads.get(name)?.push(node);
|
|
147
|
+
} else {
|
|
148
|
+
globalReads.set(name, [node]);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (isDeclaration) {
|
|
152
|
+
const isModuleScope = _identifier.identifier.isModuleScope(ancestors);
|
|
153
|
+
const isClassOrFuncDeclaration = _identifier.identifier.isClassOrFuncDeclarationId(ancestors);
|
|
154
|
+
const isVarDeclarationInGlobalScope = _identifier.identifier.isVarDeclarationInGlobalScope(ancestors);
|
|
155
|
+
if (isModuleScope || isClassOrFuncDeclaration || isVarDeclarationInGlobalScope) {
|
|
156
|
+
meta.declare.push(node);
|
|
157
|
+
|
|
158
|
+
// Check for hoisted reads
|
|
159
|
+
if (hoisting && globalReads.has(name)) {
|
|
160
|
+
const reads = globalReads.get(name);
|
|
161
|
+
if (reads) {
|
|
162
|
+
reads.forEach(read => {
|
|
163
|
+
if (!meta.read.includes(read)) {
|
|
164
|
+
meta.read.push(read);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
identifiers.set(name, meta);
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
if (identifiers.has(name) && !inScope && !_identifier.identifier.isIife(ancestors) && !_identifier.identifier.isFunctionExpressionId(ancestors) && !_identifier.identifier.isExportSpecifierAlias(ancestors) && !_identifier.identifier.isClassPropertyKey(ancestors) && !_identifier.identifier.isMethodDefinitionKey(ancestors) && !_identifier.identifier.isMemberKey(ancestors) && !_identifier.identifier.isPropertyKey(ancestors)) {
|
|
173
|
+
// Closure is referencing module scope identifier
|
|
174
|
+
meta.read.push(node);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
leave(node) {
|
|
180
|
+
const {
|
|
181
|
+
type
|
|
182
|
+
} = node;
|
|
183
|
+
if (_scope.scopes.includes(type)) {
|
|
184
|
+
scopes.pop();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
return identifiers;
|
|
189
|
+
};
|
|
190
|
+
exports.collectModuleIdentifiers = collectModuleIdentifiers;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getLangFromExt = void 0;
|
|
7
|
+
var _nodePath = require("node:path");
|
|
8
|
+
// Determine language from filename extension for specifier rewrite.
|
|
9
|
+
|
|
10
|
+
const getLangFromExt = filename => {
|
|
11
|
+
const ext = (0, _nodePath.extname)(filename);
|
|
12
|
+
if (ext.endsWith('.js')) {
|
|
13
|
+
return 'js';
|
|
14
|
+
}
|
|
15
|
+
if (ext.endsWith('.ts')) {
|
|
16
|
+
return 'ts';
|
|
17
|
+
}
|
|
18
|
+
if (ext === '.tsx') {
|
|
19
|
+
return 'tsx';
|
|
20
|
+
}
|
|
21
|
+
if (ext === '.jsx') {
|
|
22
|
+
return 'jsx';
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
exports.getLangFromExt = getLangFromExt;
|