@bobfrankston/npmglobalize 1.0.203 → 1.0.204
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/lib.js +106 -0
- package/package.json +1 -1
package/lib.js
CHANGED
|
@@ -2429,6 +2429,102 @@ function migrateTsconfigDeprecations(cwd, snapshot) {
|
|
|
2429
2429
|
}
|
|
2430
2430
|
return changed;
|
|
2431
2431
|
}
|
|
2432
|
+
/** Ordering of ES language levels as they appear in `target`/`lib`. Anything
|
|
2433
|
+
* unknown ranks 0 so it never looks newer than what the build asked for. */
|
|
2434
|
+
const ES_LEVEL_RANK = {
|
|
2435
|
+
es3: 3, es5: 5, es6: 6, es2015: 6, es7: 7, es2016: 7, es2017: 8, es2018: 9,
|
|
2436
|
+
es2019: 10, es2020: 11, es2021: 12, es2022: 13, es2023: 14, es2024: 15,
|
|
2437
|
+
es2025: 16, esnext: 99,
|
|
2438
|
+
};
|
|
2439
|
+
const esLevelRank = (value) => ES_LEVEL_RANK[value.toLowerCase().replace(/\.full$/, '')] ?? 0;
|
|
2440
|
+
/** tsc says "Try changing the 'lib' compiler option to 'es2023' or later"
|
|
2441
|
+
* (TS2550/TS2583/TS2584) when the code uses a standard-library API newer than
|
|
2442
|
+
* the configured level. That isn't a deprecation — it's a tsconfig the language
|
|
2443
|
+
* moved past — so raise the level to whatever the build asked for and let it
|
|
2444
|
+
* try again, rather than leaving the user to decode `replaceAll` and `toSorted`
|
|
2445
|
+
* errors by hand.
|
|
2446
|
+
*
|
|
2447
|
+
* We raise `target`: its implied `lib` keeps the DOM declarations a bare
|
|
2448
|
+
* `lib: ["es2023"]` would silently drop. An explicit `lib` overrides `target`
|
|
2449
|
+
* though, so when one is set we raise its `es*` entry in place and keep the
|
|
2450
|
+
* rest (dom, webworker, …). Reads the required level from `buildOutput`;
|
|
2451
|
+
* returns true if it changed any file. */
|
|
2452
|
+
function raiseTsconfigLibLevel(cwd, buildOutput, snapshot) {
|
|
2453
|
+
let required = '';
|
|
2454
|
+
for (const m of buildOutput.matchAll(/'lib' compiler option to '([A-Za-z0-9.]+)'/g)) {
|
|
2455
|
+
if (esLevelRank(m[1]) > esLevelRank(required))
|
|
2456
|
+
required = m[1].toLowerCase();
|
|
2457
|
+
}
|
|
2458
|
+
if (!required)
|
|
2459
|
+
return false;
|
|
2460
|
+
const tsconfigPath = path.join(cwd, 'tsconfig.json');
|
|
2461
|
+
if (!fs.existsSync(tsconfigPath))
|
|
2462
|
+
return false;
|
|
2463
|
+
const { chain } = resolveTsconfigChain(tsconfigPath);
|
|
2464
|
+
if (!chain.length)
|
|
2465
|
+
return false;
|
|
2466
|
+
const mostLeafWritable = chain.find(c => c.writable);
|
|
2467
|
+
if (!mostLeafWritable)
|
|
2468
|
+
return false;
|
|
2469
|
+
const name = (() => { try {
|
|
2470
|
+
return readPackageJson(cwd).name;
|
|
2471
|
+
}
|
|
2472
|
+
catch {
|
|
2473
|
+
return null;
|
|
2474
|
+
} })() || path.basename(cwd);
|
|
2475
|
+
const edit = (filePath, fn) => {
|
|
2476
|
+
let text;
|
|
2477
|
+
try {
|
|
2478
|
+
text = fs.readFileSync(filePath, 'utf-8');
|
|
2479
|
+
}
|
|
2480
|
+
catch {
|
|
2481
|
+
return false;
|
|
2482
|
+
}
|
|
2483
|
+
const patched = fn(text);
|
|
2484
|
+
if (patched == null || patched === text)
|
|
2485
|
+
return false;
|
|
2486
|
+
try {
|
|
2487
|
+
fs.writeFileSync(filePath, patched);
|
|
2488
|
+
if (snapshot && !snapshot.has(filePath))
|
|
2489
|
+
snapshot.set(filePath, text);
|
|
2490
|
+
return true;
|
|
2491
|
+
}
|
|
2492
|
+
catch (error) {
|
|
2493
|
+
console.error(colors.yellow(` Could not write tsconfig patch (${filePath}): ${error.message}`));
|
|
2494
|
+
return false;
|
|
2495
|
+
}
|
|
2496
|
+
};
|
|
2497
|
+
/** The file to edit for an option: where it's defined if that's writable,
|
|
2498
|
+
* else the most-leaf writable config, where an override wins. */
|
|
2499
|
+
const targetFor = (definedIn) => {
|
|
2500
|
+
const def = definedIn ? chain.find(c => c.path === definedIn) : null;
|
|
2501
|
+
return def?.writable ? def : mostLeafWritable;
|
|
2502
|
+
};
|
|
2503
|
+
// An explicit `lib` beats `target`, so it's the one that has to move.
|
|
2504
|
+
const lib = effectiveCompilerOption(chain, 'lib');
|
|
2505
|
+
if (lib && Array.isArray(lib.value)) {
|
|
2506
|
+
const entries = lib.value.filter((e) => typeof e === 'string');
|
|
2507
|
+
if (entries.some(e => esLevelRank(e) >= esLevelRank(required)))
|
|
2508
|
+
return false;
|
|
2509
|
+
const raised = [required, ...entries.filter(e => esLevelRank(e) === 0)];
|
|
2510
|
+
const tgt = targetFor(lib.definedIn);
|
|
2511
|
+
if (!edit(tgt.path, t => upsertCompilerOption(t, 'lib', JSON.stringify(raised).replace(/","/g, '", "'))))
|
|
2512
|
+
return false;
|
|
2513
|
+
console.log(colors.cyan(` Raised lib [${entries.join(', ')}] → [${raised.join(', ')}] in ${path.relative(cwd, tgt.path) || 'tsconfig.json'} (build needs ${required})`));
|
|
2514
|
+
recordBuildIssue(name, 'warning', `Raised "lib" to ${required} — the code uses standard-library APIs newer than the tsconfig allowed.`);
|
|
2515
|
+
return true;
|
|
2516
|
+
}
|
|
2517
|
+
const tg = effectiveCompilerOption(chain, 'target');
|
|
2518
|
+
const current = tg && typeof tg.value === 'string' ? tg.value : 'es5'; // tsc's own default
|
|
2519
|
+
if (esLevelRank(current) >= esLevelRank(required))
|
|
2520
|
+
return false;
|
|
2521
|
+
const tgt = targetFor(tg?.definedIn);
|
|
2522
|
+
if (!edit(tgt.path, t => upsertCompilerOption(t, 'target', `"${required}"`)))
|
|
2523
|
+
return false;
|
|
2524
|
+
console.log(colors.cyan(` Raised target "${current}" → "${required}" in ${path.relative(cwd, tgt.path) || 'tsconfig.json'} (build needs it)`));
|
|
2525
|
+
recordBuildIssue(name, 'warning', `Raised "target" ${current} → ${required} — the code uses standard-library APIs newer than the tsconfig allowed.`);
|
|
2526
|
+
return true;
|
|
2527
|
+
}
|
|
2432
2528
|
/** Write back the texts captured in a `migrateTsconfigDeprecations` snapshot
|
|
2433
2529
|
* (path → text). Used to undo a migration whose build turned out worse, and to
|
|
2434
2530
|
* re-apply it afterwards. Returns false if any write failed — the tree is then
|
|
@@ -3052,6 +3148,16 @@ export async function buildProject(cwd, opts = {}) {
|
|
|
3052
3148
|
buildResult = await runCommandAsync('npm', ['run', 'build'], { cwd, silent: true });
|
|
3053
3149
|
}
|
|
3054
3150
|
}
|
|
3151
|
+
// "Try changing the 'lib' compiler option to 'es2023' or later" — a tsconfig
|
|
3152
|
+
// pinned below the standard-library APIs the code actually uses. Raise it and
|
|
3153
|
+
// rebuild. Looped because one level can uncover the need for a higher one,
|
|
3154
|
+
// and bounded because each pass must raise the level to run again.
|
|
3155
|
+
for (let pass = 0; pass < 3 && !buildResult.success; pass++) {
|
|
3156
|
+
const out = (buildResult.stderr || '') + (buildResult.output || '');
|
|
3157
|
+
if (!raiseTsconfigLibLevel(cwd, out, tsconfigSnapshot))
|
|
3158
|
+
break;
|
|
3159
|
+
buildResult = await runCommandAsync('npm', ['run', 'build'], { cwd, silent: true });
|
|
3160
|
+
}
|
|
3055
3161
|
if (!buildResult.success && migratedUpFront && tsconfigSnapshot.size) {
|
|
3056
3162
|
// The up-front migration is a semantic change: `nodenext` resolution can
|
|
3057
3163
|
// surface real errors (missing file extensions on relative imports) that
|