@knighted/module 1.4.0-rc.2 → 1.4.0
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/builtinSpecifiers.d.ts +2 -0
- package/dist/cjs/builtinSpecifiers.d.cts +2 -0
- package/dist/cjs/cli.cjs +14 -7
- package/dist/cjs/format.cjs +3 -8
- package/dist/cjs/module.cjs +31 -25
- package/dist/cjs/types.d.cts +2 -0
- package/dist/cjs/utils/builtinSpecifiers.cjs +12 -0
- package/dist/cli.js +13 -6
- package/dist/format.js +1 -6
- package/dist/module.js +31 -25
- package/dist/types.d.ts +2 -0
- package/dist/utils/builtinSpecifiers.d.ts +2 -0
- package/dist/utils/builtinSpecifiers.js +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -125,6 +125,7 @@ type ModuleOptions = {
|
|
|
125
125
|
| '.mts'
|
|
126
126
|
| '.cts'
|
|
127
127
|
| ((value: string) => string | null | undefined)
|
|
128
|
+
rewriteTemplateLiterals?: 'allow' | 'static-only'
|
|
128
129
|
dirFilename?: 'inject' | 'preserve' | 'error'
|
|
129
130
|
importMeta?: 'preserve' | 'shim' | 'error'
|
|
130
131
|
importMetaMain?: 'shim' | 'warn' | 'error'
|
|
@@ -151,15 +152,16 @@ type ModuleOptions = {
|
|
|
151
152
|
- `appendJsExtension` (`relative-only` when targeting ESM): append `.js` to relative specifiers; never touches bare specifiers.
|
|
152
153
|
- `appendDirectoryIndex` (`index.js`): when a relative specifier ends with a slash, append this index filename (set `false` to disable).
|
|
153
154
|
- `appenders` precedence: `rewriteSpecifier` runs first; if it returns a string, that result is used. If it returns `undefined` or `null`, `appendJsExtension` and `appendDirectoryIndex` still run. Bare specifiers are never modified by appenders.
|
|
155
|
+
- `rewriteTemplateLiterals` (`allow`): when `static-only`, interpolated template literals are left untouched by specifier rewriting; string literals and non-interpolated templates still rewrite.
|
|
154
156
|
- `dirFilename` (`inject`): inject `__dirname`/`__filename`, preserve existing, or throw.
|
|
155
157
|
- `importMeta` (`shim`): rewrite `import.meta.*` to CommonJS equivalents.
|
|
156
158
|
- `importMetaMain` (`shim`): gate `import.meta.main` with shimming/warning/error when Node support is too old.
|
|
157
159
|
- `requireMainStrategy` (`import-meta-main`): use `import.meta.main` or the realpath-based `pathToFileURL(realpathSync(process.argv[1])).href` check.
|
|
158
160
|
- `importMetaPrelude` (`auto`): emit a no-op `void import.meta.filename;` touch. `on` always emits; `off` never emits; `auto` emits only when helpers that reference `import.meta.*` are synthesized (e.g., `__dirname`/`__filename` in CJS→ESM, require-main shims, createRequire helpers). Useful for bundlers/transpilers that do usage-based `import.meta` polyfilling.
|
|
159
|
-
- `detectCircularRequires` (`off`): optionally detect relative static require cycles and warn/throw.
|
|
161
|
+
- `detectCircularRequires` (`off`): optionally detect relative static require cycles across `.js`/`.mjs`/`.cjs`/`.ts`/`.mts`/`.cts` (realpath-normalized) and warn/throw.
|
|
160
162
|
- `detectDualPackageHazard` (`warn`): flag when `import` and `require` mix for the same package or root/subpath are combined in ways that can resolve to separate module instances (dual packages). Set to `error` to fail the transform.
|
|
161
163
|
- `dualPackageHazardScope` (`file`): `file` preserves the legacy per-file detector; `project` aggregates package usage across all CLI inputs (useful in monorepos/hoisted installs) and emits one diagnostic per package.
|
|
162
|
-
- `topLevelAwait` (`error`): throw, wrap, or preserve when TLA appears in CommonJS output.
|
|
164
|
+
- `topLevelAwait` (`error`): throw, wrap, or preserve when TLA appears in CommonJS output. `wrap` runs the file body inside an async IIFE (exports may resolve after the initial tick); `preserve` leaves `await` at top level, which Node will reject for CJS.
|
|
163
165
|
- `rewriteSpecifier` (off): rewrite relative specifiers to a chosen extension or via a callback. Precedence: the callback (if provided) runs first; if it returns a string, that wins. If it returns `undefined` or `null`, the appenders still apply.
|
|
164
166
|
- `requireSource` (`builtin`): whether `require` comes from Node or `createRequire`.
|
|
165
167
|
- `cjsDefault` (`auto`): bundler-style default interop vs direct `module.exports`.
|
package/dist/cjs/cli.cjs
CHANGED
|
@@ -9,19 +9,20 @@ var _nodeProcess = require("node:process");
|
|
|
9
9
|
var _nodeUtil = require("node:util");
|
|
10
10
|
var _promises = require("node:fs/promises");
|
|
11
11
|
var _nodePath = require("node:path");
|
|
12
|
-
var _nodeModule = require("node:module");
|
|
13
12
|
var _glob = require("glob");
|
|
14
13
|
var _module = require("./module.cjs");
|
|
15
14
|
var _parse = require("./parse.cjs");
|
|
16
15
|
var _format = require("./format.cjs");
|
|
17
16
|
var _specifier = require("./specifier.cjs");
|
|
18
17
|
var _lang = require("./utils/lang.cjs");
|
|
18
|
+
var _builtinSpecifiers = require("./utils/builtinSpecifiers.cjs");
|
|
19
19
|
const defaultOptions = {
|
|
20
20
|
target: 'commonjs',
|
|
21
21
|
sourceType: 'auto',
|
|
22
22
|
transformSyntax: true,
|
|
23
23
|
liveBindings: 'strict',
|
|
24
24
|
rewriteSpecifier: undefined,
|
|
25
|
+
rewriteTemplateLiterals: 'allow',
|
|
25
26
|
appendJsExtension: undefined,
|
|
26
27
|
appendDirectoryIndex: 'index.js',
|
|
27
28
|
dirFilename: 'inject',
|
|
@@ -77,11 +78,6 @@ const colorize = enabled => {
|
|
|
77
78
|
cyan: wrap(codes.cyan)
|
|
78
79
|
};
|
|
79
80
|
};
|
|
80
|
-
const builtinSpecifiers = new Set(_nodeModule.builtinModules.map(mod => mod.startsWith('node:') ? mod.slice(5) : mod).flatMap(mod => {
|
|
81
|
-
const parts = mod.split('/');
|
|
82
|
-
const base = parts[0];
|
|
83
|
-
return parts.length > 1 ? [mod, base] : [mod];
|
|
84
|
-
}));
|
|
85
81
|
const collapseSpecifier = value => value.replace(/['"`+)\s]|new String\(/g, '');
|
|
86
82
|
const appendExtensionIfNeeded = (value, mode, dirIndex) => {
|
|
87
83
|
if (mode === 'off') return;
|
|
@@ -117,7 +113,7 @@ const normalizeBuiltinSpecifier = value => {
|
|
|
117
113
|
if (/^[a-zA-Z][a-zA-Z+.-]*:/.test(specPart) && !specPart.startsWith('node:')) return;
|
|
118
114
|
const bare = specPart.startsWith('node:') ? specPart.slice(5) : specPart;
|
|
119
115
|
const base = bare.split('/')[0] ?? '';
|
|
120
|
-
if (!builtinSpecifiers.has(bare) && !builtinSpecifiers.has(base)) return;
|
|
116
|
+
if (!_builtinSpecifiers.builtinSpecifiers.has(bare) && !_builtinSpecifiers.builtinSpecifiers.has(base)) return;
|
|
121
117
|
if (specPart.startsWith('node:')) return;
|
|
122
118
|
const quote = /^['"`]/.exec(value)?.[0] ?? '';
|
|
123
119
|
return quote ? `${quote}node:${value.slice(quote.length)}` : `node:${value}`;
|
|
@@ -137,6 +133,11 @@ const optionsTable = [{
|
|
|
137
133
|
short: 'r',
|
|
138
134
|
type: 'string',
|
|
139
135
|
desc: 'Rewrite import specifiers (.js/.mjs/.cjs/.ts/.mts/.cts)'
|
|
136
|
+
}, {
|
|
137
|
+
long: 'rewrite-template-literals',
|
|
138
|
+
short: undefined,
|
|
139
|
+
type: 'string',
|
|
140
|
+
desc: 'Rewrite template literals (allow|static-only)'
|
|
140
141
|
}, {
|
|
141
142
|
long: 'append-js-extension',
|
|
142
143
|
short: 'j',
|
|
@@ -283,6 +284,7 @@ const parseAppendDirectoryIndex = value => {
|
|
|
283
284
|
const toModuleOptions = values => {
|
|
284
285
|
const target = parseEnum(values.target, ['module', 'commonjs']) ?? defaultOptions.target;
|
|
285
286
|
const transformSyntax = parseTransformSyntax(values['transform-syntax']);
|
|
287
|
+
const rewriteTemplateLiterals = parseEnum(values['rewrite-template-literals'], ['allow', 'static-only']) ?? defaultOptions.rewriteTemplateLiterals;
|
|
286
288
|
const appendJsExtension = parseEnum(values['append-js-extension'], ['off', 'relative-only', 'all']);
|
|
287
289
|
const appendDirectoryIndex = parseAppendDirectoryIndex(values['append-directory-index']);
|
|
288
290
|
const opts = {
|
|
@@ -290,6 +292,7 @@ const toModuleOptions = values => {
|
|
|
290
292
|
target,
|
|
291
293
|
transformSyntax,
|
|
292
294
|
rewriteSpecifier: values['rewrite-specifier'] ?? undefined,
|
|
295
|
+
rewriteTemplateLiterals,
|
|
293
296
|
appendJsExtension: appendJsExtension,
|
|
294
297
|
appendDirectoryIndex,
|
|
295
298
|
detectCircularRequires: parseEnum(values['detect-circular-requires'], ['off', 'warn', 'error']) ?? defaultOptions.detectCircularRequires,
|
|
@@ -347,6 +350,10 @@ const applySpecifierUpdates = async (source, filename, opts, appendMode, dirInde
|
|
|
347
350
|
if (!opts.rewriteSpecifier && appendMode === 'off' && !dirIndex) return source;
|
|
348
351
|
const lang = (0, _lang.getLangFromExt)(filename);
|
|
349
352
|
const updated = await _specifier.specifier.updateSrc(source, lang, spec => {
|
|
353
|
+
if (spec.type === 'TemplateLiteral' && opts.rewriteTemplateLiterals === 'static-only') {
|
|
354
|
+
const node = spec.node;
|
|
355
|
+
if (node.expressions.length > 0) return;
|
|
356
|
+
}
|
|
350
357
|
const normalized = normalizeBuiltinSpecifier(spec.value);
|
|
351
358
|
const rewritten = rewriteSpecifierValue(normalized ?? spec.value, opts.rewriteSpecifier);
|
|
352
359
|
const baseValue = rewritten ?? normalized ?? spec.value;
|
package/dist/cjs/format.cjs
CHANGED
|
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.format = exports.dualPackageHazardDiagnostics = exports.collectDualPackageUsage = void 0;
|
|
7
|
-
var _nodeModule = require("node:module");
|
|
8
7
|
var _nodePath = require("node:path");
|
|
9
8
|
var _promises = require("node:fs/promises");
|
|
10
9
|
var _magicString = _interopRequireDefault(require("magic-string"));
|
|
@@ -24,14 +23,10 @@ var _interopHelpers = require("./pipeline/interopHelpers.cjs");
|
|
|
24
23
|
var _exports = require("./utils/exports.cjs");
|
|
25
24
|
var _identifiers = require("./utils/identifiers.cjs");
|
|
26
25
|
var _url = require("./utils/url.cjs");
|
|
26
|
+
var _builtinSpecifiers = require("./utils/builtinSpecifiers.cjs");
|
|
27
27
|
var _walk = require("./walk.cjs");
|
|
28
28
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
29
29
|
const isRequireMainMember = (node, shadowed) => node.type === 'MemberExpression' && node.object.type === 'Identifier' && node.object.name === 'require' && !shadowed.has('require') && node.property.type === 'Identifier' && node.property.name === 'main';
|
|
30
|
-
const builtinSpecifiers = new Set(_nodeModule.builtinModules.map(mod => mod.startsWith('node:') ? mod.slice(5) : mod).flatMap(mod => {
|
|
31
|
-
const parts = mod.split('/');
|
|
32
|
-
const base = parts[0];
|
|
33
|
-
return parts.length > 1 ? [mod, base] : [mod];
|
|
34
|
-
}));
|
|
35
30
|
const stripQuery = value => value.includes('?') || value.includes('#') ? value.split(/[?#]/)[0] ?? value : value;
|
|
36
31
|
const packageFromSpecifier = spec => {
|
|
37
32
|
const cleaned = stripQuery(spec);
|
|
@@ -43,7 +38,7 @@ const packageFromSpecifier = spec => {
|
|
|
43
38
|
if (cleaned.startsWith('@')) {
|
|
44
39
|
if (parts.length < 2) return null;
|
|
45
40
|
const pkg = `${parts[0]}/${parts[1]}`;
|
|
46
|
-
if (builtinSpecifiers.has(pkg) || builtinSpecifiers.has(parts[1] ?? '')) return null;
|
|
41
|
+
if (_builtinSpecifiers.builtinSpecifiers.has(pkg) || _builtinSpecifiers.builtinSpecifiers.has(parts[1] ?? '')) return null;
|
|
47
42
|
const subpath = parts.slice(2).join('/');
|
|
48
43
|
return {
|
|
49
44
|
pkg,
|
|
@@ -51,7 +46,7 @@ const packageFromSpecifier = spec => {
|
|
|
51
46
|
};
|
|
52
47
|
}
|
|
53
48
|
const pkg = parts[0] ?? '';
|
|
54
|
-
if (!pkg || builtinSpecifiers.has(pkg)) return null;
|
|
49
|
+
if (!pkg || _builtinSpecifiers.builtinSpecifiers.has(pkg)) return null;
|
|
55
50
|
const subpath = parts.slice(1).join('/');
|
|
56
51
|
return {
|
|
57
52
|
pkg,
|
package/dist/cjs/module.cjs
CHANGED
|
@@ -10,15 +10,10 @@ var _specifier = require("./specifier.cjs");
|
|
|
10
10
|
var _parse = require("./parse.cjs");
|
|
11
11
|
var _format = require("./format.cjs");
|
|
12
12
|
var _lang = require("./utils/lang.cjs");
|
|
13
|
-
var _nodeModule = require("node:module");
|
|
14
13
|
var _walk = require("./walk.cjs");
|
|
15
14
|
var _identifiers = require("./utils/identifiers.cjs");
|
|
15
|
+
var _builtinSpecifiers = require("./utils/builtinSpecifiers.cjs");
|
|
16
16
|
const collapseSpecifier = value => value.replace(/['"`+)\s]|new String\(/g, '');
|
|
17
|
-
const builtinSpecifiers = new Set(_nodeModule.builtinModules.map(mod => mod.startsWith('node:') ? mod.slice(5) : mod).flatMap(mod => {
|
|
18
|
-
const parts = mod.split('/');
|
|
19
|
-
const base = parts[0];
|
|
20
|
-
return parts.length > 1 ? [mod, base] : [mod];
|
|
21
|
-
}));
|
|
22
17
|
const appendExtensionIfNeeded = (spec, mode, dirIndex, value = spec.value) => {
|
|
23
18
|
if (mode === 'off') return;
|
|
24
19
|
if (spec.type === 'TemplateLiteral') {
|
|
@@ -63,7 +58,7 @@ const normalizeBuiltinSpecifier = value => {
|
|
|
63
58
|
if (/^[a-zA-Z][a-zA-Z+.-]*:/.test(specPart) && !specPart.startsWith('node:')) return;
|
|
64
59
|
const bare = specPart.startsWith('node:') ? specPart.slice(5) : specPart;
|
|
65
60
|
const base = bare.split('/')[0] ?? '';
|
|
66
|
-
if (!builtinSpecifiers.has(bare) && !builtinSpecifiers.has(base)) return;
|
|
61
|
+
if (!_builtinSpecifiers.builtinSpecifiers.has(bare) && !_builtinSpecifiers.builtinSpecifiers.has(base)) return;
|
|
67
62
|
if (specPart.startsWith('node:')) return;
|
|
68
63
|
const quote = /^['"`]/.exec(value)?.[0] ?? '';
|
|
69
64
|
return quote ? `${quote}node:${value.slice(quote.length)}` : `node:${value}`;
|
|
@@ -76,6 +71,7 @@ const fileExists = async candidate => {
|
|
|
76
71
|
return false;
|
|
77
72
|
}
|
|
78
73
|
};
|
|
74
|
+
const normalizePath = async p => (0, _nodePath.resolve)(await (0, _promises.realpath)(p).catch(() => p));
|
|
79
75
|
const resolveRequirePath = async (fromFile, spec, dirIndex) => {
|
|
80
76
|
if (!spec.startsWith('./') && !spec.startsWith('../')) return null;
|
|
81
77
|
const base = (0, _nodePath.resolve)((0, _nodePath.dirname)(fromFile), spec);
|
|
@@ -84,11 +80,11 @@ const resolveRequirePath = async (fromFile, spec, dirIndex) => {
|
|
|
84
80
|
if (ext) {
|
|
85
81
|
candidates.push(base);
|
|
86
82
|
} else {
|
|
87
|
-
candidates.push(`${base}.js`, `${base}.cjs`, `${base}.mjs`);
|
|
83
|
+
candidates.push(`${base}.js`, `${base}.cjs`, `${base}.mjs`, `${base}.ts`, `${base}.mts`, `${base}.cts`);
|
|
88
84
|
candidates.push((0, _nodePath.join)(base, dirIndex));
|
|
89
85
|
}
|
|
90
86
|
for (const candidate of candidates) {
|
|
91
|
-
if (await fileExists(candidate)) return candidate;
|
|
87
|
+
if (await fileExists(candidate)) return await normalizePath(candidate);
|
|
92
88
|
}
|
|
93
89
|
return null;
|
|
94
90
|
};
|
|
@@ -118,8 +114,9 @@ const detectCircularRequireGraph = async (entryFile, mode, dirIndex) => {
|
|
|
118
114
|
const visiting = new Set();
|
|
119
115
|
const visited = new Set();
|
|
120
116
|
const dfs = async (file, stack) => {
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
const normalized = await normalizePath(file);
|
|
118
|
+
if (visiting.has(normalized)) {
|
|
119
|
+
const cycle = [...stack, normalized];
|
|
123
120
|
const msg = `Circular require detected: ${cycle.join(' -> ')}`;
|
|
124
121
|
if (mode === 'error') {
|
|
125
122
|
throw new Error(msg);
|
|
@@ -128,22 +125,22 @@ const detectCircularRequireGraph = async (entryFile, mode, dirIndex) => {
|
|
|
128
125
|
console.warn(msg);
|
|
129
126
|
return;
|
|
130
127
|
}
|
|
131
|
-
if (visited.has(
|
|
132
|
-
visiting.add(
|
|
133
|
-
stack.push(
|
|
134
|
-
let deps = cache.get(
|
|
128
|
+
if (visited.has(normalized)) return;
|
|
129
|
+
visiting.add(normalized);
|
|
130
|
+
stack.push(normalized);
|
|
131
|
+
let deps = cache.get(normalized);
|
|
135
132
|
if (!deps) {
|
|
136
|
-
deps = await collectStaticRequires(
|
|
137
|
-
cache.set(
|
|
133
|
+
deps = await collectStaticRequires(normalized, dirIndex);
|
|
134
|
+
cache.set(normalized, deps);
|
|
138
135
|
}
|
|
139
136
|
for (const dep of deps) {
|
|
140
137
|
await dfs(dep, stack);
|
|
141
138
|
}
|
|
142
139
|
stack.pop();
|
|
143
|
-
visiting.delete(
|
|
144
|
-
visited.add(
|
|
140
|
+
visiting.delete(normalized);
|
|
141
|
+
visited.add(normalized);
|
|
145
142
|
};
|
|
146
|
-
await dfs(entryFile, []);
|
|
143
|
+
await dfs(await normalizePath(entryFile), []);
|
|
147
144
|
};
|
|
148
145
|
const mergeUsageMaps = (target, source) => {
|
|
149
146
|
for (const [pkg, usage] of source) {
|
|
@@ -186,12 +183,13 @@ const collectProjectDualPackageHazards = async (files, opts) => {
|
|
|
186
183
|
return byFile;
|
|
187
184
|
};
|
|
188
185
|
exports.collectProjectDualPackageHazards = collectProjectDualPackageHazards;
|
|
189
|
-
const
|
|
186
|
+
const createDefaultOptions = () => ({
|
|
190
187
|
target: 'commonjs',
|
|
191
188
|
sourceType: 'auto',
|
|
192
189
|
transformSyntax: true,
|
|
193
190
|
liveBindings: 'strict',
|
|
194
191
|
rewriteSpecifier: undefined,
|
|
192
|
+
rewriteTemplateLiterals: 'allow',
|
|
195
193
|
appendJsExtension: undefined,
|
|
196
194
|
appendDirectoryIndex: 'index.js',
|
|
197
195
|
dirFilename: 'inject',
|
|
@@ -210,12 +208,16 @@ const defaultOptions = {
|
|
|
210
208
|
cwd: undefined,
|
|
211
209
|
out: undefined,
|
|
212
210
|
inPlace: false
|
|
213
|
-
};
|
|
214
|
-
const transform = async (filename, options
|
|
215
|
-
const
|
|
216
|
-
|
|
211
|
+
});
|
|
212
|
+
const transform = async (filename, options) => {
|
|
213
|
+
const base = createDefaultOptions();
|
|
214
|
+
const opts = options ? {
|
|
215
|
+
...base,
|
|
217
216
|
...options,
|
|
218
217
|
filePath: filename
|
|
218
|
+
} : {
|
|
219
|
+
...base,
|
|
220
|
+
filePath: filename
|
|
219
221
|
};
|
|
220
222
|
const cwdBase = opts.cwd ? (0, _nodePath.resolve)(opts.cwd) : process.cwd();
|
|
221
223
|
const appendMode = options?.appendJsExtension ?? (opts.target === 'module' ? 'relative-only' : 'off');
|
|
@@ -227,6 +229,10 @@ const transform = async (filename, options = defaultOptions) => {
|
|
|
227
229
|
let source = await (0, _format.format)(code, ast, opts);
|
|
228
230
|
if (opts.rewriteSpecifier || appendMode !== 'off' || dirIndex) {
|
|
229
231
|
const code = await _specifier.specifier.updateSrc(source, (0, _lang.getLangFromExt)(filename), spec => {
|
|
232
|
+
if (spec.type === 'TemplateLiteral' && opts.rewriteTemplateLiterals === 'static-only') {
|
|
233
|
+
const node = spec.node;
|
|
234
|
+
if (node.expressions.length > 0) return;
|
|
235
|
+
}
|
|
230
236
|
const normalized = normalizeBuiltinSpecifier(spec.value);
|
|
231
237
|
const rewritten = rewriteSpecifierValue(normalized ?? spec.value, opts.rewriteSpecifier);
|
|
232
238
|
const baseValue = rewritten ?? normalized ?? spec.value;
|
package/dist/cjs/types.d.cts
CHANGED
|
@@ -17,6 +17,8 @@ export type ModuleOptions = {
|
|
|
17
17
|
liveBindings?: 'strict' | 'loose' | 'off';
|
|
18
18
|
/** Rewrite import specifiers (e.g. add extensions). */
|
|
19
19
|
rewriteSpecifier?: RewriteSpecifier;
|
|
20
|
+
/** Whether to rewrite template literals that contain expressions. Default allows rewrites; set to 'static-only' to skip interpolated templates. */
|
|
21
|
+
rewriteTemplateLiterals?: 'allow' | 'static-only';
|
|
20
22
|
/** Whether to append .js to relative imports. */
|
|
21
23
|
appendJsExtension?: 'off' | 'relative-only' | 'all';
|
|
22
24
|
/** Add directory index (e.g. /index.js) or disable. */
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.builtinSpecifiers = void 0;
|
|
7
|
+
var _nodeModule = require("node:module");
|
|
8
|
+
const builtinSpecifiers = exports.builtinSpecifiers = new Set(_nodeModule.builtinModules.map(mod => mod.startsWith('node:') ? mod.slice(5) : mod).flatMap(mod => {
|
|
9
|
+
const parts = mod.split('/');
|
|
10
|
+
const base = parts[0];
|
|
11
|
+
return parts.length > 1 ? [mod, base] : [mod];
|
|
12
|
+
}));
|
package/dist/cli.js
CHANGED
|
@@ -3,19 +3,20 @@ import { stdin as defaultStdin, stdout as defaultStdout, stderr as defaultStderr
|
|
|
3
3
|
import { parseArgs } from 'node:util';
|
|
4
4
|
import { readFile, mkdir } from 'node:fs/promises';
|
|
5
5
|
import { dirname, resolve, relative, join } from 'node:path';
|
|
6
|
-
import { builtinModules } from 'node:module';
|
|
7
6
|
import { glob } from 'glob';
|
|
8
7
|
import { transform, collectProjectDualPackageHazards } from './module.js';
|
|
9
8
|
import { parse } from './parse.js';
|
|
10
9
|
import { format } from './format.js';
|
|
11
10
|
import { specifier } from './specifier.js';
|
|
12
11
|
import { getLangFromExt } from './utils/lang.js';
|
|
12
|
+
import { builtinSpecifiers } from './utils/builtinSpecifiers.js';
|
|
13
13
|
const defaultOptions = {
|
|
14
14
|
target: 'commonjs',
|
|
15
15
|
sourceType: 'auto',
|
|
16
16
|
transformSyntax: true,
|
|
17
17
|
liveBindings: 'strict',
|
|
18
18
|
rewriteSpecifier: undefined,
|
|
19
|
+
rewriteTemplateLiterals: 'allow',
|
|
19
20
|
appendJsExtension: undefined,
|
|
20
21
|
appendDirectoryIndex: 'index.js',
|
|
21
22
|
dirFilename: 'inject',
|
|
@@ -71,11 +72,6 @@ const colorize = enabled => {
|
|
|
71
72
|
cyan: wrap(codes.cyan)
|
|
72
73
|
};
|
|
73
74
|
};
|
|
74
|
-
const builtinSpecifiers = new Set(builtinModules.map(mod => mod.startsWith('node:') ? mod.slice(5) : mod).flatMap(mod => {
|
|
75
|
-
const parts = mod.split('/');
|
|
76
|
-
const base = parts[0];
|
|
77
|
-
return parts.length > 1 ? [mod, base] : [mod];
|
|
78
|
-
}));
|
|
79
75
|
const collapseSpecifier = value => value.replace(/['"`+)\s]|new String\(/g, '');
|
|
80
76
|
const appendExtensionIfNeeded = (value, mode, dirIndex) => {
|
|
81
77
|
if (mode === 'off') return;
|
|
@@ -131,6 +127,11 @@ const optionsTable = [{
|
|
|
131
127
|
short: 'r',
|
|
132
128
|
type: 'string',
|
|
133
129
|
desc: 'Rewrite import specifiers (.js/.mjs/.cjs/.ts/.mts/.cts)'
|
|
130
|
+
}, {
|
|
131
|
+
long: 'rewrite-template-literals',
|
|
132
|
+
short: undefined,
|
|
133
|
+
type: 'string',
|
|
134
|
+
desc: 'Rewrite template literals (allow|static-only)'
|
|
134
135
|
}, {
|
|
135
136
|
long: 'append-js-extension',
|
|
136
137
|
short: 'j',
|
|
@@ -277,6 +278,7 @@ const parseAppendDirectoryIndex = value => {
|
|
|
277
278
|
const toModuleOptions = values => {
|
|
278
279
|
const target = parseEnum(values.target, ['module', 'commonjs']) ?? defaultOptions.target;
|
|
279
280
|
const transformSyntax = parseTransformSyntax(values['transform-syntax']);
|
|
281
|
+
const rewriteTemplateLiterals = parseEnum(values['rewrite-template-literals'], ['allow', 'static-only']) ?? defaultOptions.rewriteTemplateLiterals;
|
|
280
282
|
const appendJsExtension = parseEnum(values['append-js-extension'], ['off', 'relative-only', 'all']);
|
|
281
283
|
const appendDirectoryIndex = parseAppendDirectoryIndex(values['append-directory-index']);
|
|
282
284
|
const opts = {
|
|
@@ -284,6 +286,7 @@ const toModuleOptions = values => {
|
|
|
284
286
|
target,
|
|
285
287
|
transformSyntax,
|
|
286
288
|
rewriteSpecifier: values['rewrite-specifier'] ?? undefined,
|
|
289
|
+
rewriteTemplateLiterals,
|
|
287
290
|
appendJsExtension: appendJsExtension,
|
|
288
291
|
appendDirectoryIndex,
|
|
289
292
|
detectCircularRequires: parseEnum(values['detect-circular-requires'], ['off', 'warn', 'error']) ?? defaultOptions.detectCircularRequires,
|
|
@@ -341,6 +344,10 @@ const applySpecifierUpdates = async (source, filename, opts, appendMode, dirInde
|
|
|
341
344
|
if (!opts.rewriteSpecifier && appendMode === 'off' && !dirIndex) return source;
|
|
342
345
|
const lang = getLangFromExt(filename);
|
|
343
346
|
const updated = await specifier.updateSrc(source, lang, spec => {
|
|
347
|
+
if (spec.type === 'TemplateLiteral' && opts.rewriteTemplateLiterals === 'static-only') {
|
|
348
|
+
const node = spec.node;
|
|
349
|
+
if (node.expressions.length > 0) return;
|
|
350
|
+
}
|
|
344
351
|
const normalized = normalizeBuiltinSpecifier(spec.value);
|
|
345
352
|
const rewritten = rewriteSpecifierValue(normalized ?? spec.value, opts.rewriteSpecifier);
|
|
346
353
|
const baseValue = rewritten ?? normalized ?? spec.value;
|
package/dist/format.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { builtinModules } from 'node:module';
|
|
2
1
|
import { dirname, join, resolve as pathResolve } from 'node:path';
|
|
3
2
|
import { readFile as fsReadFile, stat as fsStat } from 'node:fs/promises';
|
|
4
3
|
import MagicString from 'magic-string';
|
|
@@ -18,13 +17,9 @@ import { interopHelper } from './pipeline/interopHelpers.js';
|
|
|
18
17
|
import { collectCjsExports } from './utils/exports.js';
|
|
19
18
|
import { collectModuleIdentifiers } from './utils/identifiers.js';
|
|
20
19
|
import { isValidUrl } from './utils/url.js';
|
|
20
|
+
import { builtinSpecifiers } from './utils/builtinSpecifiers.js';
|
|
21
21
|
import { ancestorWalk } from './walk.js';
|
|
22
22
|
const isRequireMainMember = (node, shadowed) => node.type === 'MemberExpression' && node.object.type === 'Identifier' && node.object.name === 'require' && !shadowed.has('require') && node.property.type === 'Identifier' && node.property.name === 'main';
|
|
23
|
-
const builtinSpecifiers = new Set(builtinModules.map(mod => mod.startsWith('node:') ? mod.slice(5) : mod).flatMap(mod => {
|
|
24
|
-
const parts = mod.split('/');
|
|
25
|
-
const base = parts[0];
|
|
26
|
-
return parts.length > 1 ? [mod, base] : [mod];
|
|
27
|
-
}));
|
|
28
23
|
const stripQuery = value => value.includes('?') || value.includes('#') ? value.split(/[?#]/)[0] ?? value : value;
|
|
29
24
|
const packageFromSpecifier = spec => {
|
|
30
25
|
const cleaned = stripQuery(spec);
|
package/dist/module.js
CHANGED
|
@@ -4,18 +4,13 @@ import { specifier } from './specifier.js';
|
|
|
4
4
|
import { parse } from './parse.js';
|
|
5
5
|
import { format, collectDualPackageUsage, dualPackageHazardDiagnostics } from './format.js';
|
|
6
6
|
import { getLangFromExt } from './utils/lang.js';
|
|
7
|
-
import { builtinModules } from 'node:module';
|
|
8
7
|
import { resolve as pathResolve, dirname as pathDirname, extname, join } from 'node:path';
|
|
9
|
-
import { readFile as fsReadFile, stat } from 'node:fs/promises';
|
|
8
|
+
import { readFile as fsReadFile, stat, realpath } from 'node:fs/promises';
|
|
10
9
|
import { parse as parseModule } from './parse.js';
|
|
11
10
|
import { walk } from './walk.js';
|
|
12
11
|
import { collectModuleIdentifiers } from './utils/identifiers.js';
|
|
12
|
+
import { builtinSpecifiers } from './utils/builtinSpecifiers.js';
|
|
13
13
|
const collapseSpecifier = value => value.replace(/['"`+)\s]|new String\(/g, '');
|
|
14
|
-
const builtinSpecifiers = new Set(builtinModules.map(mod => mod.startsWith('node:') ? mod.slice(5) : mod).flatMap(mod => {
|
|
15
|
-
const parts = mod.split('/');
|
|
16
|
-
const base = parts[0];
|
|
17
|
-
return parts.length > 1 ? [mod, base] : [mod];
|
|
18
|
-
}));
|
|
19
14
|
const appendExtensionIfNeeded = (spec, mode, dirIndex, value = spec.value) => {
|
|
20
15
|
if (mode === 'off') return;
|
|
21
16
|
if (spec.type === 'TemplateLiteral') {
|
|
@@ -73,6 +68,7 @@ const fileExists = async candidate => {
|
|
|
73
68
|
return false;
|
|
74
69
|
}
|
|
75
70
|
};
|
|
71
|
+
const normalizePath = async p => pathResolve(await realpath(p).catch(() => p));
|
|
76
72
|
const resolveRequirePath = async (fromFile, spec, dirIndex) => {
|
|
77
73
|
if (!spec.startsWith('./') && !spec.startsWith('../')) return null;
|
|
78
74
|
const base = pathResolve(pathDirname(fromFile), spec);
|
|
@@ -81,11 +77,11 @@ const resolveRequirePath = async (fromFile, spec, dirIndex) => {
|
|
|
81
77
|
if (ext) {
|
|
82
78
|
candidates.push(base);
|
|
83
79
|
} else {
|
|
84
|
-
candidates.push(`${base}.js`, `${base}.cjs`, `${base}.mjs`);
|
|
80
|
+
candidates.push(`${base}.js`, `${base}.cjs`, `${base}.mjs`, `${base}.ts`, `${base}.mts`, `${base}.cts`);
|
|
85
81
|
candidates.push(join(base, dirIndex));
|
|
86
82
|
}
|
|
87
83
|
for (const candidate of candidates) {
|
|
88
|
-
if (await fileExists(candidate)) return candidate;
|
|
84
|
+
if (await fileExists(candidate)) return await normalizePath(candidate);
|
|
89
85
|
}
|
|
90
86
|
return null;
|
|
91
87
|
};
|
|
@@ -115,8 +111,9 @@ const detectCircularRequireGraph = async (entryFile, mode, dirIndex) => {
|
|
|
115
111
|
const visiting = new Set();
|
|
116
112
|
const visited = new Set();
|
|
117
113
|
const dfs = async (file, stack) => {
|
|
118
|
-
|
|
119
|
-
|
|
114
|
+
const normalized = await normalizePath(file);
|
|
115
|
+
if (visiting.has(normalized)) {
|
|
116
|
+
const cycle = [...stack, normalized];
|
|
120
117
|
const msg = `Circular require detected: ${cycle.join(' -> ')}`;
|
|
121
118
|
if (mode === 'error') {
|
|
122
119
|
throw new Error(msg);
|
|
@@ -125,22 +122,22 @@ const detectCircularRequireGraph = async (entryFile, mode, dirIndex) => {
|
|
|
125
122
|
console.warn(msg);
|
|
126
123
|
return;
|
|
127
124
|
}
|
|
128
|
-
if (visited.has(
|
|
129
|
-
visiting.add(
|
|
130
|
-
stack.push(
|
|
131
|
-
let deps = cache.get(
|
|
125
|
+
if (visited.has(normalized)) return;
|
|
126
|
+
visiting.add(normalized);
|
|
127
|
+
stack.push(normalized);
|
|
128
|
+
let deps = cache.get(normalized);
|
|
132
129
|
if (!deps) {
|
|
133
|
-
deps = await collectStaticRequires(
|
|
134
|
-
cache.set(
|
|
130
|
+
deps = await collectStaticRequires(normalized, dirIndex);
|
|
131
|
+
cache.set(normalized, deps);
|
|
135
132
|
}
|
|
136
133
|
for (const dep of deps) {
|
|
137
134
|
await dfs(dep, stack);
|
|
138
135
|
}
|
|
139
136
|
stack.pop();
|
|
140
|
-
visiting.delete(
|
|
141
|
-
visited.add(
|
|
137
|
+
visiting.delete(normalized);
|
|
138
|
+
visited.add(normalized);
|
|
142
139
|
};
|
|
143
|
-
await dfs(entryFile, []);
|
|
140
|
+
await dfs(await normalizePath(entryFile), []);
|
|
144
141
|
};
|
|
145
142
|
const mergeUsageMaps = (target, source) => {
|
|
146
143
|
for (const [pkg, usage] of source) {
|
|
@@ -182,12 +179,13 @@ const collectProjectDualPackageHazards = async (files, opts) => {
|
|
|
182
179
|
}
|
|
183
180
|
return byFile;
|
|
184
181
|
};
|
|
185
|
-
const
|
|
182
|
+
const createDefaultOptions = () => ({
|
|
186
183
|
target: 'commonjs',
|
|
187
184
|
sourceType: 'auto',
|
|
188
185
|
transformSyntax: true,
|
|
189
186
|
liveBindings: 'strict',
|
|
190
187
|
rewriteSpecifier: undefined,
|
|
188
|
+
rewriteTemplateLiterals: 'allow',
|
|
191
189
|
appendJsExtension: undefined,
|
|
192
190
|
appendDirectoryIndex: 'index.js',
|
|
193
191
|
dirFilename: 'inject',
|
|
@@ -206,12 +204,16 @@ const defaultOptions = {
|
|
|
206
204
|
cwd: undefined,
|
|
207
205
|
out: undefined,
|
|
208
206
|
inPlace: false
|
|
209
|
-
};
|
|
210
|
-
const transform = async (filename, options
|
|
211
|
-
const
|
|
212
|
-
|
|
207
|
+
});
|
|
208
|
+
const transform = async (filename, options) => {
|
|
209
|
+
const base = createDefaultOptions();
|
|
210
|
+
const opts = options ? {
|
|
211
|
+
...base,
|
|
213
212
|
...options,
|
|
214
213
|
filePath: filename
|
|
214
|
+
} : {
|
|
215
|
+
...base,
|
|
216
|
+
filePath: filename
|
|
215
217
|
};
|
|
216
218
|
const cwdBase = opts.cwd ? resolve(opts.cwd) : process.cwd();
|
|
217
219
|
const appendMode = options?.appendJsExtension ?? (opts.target === 'module' ? 'relative-only' : 'off');
|
|
@@ -223,6 +225,10 @@ const transform = async (filename, options = defaultOptions) => {
|
|
|
223
225
|
let source = await format(code, ast, opts);
|
|
224
226
|
if (opts.rewriteSpecifier || appendMode !== 'off' || dirIndex) {
|
|
225
227
|
const code = await specifier.updateSrc(source, getLangFromExt(filename), spec => {
|
|
228
|
+
if (spec.type === 'TemplateLiteral' && opts.rewriteTemplateLiterals === 'static-only') {
|
|
229
|
+
const node = spec.node;
|
|
230
|
+
if (node.expressions.length > 0) return;
|
|
231
|
+
}
|
|
226
232
|
const normalized = normalizeBuiltinSpecifier(spec.value);
|
|
227
233
|
const rewritten = rewriteSpecifierValue(normalized ?? spec.value, opts.rewriteSpecifier);
|
|
228
234
|
const baseValue = rewritten ?? normalized ?? spec.value;
|
package/dist/types.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export type ModuleOptions = {
|
|
|
17
17
|
liveBindings?: 'strict' | 'loose' | 'off';
|
|
18
18
|
/** Rewrite import specifiers (e.g. add extensions). */
|
|
19
19
|
rewriteSpecifier?: RewriteSpecifier;
|
|
20
|
+
/** Whether to rewrite template literals that contain expressions. Default allows rewrites; set to 'static-only' to skip interpolated templates. */
|
|
21
|
+
rewriteTemplateLiterals?: 'allow' | 'static-only';
|
|
20
22
|
/** Whether to append .js to relative imports. */
|
|
21
23
|
appendJsExtension?: 'off' | 'relative-only' | 'all';
|
|
22
24
|
/** Add directory index (e.g. /index.js) or disable. */
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { builtinModules } from 'node:module';
|
|
2
|
+
const builtinSpecifiers = new Set(builtinModules.map(mod => mod.startsWith('node:') ? mod.slice(5) : mod).flatMap(mod => {
|
|
3
|
+
const parts = mod.split('/');
|
|
4
|
+
const base = parts[0];
|
|
5
|
+
return parts.length > 1 ? [mod, base] : [mod];
|
|
6
|
+
}));
|
|
7
|
+
export { builtinSpecifiers };
|