@knighted/duel 2.1.7 → 3.1.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 +14 -1
- package/dist/cjs/duel.cjs +230 -25
- package/dist/cjs/init.cjs +11 -1
- package/dist/cjs/init.d.cts +1 -0
- package/dist/esm/duel.js +231 -26
- package/dist/esm/init.d.ts +1 -0
- package/dist/esm/init.js +11 -1
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Tool for building a Node.js [dual package](https://nodejs.org/api/packages.html#
|
|
|
16
16
|
|
|
17
17
|
## Requirements
|
|
18
18
|
|
|
19
|
-
- Node >=
|
|
19
|
+
- Node >= 22.21.1 (<23) or >= 24 (<25)
|
|
20
20
|
|
|
21
21
|
## Example
|
|
22
22
|
|
|
@@ -73,6 +73,13 @@ Assuming an `outDir` of `dist`, running the above will create `dist/esm` and `di
|
|
|
73
73
|
|
|
74
74
|
TypeScript will throw compiler errors when using `import.meta` globals while targeting a CommonJS dual build, but _will not_ throw compiler errors when the inverse is true, i.e. using CommonJS globals (`__filename`, `__dirname`, etc.) while targeting an ES module dual build. There is an [open issue](https://github.com/microsoft/TypeScript/issues/58658) regarding this unexpected behavior. You can use the `--modules` option to have the [differences between ES modules and CommonJS](https://nodejs.org/api/esm.html#differences-between-es-modules-and-commonjs) transformed by `duel` prior to running compilation with `tsc` so that there are no compilation or runtime errors.
|
|
75
75
|
|
|
76
|
+
`duel` infers the primary vs dual build orientation from your `package.json` `type`:
|
|
77
|
+
|
|
78
|
+
- `"type": "module"` → primary ESM, dual CJS
|
|
79
|
+
- `"type": "commonjs"` → primary CJS, dual ESM
|
|
80
|
+
|
|
81
|
+
The `--dirs` flag nests outputs under `outDir/esm` and `outDir/cjs` accordingly.
|
|
82
|
+
|
|
76
83
|
Note, there is a slight performance penalty since your project needs to be copied first to run the transforms before compiling with `tsc`.
|
|
77
84
|
|
|
78
85
|
```json
|
|
@@ -91,6 +98,10 @@ The available options are limited, because you should define most of them inside
|
|
|
91
98
|
- `--pkg-dir, -k` The directory to start looking for a package.json file. Defaults to `--project` dir.
|
|
92
99
|
- `--modules, -m` Transform module globals for dual build target. Defaults to false.
|
|
93
100
|
- `--dirs, -d` Outputs both builds to directories inside of `outDir`. Defaults to `false`.
|
|
101
|
+
- `--exports, -e` Generate `package.json` `exports` from build output. Values: `wildcard` | `dir` | `name`.
|
|
102
|
+
|
|
103
|
+
> [!NOTE]
|
|
104
|
+
> Exports keys are extensionless by design; the target `import`/`require`/`types` entries keep explicit file extensions so Node resolution remains deterministic.
|
|
94
105
|
|
|
95
106
|
You can run `duel --help` to get the same info. Below is the output of that:
|
|
96
107
|
|
|
@@ -124,3 +135,5 @@ Fortunately, Node.js has added `--experimental-require-module` so that you can [
|
|
|
124
135
|
## Documentation
|
|
125
136
|
|
|
126
137
|
- [docs/faq.md](docs/faq.md)
|
|
138
|
+
- [docs/exports.md](docs/exports.md)
|
|
139
|
+
- [docs/migrate-v2-v3.md](docs/migrate-v2-v3.md)
|
package/dist/cjs/duel.cjs
CHANGED
|
@@ -10,19 +10,181 @@ const node_crypto_1 = require("node:crypto");
|
|
|
10
10
|
const node_perf_hooks_1 = require("node:perf_hooks");
|
|
11
11
|
const glob_1 = require("glob");
|
|
12
12
|
const find_up_1 = require("find-up");
|
|
13
|
-
const specifier_1 = require("@knighted/specifier");
|
|
14
13
|
const module_1 = require("@knighted/module");
|
|
15
14
|
const init_js_1 = require("./init.cjs");
|
|
16
15
|
const util_js_1 = require("./util.cjs");
|
|
16
|
+
const stripKnownExt = path => {
|
|
17
|
+
return path.replace(/(\.d\.(?:ts|mts|cts)|\.(?:mjs|cjs|js))$/, '');
|
|
18
|
+
};
|
|
19
|
+
const ensureDotSlash = path => {
|
|
20
|
+
return path.startsWith('./') ? path : `./${path}`;
|
|
21
|
+
};
|
|
22
|
+
const getSubpath = (mode, relFromRoot) => {
|
|
23
|
+
const parsed = (0, node_path_1.parse)(relFromRoot);
|
|
24
|
+
const segments = parsed.dir.split('/').filter(Boolean);
|
|
25
|
+
if (mode === 'name') {
|
|
26
|
+
return parsed.name ? `./${parsed.name}` : null;
|
|
27
|
+
}
|
|
28
|
+
if (mode === 'dir') {
|
|
29
|
+
const last = segments.at(-1);
|
|
30
|
+
return last ? `./${last}` : null;
|
|
31
|
+
}
|
|
32
|
+
if (mode === 'wildcard') {
|
|
33
|
+
const first = segments[0];
|
|
34
|
+
return first ? `./${first}/*` : null;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
};
|
|
17
38
|
const handleErrorAndExit = message => {
|
|
18
39
|
const exitCode = Number(message);
|
|
19
40
|
(0, util_js_1.logError)('Compilation errors found.');
|
|
20
41
|
process.exit(exitCode);
|
|
21
42
|
};
|
|
43
|
+
const generateExports = async (options) => {
|
|
44
|
+
const { mode, pkg, pkgDir, esmRoot, cjsRoot, mainDefaultKind, mainPath } = options;
|
|
45
|
+
const toPosix = path => path.replace(/\\/g, '/');
|
|
46
|
+
const esmRootPosix = toPosix(esmRoot);
|
|
47
|
+
const cjsRootPosix = toPosix(cjsRoot);
|
|
48
|
+
const esmIgnore = ['node_modules/**'];
|
|
49
|
+
const cjsIgnore = ['node_modules/**'];
|
|
50
|
+
const baseMap = new Map();
|
|
51
|
+
const subpathMap = new Map();
|
|
52
|
+
const baseToSubpath = new Map();
|
|
53
|
+
if (cjsRootPosix.startsWith(`${esmRootPosix}/`)) {
|
|
54
|
+
esmIgnore.push(`${cjsRootPosix}/**`);
|
|
55
|
+
}
|
|
56
|
+
if (esmRootPosix.startsWith(`${cjsRootPosix}/`)) {
|
|
57
|
+
cjsIgnore.push(`${esmRootPosix}/**`);
|
|
58
|
+
}
|
|
59
|
+
const recordPath = (kind, filePath, root) => {
|
|
60
|
+
const relPkg = toPosix((0, node_path_1.relative)(pkgDir, filePath));
|
|
61
|
+
const relFromRoot = toPosix((0, node_path_1.relative)(root, filePath));
|
|
62
|
+
const withDot = ensureDotSlash(relPkg);
|
|
63
|
+
const baseKey = stripKnownExt(relPkg);
|
|
64
|
+
const baseEntry = baseMap.get(baseKey) ?? {};
|
|
65
|
+
baseEntry[kind] = withDot;
|
|
66
|
+
baseMap.set(baseKey, baseEntry);
|
|
67
|
+
const subpath = getSubpath(mode, relFromRoot);
|
|
68
|
+
if (kind === 'types') {
|
|
69
|
+
const mappedSubpath = baseToSubpath.get(baseKey);
|
|
70
|
+
if (mappedSubpath) {
|
|
71
|
+
const subEntry = subpathMap.get(mappedSubpath) ?? {};
|
|
72
|
+
subEntry.types = withDot;
|
|
73
|
+
subpathMap.set(mappedSubpath, subEntry);
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (subpath && subpath !== '.') {
|
|
78
|
+
const subEntry = subpathMap.get(subpath) ?? {};
|
|
79
|
+
subEntry[kind] = withDot;
|
|
80
|
+
subpathMap.set(subpath, subEntry);
|
|
81
|
+
baseToSubpath.set(baseKey, subpath);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const esmFiles = await (0, glob_1.glob)(`${esmRootPosix}/**/*.{js,mjs,d.ts,d.mts}`, {
|
|
85
|
+
ignore: esmIgnore,
|
|
86
|
+
});
|
|
87
|
+
for (const file of esmFiles) {
|
|
88
|
+
if (/\.d\.(ts|mts)$/.test(file)) {
|
|
89
|
+
recordPath('types', file, esmRoot);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
recordPath('import', file, esmRoot);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const cjsFiles = await (0, glob_1.glob)(`${cjsRootPosix}/**/*.{js,cjs,d.ts,d.cts}`, {
|
|
96
|
+
ignore: cjsIgnore,
|
|
97
|
+
});
|
|
98
|
+
for (const file of cjsFiles) {
|
|
99
|
+
if (/\.d\.(ts|cts)$/.test(file)) {
|
|
100
|
+
recordPath('types', file, cjsRoot);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
recordPath('require', file, cjsRoot);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const exportsMap = {};
|
|
107
|
+
const mainBase = mainPath ? stripKnownExt(mainPath.replace(/^\.\//, '')) : null;
|
|
108
|
+
const mainEntry = mainBase ? (baseMap.get(mainBase) ?? {}) : {};
|
|
109
|
+
if (mainPath) {
|
|
110
|
+
const rootEntry = {};
|
|
111
|
+
if (mainEntry.types) {
|
|
112
|
+
rootEntry.types = mainEntry.types;
|
|
113
|
+
}
|
|
114
|
+
if (mainDefaultKind === 'import') {
|
|
115
|
+
rootEntry.import = mainEntry.import ?? ensureDotSlash(mainPath);
|
|
116
|
+
if (mainEntry.require) {
|
|
117
|
+
rootEntry.require = mainEntry.require;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
rootEntry.require = mainEntry.require ?? ensureDotSlash(mainPath);
|
|
122
|
+
if (mainEntry.import) {
|
|
123
|
+
rootEntry.import = mainEntry.import;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
rootEntry.default = ensureDotSlash(mainPath);
|
|
127
|
+
exportsMap['.'] = rootEntry;
|
|
128
|
+
}
|
|
129
|
+
const defaultKind = mainDefaultKind ?? 'import';
|
|
130
|
+
for (const [subpath, entry] of subpathMap.entries()) {
|
|
131
|
+
const out = {};
|
|
132
|
+
if (entry.types) {
|
|
133
|
+
out.types = entry.types;
|
|
134
|
+
}
|
|
135
|
+
if (entry.import) {
|
|
136
|
+
out.import = entry.import;
|
|
137
|
+
}
|
|
138
|
+
if (entry.require) {
|
|
139
|
+
out.require = entry.require;
|
|
140
|
+
}
|
|
141
|
+
const def = defaultKind === 'import'
|
|
142
|
+
? (entry.import ?? entry.require)
|
|
143
|
+
: (entry.require ?? entry.import);
|
|
144
|
+
if (def) {
|
|
145
|
+
out.default = def;
|
|
146
|
+
}
|
|
147
|
+
if (Object.keys(out).length) {
|
|
148
|
+
exportsMap[subpath] = out;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (!exportsMap['.'] && baseMap.size) {
|
|
152
|
+
const [subpath, entry] = subpathMap.entries().next().value ?? [];
|
|
153
|
+
if (entry) {
|
|
154
|
+
const out = {};
|
|
155
|
+
if (entry.types) {
|
|
156
|
+
out.types = entry.types;
|
|
157
|
+
}
|
|
158
|
+
if (entry.import) {
|
|
159
|
+
out.import = entry.import;
|
|
160
|
+
}
|
|
161
|
+
if (entry.require) {
|
|
162
|
+
out.require = entry.require;
|
|
163
|
+
}
|
|
164
|
+
const def = defaultKind === 'import'
|
|
165
|
+
? (entry.import ?? entry.require)
|
|
166
|
+
: (entry.require ?? entry.import);
|
|
167
|
+
if (def) {
|
|
168
|
+
out.default = def;
|
|
169
|
+
}
|
|
170
|
+
if (Object.keys(out).length) {
|
|
171
|
+
exportsMap['.'] = out;
|
|
172
|
+
exportsMap[subpath] ??= out;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (Object.keys(exportsMap).length) {
|
|
177
|
+
const pkgJson = {
|
|
178
|
+
...pkg.packageJson,
|
|
179
|
+
exports: exportsMap,
|
|
180
|
+
};
|
|
181
|
+
await (0, promises_1.writeFile)(pkg.path, `${JSON.stringify(pkgJson, null, 2)}\n`);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
22
184
|
const duel = async (args) => {
|
|
23
185
|
const ctx = await (0, init_js_1.init)(args);
|
|
24
186
|
if (ctx) {
|
|
25
|
-
const { projectDir, tsconfig, configPath, modules, dirs, pkg } = ctx;
|
|
187
|
+
const { projectDir, tsconfig, configPath, modules, dirs, pkg, exports: exportsOpt, } = ctx;
|
|
26
188
|
const tsc = await (0, find_up_1.findUp)(async (dir) => {
|
|
27
189
|
const tscBin = (0, node_path_1.join)(dir, 'node_modules', '.bin', 'tsc');
|
|
28
190
|
try {
|
|
@@ -46,11 +208,17 @@ const duel = async (args) => {
|
|
|
46
208
|
});
|
|
47
209
|
};
|
|
48
210
|
const pkgDir = (0, node_path_1.dirname)(pkg.path);
|
|
211
|
+
const mainPath = pkg.packageJson.main;
|
|
212
|
+
const mainDefaultKind = mainPath?.endsWith('.cjs') ? 'require' : 'import';
|
|
49
213
|
const outDir = tsconfig.compilerOptions?.outDir ?? 'dist';
|
|
50
214
|
const absoluteOutDir = (0, node_path_1.resolve)(projectDir, outDir);
|
|
51
215
|
const originalType = pkg.packageJson.type ?? 'commonjs';
|
|
52
216
|
const isCjsBuild = originalType !== 'commonjs';
|
|
53
|
-
const
|
|
217
|
+
const primaryOutDir = dirs
|
|
218
|
+
? isCjsBuild
|
|
219
|
+
? (0, node_path_1.join)(absoluteOutDir, 'esm')
|
|
220
|
+
: (0, node_path_1.join)(absoluteOutDir, 'cjs')
|
|
221
|
+
: absoluteOutDir;
|
|
54
222
|
const hex = (0, node_crypto_1.randomBytes)(4).toString('hex');
|
|
55
223
|
const getOverrideTsConfig = () => {
|
|
56
224
|
return {
|
|
@@ -63,29 +231,39 @@ const duel = async (args) => {
|
|
|
63
231
|
};
|
|
64
232
|
};
|
|
65
233
|
const runPrimaryBuild = () => {
|
|
66
|
-
return runBuild(configPath,
|
|
67
|
-
? isCjsBuild
|
|
68
|
-
? (0, node_path_1.join)(absoluteOutDir, 'esm')
|
|
69
|
-
: (0, node_path_1.join)(absoluteOutDir, 'cjs')
|
|
70
|
-
: absoluteOutDir);
|
|
234
|
+
return runBuild(configPath, primaryOutDir);
|
|
71
235
|
};
|
|
72
|
-
const updateSpecifiersAndFileExtensions = async (filenames) => {
|
|
236
|
+
const updateSpecifiersAndFileExtensions = async (filenames, target, ext) => {
|
|
73
237
|
for (const filename of filenames) {
|
|
74
238
|
const dts = /(\.d\.ts)$/;
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
239
|
+
const isDts = dts.test(filename);
|
|
240
|
+
const outFilename = isDts
|
|
241
|
+
? filename.replace(dts, target === 'commonjs' ? '.d.cts' : '.d.mts')
|
|
242
|
+
: filename.replace(/\.js$/, ext);
|
|
243
|
+
if (isDts) {
|
|
244
|
+
const source = await (0, promises_1.readFile)(filename, 'utf8');
|
|
245
|
+
const rewritten = source.replace(/(?<=['"])(\.\.?(?:\/[\w.-]+)*)\.js(?=['"])/g, `$1${ext}`);
|
|
246
|
+
await (0, promises_1.writeFile)(outFilename, rewritten);
|
|
247
|
+
if (outFilename !== filename) {
|
|
248
|
+
await (0, promises_1.rm)(filename, { force: true });
|
|
249
|
+
}
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const rewriteSpecifier = (value = '') => {
|
|
80
253
|
const collapsed = value.replace(/['"`+)\s]|new String\(/g, '');
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// $2 is for any closing quotation/parens around BE or NE
|
|
84
|
-
return value.replace(/(.+)\.js([)'"`]*)?$/, `$1${targetExt}$2`);
|
|
254
|
+
if (/^(?:\.|\.\.)\//.test(collapsed)) {
|
|
255
|
+
return value.replace(/(.+)\.js([)"'`]*)?$/, `$1${ext}$2`);
|
|
85
256
|
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
257
|
+
};
|
|
258
|
+
const writeOptions = {
|
|
259
|
+
target,
|
|
260
|
+
rewriteSpecifier,
|
|
261
|
+
...(outFilename === filename ? { inPlace: true } : { out: outFilename }),
|
|
262
|
+
};
|
|
263
|
+
await (0, module_1.transform)(filename, writeOptions);
|
|
264
|
+
if (outFilename !== filename) {
|
|
265
|
+
await (0, promises_1.rm)(filename, { force: true });
|
|
266
|
+
}
|
|
89
267
|
}
|
|
90
268
|
};
|
|
91
269
|
const logSuccess = start => {
|
|
@@ -112,7 +290,11 @@ const duel = async (args) => {
|
|
|
112
290
|
const compileFiles = (0, util_js_1.getCompileFiles)(tsc, projectDir);
|
|
113
291
|
dualConfigPath = (0, node_path_1.join)(subDir, `tsconfig.${hex}.json`);
|
|
114
292
|
await (0, promises_1.mkdir)(subDir);
|
|
115
|
-
await Promise.all(compileFiles.map(
|
|
293
|
+
await Promise.all(compileFiles.map(async (file) => {
|
|
294
|
+
const dest = (0, node_path_1.join)(subDir, (0, node_path_1.relative)(projectDir, file).replace(/^(\.\.\/)+/, ''));
|
|
295
|
+
await (0, promises_1.mkdir)((0, node_path_1.dirname)(dest), { recursive: true });
|
|
296
|
+
await (0, promises_1.cp)(file, dest);
|
|
297
|
+
}));
|
|
116
298
|
/**
|
|
117
299
|
* Transform ambiguous modules for the target dual build.
|
|
118
300
|
* @see https://github.com/microsoft/TypeScript/issues/58658
|
|
@@ -129,7 +311,11 @@ const duel = async (args) => {
|
|
|
129
311
|
*
|
|
130
312
|
* @see https://github.com/microsoft/TypeScript/issues/58658
|
|
131
313
|
*/
|
|
132
|
-
await (0, module_1.transform)(file, {
|
|
314
|
+
await (0, module_1.transform)(file, {
|
|
315
|
+
out: file,
|
|
316
|
+
target: isCjsBuild ? 'commonjs' : 'module',
|
|
317
|
+
transformSyntax: false,
|
|
318
|
+
});
|
|
133
319
|
}
|
|
134
320
|
}
|
|
135
321
|
/**
|
|
@@ -161,10 +347,29 @@ const duel = async (args) => {
|
|
|
161
347
|
}
|
|
162
348
|
}
|
|
163
349
|
if (success) {
|
|
350
|
+
const dualTarget = isCjsBuild ? 'commonjs' : 'module';
|
|
351
|
+
const dualTargetExt = isCjsBuild ? '.cjs' : dirs ? '.js' : '.mjs';
|
|
164
352
|
const filenames = await (0, glob_1.glob)(`${absoluteDualOutDir.replace(/\\/g, '/')}/**/*{.js,.d.ts}`, {
|
|
165
353
|
ignore: 'node_modules/**',
|
|
166
354
|
});
|
|
167
|
-
await updateSpecifiersAndFileExtensions(filenames);
|
|
355
|
+
await updateSpecifiersAndFileExtensions(filenames, dualTarget, dualTargetExt);
|
|
356
|
+
if (dirs && originalType === 'commonjs') {
|
|
357
|
+
const primaryFiles = await (0, glob_1.glob)(`${primaryOutDir.replace(/\\/g, '/')}/**/*{.js,.d.ts}`, { ignore: 'node_modules/**' });
|
|
358
|
+
await updateSpecifiersAndFileExtensions(primaryFiles, 'commonjs', '.cjs');
|
|
359
|
+
}
|
|
360
|
+
if (exportsOpt) {
|
|
361
|
+
const esmRoot = isCjsBuild ? primaryOutDir : absoluteDualOutDir;
|
|
362
|
+
const cjsRoot = isCjsBuild ? absoluteDualOutDir : primaryOutDir;
|
|
363
|
+
await generateExports({
|
|
364
|
+
mode: exportsOpt,
|
|
365
|
+
pkg,
|
|
366
|
+
pkgDir,
|
|
367
|
+
esmRoot,
|
|
368
|
+
cjsRoot,
|
|
369
|
+
mainDefaultKind,
|
|
370
|
+
mainPath,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
168
373
|
logSuccess(startTime);
|
|
169
374
|
}
|
|
170
375
|
}
|
|
@@ -173,7 +378,7 @@ const duel = async (args) => {
|
|
|
173
378
|
exports.duel = duel;
|
|
174
379
|
(async () => {
|
|
175
380
|
const realFileUrlArgv1 = await (0, util_js_1.getRealPathAsFileUrl)(node_process_1.argv[1] ?? '');
|
|
176
|
-
if (require("node:url").pathToFileURL(__filename).
|
|
381
|
+
if (require("node:url").pathToFileURL(__filename).href === realFileUrlArgv1) {
|
|
177
382
|
await duel();
|
|
178
383
|
}
|
|
179
384
|
})();
|
package/dist/cjs/init.cjs
CHANGED
|
@@ -37,6 +37,10 @@ const init = async (args) => {
|
|
|
37
37
|
short: 'd',
|
|
38
38
|
default: false,
|
|
39
39
|
},
|
|
40
|
+
exports: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
short: 'e',
|
|
43
|
+
},
|
|
40
44
|
help: {
|
|
41
45
|
type: 'boolean',
|
|
42
46
|
short: 'h',
|
|
@@ -57,10 +61,11 @@ const init = async (args) => {
|
|
|
57
61
|
(0, util_js_1.log)('--pkg-dir, -k [path] \t The directory to start looking for a package.json file. Defaults to --project directory.');
|
|
58
62
|
(0, util_js_1.log)('--modules, -m \t\t Transform module globals for dual build target. Defaults to false.');
|
|
59
63
|
(0, util_js_1.log)('--dirs, -d \t\t Output both builds to directories inside of outDir. [esm, cjs].');
|
|
64
|
+
(0, util_js_1.log)('--exports, -e \t Generate package.json exports. Values: wildcard | dir | name.');
|
|
60
65
|
(0, util_js_1.log)('--help, -h \t\t Print this message.');
|
|
61
66
|
}
|
|
62
67
|
else {
|
|
63
|
-
const { project, 'target-extension': targetExt, 'pkg-dir': pkgDir, modules, dirs, } = parsed;
|
|
68
|
+
const { project, 'target-extension': targetExt, 'pkg-dir': pkgDir, modules, dirs, exports: exportsOpt, } = parsed;
|
|
64
69
|
let configPath = (0, node_path_1.resolve)(project);
|
|
65
70
|
let stats = null;
|
|
66
71
|
let pkg = null;
|
|
@@ -96,10 +101,15 @@ const init = async (args) => {
|
|
|
96
101
|
if (!tsconfig.compilerOptions?.outDir) {
|
|
97
102
|
(0, util_js_1.log)('No outDir defined in tsconfig.json. Build output will be in "dist".');
|
|
98
103
|
}
|
|
104
|
+
if (exportsOpt && !['wildcard', 'dir', 'name'].includes(exportsOpt)) {
|
|
105
|
+
(0, util_js_1.logError)('--exports expects one of: wildcard | dir | name');
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
99
108
|
return {
|
|
100
109
|
pkg,
|
|
101
110
|
dirs,
|
|
102
111
|
modules,
|
|
112
|
+
exports: exportsOpt,
|
|
103
113
|
tsconfig,
|
|
104
114
|
projectDir,
|
|
105
115
|
configPath,
|
package/dist/cjs/init.d.cts
CHANGED
|
@@ -2,6 +2,7 @@ export function init(args: any): Promise<false | {
|
|
|
2
2
|
pkg: import("read-package-up", { with: { "resolution-mode": "import" } }).NormalizedReadResult;
|
|
3
3
|
dirs: boolean;
|
|
4
4
|
modules: boolean;
|
|
5
|
+
exports: string | undefined;
|
|
5
6
|
tsconfig: {
|
|
6
7
|
compilerOptions?: import("get-tsconfig").TsConfigJson.CompilerOptions | undefined;
|
|
7
8
|
watchOptions?: import("get-tsconfig").TsConfigJson.WatchOptions | undefined;
|
package/dist/esm/duel.js
CHANGED
|
@@ -1,25 +1,187 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { argv, platform } from 'node:process';
|
|
3
|
-
import { join, dirname, resolve, relative } from 'node:path';
|
|
3
|
+
import { join, dirname, resolve, relative, parse as parsePath } from 'node:path';
|
|
4
4
|
import { spawn } from 'node:child_process';
|
|
5
|
-
import { writeFile, rm, rename, mkdir, cp, access } from 'node:fs/promises';
|
|
5
|
+
import { writeFile, rm, rename, mkdir, cp, access, readFile } from 'node:fs/promises';
|
|
6
6
|
import { randomBytes } from 'node:crypto';
|
|
7
7
|
import { performance } from 'node:perf_hooks';
|
|
8
8
|
import { glob } from 'glob';
|
|
9
9
|
import { findUp } from 'find-up';
|
|
10
|
-
import { specifier } from '@knighted/specifier';
|
|
11
10
|
import { transform } from '@knighted/module';
|
|
12
11
|
import { init } from './init.js';
|
|
13
12
|
import { getRealPathAsFileUrl, getCompileFiles, logError, log } from './util.js';
|
|
13
|
+
const stripKnownExt = path => {
|
|
14
|
+
return path.replace(/(\.d\.(?:ts|mts|cts)|\.(?:mjs|cjs|js))$/, '');
|
|
15
|
+
};
|
|
16
|
+
const ensureDotSlash = path => {
|
|
17
|
+
return path.startsWith('./') ? path : `./${path}`;
|
|
18
|
+
};
|
|
19
|
+
const getSubpath = (mode, relFromRoot) => {
|
|
20
|
+
const parsed = parsePath(relFromRoot);
|
|
21
|
+
const segments = parsed.dir.split('/').filter(Boolean);
|
|
22
|
+
if (mode === 'name') {
|
|
23
|
+
return parsed.name ? `./${parsed.name}` : null;
|
|
24
|
+
}
|
|
25
|
+
if (mode === 'dir') {
|
|
26
|
+
const last = segments.at(-1);
|
|
27
|
+
return last ? `./${last}` : null;
|
|
28
|
+
}
|
|
29
|
+
if (mode === 'wildcard') {
|
|
30
|
+
const first = segments[0];
|
|
31
|
+
return first ? `./${first}/*` : null;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
};
|
|
14
35
|
const handleErrorAndExit = message => {
|
|
15
36
|
const exitCode = Number(message);
|
|
16
37
|
logError('Compilation errors found.');
|
|
17
38
|
process.exit(exitCode);
|
|
18
39
|
};
|
|
40
|
+
const generateExports = async (options) => {
|
|
41
|
+
const { mode, pkg, pkgDir, esmRoot, cjsRoot, mainDefaultKind, mainPath } = options;
|
|
42
|
+
const toPosix = path => path.replace(/\\/g, '/');
|
|
43
|
+
const esmRootPosix = toPosix(esmRoot);
|
|
44
|
+
const cjsRootPosix = toPosix(cjsRoot);
|
|
45
|
+
const esmIgnore = ['node_modules/**'];
|
|
46
|
+
const cjsIgnore = ['node_modules/**'];
|
|
47
|
+
const baseMap = new Map();
|
|
48
|
+
const subpathMap = new Map();
|
|
49
|
+
const baseToSubpath = new Map();
|
|
50
|
+
if (cjsRootPosix.startsWith(`${esmRootPosix}/`)) {
|
|
51
|
+
esmIgnore.push(`${cjsRootPosix}/**`);
|
|
52
|
+
}
|
|
53
|
+
if (esmRootPosix.startsWith(`${cjsRootPosix}/`)) {
|
|
54
|
+
cjsIgnore.push(`${esmRootPosix}/**`);
|
|
55
|
+
}
|
|
56
|
+
const recordPath = (kind, filePath, root) => {
|
|
57
|
+
const relPkg = toPosix(relative(pkgDir, filePath));
|
|
58
|
+
const relFromRoot = toPosix(relative(root, filePath));
|
|
59
|
+
const withDot = ensureDotSlash(relPkg);
|
|
60
|
+
const baseKey = stripKnownExt(relPkg);
|
|
61
|
+
const baseEntry = baseMap.get(baseKey) ?? {};
|
|
62
|
+
baseEntry[kind] = withDot;
|
|
63
|
+
baseMap.set(baseKey, baseEntry);
|
|
64
|
+
const subpath = getSubpath(mode, relFromRoot);
|
|
65
|
+
if (kind === 'types') {
|
|
66
|
+
const mappedSubpath = baseToSubpath.get(baseKey);
|
|
67
|
+
if (mappedSubpath) {
|
|
68
|
+
const subEntry = subpathMap.get(mappedSubpath) ?? {};
|
|
69
|
+
subEntry.types = withDot;
|
|
70
|
+
subpathMap.set(mappedSubpath, subEntry);
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (subpath && subpath !== '.') {
|
|
75
|
+
const subEntry = subpathMap.get(subpath) ?? {};
|
|
76
|
+
subEntry[kind] = withDot;
|
|
77
|
+
subpathMap.set(subpath, subEntry);
|
|
78
|
+
baseToSubpath.set(baseKey, subpath);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const esmFiles = await glob(`${esmRootPosix}/**/*.{js,mjs,d.ts,d.mts}`, {
|
|
82
|
+
ignore: esmIgnore,
|
|
83
|
+
});
|
|
84
|
+
for (const file of esmFiles) {
|
|
85
|
+
if (/\.d\.(ts|mts)$/.test(file)) {
|
|
86
|
+
recordPath('types', file, esmRoot);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
recordPath('import', file, esmRoot);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const cjsFiles = await glob(`${cjsRootPosix}/**/*.{js,cjs,d.ts,d.cts}`, {
|
|
93
|
+
ignore: cjsIgnore,
|
|
94
|
+
});
|
|
95
|
+
for (const file of cjsFiles) {
|
|
96
|
+
if (/\.d\.(ts|cts)$/.test(file)) {
|
|
97
|
+
recordPath('types', file, cjsRoot);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
recordPath('require', file, cjsRoot);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const exportsMap = {};
|
|
104
|
+
const mainBase = mainPath ? stripKnownExt(mainPath.replace(/^\.\//, '')) : null;
|
|
105
|
+
const mainEntry = mainBase ? (baseMap.get(mainBase) ?? {}) : {};
|
|
106
|
+
if (mainPath) {
|
|
107
|
+
const rootEntry = {};
|
|
108
|
+
if (mainEntry.types) {
|
|
109
|
+
rootEntry.types = mainEntry.types;
|
|
110
|
+
}
|
|
111
|
+
if (mainDefaultKind === 'import') {
|
|
112
|
+
rootEntry.import = mainEntry.import ?? ensureDotSlash(mainPath);
|
|
113
|
+
if (mainEntry.require) {
|
|
114
|
+
rootEntry.require = mainEntry.require;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
rootEntry.require = mainEntry.require ?? ensureDotSlash(mainPath);
|
|
119
|
+
if (mainEntry.import) {
|
|
120
|
+
rootEntry.import = mainEntry.import;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
rootEntry.default = ensureDotSlash(mainPath);
|
|
124
|
+
exportsMap['.'] = rootEntry;
|
|
125
|
+
}
|
|
126
|
+
const defaultKind = mainDefaultKind ?? 'import';
|
|
127
|
+
for (const [subpath, entry] of subpathMap.entries()) {
|
|
128
|
+
const out = {};
|
|
129
|
+
if (entry.types) {
|
|
130
|
+
out.types = entry.types;
|
|
131
|
+
}
|
|
132
|
+
if (entry.import) {
|
|
133
|
+
out.import = entry.import;
|
|
134
|
+
}
|
|
135
|
+
if (entry.require) {
|
|
136
|
+
out.require = entry.require;
|
|
137
|
+
}
|
|
138
|
+
const def = defaultKind === 'import'
|
|
139
|
+
? (entry.import ?? entry.require)
|
|
140
|
+
: (entry.require ?? entry.import);
|
|
141
|
+
if (def) {
|
|
142
|
+
out.default = def;
|
|
143
|
+
}
|
|
144
|
+
if (Object.keys(out).length) {
|
|
145
|
+
exportsMap[subpath] = out;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (!exportsMap['.'] && baseMap.size) {
|
|
149
|
+
const [subpath, entry] = subpathMap.entries().next().value ?? [];
|
|
150
|
+
if (entry) {
|
|
151
|
+
const out = {};
|
|
152
|
+
if (entry.types) {
|
|
153
|
+
out.types = entry.types;
|
|
154
|
+
}
|
|
155
|
+
if (entry.import) {
|
|
156
|
+
out.import = entry.import;
|
|
157
|
+
}
|
|
158
|
+
if (entry.require) {
|
|
159
|
+
out.require = entry.require;
|
|
160
|
+
}
|
|
161
|
+
const def = defaultKind === 'import'
|
|
162
|
+
? (entry.import ?? entry.require)
|
|
163
|
+
: (entry.require ?? entry.import);
|
|
164
|
+
if (def) {
|
|
165
|
+
out.default = def;
|
|
166
|
+
}
|
|
167
|
+
if (Object.keys(out).length) {
|
|
168
|
+
exportsMap['.'] = out;
|
|
169
|
+
exportsMap[subpath] ??= out;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (Object.keys(exportsMap).length) {
|
|
174
|
+
const pkgJson = {
|
|
175
|
+
...pkg.packageJson,
|
|
176
|
+
exports: exportsMap,
|
|
177
|
+
};
|
|
178
|
+
await writeFile(pkg.path, `${JSON.stringify(pkgJson, null, 2)}\n`);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
19
181
|
const duel = async (args) => {
|
|
20
182
|
const ctx = await init(args);
|
|
21
183
|
if (ctx) {
|
|
22
|
-
const { projectDir, tsconfig, configPath, modules, dirs, pkg } = ctx;
|
|
184
|
+
const { projectDir, tsconfig, configPath, modules, dirs, pkg, exports: exportsOpt, } = ctx;
|
|
23
185
|
const tsc = await findUp(async (dir) => {
|
|
24
186
|
const tscBin = join(dir, 'node_modules', '.bin', 'tsc');
|
|
25
187
|
try {
|
|
@@ -43,11 +205,17 @@ const duel = async (args) => {
|
|
|
43
205
|
});
|
|
44
206
|
};
|
|
45
207
|
const pkgDir = dirname(pkg.path);
|
|
208
|
+
const mainPath = pkg.packageJson.main;
|
|
209
|
+
const mainDefaultKind = mainPath?.endsWith('.cjs') ? 'require' : 'import';
|
|
46
210
|
const outDir = tsconfig.compilerOptions?.outDir ?? 'dist';
|
|
47
211
|
const absoluteOutDir = resolve(projectDir, outDir);
|
|
48
212
|
const originalType = pkg.packageJson.type ?? 'commonjs';
|
|
49
213
|
const isCjsBuild = originalType !== 'commonjs';
|
|
50
|
-
const
|
|
214
|
+
const primaryOutDir = dirs
|
|
215
|
+
? isCjsBuild
|
|
216
|
+
? join(absoluteOutDir, 'esm')
|
|
217
|
+
: join(absoluteOutDir, 'cjs')
|
|
218
|
+
: absoluteOutDir;
|
|
51
219
|
const hex = randomBytes(4).toString('hex');
|
|
52
220
|
const getOverrideTsConfig = () => {
|
|
53
221
|
return {
|
|
@@ -60,29 +228,39 @@ const duel = async (args) => {
|
|
|
60
228
|
};
|
|
61
229
|
};
|
|
62
230
|
const runPrimaryBuild = () => {
|
|
63
|
-
return runBuild(configPath,
|
|
64
|
-
? isCjsBuild
|
|
65
|
-
? join(absoluteOutDir, 'esm')
|
|
66
|
-
: join(absoluteOutDir, 'cjs')
|
|
67
|
-
: absoluteOutDir);
|
|
231
|
+
return runBuild(configPath, primaryOutDir);
|
|
68
232
|
};
|
|
69
|
-
const updateSpecifiersAndFileExtensions = async (filenames) => {
|
|
233
|
+
const updateSpecifiersAndFileExtensions = async (filenames, target, ext) => {
|
|
70
234
|
for (const filename of filenames) {
|
|
71
235
|
const dts = /(\.d\.ts)$/;
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
236
|
+
const isDts = dts.test(filename);
|
|
237
|
+
const outFilename = isDts
|
|
238
|
+
? filename.replace(dts, target === 'commonjs' ? '.d.cts' : '.d.mts')
|
|
239
|
+
: filename.replace(/\.js$/, ext);
|
|
240
|
+
if (isDts) {
|
|
241
|
+
const source = await readFile(filename, 'utf8');
|
|
242
|
+
const rewritten = source.replace(/(?<=['"])(\.\.?(?:\/[\w.-]+)*)\.js(?=['"])/g, `$1${ext}`);
|
|
243
|
+
await writeFile(outFilename, rewritten);
|
|
244
|
+
if (outFilename !== filename) {
|
|
245
|
+
await rm(filename, { force: true });
|
|
246
|
+
}
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const rewriteSpecifier = (value = '') => {
|
|
77
250
|
const collapsed = value.replace(/['"`+)\s]|new String\(/g, '');
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// $2 is for any closing quotation/parens around BE or NE
|
|
81
|
-
return value.replace(/(.+)\.js([)'"`]*)?$/, `$1${targetExt}$2`);
|
|
251
|
+
if (/^(?:\.|\.\.)\//.test(collapsed)) {
|
|
252
|
+
return value.replace(/(.+)\.js([)"'`]*)?$/, `$1${ext}$2`);
|
|
82
253
|
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
254
|
+
};
|
|
255
|
+
const writeOptions = {
|
|
256
|
+
target,
|
|
257
|
+
rewriteSpecifier,
|
|
258
|
+
...(outFilename === filename ? { inPlace: true } : { out: outFilename }),
|
|
259
|
+
};
|
|
260
|
+
await transform(filename, writeOptions);
|
|
261
|
+
if (outFilename !== filename) {
|
|
262
|
+
await rm(filename, { force: true });
|
|
263
|
+
}
|
|
86
264
|
}
|
|
87
265
|
};
|
|
88
266
|
const logSuccess = start => {
|
|
@@ -109,7 +287,11 @@ const duel = async (args) => {
|
|
|
109
287
|
const compileFiles = getCompileFiles(tsc, projectDir);
|
|
110
288
|
dualConfigPath = join(subDir, `tsconfig.${hex}.json`);
|
|
111
289
|
await mkdir(subDir);
|
|
112
|
-
await Promise.all(compileFiles.map(
|
|
290
|
+
await Promise.all(compileFiles.map(async (file) => {
|
|
291
|
+
const dest = join(subDir, relative(projectDir, file).replace(/^(\.\.\/)+/, ''));
|
|
292
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
293
|
+
await cp(file, dest);
|
|
294
|
+
}));
|
|
113
295
|
/**
|
|
114
296
|
* Transform ambiguous modules for the target dual build.
|
|
115
297
|
* @see https://github.com/microsoft/TypeScript/issues/58658
|
|
@@ -126,7 +308,11 @@ const duel = async (args) => {
|
|
|
126
308
|
*
|
|
127
309
|
* @see https://github.com/microsoft/TypeScript/issues/58658
|
|
128
310
|
*/
|
|
129
|
-
await transform(file, {
|
|
311
|
+
await transform(file, {
|
|
312
|
+
out: file,
|
|
313
|
+
target: isCjsBuild ? 'commonjs' : 'module',
|
|
314
|
+
transformSyntax: false,
|
|
315
|
+
});
|
|
130
316
|
}
|
|
131
317
|
}
|
|
132
318
|
/**
|
|
@@ -158,10 +344,29 @@ const duel = async (args) => {
|
|
|
158
344
|
}
|
|
159
345
|
}
|
|
160
346
|
if (success) {
|
|
347
|
+
const dualTarget = isCjsBuild ? 'commonjs' : 'module';
|
|
348
|
+
const dualTargetExt = isCjsBuild ? '.cjs' : dirs ? '.js' : '.mjs';
|
|
161
349
|
const filenames = await glob(`${absoluteDualOutDir.replace(/\\/g, '/')}/**/*{.js,.d.ts}`, {
|
|
162
350
|
ignore: 'node_modules/**',
|
|
163
351
|
});
|
|
164
|
-
await updateSpecifiersAndFileExtensions(filenames);
|
|
352
|
+
await updateSpecifiersAndFileExtensions(filenames, dualTarget, dualTargetExt);
|
|
353
|
+
if (dirs && originalType === 'commonjs') {
|
|
354
|
+
const primaryFiles = await glob(`${primaryOutDir.replace(/\\/g, '/')}/**/*{.js,.d.ts}`, { ignore: 'node_modules/**' });
|
|
355
|
+
await updateSpecifiersAndFileExtensions(primaryFiles, 'commonjs', '.cjs');
|
|
356
|
+
}
|
|
357
|
+
if (exportsOpt) {
|
|
358
|
+
const esmRoot = isCjsBuild ? primaryOutDir : absoluteDualOutDir;
|
|
359
|
+
const cjsRoot = isCjsBuild ? absoluteDualOutDir : primaryOutDir;
|
|
360
|
+
await generateExports({
|
|
361
|
+
mode: exportsOpt,
|
|
362
|
+
pkg,
|
|
363
|
+
pkgDir,
|
|
364
|
+
esmRoot,
|
|
365
|
+
cjsRoot,
|
|
366
|
+
mainDefaultKind,
|
|
367
|
+
mainPath,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
165
370
|
logSuccess(startTime);
|
|
166
371
|
}
|
|
167
372
|
}
|
package/dist/esm/init.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export function init(args: any): Promise<false | {
|
|
|
2
2
|
pkg: import("read-package-up").NormalizedReadResult;
|
|
3
3
|
dirs: boolean;
|
|
4
4
|
modules: boolean;
|
|
5
|
+
exports: string | undefined;
|
|
5
6
|
tsconfig: {
|
|
6
7
|
compilerOptions?: import("get-tsconfig").TsConfigJson.CompilerOptions | undefined;
|
|
7
8
|
watchOptions?: import("get-tsconfig").TsConfigJson.WatchOptions | undefined;
|
package/dist/esm/init.js
CHANGED
|
@@ -34,6 +34,10 @@ const init = async (args) => {
|
|
|
34
34
|
short: 'd',
|
|
35
35
|
default: false,
|
|
36
36
|
},
|
|
37
|
+
exports: {
|
|
38
|
+
type: 'string',
|
|
39
|
+
short: 'e',
|
|
40
|
+
},
|
|
37
41
|
help: {
|
|
38
42
|
type: 'boolean',
|
|
39
43
|
short: 'h',
|
|
@@ -54,10 +58,11 @@ const init = async (args) => {
|
|
|
54
58
|
log('--pkg-dir, -k [path] \t The directory to start looking for a package.json file. Defaults to --project directory.');
|
|
55
59
|
log('--modules, -m \t\t Transform module globals for dual build target. Defaults to false.');
|
|
56
60
|
log('--dirs, -d \t\t Output both builds to directories inside of outDir. [esm, cjs].');
|
|
61
|
+
log('--exports, -e \t Generate package.json exports. Values: wildcard | dir | name.');
|
|
57
62
|
log('--help, -h \t\t Print this message.');
|
|
58
63
|
}
|
|
59
64
|
else {
|
|
60
|
-
const { project, 'target-extension': targetExt, 'pkg-dir': pkgDir, modules, dirs, } = parsed;
|
|
65
|
+
const { project, 'target-extension': targetExt, 'pkg-dir': pkgDir, modules, dirs, exports: exportsOpt, } = parsed;
|
|
61
66
|
let configPath = resolve(project);
|
|
62
67
|
let stats = null;
|
|
63
68
|
let pkg = null;
|
|
@@ -93,10 +98,15 @@ const init = async (args) => {
|
|
|
93
98
|
if (!tsconfig.compilerOptions?.outDir) {
|
|
94
99
|
log('No outDir defined in tsconfig.json. Build output will be in "dist".');
|
|
95
100
|
}
|
|
101
|
+
if (exportsOpt && !['wildcard', 'dir', 'name'].includes(exportsOpt)) {
|
|
102
|
+
logError('--exports expects one of: wildcard | dir | name');
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
96
105
|
return {
|
|
97
106
|
pkg,
|
|
98
107
|
dirs,
|
|
99
108
|
modules,
|
|
109
|
+
exports: exportsOpt,
|
|
100
110
|
tsconfig,
|
|
101
111
|
projectDir,
|
|
102
112
|
configPath,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knighted/duel",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "TypeScript dual packages.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/esm/duel.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"./package.json": "./package.json"
|
|
17
17
|
},
|
|
18
18
|
"engines": {
|
|
19
|
-
"node": ">=
|
|
19
|
+
"node": ">=22.21.1 <23 || >=24 <25"
|
|
20
20
|
},
|
|
21
21
|
"engineStrict": true,
|
|
22
22
|
"scripts": {
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@eslint/js": "^9.39.1",
|
|
70
|
+
"@knighted/module": "^1.0.0-rc.2",
|
|
70
71
|
"@tsconfig/recommended": "^1.0.10",
|
|
71
72
|
"@types/node": "^24.10.1",
|
|
72
73
|
"c8": "^10.1.3",
|
|
@@ -82,8 +83,6 @@
|
|
|
82
83
|
"vite": "^7.2.4"
|
|
83
84
|
},
|
|
84
85
|
"dependencies": {
|
|
85
|
-
"@knighted/module": "^1.0.0-alpha.10",
|
|
86
|
-
"@knighted/specifier": "^2.0.9",
|
|
87
86
|
"find-up": "^8.0.0",
|
|
88
87
|
"get-tsconfig": "^4.13.0",
|
|
89
88
|
"glob": "^13.0.0",
|