@knighted/module 1.0.0-rc.0 → 1.0.0-rc.2
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 +4 -2
- package/dist/cjs/lang.d.cts +2 -3
- package/dist/cjs/module.cjs +1 -1
- package/dist/cjs/specifier.cjs +265 -0
- package/dist/cjs/specifier.d.cts +16 -0
- package/dist/cjs/utils/lang.cjs +0 -1
- package/dist/cjs/utils.cjs +1 -18
- package/dist/cjs/utils.d.cts +1 -4
- package/dist/lang.d.cts +2 -3
- package/dist/lang.d.ts +2 -3
- package/dist/module.js +1 -1
- package/dist/specifier.d.cts +16 -0
- package/dist/specifier.d.ts +16 -0
- package/dist/specifier.js +259 -0
- package/dist/src/specifier.d.ts +16 -0
- package/dist/src/utils/lang.d.ts +2 -3
- package/dist/src/utils.d.ts +1 -4
- package/dist/utils/lang.js +0 -2
- package/dist/utils.d.cts +1 -4
- package/dist/utils.d.ts +1 -4
- package/dist/utils.js +1 -17
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ By default `@knighted/module` transforms the one-to-one [differences between ES
|
|
|
22
22
|
|
|
23
23
|
## Requirements
|
|
24
24
|
|
|
25
|
-
- Node
|
|
25
|
+
- Node 22 or 24 (tested on 22.21.1 and 24.11.1)
|
|
26
26
|
|
|
27
27
|
## Install
|
|
28
28
|
|
|
@@ -137,9 +137,11 @@ Behavior notes (defaults in parentheses)
|
|
|
137
137
|
|
|
138
138
|
See [docs/esm-to-cjs.md](docs/esm-to-cjs.md) for deeper notes on live bindings, interop helpers, top-level await behavior, and `import.meta.main` handling. For CommonJS to ESM lowering details, read [docs/cjs-to-esm.md](docs/cjs-to-esm.md).
|
|
139
139
|
|
|
140
|
+
> [!NOTE]
|
|
141
|
+
> 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`.
|
|
142
|
+
|
|
140
143
|
## Roadmap
|
|
141
144
|
|
|
142
|
-
- Remove `@knighted/specifier` and avoid double parsing.
|
|
143
145
|
- Emit source maps and clearer diagnostics for transform choices.
|
|
144
146
|
- Broaden fixtures covering live-binding and top-level await edge cases across Node versions.
|
|
145
147
|
- Benchmark scope analysis choices: compare `periscopic`, `scope-analyzer`, and `eslint-scope` on fixtures and pick the final adapter.
|
package/dist/cjs/lang.d.cts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
declare const getLangFromExt: (filename: string) => UpdateSrcLang;
|
|
1
|
+
import type { ParserOptions } from 'oxc-parser';
|
|
2
|
+
declare const getLangFromExt: (filename: string) => ParserOptions["lang"] | undefined;
|
|
4
3
|
export { getLangFromExt };
|
package/dist/cjs/module.cjs
CHANGED
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.transform = void 0;
|
|
7
7
|
var _nodePath = require("node:path");
|
|
8
8
|
var _promises = require("node:fs/promises");
|
|
9
|
-
var _specifier = require("
|
|
9
|
+
var _specifier = require("./specifier.cjs");
|
|
10
10
|
var _parse = require("#parse");
|
|
11
11
|
var _format = require("#format");
|
|
12
12
|
var _lang = require("#utils/lang.js");
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.specifier = void 0;
|
|
7
|
+
var _nodePath = require("node:path");
|
|
8
|
+
var _promises = require("node:fs/promises");
|
|
9
|
+
var _magicString = _interopRequireDefault(require("magic-string"));
|
|
10
|
+
var _oxcParser = require("oxc-parser");
|
|
11
|
+
var _walk = require("#walk");
|
|
12
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
|
+
const isStringLiteral = node => {
|
|
14
|
+
return node.type === 'Literal' && typeof node.value === 'string';
|
|
15
|
+
};
|
|
16
|
+
const isBinaryExpression = node => {
|
|
17
|
+
// Distinguish between BinaryExpression and PrivateInExpression
|
|
18
|
+
return node.type === 'BinaryExpression' && node.operator !== 'in';
|
|
19
|
+
};
|
|
20
|
+
const isCallExpression = node => {
|
|
21
|
+
return node.type === 'CallExpression' && node.callee !== undefined;
|
|
22
|
+
};
|
|
23
|
+
const formatSpecifiers = async (src, ast, cb) => {
|
|
24
|
+
const code = new _magicString.default(src);
|
|
25
|
+
const formatExpression = expression => {
|
|
26
|
+
const node = isCallExpression(expression) ? expression.arguments[0] : expression.source;
|
|
27
|
+
const {
|
|
28
|
+
type
|
|
29
|
+
} = node;
|
|
30
|
+
switch (type) {
|
|
31
|
+
case 'Literal':
|
|
32
|
+
{
|
|
33
|
+
if (isStringLiteral(node)) {
|
|
34
|
+
const {
|
|
35
|
+
start,
|
|
36
|
+
end,
|
|
37
|
+
value
|
|
38
|
+
} = node;
|
|
39
|
+
const updated = cb({
|
|
40
|
+
type: 'StringLiteral',
|
|
41
|
+
parent: expression,
|
|
42
|
+
node,
|
|
43
|
+
start,
|
|
44
|
+
end,
|
|
45
|
+
value
|
|
46
|
+
});
|
|
47
|
+
if (typeof updated === 'string') {
|
|
48
|
+
code.update(start + 1, end - 1, updated);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case 'TemplateLiteral':
|
|
54
|
+
{
|
|
55
|
+
const {
|
|
56
|
+
start,
|
|
57
|
+
end
|
|
58
|
+
} = node;
|
|
59
|
+
const value = src.slice(start + 1, end - 1);
|
|
60
|
+
const updated = cb({
|
|
61
|
+
type: 'TemplateLiteral',
|
|
62
|
+
parent: expression,
|
|
63
|
+
node,
|
|
64
|
+
start,
|
|
65
|
+
end,
|
|
66
|
+
value
|
|
67
|
+
});
|
|
68
|
+
if (typeof updated === 'string') {
|
|
69
|
+
code.update(start + 1, end - 1, updated);
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case 'BinaryExpression':
|
|
74
|
+
{
|
|
75
|
+
if (isBinaryExpression(node)) {
|
|
76
|
+
const {
|
|
77
|
+
start,
|
|
78
|
+
end
|
|
79
|
+
} = node;
|
|
80
|
+
const value = src.slice(start, end);
|
|
81
|
+
const updated = cb({
|
|
82
|
+
type: 'BinaryExpression',
|
|
83
|
+
parent: expression,
|
|
84
|
+
node,
|
|
85
|
+
start,
|
|
86
|
+
end,
|
|
87
|
+
value
|
|
88
|
+
});
|
|
89
|
+
if (typeof updated === 'string') {
|
|
90
|
+
code.update(start, end, updated);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
case 'NewExpression':
|
|
96
|
+
{
|
|
97
|
+
if (node.callee.type === 'Identifier' && node.callee.name === 'String') {
|
|
98
|
+
const {
|
|
99
|
+
start,
|
|
100
|
+
end
|
|
101
|
+
} = node;
|
|
102
|
+
const value = src.slice(start, end);
|
|
103
|
+
const updated = cb({
|
|
104
|
+
type: 'NewExpression',
|
|
105
|
+
parent: expression,
|
|
106
|
+
node,
|
|
107
|
+
start,
|
|
108
|
+
end,
|
|
109
|
+
value
|
|
110
|
+
});
|
|
111
|
+
if (typeof updated === 'string') {
|
|
112
|
+
code.update(start, end, updated);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
await (0, _walk.walk)(ast.program, {
|
|
120
|
+
enter(node) {
|
|
121
|
+
if (node.type === 'ExpressionStatement') {
|
|
122
|
+
const {
|
|
123
|
+
expression
|
|
124
|
+
} = node;
|
|
125
|
+
if (expression.type === 'ImportExpression') {
|
|
126
|
+
formatExpression(expression);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (node.type === 'CallExpression') {
|
|
130
|
+
// Handle require(), require.resolve(), import.meta.resolve()
|
|
131
|
+
if (node.callee.type === 'Identifier' && node.callee.name === 'require' || node.callee.type === 'MemberExpression' && node.callee.object.type === 'Identifier' && node.callee.object.name === 'require' && node.callee.property.type === 'Identifier' && node.callee.property.name === 'resolve' || node.callee.type === 'MemberExpression' && node.callee.object.type === 'MetaProperty' && node.callee.object.meta.name === 'import' && node.callee.property.type === 'Identifier' && node.callee.property.name === 'resolve') {
|
|
132
|
+
formatExpression(node);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (node.type === 'ArrowFunctionExpression') {
|
|
136
|
+
const {
|
|
137
|
+
body
|
|
138
|
+
} = node;
|
|
139
|
+
if (body.type === 'ImportExpression') {
|
|
140
|
+
formatExpression(body);
|
|
141
|
+
}
|
|
142
|
+
if (body.type === 'CallExpression' && body.callee.type === 'Identifier' && body.callee.name === 'require') {
|
|
143
|
+
formatExpression(body);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (node.type === 'MemberExpression' && node.object.type === 'ImportExpression' && node.property.type === 'Identifier' && node.property.name === 'then') {
|
|
147
|
+
formatExpression(node.object);
|
|
148
|
+
}
|
|
149
|
+
if (node.type === 'TSImportType') {
|
|
150
|
+
const source = node.source;
|
|
151
|
+
if (source && isStringLiteral(source)) {
|
|
152
|
+
const {
|
|
153
|
+
start,
|
|
154
|
+
end,
|
|
155
|
+
value
|
|
156
|
+
} = source;
|
|
157
|
+
const updated = cb({
|
|
158
|
+
type: 'StringLiteral',
|
|
159
|
+
node: source,
|
|
160
|
+
parent: node,
|
|
161
|
+
start,
|
|
162
|
+
end,
|
|
163
|
+
value
|
|
164
|
+
});
|
|
165
|
+
if (typeof updated === 'string') {
|
|
166
|
+
code.update(start + 1, end - 1, updated);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (node.type === 'ImportDeclaration') {
|
|
171
|
+
const {
|
|
172
|
+
source
|
|
173
|
+
} = node;
|
|
174
|
+
const {
|
|
175
|
+
start,
|
|
176
|
+
end,
|
|
177
|
+
value
|
|
178
|
+
} = source;
|
|
179
|
+
const updated = cb({
|
|
180
|
+
type: 'StringLiteral',
|
|
181
|
+
node: source,
|
|
182
|
+
parent: node,
|
|
183
|
+
start,
|
|
184
|
+
end,
|
|
185
|
+
value
|
|
186
|
+
});
|
|
187
|
+
if (typeof updated === 'string') {
|
|
188
|
+
code.update(start + 1, end - 1, updated);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (node.type === 'ExportNamedDeclaration' && node.source) {
|
|
192
|
+
const {
|
|
193
|
+
source
|
|
194
|
+
} = node;
|
|
195
|
+
const {
|
|
196
|
+
start,
|
|
197
|
+
end,
|
|
198
|
+
value
|
|
199
|
+
} = source;
|
|
200
|
+
const updated = cb({
|
|
201
|
+
type: 'StringLiteral',
|
|
202
|
+
node: source,
|
|
203
|
+
parent: node,
|
|
204
|
+
start,
|
|
205
|
+
end,
|
|
206
|
+
value
|
|
207
|
+
});
|
|
208
|
+
if (typeof updated === 'string') {
|
|
209
|
+
code.update(start + 1, end - 1, updated);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (node.type === 'ExportAllDeclaration') {
|
|
213
|
+
const {
|
|
214
|
+
source
|
|
215
|
+
} = node;
|
|
216
|
+
const {
|
|
217
|
+
start,
|
|
218
|
+
end,
|
|
219
|
+
value
|
|
220
|
+
} = source;
|
|
221
|
+
const updated = cb({
|
|
222
|
+
type: 'StringLiteral',
|
|
223
|
+
node: source,
|
|
224
|
+
parent: node,
|
|
225
|
+
start,
|
|
226
|
+
end,
|
|
227
|
+
value
|
|
228
|
+
});
|
|
229
|
+
if (typeof updated === 'string') {
|
|
230
|
+
code.update(start + 1, end - 1, updated);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
return code.toString();
|
|
236
|
+
};
|
|
237
|
+
const isValidFilename = async filename => {
|
|
238
|
+
let stats;
|
|
239
|
+
try {
|
|
240
|
+
stats = await (0, _promises.stat)(filename);
|
|
241
|
+
} catch {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
if (!stats.isFile()) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
return true;
|
|
248
|
+
};
|
|
249
|
+
const specifier = exports.specifier = {
|
|
250
|
+
async update(path, callback) {
|
|
251
|
+
const filename = (0, _nodePath.resolve)(path);
|
|
252
|
+
const validated = await isValidFilename(filename);
|
|
253
|
+
if (!validated) {
|
|
254
|
+
throw new Error(`The provided path ${path} does not resolve to a file on disk.`);
|
|
255
|
+
}
|
|
256
|
+
const src = (await (0, _promises.readFile)(filename)).toString();
|
|
257
|
+
const ast = (0, _oxcParser.parseSync)(filename, src);
|
|
258
|
+
return await formatSpecifiers(src, ast, callback);
|
|
259
|
+
},
|
|
260
|
+
async updateSrc(src, lang, callback) {
|
|
261
|
+
const filename = lang === 'ts' ? 'file.ts' : lang === 'tsx' ? 'file.tsx' : lang === 'js' ? 'file.js' : 'file.jsx';
|
|
262
|
+
const ast = (0, _oxcParser.parseSync)(filename, src);
|
|
263
|
+
return await formatSpecifiers(src, ast, callback);
|
|
264
|
+
}
|
|
265
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ParserOptions, StringLiteral, TemplateLiteral, BinaryExpression, NewExpression, ImportDeclaration, ExportNamedDeclaration, ExportAllDeclaration, TSImportType, ImportExpression, CallExpression } from 'oxc-parser';
|
|
2
|
+
type Spec = {
|
|
3
|
+
type: 'StringLiteral' | 'TemplateLiteral' | 'BinaryExpression' | 'NewExpression';
|
|
4
|
+
node: StringLiteral | TemplateLiteral | BinaryExpression | NewExpression;
|
|
5
|
+
parent: CallExpression | ImportDeclaration | ExportNamedDeclaration | ExportAllDeclaration | ImportExpression | TSImportType;
|
|
6
|
+
start: number;
|
|
7
|
+
end: number;
|
|
8
|
+
value: string;
|
|
9
|
+
};
|
|
10
|
+
type Callback = (spec: Spec) => string | void;
|
|
11
|
+
declare const specifier: {
|
|
12
|
+
update(path: string, callback: Callback): Promise<string>;
|
|
13
|
+
updateSrc(src: string, lang: ParserOptions["lang"], callback: Callback): Promise<string>;
|
|
14
|
+
};
|
|
15
|
+
export { specifier };
|
|
16
|
+
export type { Spec, Callback };
|
package/dist/cjs/utils/lang.cjs
CHANGED
|
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.getLangFromExt = void 0;
|
|
7
7
|
var _nodePath = require("node:path");
|
|
8
8
|
// Determine language from filename extension for specifier rewrite.
|
|
9
|
-
|
|
10
9
|
const getLangFromExt = filename => {
|
|
11
10
|
const ext = (0, _nodePath.extname)(filename).toLowerCase();
|
|
12
11
|
if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
|
package/dist/cjs/utils.cjs
CHANGED
|
@@ -3,27 +3,10 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.requireMainRgx = exports.isValidUrl = exports.
|
|
7
|
-
var _nodePath = require("node:path");
|
|
6
|
+
exports.requireMainRgx = exports.isValidUrl = exports.exportsRename = exports.collectScopeIdentifiers = exports.collectModuleIdentifiers = exports.collectCjsExports = void 0;
|
|
8
7
|
var _walk = require("./walk.cjs");
|
|
9
8
|
var _identifier = require("./helpers/identifier.cjs");
|
|
10
9
|
var _scopeNodes = require("./utils/scopeNodes.cjs");
|
|
11
|
-
const getLangFromExt = filename => {
|
|
12
|
-
const ext = (0, _nodePath.extname)(filename).toLowerCase();
|
|
13
|
-
if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
|
|
14
|
-
return 'js';
|
|
15
|
-
}
|
|
16
|
-
if (ext === '.ts' || ext === '.mts' || ext === '.cts') {
|
|
17
|
-
return 'ts';
|
|
18
|
-
}
|
|
19
|
-
if (ext === '.tsx') {
|
|
20
|
-
return 'tsx';
|
|
21
|
-
}
|
|
22
|
-
if (ext === '.jsx') {
|
|
23
|
-
return 'jsx';
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
exports.getLangFromExt = getLangFromExt;
|
|
27
10
|
const isValidUrl = url => {
|
|
28
11
|
try {
|
|
29
12
|
new URL(url);
|
package/dist/cjs/utils.d.cts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import type { Node } from 'oxc-parser';
|
|
2
|
-
import type { Specifier } from '@knighted/specifier';
|
|
3
2
|
import type { IdentMeta, Scope, CjsExport } from './types.cjs';
|
|
4
|
-
type UpdateSrcLang = Parameters<Specifier['updateSrc']>[1];
|
|
5
|
-
declare const getLangFromExt: (filename: string) => UpdateSrcLang;
|
|
6
3
|
declare const isValidUrl: (url: string) => boolean;
|
|
7
4
|
declare const exportsRename = "__exports";
|
|
8
5
|
declare const requireMainRgx: RegExp;
|
|
@@ -23,4 +20,4 @@ declare const collectScopeIdentifiers: (node: Node, scopes: Scope[]) => void;
|
|
|
23
20
|
* which are also valid module scope identifiers.
|
|
24
21
|
*/
|
|
25
22
|
declare const collectModuleIdentifiers: (ast: Node, hoisting?: boolean) => Promise<Map<string, IdentMeta>>;
|
|
26
|
-
export {
|
|
23
|
+
export { isValidUrl, collectScopeIdentifiers, collectModuleIdentifiers, collectCjsExports, exportsRename, requireMainRgx, };
|
package/dist/lang.d.cts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
declare const getLangFromExt: (filename: string) => UpdateSrcLang;
|
|
1
|
+
import type { ParserOptions } from 'oxc-parser';
|
|
2
|
+
declare const getLangFromExt: (filename: string) => ParserOptions["lang"] | undefined;
|
|
4
3
|
export { getLangFromExt };
|
package/dist/lang.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
declare const getLangFromExt: (filename: string) => UpdateSrcLang;
|
|
1
|
+
import type { ParserOptions } from 'oxc-parser';
|
|
2
|
+
declare const getLangFromExt: (filename: string) => ParserOptions["lang"] | undefined;
|
|
4
3
|
export { getLangFromExt };
|
package/dist/module.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
2
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { specifier } from '
|
|
3
|
+
import { specifier } from './specifier.js';
|
|
4
4
|
import { parse } from '#parse';
|
|
5
5
|
import { format } from '#format';
|
|
6
6
|
import { getLangFromExt } from '#utils/lang.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ParserOptions, StringLiteral, TemplateLiteral, BinaryExpression, NewExpression, ImportDeclaration, ExportNamedDeclaration, ExportAllDeclaration, TSImportType, ImportExpression, CallExpression } from 'oxc-parser';
|
|
2
|
+
type Spec = {
|
|
3
|
+
type: 'StringLiteral' | 'TemplateLiteral' | 'BinaryExpression' | 'NewExpression';
|
|
4
|
+
node: StringLiteral | TemplateLiteral | BinaryExpression | NewExpression;
|
|
5
|
+
parent: CallExpression | ImportDeclaration | ExportNamedDeclaration | ExportAllDeclaration | ImportExpression | TSImportType;
|
|
6
|
+
start: number;
|
|
7
|
+
end: number;
|
|
8
|
+
value: string;
|
|
9
|
+
};
|
|
10
|
+
type Callback = (spec: Spec) => string | void;
|
|
11
|
+
declare const specifier: {
|
|
12
|
+
update(path: string, callback: Callback): Promise<string>;
|
|
13
|
+
updateSrc(src: string, lang: ParserOptions["lang"], callback: Callback): Promise<string>;
|
|
14
|
+
};
|
|
15
|
+
export { specifier };
|
|
16
|
+
export type { Spec, Callback };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ParserOptions, StringLiteral, TemplateLiteral, BinaryExpression, NewExpression, ImportDeclaration, ExportNamedDeclaration, ExportAllDeclaration, TSImportType, ImportExpression, CallExpression } from 'oxc-parser';
|
|
2
|
+
type Spec = {
|
|
3
|
+
type: 'StringLiteral' | 'TemplateLiteral' | 'BinaryExpression' | 'NewExpression';
|
|
4
|
+
node: StringLiteral | TemplateLiteral | BinaryExpression | NewExpression;
|
|
5
|
+
parent: CallExpression | ImportDeclaration | ExportNamedDeclaration | ExportAllDeclaration | ImportExpression | TSImportType;
|
|
6
|
+
start: number;
|
|
7
|
+
end: number;
|
|
8
|
+
value: string;
|
|
9
|
+
};
|
|
10
|
+
type Callback = (spec: Spec) => string | void;
|
|
11
|
+
declare const specifier: {
|
|
12
|
+
update(path: string, callback: Callback): Promise<string>;
|
|
13
|
+
updateSrc(src: string, lang: ParserOptions["lang"], callback: Callback): Promise<string>;
|
|
14
|
+
};
|
|
15
|
+
export { specifier };
|
|
16
|
+
export type { Spec, Callback };
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { stat, readFile } from 'node:fs/promises';
|
|
3
|
+
import MagicString from 'magic-string';
|
|
4
|
+
import { parseSync } from 'oxc-parser';
|
|
5
|
+
import { walk } from '#walk';
|
|
6
|
+
const isStringLiteral = node => {
|
|
7
|
+
return node.type === 'Literal' && typeof node.value === 'string';
|
|
8
|
+
};
|
|
9
|
+
const isBinaryExpression = node => {
|
|
10
|
+
// Distinguish between BinaryExpression and PrivateInExpression
|
|
11
|
+
return node.type === 'BinaryExpression' && node.operator !== 'in';
|
|
12
|
+
};
|
|
13
|
+
const isCallExpression = node => {
|
|
14
|
+
return node.type === 'CallExpression' && node.callee !== undefined;
|
|
15
|
+
};
|
|
16
|
+
const formatSpecifiers = async (src, ast, cb) => {
|
|
17
|
+
const code = new MagicString(src);
|
|
18
|
+
const formatExpression = expression => {
|
|
19
|
+
const node = isCallExpression(expression) ? expression.arguments[0] : expression.source;
|
|
20
|
+
const {
|
|
21
|
+
type
|
|
22
|
+
} = node;
|
|
23
|
+
switch (type) {
|
|
24
|
+
case 'Literal':
|
|
25
|
+
{
|
|
26
|
+
if (isStringLiteral(node)) {
|
|
27
|
+
const {
|
|
28
|
+
start,
|
|
29
|
+
end,
|
|
30
|
+
value
|
|
31
|
+
} = node;
|
|
32
|
+
const updated = cb({
|
|
33
|
+
type: 'StringLiteral',
|
|
34
|
+
parent: expression,
|
|
35
|
+
node,
|
|
36
|
+
start,
|
|
37
|
+
end,
|
|
38
|
+
value
|
|
39
|
+
});
|
|
40
|
+
if (typeof updated === 'string') {
|
|
41
|
+
code.update(start + 1, end - 1, updated);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
case 'TemplateLiteral':
|
|
47
|
+
{
|
|
48
|
+
const {
|
|
49
|
+
start,
|
|
50
|
+
end
|
|
51
|
+
} = node;
|
|
52
|
+
const value = src.slice(start + 1, end - 1);
|
|
53
|
+
const updated = cb({
|
|
54
|
+
type: 'TemplateLiteral',
|
|
55
|
+
parent: expression,
|
|
56
|
+
node,
|
|
57
|
+
start,
|
|
58
|
+
end,
|
|
59
|
+
value
|
|
60
|
+
});
|
|
61
|
+
if (typeof updated === 'string') {
|
|
62
|
+
code.update(start + 1, end - 1, updated);
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
case 'BinaryExpression':
|
|
67
|
+
{
|
|
68
|
+
if (isBinaryExpression(node)) {
|
|
69
|
+
const {
|
|
70
|
+
start,
|
|
71
|
+
end
|
|
72
|
+
} = node;
|
|
73
|
+
const value = src.slice(start, end);
|
|
74
|
+
const updated = cb({
|
|
75
|
+
type: 'BinaryExpression',
|
|
76
|
+
parent: expression,
|
|
77
|
+
node,
|
|
78
|
+
start,
|
|
79
|
+
end,
|
|
80
|
+
value
|
|
81
|
+
});
|
|
82
|
+
if (typeof updated === 'string') {
|
|
83
|
+
code.update(start, end, updated);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
case 'NewExpression':
|
|
89
|
+
{
|
|
90
|
+
if (node.callee.type === 'Identifier' && node.callee.name === 'String') {
|
|
91
|
+
const {
|
|
92
|
+
start,
|
|
93
|
+
end
|
|
94
|
+
} = node;
|
|
95
|
+
const value = src.slice(start, end);
|
|
96
|
+
const updated = cb({
|
|
97
|
+
type: 'NewExpression',
|
|
98
|
+
parent: expression,
|
|
99
|
+
node,
|
|
100
|
+
start,
|
|
101
|
+
end,
|
|
102
|
+
value
|
|
103
|
+
});
|
|
104
|
+
if (typeof updated === 'string') {
|
|
105
|
+
code.update(start, end, updated);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
await walk(ast.program, {
|
|
113
|
+
enter(node) {
|
|
114
|
+
if (node.type === 'ExpressionStatement') {
|
|
115
|
+
const {
|
|
116
|
+
expression
|
|
117
|
+
} = node;
|
|
118
|
+
if (expression.type === 'ImportExpression') {
|
|
119
|
+
formatExpression(expression);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (node.type === 'CallExpression') {
|
|
123
|
+
// Handle require(), require.resolve(), import.meta.resolve()
|
|
124
|
+
if (node.callee.type === 'Identifier' && node.callee.name === 'require' || node.callee.type === 'MemberExpression' && node.callee.object.type === 'Identifier' && node.callee.object.name === 'require' && node.callee.property.type === 'Identifier' && node.callee.property.name === 'resolve' || node.callee.type === 'MemberExpression' && node.callee.object.type === 'MetaProperty' && node.callee.object.meta.name === 'import' && node.callee.property.type === 'Identifier' && node.callee.property.name === 'resolve') {
|
|
125
|
+
formatExpression(node);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (node.type === 'ArrowFunctionExpression') {
|
|
129
|
+
const {
|
|
130
|
+
body
|
|
131
|
+
} = node;
|
|
132
|
+
if (body.type === 'ImportExpression') {
|
|
133
|
+
formatExpression(body);
|
|
134
|
+
}
|
|
135
|
+
if (body.type === 'CallExpression' && body.callee.type === 'Identifier' && body.callee.name === 'require') {
|
|
136
|
+
formatExpression(body);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (node.type === 'MemberExpression' && node.object.type === 'ImportExpression' && node.property.type === 'Identifier' && node.property.name === 'then') {
|
|
140
|
+
formatExpression(node.object);
|
|
141
|
+
}
|
|
142
|
+
if (node.type === 'TSImportType') {
|
|
143
|
+
const source = node.source;
|
|
144
|
+
if (source && isStringLiteral(source)) {
|
|
145
|
+
const {
|
|
146
|
+
start,
|
|
147
|
+
end,
|
|
148
|
+
value
|
|
149
|
+
} = source;
|
|
150
|
+
const updated = cb({
|
|
151
|
+
type: 'StringLiteral',
|
|
152
|
+
node: source,
|
|
153
|
+
parent: node,
|
|
154
|
+
start,
|
|
155
|
+
end,
|
|
156
|
+
value
|
|
157
|
+
});
|
|
158
|
+
if (typeof updated === 'string') {
|
|
159
|
+
code.update(start + 1, end - 1, updated);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (node.type === 'ImportDeclaration') {
|
|
164
|
+
const {
|
|
165
|
+
source
|
|
166
|
+
} = node;
|
|
167
|
+
const {
|
|
168
|
+
start,
|
|
169
|
+
end,
|
|
170
|
+
value
|
|
171
|
+
} = source;
|
|
172
|
+
const updated = cb({
|
|
173
|
+
type: 'StringLiteral',
|
|
174
|
+
node: source,
|
|
175
|
+
parent: node,
|
|
176
|
+
start,
|
|
177
|
+
end,
|
|
178
|
+
value
|
|
179
|
+
});
|
|
180
|
+
if (typeof updated === 'string') {
|
|
181
|
+
code.update(start + 1, end - 1, updated);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (node.type === 'ExportNamedDeclaration' && node.source) {
|
|
185
|
+
const {
|
|
186
|
+
source
|
|
187
|
+
} = node;
|
|
188
|
+
const {
|
|
189
|
+
start,
|
|
190
|
+
end,
|
|
191
|
+
value
|
|
192
|
+
} = source;
|
|
193
|
+
const updated = cb({
|
|
194
|
+
type: 'StringLiteral',
|
|
195
|
+
node: source,
|
|
196
|
+
parent: node,
|
|
197
|
+
start,
|
|
198
|
+
end,
|
|
199
|
+
value
|
|
200
|
+
});
|
|
201
|
+
if (typeof updated === 'string') {
|
|
202
|
+
code.update(start + 1, end - 1, updated);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (node.type === 'ExportAllDeclaration') {
|
|
206
|
+
const {
|
|
207
|
+
source
|
|
208
|
+
} = node;
|
|
209
|
+
const {
|
|
210
|
+
start,
|
|
211
|
+
end,
|
|
212
|
+
value
|
|
213
|
+
} = source;
|
|
214
|
+
const updated = cb({
|
|
215
|
+
type: 'StringLiteral',
|
|
216
|
+
node: source,
|
|
217
|
+
parent: node,
|
|
218
|
+
start,
|
|
219
|
+
end,
|
|
220
|
+
value
|
|
221
|
+
});
|
|
222
|
+
if (typeof updated === 'string') {
|
|
223
|
+
code.update(start + 1, end - 1, updated);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
return code.toString();
|
|
229
|
+
};
|
|
230
|
+
const isValidFilename = async filename => {
|
|
231
|
+
let stats;
|
|
232
|
+
try {
|
|
233
|
+
stats = await stat(filename);
|
|
234
|
+
} catch {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
if (!stats.isFile()) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
return true;
|
|
241
|
+
};
|
|
242
|
+
const specifier = {
|
|
243
|
+
async update(path, callback) {
|
|
244
|
+
const filename = resolve(path);
|
|
245
|
+
const validated = await isValidFilename(filename);
|
|
246
|
+
if (!validated) {
|
|
247
|
+
throw new Error(`The provided path ${path} does not resolve to a file on disk.`);
|
|
248
|
+
}
|
|
249
|
+
const src = (await readFile(filename)).toString();
|
|
250
|
+
const ast = parseSync(filename, src);
|
|
251
|
+
return await formatSpecifiers(src, ast, callback);
|
|
252
|
+
},
|
|
253
|
+
async updateSrc(src, lang, callback) {
|
|
254
|
+
const filename = lang === 'ts' ? 'file.ts' : lang === 'tsx' ? 'file.tsx' : lang === 'js' ? 'file.js' : 'file.jsx';
|
|
255
|
+
const ast = parseSync(filename, src);
|
|
256
|
+
return await formatSpecifiers(src, ast, callback);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
export { specifier };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ParserOptions, StringLiteral, TemplateLiteral, BinaryExpression, NewExpression, ImportDeclaration, ExportNamedDeclaration, ExportAllDeclaration, TSImportType, ImportExpression, CallExpression } from 'oxc-parser';
|
|
2
|
+
type Spec = {
|
|
3
|
+
type: 'StringLiteral' | 'TemplateLiteral' | 'BinaryExpression' | 'NewExpression';
|
|
4
|
+
node: StringLiteral | TemplateLiteral | BinaryExpression | NewExpression;
|
|
5
|
+
parent: CallExpression | ImportDeclaration | ExportNamedDeclaration | ExportAllDeclaration | ImportExpression | TSImportType;
|
|
6
|
+
start: number;
|
|
7
|
+
end: number;
|
|
8
|
+
value: string;
|
|
9
|
+
};
|
|
10
|
+
type Callback = (spec: Spec) => string | void;
|
|
11
|
+
declare const specifier: {
|
|
12
|
+
update(path: string, callback: Callback): Promise<string>;
|
|
13
|
+
updateSrc(src: string, lang: ParserOptions["lang"], callback: Callback): Promise<string>;
|
|
14
|
+
};
|
|
15
|
+
export { specifier };
|
|
16
|
+
export type { Spec, Callback };
|
package/dist/src/utils/lang.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
declare const getLangFromExt: (filename: string) => UpdateSrcLang;
|
|
1
|
+
import type { ParserOptions } from 'oxc-parser';
|
|
2
|
+
declare const getLangFromExt: (filename: string) => ParserOptions["lang"] | undefined;
|
|
4
3
|
export { getLangFromExt };
|
package/dist/src/utils.d.ts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import type { Node } from 'oxc-parser';
|
|
2
|
-
import type { Specifier } from '@knighted/specifier';
|
|
3
2
|
import type { IdentMeta, Scope, CjsExport } from './types.js';
|
|
4
|
-
type UpdateSrcLang = Parameters<Specifier['updateSrc']>[1];
|
|
5
|
-
declare const getLangFromExt: (filename: string) => UpdateSrcLang;
|
|
6
3
|
declare const isValidUrl: (url: string) => boolean;
|
|
7
4
|
declare const exportsRename = "__exports";
|
|
8
5
|
declare const requireMainRgx: RegExp;
|
|
@@ -23,4 +20,4 @@ declare const collectScopeIdentifiers: (node: Node, scopes: Scope[]) => void;
|
|
|
23
20
|
* which are also valid module scope identifiers.
|
|
24
21
|
*/
|
|
25
22
|
declare const collectModuleIdentifiers: (ast: Node, hoisting?: boolean) => Promise<Map<string, IdentMeta>>;
|
|
26
|
-
export {
|
|
23
|
+
export { isValidUrl, collectScopeIdentifiers, collectModuleIdentifiers, collectCjsExports, exportsRename, requireMainRgx, };
|
package/dist/utils/lang.js
CHANGED
package/dist/utils.d.cts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import type { Node } from 'oxc-parser';
|
|
2
|
-
import type { Specifier } from '@knighted/specifier';
|
|
3
2
|
import type { IdentMeta, Scope, CjsExport } from './types.cjs';
|
|
4
|
-
type UpdateSrcLang = Parameters<Specifier['updateSrc']>[1];
|
|
5
|
-
declare const getLangFromExt: (filename: string) => UpdateSrcLang;
|
|
6
3
|
declare const isValidUrl: (url: string) => boolean;
|
|
7
4
|
declare const exportsRename = "__exports";
|
|
8
5
|
declare const requireMainRgx: RegExp;
|
|
@@ -23,4 +20,4 @@ declare const collectScopeIdentifiers: (node: Node, scopes: Scope[]) => void;
|
|
|
23
20
|
* which are also valid module scope identifiers.
|
|
24
21
|
*/
|
|
25
22
|
declare const collectModuleIdentifiers: (ast: Node, hoisting?: boolean) => Promise<Map<string, IdentMeta>>;
|
|
26
|
-
export {
|
|
23
|
+
export { isValidUrl, collectScopeIdentifiers, collectModuleIdentifiers, collectCjsExports, exportsRename, requireMainRgx, };
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import type { Node } from 'oxc-parser';
|
|
2
|
-
import type { Specifier } from '@knighted/specifier';
|
|
3
2
|
import type { IdentMeta, Scope, CjsExport } from './types.js';
|
|
4
|
-
type UpdateSrcLang = Parameters<Specifier['updateSrc']>[1];
|
|
5
|
-
declare const getLangFromExt: (filename: string) => UpdateSrcLang;
|
|
6
3
|
declare const isValidUrl: (url: string) => boolean;
|
|
7
4
|
declare const exportsRename = "__exports";
|
|
8
5
|
declare const requireMainRgx: RegExp;
|
|
@@ -23,4 +20,4 @@ declare const collectScopeIdentifiers: (node: Node, scopes: Scope[]) => void;
|
|
|
23
20
|
* which are also valid module scope identifiers.
|
|
24
21
|
*/
|
|
25
22
|
declare const collectModuleIdentifiers: (ast: Node, hoisting?: boolean) => Promise<Map<string, IdentMeta>>;
|
|
26
|
-
export {
|
|
23
|
+
export { isValidUrl, collectScopeIdentifiers, collectModuleIdentifiers, collectCjsExports, exportsRename, requireMainRgx, };
|
package/dist/utils.js
CHANGED
|
@@ -1,22 +1,6 @@
|
|
|
1
|
-
import { extname } from 'node:path';
|
|
2
1
|
import { ancestorWalk } from './walk.js';
|
|
3
2
|
import { identifier } from './helpers/identifier.js';
|
|
4
3
|
import { scopeNodes } from './utils/scopeNodes.js';
|
|
5
|
-
const getLangFromExt = filename => {
|
|
6
|
-
const ext = extname(filename).toLowerCase();
|
|
7
|
-
if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
|
|
8
|
-
return 'js';
|
|
9
|
-
}
|
|
10
|
-
if (ext === '.ts' || ext === '.mts' || ext === '.cts') {
|
|
11
|
-
return 'ts';
|
|
12
|
-
}
|
|
13
|
-
if (ext === '.tsx') {
|
|
14
|
-
return 'tsx';
|
|
15
|
-
}
|
|
16
|
-
if (ext === '.jsx') {
|
|
17
|
-
return 'jsx';
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
4
|
const isValidUrl = url => {
|
|
21
5
|
try {
|
|
22
6
|
new URL(url);
|
|
@@ -278,4 +262,4 @@ const collectModuleIdentifiers = async (ast, hoisting = true) => {
|
|
|
278
262
|
});
|
|
279
263
|
return identifiers;
|
|
280
264
|
};
|
|
281
|
-
export {
|
|
265
|
+
export { isValidUrl, collectScopeIdentifiers, collectModuleIdentifiers, collectCjsExports, exportsRename, requireMainRgx };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knighted/module",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.2",
|
|
4
4
|
"description": "Transforms differences between ES modules and CommonJS.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/module.js",
|
|
@@ -19,15 +19,15 @@
|
|
|
19
19
|
"./package.json": "./package.json"
|
|
20
20
|
},
|
|
21
21
|
"imports": {
|
|
22
|
-
"#parse": "./
|
|
23
|
-
"#format": "./
|
|
24
|
-
"#utils/*.js": "./
|
|
25
|
-
"#walk": "./
|
|
26
|
-
"#helpers/*.js": "./
|
|
27
|
-
"#formatters/*.js": "./
|
|
22
|
+
"#parse": "./dist/parse.js",
|
|
23
|
+
"#format": "./dist/format.js",
|
|
24
|
+
"#utils/*.js": "./dist/utils/*.js",
|
|
25
|
+
"#walk": "./dist/walk.js",
|
|
26
|
+
"#helpers/*.js": "./dist/helpers/*.js",
|
|
27
|
+
"#formatters/*.js": "./dist/formatters/*.js"
|
|
28
28
|
},
|
|
29
29
|
"engines": {
|
|
30
|
-
"node": ">=22.21.1"
|
|
30
|
+
"node": ">=22.21.1 <23 || >=24 <25"
|
|
31
31
|
},
|
|
32
32
|
"engineStrict": true,
|
|
33
33
|
"scripts": {
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"build:types": "tsc --emitDeclarationOnly",
|
|
41
41
|
"build:dual": "babel-dual-package src --extensions .ts",
|
|
42
42
|
"build": "npm run build:types && npm run build:dual",
|
|
43
|
-
"prepack": "npm run build"
|
|
43
|
+
"prepack": "npm run build && node scripts/setImportsToDist.js",
|
|
44
|
+
"postpack": "node scripts/restoreImportsToSrc.js"
|
|
44
45
|
},
|
|
45
46
|
"keywords": [
|
|
46
47
|
"transform",
|
|
@@ -78,7 +79,6 @@
|
|
|
78
79
|
"typescript": "^5.9.3"
|
|
79
80
|
},
|
|
80
81
|
"dependencies": {
|
|
81
|
-
"@knighted/specifier": "^2.0.9",
|
|
82
82
|
"magic-string": "^0.30.21",
|
|
83
83
|
"node-module-type": "^1.0.4",
|
|
84
84
|
"oxc-parser": "^0.105.0",
|