@bobfrankston/npmglobalize 1.0.215 → 1.0.216
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 +17 -0
- package/lib.d.ts +9 -0
- package/lib.js +52 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -562,6 +562,23 @@ Settings can be saved in `.globalize.json5`:
|
|
|
562
562
|
|
|
563
563
|
Configuration persists across runs. CLI flags override config file.
|
|
564
564
|
|
|
565
|
+
### `.stale` — retired directories
|
|
566
|
+
|
|
567
|
+
A directory that is no longer live or maintained can be retired in place by
|
|
568
|
+
dropping a `.stale` file into it — empty, or better holding one line saying why
|
|
569
|
+
and when. It is a marker file, not a setting in `.globalize.json5`, because
|
|
570
|
+
stale is a property of the directory (most retired directories never had a
|
|
571
|
+
config) and a file is findable without parsing.
|
|
572
|
+
|
|
573
|
+
npmglobalize refuses to build, publish or install a directory marked `.stale`,
|
|
574
|
+
and refuses to cascade into a `file:` dependency that is marked. Both are
|
|
575
|
+
reported as **errors**, not skips: a live package that depends on a retired one
|
|
576
|
+
has a reference that needs fixing. Remove the marker to make the directory live
|
|
577
|
+
again.
|
|
578
|
+
|
|
579
|
+
`cleanprev.ps1` (in `%OneDrive%\xfer\bin`) strips `node_modules`, lock files and
|
|
580
|
+
tsc output from every `.stale` directory the same way it does for `prev/`.
|
|
581
|
+
|
|
565
582
|
### `usePaths` — Standalone packages
|
|
566
583
|
|
|
567
584
|
Default: `true`. Set to `false` (or pass `-no-use-paths` / `-nup`) to mark a
|
package/lib.d.ts
CHANGED
|
@@ -297,6 +297,15 @@ export type UnpublishedDep = {
|
|
|
297
297
|
* NOT `fs.realpathSync.native`: that would also resolve a junction the user is
|
|
298
298
|
* standing inside, which is a behaviour change nobody asked for. */
|
|
299
299
|
export declare function canonicalCase(target: string): string;
|
|
300
|
+
/** A directory retired in place carries a `.stale` marker file (Bob's convention,
|
|
301
|
+
* 2026-09-06; `cleanprev.ps1` strips such directories like a `prev/`). The file
|
|
302
|
+
* is empty or holds a line saying why and when. Returns that line (or a fixed
|
|
303
|
+
* phrase when empty), or null when the directory is not marked.
|
|
304
|
+
*
|
|
305
|
+
* 2026-09-06 — Claude Code (Fable 5.1), at Bob's direction. A marker file rather
|
|
306
|
+
* than a flag in .globalize.json5: stale is a property of a directory, most stale
|
|
307
|
+
* directories never had a config, and a file is findable without parsing. */
|
|
308
|
+
export declare function staleMarker(dir: string): string;
|
|
300
309
|
/** Vet the casing of every `file:` dependency path, in BOTH places it is recorded:
|
|
301
310
|
* the spec in package.json and the junction npm created in node_modules.
|
|
302
311
|
*
|
package/lib.js
CHANGED
|
@@ -1370,6 +1370,25 @@ function caseDiffers(a, b) {
|
|
|
1370
1370
|
const strip = (p) => p.replace(/[\\/]+$/, '');
|
|
1371
1371
|
return strip(a) !== strip(b);
|
|
1372
1372
|
}
|
|
1373
|
+
/** A directory retired in place carries a `.stale` marker file (Bob's convention,
|
|
1374
|
+
* 2026-09-06; `cleanprev.ps1` strips such directories like a `prev/`). The file
|
|
1375
|
+
* is empty or holds a line saying why and when. Returns that line (or a fixed
|
|
1376
|
+
* phrase when empty), or null when the directory is not marked.
|
|
1377
|
+
*
|
|
1378
|
+
* 2026-09-06 — Claude Code (Fable 5.1), at Bob's direction. A marker file rather
|
|
1379
|
+
* than a flag in .globalize.json5: stale is a property of a directory, most stale
|
|
1380
|
+
* directories never had a config, and a file is findable without parsing. */
|
|
1381
|
+
export function staleMarker(dir) {
|
|
1382
|
+
const marker = path.join(dir, '.stale');
|
|
1383
|
+
if (!fs.existsSync(marker))
|
|
1384
|
+
return null;
|
|
1385
|
+
let note = '';
|
|
1386
|
+
try {
|
|
1387
|
+
note = fs.readFileSync(marker, 'utf8').split(/\r?\n/).map(l => l.trim()).find(l => l) || '';
|
|
1388
|
+
}
|
|
1389
|
+
catch { /* unreadable note: still stale */ }
|
|
1390
|
+
return note || 'no reason recorded in .stale';
|
|
1391
|
+
}
|
|
1373
1392
|
/** Vet the casing of every `file:` dependency path, in BOTH places it is recorded:
|
|
1374
1393
|
* the spec in package.json and the junction npm created in node_modules.
|
|
1375
1394
|
*
|
|
@@ -2328,6 +2347,15 @@ export async function ensureFileDepModules(cwd, verbose = false, visited = new S
|
|
|
2328
2347
|
const target = path.resolve(cwd, spec.slice('file:'.length));
|
|
2329
2348
|
if (!fs.existsSync(path.join(target, 'package.json')))
|
|
2330
2349
|
continue;
|
|
2350
|
+
// 2026-09-06 — Claude Code (Fable 5.1): never install into, or walk
|
|
2351
|
+
// through, a dep that is retired (`.stale`). Reported as an error so the
|
|
2352
|
+
// stale reference in package.json gets fixed rather than papered over.
|
|
2353
|
+
const targetStale = staleMarker(target);
|
|
2354
|
+
if (targetStale) {
|
|
2355
|
+
console.error(colors.red(`✗ file: dependency ${name} -> ${target} is marked .stale (${targetStale}); not installing into a retired directory.`));
|
|
2356
|
+
recordBuildIssue(pkg?.name || abs, 'error', `file: dependency ${name} (${target}) is marked .stale (${targetStale}).`);
|
|
2357
|
+
continue;
|
|
2358
|
+
}
|
|
2331
2359
|
let targetPkg;
|
|
2332
2360
|
try {
|
|
2333
2361
|
targetPkg = readPackageJson(target);
|
|
@@ -3505,7 +3533,7 @@ export async function buildFileDepsTopologically(cwd, opts = {}, visited = new S
|
|
|
3505
3533
|
const deps = pkg?.[key];
|
|
3506
3534
|
if (!deps || typeof deps !== 'object')
|
|
3507
3535
|
continue;
|
|
3508
|
-
for (const [, spec] of Object.entries(deps)) {
|
|
3536
|
+
for (const [name, spec] of Object.entries(deps)) {
|
|
3509
3537
|
if (typeof spec !== 'string' || !spec.startsWith('file:'))
|
|
3510
3538
|
continue;
|
|
3511
3539
|
const target = path.resolve(cwd, spec.slice('file:'.length));
|
|
@@ -3514,6 +3542,18 @@ export async function buildFileDepsTopologically(cwd, opts = {}, visited = new S
|
|
|
3514
3542
|
const targetAbs = path.resolve(target);
|
|
3515
3543
|
if (visited.has(targetAbs))
|
|
3516
3544
|
continue;
|
|
3545
|
+
// 2026-09-06 — Claude Code (Fable 5.1): a retired (`.stale`) dep is not
|
|
3546
|
+
// built. Counts as a failed dep so the consumer's build stops here too,
|
|
3547
|
+
// unless -force says to carry on.
|
|
3548
|
+
const targetStale = staleMarker(target);
|
|
3549
|
+
if (targetStale) {
|
|
3550
|
+
console.error(colors.red(`✗ file: dependency ${name} -> ${target} is marked .stale (${targetStale}); not building a retired directory.`));
|
|
3551
|
+
recordBuildIssue(pkg?.name || abs, 'error', `file: dependency ${name} (${target}) is marked .stale (${targetStale}).`);
|
|
3552
|
+
allOk = false;
|
|
3553
|
+
if (!opts.force)
|
|
3554
|
+
return false;
|
|
3555
|
+
continue;
|
|
3556
|
+
}
|
|
3517
3557
|
// Recurse first (deps before consumer)
|
|
3518
3558
|
const childOk = await buildFileDepsTopologically(target, opts, visited);
|
|
3519
3559
|
if (!childOk)
|
|
@@ -6141,6 +6181,17 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
6141
6181
|
publishDepsYes = false, // -pd: auto-yes to dep-cascade prompts (private only)
|
|
6142
6182
|
publicDeps = false, // -public-deps: cascade public visibility to all deps
|
|
6143
6183
|
noPrescan = false, forcePublish = false, fix = true, fixTags = false, rebase = false, show = false, local = false, freeze = false, usePaths = true, allowTs, adopt = false, importCheck = false } = options;
|
|
6184
|
+
// 2026-09-06 — Claude Code (Fable 5.1), at Bob's direction: a directory marked
|
|
6185
|
+
// `.stale` is retired. Refuse to build, publish or install it — and because the
|
|
6186
|
+
// dep cascade reaches deps through this same function, refuse to cascade into
|
|
6187
|
+
// one too. The refusal is an error, not a skip: a live package depending on a
|
|
6188
|
+
// stale one is a fact the user has to act on.
|
|
6189
|
+
const staleNote = staleMarker(cwd);
|
|
6190
|
+
if (staleNote) {
|
|
6191
|
+
console.error(colors.red(`✗ ${cwd} is marked .stale (${staleNote}). Not publishing a retired directory.`));
|
|
6192
|
+
recordBuildIssue(path.basename(cwd), 'error', `Directory is marked .stale (${staleNote}); remove the marker to publish it again.`);
|
|
6193
|
+
return false;
|
|
6194
|
+
}
|
|
6144
6195
|
// Show tool version only for recursive dep calls (CLI already prints it at startup)
|
|
6145
6196
|
const toolVersion = getToolVersion();
|
|
6146
6197
|
if (!options._fromWorkspace && !options._fromCli) {
|