@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/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 { randomBytes } from 'node:crypto';
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
- const build = spawn(process.execPath, tsArgs, { stdio: 'inherit' });
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 hex = randomBytes(4).toString('hex');
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
- const getOverrideTsConfig = () => {
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.${hex}.json`);
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 mkdir(subDir, { recursive: true });
286
- await maybeLinkNodeModules(projectRoot, subDir);
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 copyProjectTree = async (allowDist) => {
301
- await cp(projectDir, projectCopyDest, {
302
- recursive: true,
303
- filter: makeCopyFilter(projectDir, allowDist),
304
- });
305
- if (hasReferences) {
306
- for (const ref of tsconfig.references ?? []) {
307
- if (!ref.path)
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 refAbs = resolve(projectDir, ref.path);
310
- const refRel = relative(projectRoot, refAbs);
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: makeCopyFilter(refAbs, allowDist),
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
- if (copyMode === 'full') {
320
- const allowDist = hasReferences;
321
- await copyProjectTree(allowDist);
322
- }
323
- else {
324
- const filesToCopy = new Set([...compileFiles, ...configFiles, ...packageJsons]);
325
- for (const file of filesToCopy) {
326
- let rel = relative(projectRoot, file);
327
- rel = normalize(rel);
328
- if (rel.startsWith('..')) {
329
- const altRel = hasReferences ? normalize(relative(parentRoot, file)) : rel;
330
- if (!altRel.startsWith('..')) {
331
- rel = altRel;
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
- else {
334
- logWarn(`Skipping copy for ${file} outside of project root ${projectRoot}`);
335
- continue;
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
- const missingConfigs = [];
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
- await access(dest);
347
- }
348
- catch {
349
- missingConfigs.push({ src: configFile, dest });
466
+ parsed = parseTsconfig(dest);
350
467
  }
351
- }
352
- if (missingConfigs.length) {
353
- logWarn(`Copying ${missingConfigs.length} missing referenced config(s) into temp workspace: ${missingConfigs
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 writeFile(join(subDir, relative(projectRoot, pkg.path)), JSON.stringify({
366
- type: isCjsBuild ? 'commonjs' : 'module',
367
- }));
368
- await mkdir(dualConfigDir, { recursive: true });
369
- await writeFile(dualConfigPath, JSON.stringify({
370
- ...tsconfigDual,
371
- compilerOptions: {
372
- ...tsconfigDual.compilerOptions,
373
- outDir: absoluteDualOutDir,
374
- },
375
- }, null, 2));
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 runBuild(dualConfigPath, hasReferences ? undefined : absoluteDualOutDir);
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
- finally {
414
- const keepTemp = process.env.DUEL_KEEP_TEMP === '1';
415
- // Cleanup temp dir unless debugging is requested
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
- const filenames = await glob(`${absoluteDualOutDir.replace(/\\/g, '/')}/**/*{.js,.d.ts}`, {
431
- ignore: 'node_modules/**',
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}`, { ignore: 'node_modules/**' });
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
- syntaxMode,
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
- (async () => {
473
- const realFileUrlArgv1 = await getRealPathAsFileUrl(argv[1] ?? '');
474
- if (import.meta.url === realFileUrlArgv1) {
475
- await duel();
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
- pkg = await readPackageUp({ cwd: pkgDir ?? configPath });
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;
@@ -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>;