@bobfrankston/npmglobalize 1.0.202 → 1.0.203
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 +96 -4
- package/package.json +1 -1
package/lib.js
CHANGED
|
@@ -42,6 +42,16 @@ const _buildIssues = [];
|
|
|
42
42
|
export function recordBuildIssue(module, severity, message) {
|
|
43
43
|
_buildIssues.push({ module, severity, message });
|
|
44
44
|
}
|
|
45
|
+
/** How many issues have been recorded so far — pair with `dropBuildIssuesFrom`
|
|
46
|
+
* to un-record issues for an action that was subsequently undone. */
|
|
47
|
+
function markBuildIssues() {
|
|
48
|
+
return _buildIssues.length;
|
|
49
|
+
}
|
|
50
|
+
/** Drop every issue recorded after `mark` (see `markBuildIssues`). */
|
|
51
|
+
function dropBuildIssuesFrom(mark) {
|
|
52
|
+
if (mark >= 0 && mark < _buildIssues.length)
|
|
53
|
+
_buildIssues.length = mark;
|
|
54
|
+
}
|
|
45
55
|
/** Get all accumulated build issues */
|
|
46
56
|
export function getBuildIssues() {
|
|
47
57
|
return _buildIssues;
|
|
@@ -2323,8 +2333,12 @@ function ensureTsconfigNodeTypes(cwd) {
|
|
|
2323
2333
|
* most-leaf writable config so it wins. This is a *semantic* change: the rebuild
|
|
2324
2334
|
* may now surface genuine resolution errors — that is the point. Those are real
|
|
2325
2335
|
* bugs the `node10` resolver was hiding, not deprecations. Returns true if it
|
|
2326
|
-
* changed any file.
|
|
2327
|
-
|
|
2336
|
+
* changed any file.
|
|
2337
|
+
*
|
|
2338
|
+
* Pass `snapshot` to capture each touched file's original text (path → text)
|
|
2339
|
+
* before it is rewritten, so the caller can put the tsconfigs back if the
|
|
2340
|
+
* migrated build turns out to be worse than the un-migrated one. */
|
|
2341
|
+
function migrateTsconfigDeprecations(cwd, snapshot) {
|
|
2328
2342
|
const tsconfigPath = path.join(cwd, 'tsconfig.json');
|
|
2329
2343
|
if (!fs.existsSync(tsconfigPath))
|
|
2330
2344
|
return false;
|
|
@@ -2358,6 +2372,8 @@ function migrateTsconfigDeprecations(cwd) {
|
|
|
2358
2372
|
return false;
|
|
2359
2373
|
try {
|
|
2360
2374
|
fs.writeFileSync(filePath, patched);
|
|
2375
|
+
if (snapshot && !snapshot.has(filePath))
|
|
2376
|
+
snapshot.set(filePath, text);
|
|
2361
2377
|
return true;
|
|
2362
2378
|
}
|
|
2363
2379
|
catch (error) {
|
|
@@ -2413,6 +2429,35 @@ function migrateTsconfigDeprecations(cwd) {
|
|
|
2413
2429
|
}
|
|
2414
2430
|
return changed;
|
|
2415
2431
|
}
|
|
2432
|
+
/** Write back the texts captured in a `migrateTsconfigDeprecations` snapshot
|
|
2433
|
+
* (path → text). Used to undo a migration whose build turned out worse, and to
|
|
2434
|
+
* re-apply it afterwards. Returns false if any write failed — the tree is then
|
|
2435
|
+
* in a mixed state, which the caller reports rather than hides. */
|
|
2436
|
+
function restoreTsconfigTexts(texts) {
|
|
2437
|
+
let ok = true;
|
|
2438
|
+
for (const [filePath, text] of texts) {
|
|
2439
|
+
try {
|
|
2440
|
+
fs.writeFileSync(filePath, text);
|
|
2441
|
+
}
|
|
2442
|
+
catch (error) {
|
|
2443
|
+
ok = false;
|
|
2444
|
+
console.error(colors.yellow(` Could not restore tsconfig (${filePath}): ${error.message}`));
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
return ok;
|
|
2448
|
+
}
|
|
2449
|
+
/** Current text of every file in a snapshot, so a migration can be re-applied
|
|
2450
|
+
* after being tried in reverse. */
|
|
2451
|
+
function readTsconfigTexts(paths) {
|
|
2452
|
+
const out = new Map();
|
|
2453
|
+
for (const p of paths) {
|
|
2454
|
+
try {
|
|
2455
|
+
out.set(p, fs.readFileSync(p, 'utf-8'));
|
|
2456
|
+
}
|
|
2457
|
+
catch { /* gone — nothing to re-apply */ }
|
|
2458
|
+
}
|
|
2459
|
+
return out;
|
|
2460
|
+
}
|
|
2416
2461
|
/** Newest mtime of any regular file under `dir` (recursive). Symlinks/junctions
|
|
2417
2462
|
* are not followed — the file: dep webs here link back into sibling checkouts. */
|
|
2418
2463
|
function newestMtimeUnder(dir) {
|
|
@@ -2913,15 +2958,29 @@ export async function buildProject(cwd, opts = {}) {
|
|
|
2913
2958
|
// with no tsconfig at all, and still need its import map regenerated.
|
|
2914
2959
|
await ensureImportgenInBuild(cwd);
|
|
2915
2960
|
let shouldBuild = false;
|
|
2961
|
+
let hasTsconfig = false;
|
|
2916
2962
|
try {
|
|
2917
2963
|
const tsconfigPath = path.join(cwd, 'tsconfig.json');
|
|
2918
2964
|
const content = fs.readFileSync(tsconfigPath, 'utf-8');
|
|
2919
2965
|
const tsconfig = JSON5.parse(content);
|
|
2966
|
+
hasTsconfig = true;
|
|
2920
2967
|
shouldBuild = tsconfig.compilerOptions?.noEmit !== true;
|
|
2921
2968
|
}
|
|
2922
2969
|
catch {
|
|
2923
2970
|
// No tsconfig — skip
|
|
2924
2971
|
}
|
|
2972
|
+
// Fix removed-in-TS7 tsconfig settings up front rather than waiting for a
|
|
2973
|
+
// build to fail on them. A deprecated `moduleResolution` is only a *hard*
|
|
2974
|
+
// error under the tsc that flags it (TS6+, and the editor's bundled
|
|
2975
|
+
// compiler), so a package pinned to an older tsc — or one that never gets
|
|
2976
|
+
// to the build at all (noEmit, no build script, output already fresh) —
|
|
2977
|
+
// would otherwise keep the deprecation until TS7 removes it outright.
|
|
2978
|
+
// Deliberately ahead of every early return below: the tsconfig is worth
|
|
2979
|
+
// fixing whether or not this package builds. Migrating also touches
|
|
2980
|
+
// tsconfig.json, which makes the freshness check correctly read as stale.
|
|
2981
|
+
const tsconfigSnapshot = new Map();
|
|
2982
|
+
const issueMark = markBuildIssues();
|
|
2983
|
+
const migratedUpFront = hasTsconfig && migrateTsconfigDeprecations(cwd, tsconfigSnapshot);
|
|
2925
2984
|
let earlyPkg;
|
|
2926
2985
|
try {
|
|
2927
2986
|
earlyPkg = readPackageJson(cwd);
|
|
@@ -2974,7 +3033,9 @@ export async function buildProject(cwd, opts = {}) {
|
|
|
2974
3033
|
// freshness — one the user declined to wire in isn't this build's business.
|
|
2975
3034
|
const finalBuild = typeof pkg.scripts?.build === 'string' ? pkg.scripts.build : '';
|
|
2976
3035
|
const wiredSubs = detectSubProjects(cwd).filter(s => scriptBuildsSubProject(finalBuild, s.dir));
|
|
2977
|
-
|
|
3036
|
+
// A migration just changed how modules resolve — never report that as fresh;
|
|
3037
|
+
// the rebuild is what validates it (and what the revert path above needs).
|
|
3038
|
+
if (!opts.forceBuild && !migratedUpFront && isBuildUpToDate(cwd) && areSubProjectsUpToDate(cwd, wiredSubs)
|
|
2978
3039
|
&& (!runsImportgen || isImportMapUpToDate(cwd))) {
|
|
2979
3040
|
console.log(colors.dim(`– Build up to date (${pkg.name || path.basename(cwd)})`));
|
|
2980
3041
|
return true;
|
|
@@ -2987,10 +3048,41 @@ export async function buildProject(cwd, opts = {}) {
|
|
|
2987
3048
|
// We don't silence with ignoreDeprecations — the retry may now surface real
|
|
2988
3049
|
// resolution errors, which are genuine bugs to fix, not deprecations.
|
|
2989
3050
|
const out = (buildResult.stderr || '') + (buildResult.output || '');
|
|
2990
|
-
if (/error TS510[17]\b/.test(out) && migrateTsconfigDeprecations(cwd)) {
|
|
3051
|
+
if (/error TS510[17]\b/.test(out) && migrateTsconfigDeprecations(cwd, tsconfigSnapshot)) {
|
|
2991
3052
|
buildResult = await runCommandAsync('npm', ['run', 'build'], { cwd, silent: true });
|
|
2992
3053
|
}
|
|
2993
3054
|
}
|
|
3055
|
+
if (!buildResult.success && migratedUpFront && tsconfigSnapshot.size) {
|
|
3056
|
+
// The up-front migration is a semantic change: `nodenext` resolution can
|
|
3057
|
+
// surface real errors (missing file extensions on relative imports) that
|
|
3058
|
+
// `node10` was hiding. Those are genuine bugs — but they are not this
|
|
3059
|
+
// publish's business, and silently breaking a build that worked a moment
|
|
3060
|
+
// ago is worse than leaving the deprecation in place. So put the
|
|
3061
|
+
// tsconfigs back, and if that restores a working build, hand the user
|
|
3062
|
+
// the errors plus the command that migrates for real.
|
|
3063
|
+
const migratedOutput = (buildResult.stderr || '') + (buildResult.output || '');
|
|
3064
|
+
const migratedTexts = readTsconfigTexts(tsconfigSnapshot.keys());
|
|
3065
|
+
if (restoreTsconfigTexts(tsconfigSnapshot)) {
|
|
3066
|
+
const retry = await runCommandAsync('npm', ['run', 'build'], { cwd, silent: true });
|
|
3067
|
+
if (retry.success) {
|
|
3068
|
+
dropBuildIssuesFrom(issueMark); // the migration was undone; its warnings no longer apply
|
|
3069
|
+
const firstErr = extractFirstTscError(migratedOutput);
|
|
3070
|
+
const label = pkg.name || path.basename(cwd);
|
|
3071
|
+
console.log(colors.yellow(` Reverted the tsconfig TS7 migration in ${label} — it broke the build:`));
|
|
3072
|
+
if (firstErr)
|
|
3073
|
+
console.log(colors.yellow(` ${firstErr}`));
|
|
3074
|
+
console.log(colors.yellow(` Deprecated settings left in place. Fix with: npmglobalize ${cwd} -tsfix`));
|
|
3075
|
+
recordBuildIssue(label, 'warning', `tsconfig still uses settings TypeScript 7 removes: migrating to "nodenext" broke the build (${firstErr || 'see build output'}). Run \`npmglobalize ${cwd} -tsfix\` and fix the surfaced imports.`);
|
|
3076
|
+
buildResult = retry;
|
|
3077
|
+
}
|
|
3078
|
+
else {
|
|
3079
|
+
// The build was already failing for its own reasons — the migration
|
|
3080
|
+
// isn't what broke it, so keep the migrated (TS7-ready) tsconfigs
|
|
3081
|
+
// and report the migrated build's failure.
|
|
3082
|
+
restoreTsconfigTexts(migratedTexts);
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
2994
3086
|
if (buildResult.success) {
|
|
2995
3087
|
console.log(colors.green(`✓ Build succeeded (${pkg.name || path.basename(cwd)})`));
|
|
2996
3088
|
return true;
|