@bobfrankston/npmglobalize 1.0.145 → 1.0.147
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 +11 -0
- package/cli.js +11 -0
- package/lib/types.d.ts +4 -0
- package/lib.d.ts +19 -0
- package/lib.js +120 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,6 +86,14 @@ npmglobalize
|
|
|
86
86
|
npmglobalize -npd # --no-publish-deps
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
+
### 🔍 Prescan (Default)
|
|
90
|
+
|
|
91
|
+
Before any transform/publish, `npmglobalize` walks the full `file:` dep graph and reports all problems up front (unresolvable paths, missing `package.json`, unscoped packages with private intent, etc.). This lets you fix the whole punch list at once rather than being interrupted mid-cascade.
|
|
92
|
+
|
|
93
|
+
Errors abort (unless `--force`); warnings prompt to continue.
|
|
94
|
+
|
|
95
|
+
Skip the prescan with `-no-prescan` / `-nps`.
|
|
96
|
+
|
|
89
97
|
**Force republish** all file: dependencies even if versions exist:
|
|
90
98
|
```bash
|
|
91
99
|
npmglobalize --force-publish
|
|
@@ -264,7 +272,10 @@ Publishing requires being on a branch so commits and tags can be properly tracke
|
|
|
264
272
|
```
|
|
265
273
|
-update-deps, -ud Update package.json to latest versions (safe: minor/patch)
|
|
266
274
|
-update-major Allow major version updates (breaking changes)
|
|
275
|
+
-publish-deps Auto-publish file: dependencies (default)
|
|
276
|
+
-pd Like -publish-deps, plus auto-yes to dep-cascade prompts (private only)
|
|
267
277
|
-no-publish-deps, -npd Skip auto-publishing file: dependencies
|
|
278
|
+
-no-prescan, -nps Skip upfront dep-graph prescan
|
|
268
279
|
-force-publish Republish dependencies even if version exists
|
|
269
280
|
-fix Run npm audit fix after transformation
|
|
270
281
|
-no-fix Don't run npm audit
|
package/cli.js
CHANGED
|
@@ -31,7 +31,10 @@ Release Options:
|
|
|
31
31
|
Dependency Options:
|
|
32
32
|
-update-deps, -ud Update package.json to latest (minor/patch only, safe)
|
|
33
33
|
-update-major Allow major version updates (breaking changes)
|
|
34
|
+
-publish-deps Auto-publish file: dependencies (default)
|
|
35
|
+
-pd Like -publish-deps, plus auto-yes to dep-cascade prompts (private only)
|
|
34
36
|
-no-publish-deps, -npd Don't auto-publish file: dependencies (use with caution)
|
|
37
|
+
-no-prescan, -nps Skip upfront dep-graph prescan
|
|
35
38
|
-force-publish Republish dependencies even if version exists
|
|
36
39
|
-fix Run npm audit fix after transformation
|
|
37
40
|
|
|
@@ -251,10 +254,18 @@ function parseArgs(args) {
|
|
|
251
254
|
case '-publish-deps':
|
|
252
255
|
options.publishDeps = true; // Explicitly enable (though it's default)
|
|
253
256
|
break;
|
|
257
|
+
case '-pd':
|
|
258
|
+
options.publishDeps = true;
|
|
259
|
+
options.publishDepsYes = true; // Auto-yes to dep-cascade prompts (private only)
|
|
260
|
+
break;
|
|
254
261
|
case '-no-publish-deps':
|
|
255
262
|
case '-npd':
|
|
256
263
|
options.publishDeps = false; // Disable auto-publishing
|
|
257
264
|
break;
|
|
265
|
+
case '-no-prescan':
|
|
266
|
+
case '-nps':
|
|
267
|
+
options.noPrescan = true;
|
|
268
|
+
break;
|
|
258
269
|
case '-force-publish':
|
|
259
270
|
options.forcePublish = true;
|
|
260
271
|
break;
|
package/lib/types.d.ts
CHANGED
|
@@ -52,6 +52,10 @@ export interface GlobalizeOptions {
|
|
|
52
52
|
updateMajor?: boolean;
|
|
53
53
|
/** Publish file: dependencies before converting them */
|
|
54
54
|
publishDeps?: boolean;
|
|
55
|
+
/** Auto-yes to dep-cascade prompts (add scope for private); does NOT auto-yes public prompts */
|
|
56
|
+
publishDepsYes?: boolean;
|
|
57
|
+
/** Skip the upfront dep-graph prescan */
|
|
58
|
+
noPrescan?: boolean;
|
|
55
59
|
/** Force republish dependencies even if version exists on npm */
|
|
56
60
|
forcePublish?: boolean;
|
|
57
61
|
/** Run npm audit and fix vulnerabilities */
|
package/lib.d.ts
CHANGED
|
@@ -67,6 +67,8 @@ export interface GlobalizeOptions {
|
|
|
67
67
|
updateMajor?: boolean;
|
|
68
68
|
/** Publish file: dependencies before converting them */
|
|
69
69
|
publishDeps?: boolean;
|
|
70
|
+
/** Auto-yes to dep-cascade prompts (add scope to make private, etc.); does NOT auto-yes public prompts */
|
|
71
|
+
publishDepsYes?: boolean;
|
|
70
72
|
/** Force republish dependencies even if version exists on npm */
|
|
71
73
|
forcePublish?: boolean;
|
|
72
74
|
/** Run npm audit and fix vulnerabilities */
|
|
@@ -96,6 +98,10 @@ export interface GlobalizeOptions {
|
|
|
96
98
|
/** Internal: auto-initialize git repos without prompting (user chose "all") */
|
|
97
99
|
autoInit?: boolean;
|
|
98
100
|
/** Internal: signals this call is from workspace orchestrator */
|
|
101
|
+
/** Skip the upfront dep-graph prescan */
|
|
102
|
+
noPrescan?: boolean;
|
|
103
|
+
/** Internal: recursive dep-cascade call (suppresses prescan + banner) */
|
|
104
|
+
_fromDep?: boolean;
|
|
99
105
|
_fromWorkspace?: boolean;
|
|
100
106
|
/** Internal: signals this call is from CLI (version already printed) */
|
|
101
107
|
_fromCli?: boolean;
|
|
@@ -190,6 +196,19 @@ export declare function transformDeps(pkg: any, baseDir: string, verbose?: boole
|
|
|
190
196
|
path: string;
|
|
191
197
|
}>;
|
|
192
198
|
};
|
|
199
|
+
/** A problem discovered by the prescan. */
|
|
200
|
+
export interface PrescanIssue {
|
|
201
|
+
severity: 'error' | 'warning';
|
|
202
|
+
package: string;
|
|
203
|
+
path: string;
|
|
204
|
+
message: string;
|
|
205
|
+
suggestion?: string;
|
|
206
|
+
}
|
|
207
|
+
/** Walk the full file: dep graph and collect issues up front — missing scopes,
|
|
208
|
+
* unresolvable paths, missing package.json, unpublished transitives. Lets the
|
|
209
|
+
* user resolve all problems before starting the publish cascade instead of
|
|
210
|
+
* being interrupted mid-run. */
|
|
211
|
+
export declare function prescanDepGraph(baseDir: string, visited?: Set<string>, verbose?: boolean): PrescanIssue[];
|
|
193
212
|
/** Restore file: dependencies from .dependencies using a three-way merge that
|
|
194
213
|
* preserves any external modifications made since the last transform. */
|
|
195
214
|
export declare function restoreDeps(pkg: any, verbose?: boolean): boolean;
|
package/lib.js
CHANGED
|
@@ -986,6 +986,83 @@ function printDepTree(baseDir, indent = 0, visited = new Set()) {
|
|
|
986
986
|
}
|
|
987
987
|
}
|
|
988
988
|
}
|
|
989
|
+
/** Walk the full file: dep graph and collect issues up front — missing scopes,
|
|
990
|
+
* unresolvable paths, missing package.json, unpublished transitives. Lets the
|
|
991
|
+
* user resolve all problems before starting the publish cascade instead of
|
|
992
|
+
* being interrupted mid-run. */
|
|
993
|
+
export function prescanDepGraph(baseDir, visited = new Set(), verbose = false) {
|
|
994
|
+
const issues = [];
|
|
995
|
+
let realDir;
|
|
996
|
+
try {
|
|
997
|
+
realDir = fs.realpathSync(baseDir);
|
|
998
|
+
}
|
|
999
|
+
catch {
|
|
1000
|
+
return issues;
|
|
1001
|
+
}
|
|
1002
|
+
if (visited.has(realDir))
|
|
1003
|
+
return issues;
|
|
1004
|
+
visited.add(realDir);
|
|
1005
|
+
let pkg;
|
|
1006
|
+
try {
|
|
1007
|
+
pkg = readPackageJson(baseDir);
|
|
1008
|
+
}
|
|
1009
|
+
catch {
|
|
1010
|
+
return issues;
|
|
1011
|
+
}
|
|
1012
|
+
for (const key of DEP_KEYS) {
|
|
1013
|
+
if (!pkg[key])
|
|
1014
|
+
continue;
|
|
1015
|
+
const deps = pkg['.' + key] || pkg[key];
|
|
1016
|
+
for (const [name, value] of Object.entries(deps)) {
|
|
1017
|
+
if (!isFileRef(value))
|
|
1018
|
+
continue;
|
|
1019
|
+
let targetPath;
|
|
1020
|
+
try {
|
|
1021
|
+
targetPath = resolveFilePath(value, baseDir);
|
|
1022
|
+
}
|
|
1023
|
+
catch {
|
|
1024
|
+
issues.push({ severity: 'error', package: name, path: value, message: 'unresolvable file: path' });
|
|
1025
|
+
continue;
|
|
1026
|
+
}
|
|
1027
|
+
if (!fs.existsSync(targetPath)) {
|
|
1028
|
+
issues.push({
|
|
1029
|
+
severity: 'error', package: name, path: targetPath,
|
|
1030
|
+
message: `file: target does not exist`,
|
|
1031
|
+
suggestion: `check that "${value}" is the correct relative path`
|
|
1032
|
+
});
|
|
1033
|
+
continue;
|
|
1034
|
+
}
|
|
1035
|
+
let targetPkg;
|
|
1036
|
+
try {
|
|
1037
|
+
targetPkg = readPackageJson(targetPath);
|
|
1038
|
+
}
|
|
1039
|
+
catch {
|
|
1040
|
+
issues.push({ severity: 'error', package: name, path: targetPath, message: 'no package.json at file: target' });
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
const depName = targetPkg.name || name;
|
|
1044
|
+
// Unscoped + private-intent: will trigger a mid-cascade scope prompt
|
|
1045
|
+
if (!depName.startsWith('@')) {
|
|
1046
|
+
const depConfig = readConfig(targetPath);
|
|
1047
|
+
const depVis = depConfig.npmVisibility
|
|
1048
|
+
?? (targetPkg.publishConfig?.access === 'public' ? 'public' : undefined);
|
|
1049
|
+
const noprefix = depConfig.noprefix === true;
|
|
1050
|
+
if (depVis !== 'public' && !noprefix) {
|
|
1051
|
+
issues.push({
|
|
1052
|
+
severity: 'warning',
|
|
1053
|
+
package: depName,
|
|
1054
|
+
path: targetPath,
|
|
1055
|
+
message: 'unscoped package — private publishing will prompt for a scope',
|
|
1056
|
+
suggestion: 'add a scope (e.g., @bobfrankston/) OR set npmVisibility="public" OR set noprefix=true in .globalize.json5'
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
// Recurse into the dep
|
|
1061
|
+
issues.push(...prescanDepGraph(targetPath, visited, verbose));
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
return issues;
|
|
1065
|
+
}
|
|
989
1066
|
/** Restore file: dependencies from .dependencies using a three-way merge that
|
|
990
1067
|
* preserves any external modifications made since the last transform. */
|
|
991
1068
|
export function restoreDeps(pkg, verbose = false) {
|
|
@@ -2462,7 +2539,8 @@ async function doLocalInstall(cwd, options) {
|
|
|
2462
2539
|
}
|
|
2463
2540
|
export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
2464
2541
|
const { bump = 'patch', noPublish = false, cleanup = false, install = false, link = false, wsl = false, force = false, files = true, dryRun = false, quiet = true, verbose = false, init = false, gitVisibility = 'private', npmVisibility = 'private', message, conform = false, asis = false, updateDeps = false, updateMajor = false, publishDeps = true, // Default to publishing deps for safety
|
|
2465
|
-
|
|
2542
|
+
publishDepsYes = false, // -pd: auto-yes to dep-cascade prompts (private only)
|
|
2543
|
+
noPrescan = false, forcePublish = false, fix = true, fixTags = false, rebase = false, show = false, local = false, freeze = false } = options;
|
|
2466
2544
|
// Show tool version only for recursive dep calls (CLI already prints it at startup)
|
|
2467
2545
|
const toolVersion = getToolVersion();
|
|
2468
2546
|
if (!options._fromWorkspace && !options._fromCli) {
|
|
@@ -2568,6 +2646,37 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
2568
2646
|
}
|
|
2569
2647
|
return true;
|
|
2570
2648
|
}
|
|
2649
|
+
// Prescan the file: dep graph up front — surface all problems before
|
|
2650
|
+
// any transform/publish, so the user can fix them once and run clean.
|
|
2651
|
+
// Only run at the top level (not during recursive cascade or workspace).
|
|
2652
|
+
if (!noPrescan && !cleanup && !options._fromDep && !options._fromWorkspace) {
|
|
2653
|
+
const issues = prescanDepGraph(cwd, new Set(), verbose);
|
|
2654
|
+
if (issues.length > 0) {
|
|
2655
|
+
const errors = issues.filter(i => i.severity === 'error');
|
|
2656
|
+
const warnings = issues.filter(i => i.severity === 'warning');
|
|
2657
|
+
console.log(colors.yellow(`Prescan found ${issues.length} issue(s) in the file: dep graph:`));
|
|
2658
|
+
for (const i of issues) {
|
|
2659
|
+
const tag = i.severity === 'error' ? colors.red('ERROR') : colors.yellow('WARN');
|
|
2660
|
+
console.log(` ${tag} ${i.package}: ${i.message}`);
|
|
2661
|
+
console.log(colors.dim(` at ${i.path}`));
|
|
2662
|
+
if (i.suggestion)
|
|
2663
|
+
console.log(colors.dim(` → ${i.suggestion}`));
|
|
2664
|
+
}
|
|
2665
|
+
console.log('');
|
|
2666
|
+
if (errors.length > 0 && !force) {
|
|
2667
|
+
console.error(colors.red(`Prescan aborted: ${errors.length} error(s). Fix them or rerun with --force.`));
|
|
2668
|
+
return false;
|
|
2669
|
+
}
|
|
2670
|
+
if (warnings.length > 0 && !force && !publishDepsYes) {
|
|
2671
|
+
const ok = await confirm('Continue despite prescan warnings?', false);
|
|
2672
|
+
if (!ok)
|
|
2673
|
+
return false;
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
else if (verbose) {
|
|
2677
|
+
console.log(colors.dim('Prescan: no issues found in dep graph.'));
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2571
2680
|
// Check ignore files first (unless cleanup mode)
|
|
2572
2681
|
if (!cleanup && !asis) {
|
|
2573
2682
|
const checkResult = checkIgnoreFiles(cwd, { conform, asis, verbose });
|
|
@@ -3263,7 +3372,9 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3263
3372
|
fix,
|
|
3264
3373
|
conform, // Propagate conform to dependencies
|
|
3265
3374
|
publishDeps, // Propagate so transitive deps also get published
|
|
3375
|
+
publishDepsYes, // Propagate auto-yes through cascade
|
|
3266
3376
|
forcePublish, // Propagate so transitive deps get force-published too
|
|
3377
|
+
_fromDep: true, // Suppress nested prescan
|
|
3267
3378
|
rebase, // Propagate so behind-remote deps get rebased automatically
|
|
3268
3379
|
autoInit: options.autoInit // Propagate "init all" choice
|
|
3269
3380
|
});
|
|
@@ -3289,7 +3400,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3289
3400
|
console.log(colors.yellow(' 3. Use --force to continue anyway (NOT RECOMMENDED)'));
|
|
3290
3401
|
console.log(colors.yellow(' 4. Use -npd (--no-publish-deps) to skip dependency publishing'));
|
|
3291
3402
|
console.log('');
|
|
3292
|
-
if (!force) {
|
|
3403
|
+
if (!force && !publishDepsYes) {
|
|
3293
3404
|
const shouldContinue = await confirm('Continue with unpublished dependencies?', false);
|
|
3294
3405
|
if (!shouldContinue) {
|
|
3295
3406
|
return false;
|
|
@@ -3561,17 +3672,11 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3561
3672
|
console.log(colors.green(`✓ Installed globally: ${pkgName}@${pkgVersion}`));
|
|
3562
3673
|
}
|
|
3563
3674
|
else {
|
|
3564
|
-
// Registry install failed —
|
|
3675
|
+
// Registry install failed — check if it's an auth issue
|
|
3565
3676
|
const auth = checkNpmAuth();
|
|
3566
3677
|
if (!auth.authenticated) {
|
|
3567
|
-
console.
|
|
3568
|
-
|
|
3569
|
-
else {
|
|
3570
|
-
console.log(colors.yellow(` Registry install failed — falling back to local install...`));
|
|
3571
|
-
}
|
|
3572
|
-
const localFallback = runCommand('npm', ['install', '-g', '.'], { cwd, silent: false, showCommand: true });
|
|
3573
|
-
if (localFallback.success) {
|
|
3574
|
-
console.log(colors.green(`✓ Installed globally from local: ${pkgName}@${pkgVersion}`));
|
|
3678
|
+
console.error(colors.red(` Registry install failed — npm auth problem: ${auth.error || 'unknown'}`));
|
|
3679
|
+
console.error(colors.yellow(` Run 'npm login' to fix, then: npm install -g ${pkgName}@${pkgVersion}`));
|
|
3575
3680
|
}
|
|
3576
3681
|
else {
|
|
3577
3682
|
console.error(colors.red(`✗ Global install failed`));
|
|
@@ -4280,19 +4385,12 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4280
4385
|
console.log(colors.green(`✓ Installed globally: ${pkgName}@${pkgVersion}`));
|
|
4281
4386
|
}
|
|
4282
4387
|
else {
|
|
4283
|
-
// Registry install failed —
|
|
4284
|
-
// Fall back to local install which doesn't need registry auth.
|
|
4388
|
+
// Registry install failed — check if it's an auth issue
|
|
4285
4389
|
const auth = checkNpmAuth();
|
|
4286
4390
|
if (!auth.authenticated) {
|
|
4287
|
-
console.
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
console.log(colors.yellow(` Registry install failed — falling back to local install...`));
|
|
4291
|
-
}
|
|
4292
|
-
const localFallback = runCommand('npm', ['install', '-g', '.'], { cwd, silent: false, showCommand: true });
|
|
4293
|
-
if (localFallback.success) {
|
|
4294
|
-
globalInstallOk = true;
|
|
4295
|
-
console.log(colors.green(`✓ Installed globally from local: ${pkgName}@${pkgVersion}`));
|
|
4391
|
+
console.error(colors.red(`✗ Global install failed — npm auth problem: ${auth.error || 'unknown'}`));
|
|
4392
|
+
console.error(colors.yellow(` Run 'npm login' to fix, then: npm install -g ${pkgName}@${pkgVersion}`));
|
|
4393
|
+
recordBuildIssue(pkgName, 'warning', `Global install failed (${auth.error || 'auth issue'}) — run 'npm login'`);
|
|
4296
4394
|
}
|
|
4297
4395
|
else {
|
|
4298
4396
|
console.error(colors.red(`✗ Global install failed`));
|