@knighted/duel 4.0.0-rc.0 → 4.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 +7 -1
- package/dist/cjs/duel.cjs +287 -93
- package/dist/cjs/init.cjs +2 -1
- package/dist/cjs/init.d.cts +23 -14
- package/dist/cjs/util.cjs +84 -3
- package/dist/cjs/util.d.cts +15 -4
- package/dist/esm/duel.js +290 -96
- package/dist/esm/init.js +2 -1
- package/dist/esm/util.d.ts +9 -0
- package/dist/esm/util.js +84 -5
- package/package.json +9 -7
package/dist/esm/duel.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { argv } from 'node:process';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
3
4
|
import { join, dirname, resolve, relative, sep, normalize } from 'node:path';
|
|
4
5
|
import { spawn } from 'node:child_process';
|
|
5
|
-
import { writeFile, rm, mkdir, cp, access } from 'node:fs/promises';
|
|
6
|
-
import {
|
|
6
|
+
import { writeFile, rm, mkdir, cp, access, readdir } from 'node:fs/promises';
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
7
8
|
import { performance } from 'node:perf_hooks';
|
|
8
9
|
import { glob } from 'glob';
|
|
9
10
|
import { findUp } from 'find-up';
|
|
10
11
|
import { transform, collectProjectDualPackageHazards } from '@knighted/module';
|
|
11
12
|
import { getTsconfig, parseTsconfig } from 'get-tsconfig';
|
|
12
13
|
import { init } from './init.js';
|
|
13
|
-
import { getRealPathAsFileUrl, getCompileFiles, log, logError, logWarn, logSuccess as logSuccessBadge, readExportsConfig, processDiagnosticsForFile, exitOnDiagnostics, maybeLinkNodeModules, runExportsValidationBlock, } from './util.js';
|
|
14
|
+
import { getRealPathAsFileUrl, getCompileFiles, log, logError, logWarn, logSuccess as logSuccessBadge, readExportsConfig, processDiagnosticsForFile, exitOnDiagnostics, maybeLinkNodeModules, runExportsValidationBlock, createTempCleanup, registerCleanupHandlers, } from './util.js';
|
|
14
15
|
import { rewriteSpecifiersAndExtensions } from './resolver.js';
|
|
15
16
|
const handleErrorAndExit = message => {
|
|
16
17
|
const parsed = parseInt(message, 10);
|
|
@@ -50,7 +51,7 @@ const duel = async (args) => {
|
|
|
50
51
|
/* continue */
|
|
51
52
|
}
|
|
52
53
|
}, { cwd: projectDir });
|
|
53
|
-
const runBuild = (project, outDir) => {
|
|
54
|
+
const runBuild = (project, outDir, tsBuildInfoFile, cwdForBuild) => {
|
|
54
55
|
return new Promise((fulfill, rejectBuild) => {
|
|
55
56
|
const useBuildMode = hasReferences;
|
|
56
57
|
const tsArgs = useBuildMode
|
|
@@ -58,7 +59,16 @@ const duel = async (args) => {
|
|
|
58
59
|
: outDir
|
|
59
60
|
? [tsc, '-p', project, '--outDir', outDir]
|
|
60
61
|
: [tsc, '-p', project];
|
|
61
|
-
|
|
62
|
+
if (!useBuildMode) {
|
|
63
|
+
tsArgs.push('--incremental');
|
|
64
|
+
if (tsBuildInfoFile) {
|
|
65
|
+
tsArgs.push('--tsBuildInfoFile', tsBuildInfoFile);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const build = spawn(process.execPath, tsArgs, {
|
|
69
|
+
stdio: 'inherit',
|
|
70
|
+
cwd: cwdForBuild ?? process.cwd(),
|
|
71
|
+
});
|
|
62
72
|
build.on('exit', code => {
|
|
63
73
|
if (code > 0) {
|
|
64
74
|
return rejectBuild(new Error(code));
|
|
@@ -74,27 +84,94 @@ const duel = async (args) => {
|
|
|
74
84
|
const absoluteOutDir = resolve(projectDir, outDir);
|
|
75
85
|
const originalType = pkg.packageJson.type ?? 'commonjs';
|
|
76
86
|
const isCjsBuild = originalType !== 'commonjs';
|
|
87
|
+
const absoluteDualOutDir = join(projectDir, isCjsBuild ? join(outDir, 'cjs') : join(outDir, 'esm'));
|
|
88
|
+
const projectRoot = dirname(projectDir);
|
|
77
89
|
const primaryOutDir = dirs
|
|
78
90
|
? isCjsBuild
|
|
79
91
|
? join(absoluteOutDir, 'esm')
|
|
80
92
|
: join(absoluteOutDir, 'cjs')
|
|
81
93
|
: absoluteOutDir;
|
|
82
|
-
const
|
|
94
|
+
const { type, exports, imports, main, module, types, typings, typesVersions, sideEffects, } = pkg.packageJson ?? {};
|
|
95
|
+
const pkgHashInputs = {
|
|
96
|
+
type,
|
|
97
|
+
exports,
|
|
98
|
+
imports,
|
|
99
|
+
main,
|
|
100
|
+
module,
|
|
101
|
+
types,
|
|
102
|
+
typings,
|
|
103
|
+
typesVersions,
|
|
104
|
+
sideEffects,
|
|
105
|
+
};
|
|
106
|
+
const hash = createHash('sha1')
|
|
107
|
+
.update(JSON.stringify({
|
|
108
|
+
configPath,
|
|
109
|
+
tsconfig,
|
|
110
|
+
packageJson: pkgHashInputs,
|
|
111
|
+
dualTarget: isCjsBuild ? 'cjs' : 'esm',
|
|
112
|
+
}))
|
|
113
|
+
.digest('hex')
|
|
114
|
+
.slice(0, 8);
|
|
115
|
+
const cacheDir = join(projectDir, '.duel-cache');
|
|
116
|
+
const primaryTsBuildInfoFile = join(cacheDir, `primary.${hash}.tsbuildinfo`);
|
|
117
|
+
const dualTsBuildInfoFile = join(cacheDir, `dual.${hash}.tsbuildinfo`);
|
|
118
|
+
const subDir = join(cacheDir, `_duel_${hash}_`);
|
|
119
|
+
const shadowDualOutDir = join(subDir, relative(projectRoot, absoluteDualOutDir));
|
|
83
120
|
const hazardMode = detectDualPackageHazard ?? 'warn';
|
|
84
121
|
const hazardScope = dualPackageHazardScope ?? 'file';
|
|
85
|
-
|
|
122
|
+
function mapReferencesToShadow(references = [], options) {
|
|
123
|
+
const { resolveRefPath, toShadowPathFn, fromDir } = options;
|
|
124
|
+
return references.map(ref => {
|
|
125
|
+
if (!ref?.path)
|
|
126
|
+
return ref;
|
|
127
|
+
const refAbs = resolveRefPath(ref.path);
|
|
128
|
+
const shadowRef = toShadowPathFn(refAbs);
|
|
129
|
+
return {
|
|
130
|
+
...ref,
|
|
131
|
+
path: relative(fromDir, shadowRef),
|
|
132
|
+
};
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const getOverrideTsConfig = dualConfigDir => {
|
|
136
|
+
const shadowReferences = mapReferencesToShadow(tsconfig.references ?? [], {
|
|
137
|
+
resolveRefPath: refPath => resolve(projectDir, refPath),
|
|
138
|
+
toShadowPathFn: abs => join(subDir, relative(projectRoot, abs)),
|
|
139
|
+
fromDir: dualConfigDir,
|
|
140
|
+
});
|
|
86
141
|
return {
|
|
87
142
|
...tsconfig,
|
|
143
|
+
references: shadowReferences,
|
|
88
144
|
compilerOptions: {
|
|
89
|
-
...tsconfig.compilerOptions,
|
|
145
|
+
...(tsconfig.compilerOptions ?? {}),
|
|
90
146
|
module: 'NodeNext',
|
|
91
147
|
moduleResolution: 'NodeNext',
|
|
148
|
+
target: tsconfig.compilerOptions?.target ?? 'ES2022',
|
|
149
|
+
// Emit dual build into the shadow workspace, then copy to real outDir
|
|
150
|
+
outDir: shadowDualOutDir,
|
|
151
|
+
incremental: true,
|
|
152
|
+
tsBuildInfoFile: dualTsBuildInfoFile,
|
|
92
153
|
},
|
|
93
154
|
};
|
|
94
155
|
};
|
|
95
156
|
const hasReferences = Array.isArray(tsconfig.references) && tsconfig.references.length > 0;
|
|
96
157
|
const runPrimaryBuild = () => {
|
|
97
|
-
return runBuild(configPath, hasReferences ? undefined : primaryOutDir);
|
|
158
|
+
return runBuild(configPath, hasReferences ? undefined : primaryOutDir, hasReferences ? undefined : primaryTsBuildInfoFile, projectDir);
|
|
159
|
+
};
|
|
160
|
+
const refreshDualBuildInfo = async () => {
|
|
161
|
+
try {
|
|
162
|
+
await access(shadowDualOutDir);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
await rm(dualTsBuildInfoFile, { force: true });
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
const refreshPrimaryBuildInfo = async () => {
|
|
169
|
+
try {
|
|
170
|
+
await access(primaryOutDir);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
await rm(primaryTsBuildInfoFile, { force: true });
|
|
174
|
+
}
|
|
98
175
|
};
|
|
99
176
|
const resolveReferenceConfigPath = (baseDir, refPath) => {
|
|
100
177
|
const abs = resolve(baseDir, refPath);
|
|
@@ -230,6 +307,7 @@ const duel = async (args) => {
|
|
|
230
307
|
let success = false;
|
|
231
308
|
const startTime = performance.now();
|
|
232
309
|
try {
|
|
310
|
+
await refreshPrimaryBuildInfo();
|
|
233
311
|
await runPrimaryBuild();
|
|
234
312
|
success = true;
|
|
235
313
|
}
|
|
@@ -237,15 +315,19 @@ const duel = async (args) => {
|
|
|
237
315
|
handleErrorAndExit(message);
|
|
238
316
|
}
|
|
239
317
|
if (success) {
|
|
240
|
-
const projectRoot = dirname(projectDir);
|
|
241
318
|
const parentRoot = dirname(projectRoot);
|
|
242
|
-
const subDir = join(projectRoot, `_${hex}_`);
|
|
243
|
-
const absoluteDualOutDir = join(projectDir, isCjsBuild ? join(outDir, 'cjs') : join(outDir, 'esm'));
|
|
244
|
-
const tsconfigDual = getOverrideTsConfig();
|
|
245
319
|
const tsconfigRel = relative(projectRoot, configPath);
|
|
246
|
-
const tsconfigDualRel = tsconfigRel.replace(/tsconfig\.json$/i, `tsconfig.${
|
|
320
|
+
const tsconfigDualRel = tsconfigRel.replace(/tsconfig\.json$/i, `tsconfig.${hash}.json`);
|
|
247
321
|
const dualConfigPath = join(subDir, tsconfigDualRel);
|
|
248
322
|
const dualConfigDir = dirname(dualConfigPath);
|
|
323
|
+
const tsconfigDual = getOverrideTsConfig(dualConfigDir);
|
|
324
|
+
const keepTemp = process.env.DUEL_KEEP_TEMP === '1';
|
|
325
|
+
const { cleanupTemp, cleanupTempSync } = createTempCleanup({
|
|
326
|
+
subDir,
|
|
327
|
+
keepTemp,
|
|
328
|
+
logWarnFn: logWarn,
|
|
329
|
+
});
|
|
330
|
+
const unregisterCleanupHandlers = registerCleanupHandlers(cleanupTempSync);
|
|
249
331
|
let errorMsg = '';
|
|
250
332
|
let exportsConfigData = null;
|
|
251
333
|
if (exportsConfig) {
|
|
@@ -282,11 +364,16 @@ const duel = async (args) => {
|
|
|
282
364
|
process.exit(1);
|
|
283
365
|
}
|
|
284
366
|
}
|
|
285
|
-
await
|
|
286
|
-
|
|
367
|
+
await Promise.all([
|
|
368
|
+
mkdir(subDir, { recursive: true }),
|
|
369
|
+
mkdir(cacheDir, { recursive: true }),
|
|
370
|
+
]);
|
|
371
|
+
const linkNodeModulesPromise = maybeLinkNodeModules(projectDir, subDir);
|
|
287
372
|
const projectRel = relative(projectRoot, projectDir);
|
|
288
373
|
const projectCopyDest = join(subDir, projectRel);
|
|
289
374
|
const makeCopyFilter = (rootDir, allowDist) => src => {
|
|
375
|
+
if (src.split(/[/\\]/).includes('.duel-cache'))
|
|
376
|
+
return false;
|
|
290
377
|
if (src.split(/[/\\]/).includes('node_modules'))
|
|
291
378
|
return false;
|
|
292
379
|
if (allowDist)
|
|
@@ -297,92 +384,170 @@ const duel = async (args) => {
|
|
|
297
384
|
const [segment] = rel.split(sep);
|
|
298
385
|
return segment !== outDir;
|
|
299
386
|
};
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
recursive: true
|
|
303
|
-
filter
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (!
|
|
387
|
+
const copyFilesToTemp = async () => {
|
|
388
|
+
const copyDirContents = async (sourceDir, destDir, allowDist) => {
|
|
389
|
+
await mkdir(destDir, { recursive: true });
|
|
390
|
+
const filter = makeCopyFilter(sourceDir, allowDist);
|
|
391
|
+
const entries = await readdir(sourceDir, { withFileTypes: true });
|
|
392
|
+
for (const entry of entries) {
|
|
393
|
+
const srcPath = join(sourceDir, entry.name);
|
|
394
|
+
if (!filter(srcPath))
|
|
308
395
|
continue;
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
const refDest = join(subDir, refRel);
|
|
312
|
-
await cp(refAbs, refDest, {
|
|
396
|
+
const dstPath = join(destDir, entry.name);
|
|
397
|
+
await cp(srcPath, dstPath, {
|
|
313
398
|
recursive: true,
|
|
314
|
-
filter
|
|
399
|
+
filter,
|
|
315
400
|
});
|
|
316
401
|
}
|
|
402
|
+
};
|
|
403
|
+
if (copyMode === 'full') {
|
|
404
|
+
const allowDist = hasReferences;
|
|
405
|
+
await copyDirContents(projectDir, projectCopyDest, allowDist);
|
|
406
|
+
if (hasReferences) {
|
|
407
|
+
for (const ref of tsconfig.references ?? []) {
|
|
408
|
+
if (!ref.path)
|
|
409
|
+
continue;
|
|
410
|
+
const refAbs = resolve(projectDir, ref.path);
|
|
411
|
+
const refRel = relative(projectRoot, refAbs);
|
|
412
|
+
const refDest = join(subDir, refRel);
|
|
413
|
+
await copyDirContents(refAbs, refDest, allowDist);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
317
416
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
417
|
+
else {
|
|
418
|
+
const filesToCopy = new Set([...compileFiles, ...configFiles, ...packageJsons]);
|
|
419
|
+
for (const file of filesToCopy) {
|
|
420
|
+
let rel = relative(projectRoot, file);
|
|
421
|
+
rel = normalize(rel);
|
|
422
|
+
if (rel.startsWith('..')) {
|
|
423
|
+
const altRel = hasReferences ? normalize(relative(parentRoot, file)) : rel;
|
|
424
|
+
if (!altRel.startsWith('..')) {
|
|
425
|
+
rel = altRel;
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
logWarn(`Skipping copy for ${file} outside of project root ${projectRoot}`);
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
332
431
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
432
|
+
const dest = join(subDir, rel);
|
|
433
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
434
|
+
await cp(file, dest);
|
|
435
|
+
}
|
|
436
|
+
const missingConfigs = [];
|
|
437
|
+
for (const configFile of configFiles) {
|
|
438
|
+
const dest = join(subDir, relative(projectRoot, configFile));
|
|
439
|
+
try {
|
|
440
|
+
await access(dest);
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
missingConfigs.push({ src: configFile, dest });
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (missingConfigs.length) {
|
|
447
|
+
logWarn(`Copying ${missingConfigs.length} missing referenced config(s) into temp workspace: ${missingConfigs
|
|
448
|
+
.map(entry => entry.src)
|
|
449
|
+
.join(', ')}`);
|
|
450
|
+
for (const { src, dest } of missingConfigs) {
|
|
451
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
452
|
+
await cp(src, dest);
|
|
336
453
|
}
|
|
337
454
|
}
|
|
338
|
-
const dest = join(subDir, rel);
|
|
339
|
-
await mkdir(dirname(dest), { recursive: true });
|
|
340
|
-
await cp(file, dest);
|
|
341
455
|
}
|
|
342
|
-
|
|
456
|
+
};
|
|
457
|
+
const toShadowPath = absPath => join(subDir, relative(projectRoot, absPath));
|
|
458
|
+
// Patch referenced tsconfig files in the shadow workspace to emit dual outputs
|
|
459
|
+
const patchReferencedConfigs = async () => {
|
|
343
460
|
for (const configFile of configFiles) {
|
|
461
|
+
if (configFile === configPath)
|
|
462
|
+
continue;
|
|
344
463
|
const dest = join(subDir, relative(projectRoot, configFile));
|
|
464
|
+
let parsed = null;
|
|
345
465
|
try {
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
catch {
|
|
349
|
-
missingConfigs.push({ src: configFile, dest });
|
|
466
|
+
parsed = parseTsconfig(dest);
|
|
350
467
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
.map(entry => entry.src)
|
|
355
|
-
.join(', ')}`);
|
|
356
|
-
for (const { src, dest } of missingConfigs) {
|
|
357
|
-
await mkdir(dirname(dest), { recursive: true });
|
|
358
|
-
await cp(src, dest);
|
|
468
|
+
catch (err) {
|
|
469
|
+
logWarn(`Skipping referenced tsconfig at ${dest} (parse failed): ${err?.message ?? err}`);
|
|
470
|
+
continue;
|
|
359
471
|
}
|
|
472
|
+
const cfg = parsed?.tsconfig ?? parsed;
|
|
473
|
+
if (!cfg || typeof cfg !== 'object')
|
|
474
|
+
continue;
|
|
475
|
+
const cfgDir = dirname(configFile);
|
|
476
|
+
const baseOut = cfg.compilerOptions?.outDir
|
|
477
|
+
? resolve(cfgDir, cfg.compilerOptions.outDir)
|
|
478
|
+
: resolve(cfgDir, 'dist');
|
|
479
|
+
const dualOutReal = join(baseOut, isCjsBuild ? 'cjs' : 'esm');
|
|
480
|
+
const dualOut = toShadowPath(dualOutReal);
|
|
481
|
+
const tsbuildReal = cfg.compilerOptions?.tsBuildInfoFile
|
|
482
|
+
? resolve(cfgDir, cfg.compilerOptions.tsBuildInfoFile)
|
|
483
|
+
: join(baseOut, 'tsconfig.tsbuildinfo');
|
|
484
|
+
const dualTsbuild = toShadowPath(join(dirname(tsbuildReal), 'tsconfig.dual.tsbuildinfo'));
|
|
485
|
+
const shadowReferences = mapReferencesToShadow(cfg.references ?? [], {
|
|
486
|
+
resolveRefPath: refPath => resolveReferenceConfigPath(cfgDir, refPath),
|
|
487
|
+
toShadowPathFn: toShadowPath,
|
|
488
|
+
fromDir: dirname(dest),
|
|
489
|
+
});
|
|
490
|
+
const patched = {
|
|
491
|
+
...cfg,
|
|
492
|
+
references: shadowReferences,
|
|
493
|
+
compilerOptions: {
|
|
494
|
+
...(cfg.compilerOptions ?? {}),
|
|
495
|
+
module: 'NodeNext',
|
|
496
|
+
moduleResolution: 'NodeNext',
|
|
497
|
+
outDir: dualOut,
|
|
498
|
+
incremental: cfg.compilerOptions?.incremental ?? true,
|
|
499
|
+
tsBuildInfoFile: dualTsbuild,
|
|
500
|
+
},
|
|
501
|
+
};
|
|
502
|
+
await writeFile(dest, JSON.stringify(patched, null, 2));
|
|
360
503
|
}
|
|
361
|
-
}
|
|
504
|
+
};
|
|
362
505
|
/**
|
|
363
506
|
* Write dual package.json and tsconfig into temp dir; avoid mutating root package.json.
|
|
364
507
|
*/
|
|
365
|
-
await
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
508
|
+
await copyFilesToTemp();
|
|
509
|
+
await patchReferencedConfigs();
|
|
510
|
+
const writeDualPackage = async () => {
|
|
511
|
+
const pkgDest = join(subDir, relative(projectRoot, pkg.path));
|
|
512
|
+
await mkdir(dirname(pkgDest), { recursive: true });
|
|
513
|
+
await writeFile(pkgDest, JSON.stringify({
|
|
514
|
+
name: pkg.packageJson?.name,
|
|
515
|
+
version: pkg.packageJson?.version,
|
|
516
|
+
type: isCjsBuild ? 'commonjs' : 'module',
|
|
517
|
+
exports: pkg.packageJson?.exports,
|
|
518
|
+
imports: pkg.packageJson?.imports,
|
|
519
|
+
main: pkg.packageJson?.main,
|
|
520
|
+
module: pkg.packageJson?.module,
|
|
521
|
+
types: pkg.packageJson?.types ?? pkg.packageJson?.typings,
|
|
522
|
+
typesVersions: pkg.packageJson?.typesVersions,
|
|
523
|
+
sideEffects: pkg.packageJson?.sideEffects,
|
|
524
|
+
}, null, 2));
|
|
525
|
+
};
|
|
526
|
+
const writeDualConfig = async () => {
|
|
527
|
+
await mkdir(dualConfigDir, { recursive: true });
|
|
528
|
+
await writeFile(dualConfigPath, JSON.stringify({
|
|
529
|
+
...tsconfigDual,
|
|
530
|
+
compilerOptions: {
|
|
531
|
+
...tsconfigDual.compilerOptions,
|
|
532
|
+
outDir: shadowDualOutDir,
|
|
533
|
+
incremental: true,
|
|
534
|
+
tsBuildInfoFile: dualTsBuildInfoFile,
|
|
535
|
+
},
|
|
536
|
+
}, null, 2));
|
|
537
|
+
};
|
|
538
|
+
await Promise.all([linkNodeModulesPromise, writeDualPackage(), writeDualConfig()]);
|
|
376
539
|
if (modules) {
|
|
377
540
|
/**
|
|
378
541
|
* Transform ambiguous modules for the target dual build.
|
|
379
542
|
* @see https://github.com/microsoft/TypeScript/issues/58658
|
|
380
543
|
*/
|
|
381
544
|
const toTransform = await glob(`${subDir.replace(/\\/g, '/')}/**/*{.js,.jsx,.ts,.tsx}`, {
|
|
382
|
-
ignore: 'node_modules
|
|
545
|
+
ignore: `${subDir.replace(/\\/g, '/')}/**/node_modules/**`,
|
|
383
546
|
});
|
|
384
547
|
let transformDiagnosticsError = false;
|
|
385
548
|
for (const file of toTransform) {
|
|
549
|
+
if (file.split(/[/\\]/).includes('node_modules'))
|
|
550
|
+
continue;
|
|
386
551
|
const isTsLike = /\.[cm]?tsx?$/.test(file);
|
|
387
552
|
const transformSyntaxMode = syntaxMode === true && isTsLike ? 'globals-only' : syntaxMode;
|
|
388
553
|
const diagnostics = [];
|
|
@@ -404,22 +569,16 @@ const duel = async (args) => {
|
|
|
404
569
|
// Build dual
|
|
405
570
|
log('Starting dual build...');
|
|
406
571
|
try {
|
|
407
|
-
await
|
|
572
|
+
await refreshDualBuildInfo();
|
|
573
|
+
await runBuild(dualConfigPath, hasReferences ? undefined : shadowDualOutDir, hasReferences ? undefined : dualTsBuildInfoFile, subDir);
|
|
408
574
|
}
|
|
409
575
|
catch ({ message }) {
|
|
410
576
|
success = false;
|
|
411
577
|
errorMsg = message;
|
|
412
578
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
if (!keepTemp) {
|
|
417
|
-
await rm(dualConfigPath, { force: true });
|
|
418
|
-
await rm(subDir, { force: true, recursive: true });
|
|
419
|
-
}
|
|
420
|
-
else {
|
|
421
|
-
logWarn(`DUEL_KEEP_TEMP=1 set; temp workspace preserved at ${subDir}`);
|
|
422
|
-
}
|
|
579
|
+
if (!success) {
|
|
580
|
+
await cleanupTemp();
|
|
581
|
+
unregisterCleanupHandlers();
|
|
423
582
|
if (errorMsg) {
|
|
424
583
|
handleErrorAndExit(errorMsg);
|
|
425
584
|
}
|
|
@@ -427,24 +586,41 @@ const duel = async (args) => {
|
|
|
427
586
|
if (success) {
|
|
428
587
|
const dualTarget = isCjsBuild ? 'commonjs' : 'module';
|
|
429
588
|
const dualTargetExt = isCjsBuild ? '.cjs' : dirs ? '.js' : '.mjs';
|
|
430
|
-
|
|
431
|
-
|
|
589
|
+
await rm(absoluteDualOutDir, { force: true, recursive: true });
|
|
590
|
+
await mkdir(dirname(absoluteDualOutDir), { recursive: true });
|
|
591
|
+
// Only copy if the shadow dual outDir was produced; absent indicates a failed emit
|
|
592
|
+
try {
|
|
593
|
+
await cp(shadowDualOutDir, absoluteDualOutDir, { recursive: true });
|
|
594
|
+
}
|
|
595
|
+
catch (err) {
|
|
596
|
+
if (err?.code === 'ENOENT') {
|
|
597
|
+
throw new Error(`Dual build output not found at ${shadowDualOutDir}`);
|
|
598
|
+
}
|
|
599
|
+
throw err;
|
|
600
|
+
}
|
|
601
|
+
const dualGlob = dualTarget === 'commonjs' ? '**/*{.js,.cjs,.d.ts}' : '**/*{.js,.mjs,.d.ts}';
|
|
602
|
+
const filenames = await glob(`${absoluteDualOutDir.replace(/\\/g, '/')}/${dualGlob}`, {
|
|
603
|
+
ignore: `${absoluteDualOutDir.replace(/\\/g, '/')}/**/node_modules/**`,
|
|
432
604
|
});
|
|
605
|
+
const rewriteSyntaxMode = dualTarget === 'commonjs' ? true : syntaxMode;
|
|
433
606
|
await rewriteSpecifiersAndExtensions(filenames, {
|
|
434
607
|
target: dualTarget,
|
|
435
608
|
ext: dualTargetExt,
|
|
436
|
-
syntaxMode,
|
|
609
|
+
syntaxMode: rewriteSyntaxMode,
|
|
437
610
|
rewritePolicy,
|
|
438
611
|
validateSpecifiers,
|
|
439
612
|
onWarn: message => logWarn(message),
|
|
440
613
|
onRewrite: (from, to) => logVerbose(`Rewrote specifiers in ${from} -> ${to}`),
|
|
441
614
|
});
|
|
442
615
|
if (dirs && originalType === 'commonjs') {
|
|
443
|
-
const primaryFiles = await glob(`${primaryOutDir.replace(/\\/g, '/')}/**/*{.js,.d.ts}`, {
|
|
616
|
+
const primaryFiles = await glob(`${primaryOutDir.replace(/\\/g, '/')}/**/*{.js,.cjs,.d.ts}`, {
|
|
617
|
+
ignore: `${primaryOutDir.replace(/\\/g, '/')}/**/node_modules/**`,
|
|
618
|
+
});
|
|
444
619
|
await rewriteSpecifiersAndExtensions(primaryFiles, {
|
|
445
620
|
target: 'commonjs',
|
|
446
621
|
ext: '.cjs',
|
|
447
|
-
|
|
622
|
+
// Always lower syntax for primary CJS output when dirs mode rewrites primary build.
|
|
623
|
+
syntaxMode: true,
|
|
448
624
|
rewritePolicy,
|
|
449
625
|
validateSpecifiers,
|
|
450
626
|
onWarn: message => logWarn(message),
|
|
@@ -464,15 +640,33 @@ const duel = async (args) => {
|
|
|
464
640
|
mainDefaultKind,
|
|
465
641
|
mainPath,
|
|
466
642
|
});
|
|
643
|
+
await cleanupTemp();
|
|
644
|
+
unregisterCleanupHandlers();
|
|
467
645
|
logSuccess(startTime);
|
|
468
646
|
}
|
|
469
647
|
}
|
|
470
648
|
}
|
|
471
649
|
};
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
650
|
+
const getCurrentHref = () => {
|
|
651
|
+
if (typeof import.meta !== 'undefined' && import.meta.url)
|
|
652
|
+
return import.meta.url;
|
|
653
|
+
if (typeof module !== 'undefined' && module?.filename) {
|
|
654
|
+
return pathToFileURL(module.filename).href;
|
|
655
|
+
}
|
|
656
|
+
return null;
|
|
657
|
+
};
|
|
658
|
+
const runIfEntry = async () => {
|
|
659
|
+
try {
|
|
660
|
+
const realFileUrlArgv1 = await getRealPathAsFileUrl(argv[1] ?? '');
|
|
661
|
+
const currentHref = getCurrentHref();
|
|
662
|
+
if (currentHref && currentHref === realFileUrlArgv1) {
|
|
663
|
+
await duel();
|
|
664
|
+
}
|
|
476
665
|
}
|
|
477
|
-
|
|
666
|
+
catch (err) {
|
|
667
|
+
logError(err?.message ?? err);
|
|
668
|
+
process.exit(1);
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
runIfEntry();
|
|
478
672
|
export { duel };
|
package/dist/esm/init.js
CHANGED
|
@@ -186,7 +186,8 @@ const init = async (args) => {
|
|
|
186
186
|
logError(`Provided --project '${project}' resolves to ${configPath} which is not a file or directory.`);
|
|
187
187
|
return false;
|
|
188
188
|
}
|
|
189
|
-
|
|
189
|
+
const pkgSearchCwd = pkgDir ?? (stats.isDirectory() ? configPath : dirname(configPath));
|
|
190
|
+
pkg = await readPackageUp({ cwd: pkgSearchCwd });
|
|
190
191
|
if (!pkg) {
|
|
191
192
|
logError('No package.json file found.');
|
|
192
193
|
return false;
|
package/dist/esm/util.d.ts
CHANGED
|
@@ -18,6 +18,15 @@ export function generateExports(options: any): Promise<{
|
|
|
18
18
|
}>;
|
|
19
19
|
export function stripKnownExt(path: any): any;
|
|
20
20
|
export function ensureDotSlash(path: any): any;
|
|
21
|
+
export function createTempCleanup({ subDir, keepTemp, logWarnFn }: {
|
|
22
|
+
subDir: any;
|
|
23
|
+
keepTemp?: boolean | undefined;
|
|
24
|
+
logWarnFn?: ((msg: any) => void) | undefined;
|
|
25
|
+
}): {
|
|
26
|
+
cleanupTempSync: () => void;
|
|
27
|
+
cleanupTemp: () => Promise<any>;
|
|
28
|
+
};
|
|
29
|
+
export function registerCleanupHandlers(cleanupTempSync: any): () => void;
|
|
21
30
|
export function processDiagnosticsForFile(diagnostics: any, projectDir: any, logDiagnosticsFn: any): any;
|
|
22
31
|
export function exitOnDiagnostics(hasError: any, exitFn?: (code?: number | string | null) => never): void;
|
|
23
32
|
export function maybeLinkNodeModules(projectRoot: any, subDir: any, symlinkFn?: typeof symlink, findUpFn?: typeof findUp): Promise<void>;
|