@bobfrankston/npmglobalize 1.0.169 → 1.0.171
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/cli.js +9 -0
- package/lib/config.js +1 -1
- package/lib/types.d.ts +2 -0
- package/lib.d.ts +24 -0
- package/lib.js +240 -25
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -37,6 +37,12 @@ Dependency Options:
|
|
|
37
37
|
-publish-deps Auto-publish file: dependencies (default)
|
|
38
38
|
-pd Like -publish-deps, plus auto-yes to dep-cascade prompts (private only)
|
|
39
39
|
-no-publish-deps, -npd Don't auto-publish file: dependencies (use with caution)
|
|
40
|
+
-public-deps Cascade npmVisibility:"public" to all transitive workspace/file:
|
|
41
|
+
deps. In workspace mode: auto-promotes every reachable workspace
|
|
42
|
+
member without prompting (writes npmVisibility:"public" into each
|
|
43
|
+
dep's .globalize.json5). In single-package mode: also propagates
|
|
44
|
+
public visibility to already-published deps in the cascade.
|
|
45
|
+
Use when a public consumer's deps weren't all marked public.
|
|
40
46
|
-no-prescan, -nps Skip upfront dep-graph prescan
|
|
41
47
|
-force-publish Republish dependencies even if version exists
|
|
42
48
|
-fix Run npm audit fix after transformation
|
|
@@ -298,6 +304,9 @@ function parseArgs(args) {
|
|
|
298
304
|
case '-npd':
|
|
299
305
|
options.publishDeps = false; // Disable auto-publishing
|
|
300
306
|
break;
|
|
307
|
+
case '-public-deps':
|
|
308
|
+
options.publicDeps = true;
|
|
309
|
+
break;
|
|
301
310
|
case '-no-use-paths':
|
|
302
311
|
case '-nup':
|
|
303
312
|
options.usePaths = false; // Skip sibling-path resolution; use latest npm version for file: deps
|
package/lib/config.js
CHANGED
|
@@ -65,7 +65,7 @@ export function writeConfig(dir, config, explicitKeys) {
|
|
|
65
65
|
const existing = readConfig(dir);
|
|
66
66
|
// Filter out temporary flags and default values (unless explicitly set)
|
|
67
67
|
const filtered = {};
|
|
68
|
-
const omitKeys = new Set(['cleanup', 'init', 'dryRun', 'message', 'conform', 'asis', 'help', 'error', 'updateDeps', 'updateMajor', 'publishDeps', 'forcePublish', 'once', 'cleanNestedModules']);
|
|
68
|
+
const omitKeys = new Set(['cleanup', 'init', 'dryRun', 'message', 'conform', 'asis', 'help', 'error', 'updateDeps', 'updateMajor', 'publishDeps', 'publicDeps', 'forcePublish', 'once', 'cleanNestedModules']);
|
|
69
69
|
for (const [key, value] of Object.entries(config)) {
|
|
70
70
|
if (omitKeys.has(key))
|
|
71
71
|
continue;
|
package/lib/types.d.ts
CHANGED
|
@@ -54,6 +54,8 @@ export interface GlobalizeOptions {
|
|
|
54
54
|
publishDeps?: boolean;
|
|
55
55
|
/** Auto-yes to dep-cascade prompts (add scope for private); does NOT auto-yes public prompts */
|
|
56
56
|
publishDepsYes?: boolean;
|
|
57
|
+
/** Cascade npmVisibility:"public" to all transitive workspace/file: deps without prompting. */
|
|
58
|
+
publicDeps?: boolean;
|
|
57
59
|
/** Skip the upfront dep-graph prescan */
|
|
58
60
|
noPrescan?: boolean;
|
|
59
61
|
/** Force republish dependencies even if version exists on npm */
|
package/lib.d.ts
CHANGED
|
@@ -73,6 +73,12 @@ export interface GlobalizeOptions {
|
|
|
73
73
|
publishDeps?: boolean;
|
|
74
74
|
/** Auto-yes to dep-cascade prompts (add scope to make private, etc.); does NOT auto-yes public prompts */
|
|
75
75
|
publishDepsYes?: boolean;
|
|
76
|
+
/** Cascade npmVisibility:"public" to all transitive workspace/file: deps without prompting.
|
|
77
|
+
* In workspace mode: fixpoint-promotes every workspace member reachable from a public consumer.
|
|
78
|
+
* In single-package mode: drops the "first publish only" guard so already-published deps also
|
|
79
|
+
* get the parent's npmVisibility propagated. Use to fix a workspace where a public member's
|
|
80
|
+
* deps weren't all marked public (and the published tarball would 404 on install). */
|
|
81
|
+
publicDeps?: boolean;
|
|
76
82
|
/** Force republish dependencies even if version exists on npm */
|
|
77
83
|
forcePublish?: boolean;
|
|
78
84
|
/** Run npm audit and fix vulnerabilities */
|
|
@@ -165,6 +171,24 @@ export declare function checkVersionExists(packageName: string, version: string)
|
|
|
165
171
|
export declare function checkPackageExists(packageName: string): boolean;
|
|
166
172
|
/** Check npm package access level (public/restricted/null if not published) */
|
|
167
173
|
export declare function checkNpmAccess(packageName: string): 'public' | 'restricted' | null;
|
|
174
|
+
/** Walk `file:` deps transitively from a starting directory and ensure each is set
|
|
175
|
+
* up to be installable from a public consumer: persists `npmVisibility:"public"` in
|
|
176
|
+
* each dep's `.globalize.json5`, and flips npm access from `restricted` to `public`
|
|
177
|
+
* for any dep that's already on npm. Returns blockers for deps that can't be made
|
|
178
|
+
* public (private:true or noPublish:true). Used by single-package `-public-deps`
|
|
179
|
+
* to handle deps that are unchanged locally — those would otherwise be skipped by
|
|
180
|
+
* the cascade and stay private on npm. */
|
|
181
|
+
export declare function cascadePublicVisibility(cwd: string, opts?: {
|
|
182
|
+
dryRun?: boolean;
|
|
183
|
+
verbose?: boolean;
|
|
184
|
+
}): Promise<{
|
|
185
|
+
promoted: string[];
|
|
186
|
+
configWritten: string[];
|
|
187
|
+
blockers: Array<{
|
|
188
|
+
name: string;
|
|
189
|
+
reason: string;
|
|
190
|
+
}>;
|
|
191
|
+
}>;
|
|
168
192
|
/** Check if public package has private/inaccessible dependencies */
|
|
169
193
|
export declare function checkPrivateDependencies(pkg: any, verbose?: boolean): {
|
|
170
194
|
name: string;
|
package/lib.js
CHANGED
|
@@ -238,7 +238,7 @@ export function writeConfig(dir, config, explicitKeys) {
|
|
|
238
238
|
const existing = readConfig(dir);
|
|
239
239
|
// Filter out temporary flags and default values (unless explicitly set)
|
|
240
240
|
const filtered = {};
|
|
241
|
-
const omitKeys = new Set(['cleanup', 'init', 'dryRun', 'message', 'conform', 'asis', 'help', 'error', 'updateDeps', 'updateMajor', 'publishDeps', 'forcePublish', 'once', 'cleanNestedModules']);
|
|
241
|
+
const omitKeys = new Set(['cleanup', 'init', 'dryRun', 'message', 'conform', 'asis', 'help', 'error', 'updateDeps', 'updateMajor', 'publishDeps', 'publicDeps', 'forcePublish', 'once', 'cleanNestedModules']);
|
|
242
242
|
for (const [key, value] of Object.entries(config)) {
|
|
243
243
|
if (omitKeys.has(key))
|
|
244
244
|
continue;
|
|
@@ -565,6 +565,113 @@ export function checkNpmAccess(packageName) {
|
|
|
565
565
|
return null;
|
|
566
566
|
}
|
|
567
567
|
}
|
|
568
|
+
/** Walk `file:` deps transitively from a starting directory and ensure each is set
|
|
569
|
+
* up to be installable from a public consumer: persists `npmVisibility:"public"` in
|
|
570
|
+
* each dep's `.globalize.json5`, and flips npm access from `restricted` to `public`
|
|
571
|
+
* for any dep that's already on npm. Returns blockers for deps that can't be made
|
|
572
|
+
* public (private:true or noPublish:true). Used by single-package `-public-deps`
|
|
573
|
+
* to handle deps that are unchanged locally — those would otherwise be skipped by
|
|
574
|
+
* the cascade and stay private on npm. */
|
|
575
|
+
export async function cascadePublicVisibility(cwd, opts = {}) {
|
|
576
|
+
const { dryRun = false, verbose = false } = opts;
|
|
577
|
+
const promoted = [];
|
|
578
|
+
const configWritten = [];
|
|
579
|
+
const blockers = [];
|
|
580
|
+
const visited = new Set();
|
|
581
|
+
const processed = new Set(); // dedupe by package name across diamond deps
|
|
582
|
+
async function walk(dir) {
|
|
583
|
+
let real;
|
|
584
|
+
try {
|
|
585
|
+
real = fs.realpathSync(dir);
|
|
586
|
+
}
|
|
587
|
+
catch {
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
if (visited.has(real))
|
|
591
|
+
return;
|
|
592
|
+
visited.add(real);
|
|
593
|
+
let pkg;
|
|
594
|
+
try {
|
|
595
|
+
pkg = readPackageJson(dir);
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
for (const key of DEP_KEYS) {
|
|
601
|
+
// Use the .dependencies backup if present (has original file: refs).
|
|
602
|
+
const deps = pkg['.' + key] || pkg[key];
|
|
603
|
+
if (!deps)
|
|
604
|
+
continue;
|
|
605
|
+
for (const [name, value] of Object.entries(deps)) {
|
|
606
|
+
if (typeof value !== 'string' || !isFileRef(value))
|
|
607
|
+
continue;
|
|
608
|
+
let targetPath;
|
|
609
|
+
try {
|
|
610
|
+
targetPath = resolveFilePath(value, dir);
|
|
611
|
+
}
|
|
612
|
+
catch {
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
if (!fs.existsSync(path.join(targetPath, 'package.json')))
|
|
616
|
+
continue;
|
|
617
|
+
let targetPkg;
|
|
618
|
+
try {
|
|
619
|
+
targetPkg = readPackageJson(targetPath);
|
|
620
|
+
}
|
|
621
|
+
catch {
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
const targetName = targetPkg.name || name;
|
|
625
|
+
if (!processed.has(targetName)) {
|
|
626
|
+
processed.add(targetName);
|
|
627
|
+
if (targetPkg.private) {
|
|
628
|
+
blockers.push({ name: targetName, reason: '"private": true in package.json (cannot publish to npm)' });
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
const targetConfig = readConfig(targetPath);
|
|
632
|
+
if (targetConfig.noPublish) {
|
|
633
|
+
blockers.push({ name: targetName, reason: 'noPublish:true in .globalize.json5' });
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
if (targetConfig.npmVisibility !== 'public') {
|
|
637
|
+
if (!dryRun) {
|
|
638
|
+
targetConfig.npmVisibility = 'public';
|
|
639
|
+
writeConfig(targetPath, targetConfig, new Set(['npmVisibility']));
|
|
640
|
+
}
|
|
641
|
+
configWritten.push(targetName);
|
|
642
|
+
if (verbose)
|
|
643
|
+
console.log(colors.dim(` ${targetName}: npmVisibility:"public" written to .globalize.json5`));
|
|
644
|
+
}
|
|
645
|
+
const access = checkNpmAccess(targetName);
|
|
646
|
+
if (access === 'restricted') {
|
|
647
|
+
if (!dryRun) {
|
|
648
|
+
const r = runCommand('npm', ['access', 'set', 'status=public', targetName], { cwd: targetPath, silent: true });
|
|
649
|
+
if (r.success) {
|
|
650
|
+
promoted.push(targetName);
|
|
651
|
+
console.log(colors.green(` ✓ Flipped ${targetName} to PUBLIC on npm`));
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
blockers.push({ name: targetName, reason: `npm access set failed: ${r.stderr || r.output || 'unknown'}` });
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
console.log(colors.dim(` [dry-run] Would flip ${targetName} to PUBLIC on npm`));
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
else if (access === 'public' && verbose) {
|
|
662
|
+
console.log(colors.dim(` ${targetName}: already PUBLIC on npm`));
|
|
663
|
+
}
|
|
664
|
+
// access === null: not on npm yet — the cascade publish step handles that.
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
await walk(targetPath);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
await walk(cwd);
|
|
673
|
+
return { promoted, configWritten, blockers };
|
|
674
|
+
}
|
|
568
675
|
/** Check if public package has private/inaccessible dependencies */
|
|
569
676
|
export function checkPrivateDependencies(pkg, verbose = false) {
|
|
570
677
|
const privateDeps = [];
|
|
@@ -3108,6 +3215,7 @@ async function doLocalInstall(cwd, options) {
|
|
|
3108
3215
|
export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
3109
3216
|
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
|
|
3110
3217
|
publishDepsYes = false, // -pd: auto-yes to dep-cascade prompts (private only)
|
|
3218
|
+
publicDeps = false, // -public-deps: cascade public visibility to all deps
|
|
3111
3219
|
noPrescan = false, forcePublish = false, fix = true, fixTags = false, rebase = false, show = false, local = false, freeze = false, usePaths = true, allowTs } = options;
|
|
3112
3220
|
// Show tool version only for recursive dep calls (CLI already prints it at startup)
|
|
3113
3221
|
const toolVersion = getToolVersion();
|
|
@@ -3843,6 +3951,32 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3843
3951
|
console.log(colors.dim(`Would add publishConfig.access: "public" to package.json`));
|
|
3844
3952
|
}
|
|
3845
3953
|
}
|
|
3954
|
+
// -public-deps pre-pass: at the top of the user-invoked call, walk file: deps
|
|
3955
|
+
// transitively and flip any that are already on npm but currently `restricted`
|
|
3956
|
+
// to `public`, persisting `npmVisibility:"public"` in each dep's .globalize.json5.
|
|
3957
|
+
// This handles the case where a public consumer's deps are unchanged locally
|
|
3958
|
+
// (so the cascade publish step would skip them) but published as private —
|
|
3959
|
+
// public installers would 404. Only runs once at the top level (suppressed in
|
|
3960
|
+
// recursive cascade and workspace-orchestrated calls, which already handle this).
|
|
3961
|
+
if (publicDeps && !options._fromDep && !options._fromWorkspace) {
|
|
3962
|
+
console.log(colors.italic('-public-deps: walking file: deps to ensure all are PUBLIC on npm...'));
|
|
3963
|
+
const result = await cascadePublicVisibility(cwd, { dryRun, verbose });
|
|
3964
|
+
if (result.promoted.length === 0 && result.configWritten.length === 0 && verbose) {
|
|
3965
|
+
console.log(colors.dim(' (no changes needed)'));
|
|
3966
|
+
}
|
|
3967
|
+
if (result.blockers.length > 0) {
|
|
3968
|
+
console.log('');
|
|
3969
|
+
console.log(colors.red('✗ -public-deps: cannot make every transitive dep public:'));
|
|
3970
|
+
for (const b of result.blockers) {
|
|
3971
|
+
console.log(colors.red(` ${b.name}: ${b.reason}`));
|
|
3972
|
+
}
|
|
3973
|
+
if (!force) {
|
|
3974
|
+
return false;
|
|
3975
|
+
}
|
|
3976
|
+
console.log(colors.yellow('Continuing with --force despite blockers...'));
|
|
3977
|
+
}
|
|
3978
|
+
console.log('');
|
|
3979
|
+
}
|
|
3846
3980
|
// Check for private dependencies in public packages
|
|
3847
3981
|
const willBePublic = effectiveNpmVisibility === 'public' ||
|
|
3848
3982
|
currentAccess === 'public' ||
|
|
@@ -3985,8 +4119,11 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3985
4119
|
if (!dryRun) {
|
|
3986
4120
|
// Check if package has EVER been published to npm (any version)
|
|
3987
4121
|
const hasBeenPublished = checkPackageExists(name);
|
|
3988
|
-
// Recursively call globalize on the dependency
|
|
3989
|
-
//
|
|
4122
|
+
// Recursively call globalize on the dependency.
|
|
4123
|
+
// Normally only pass npmVisibility to truly NEW packages (never published
|
|
4124
|
+
// before) — already-published deps keep their own visibility setting.
|
|
4125
|
+
// -public-deps overrides this: propagate npmVisibility regardless so a
|
|
4126
|
+
// public consumer's transitive deps all get promoted to public.
|
|
3990
4127
|
const depSuccess = await globalize(path, {
|
|
3991
4128
|
bump: 'patch', // Use existing version, don't bump
|
|
3992
4129
|
verbose,
|
|
@@ -3994,13 +4131,14 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3994
4131
|
force,
|
|
3995
4132
|
files,
|
|
3996
4133
|
gitVisibility,
|
|
3997
|
-
npmVisibility: hasBeenPublished ? undefined : npmVisibility,
|
|
4134
|
+
npmVisibility: (hasBeenPublished && !publicDeps) ? undefined : npmVisibility,
|
|
3998
4135
|
updateDeps,
|
|
3999
4136
|
updateMajor,
|
|
4000
4137
|
fix,
|
|
4001
4138
|
conform, // Propagate conform to dependencies
|
|
4002
4139
|
publishDeps, // Propagate so transitive deps also get published
|
|
4003
4140
|
publishDepsYes, // Propagate auto-yes through cascade
|
|
4141
|
+
publicDeps, // Propagate so transitive deps also get promoted to public
|
|
4004
4142
|
forcePublish, // Propagate so transitive deps get force-published too
|
|
4005
4143
|
_fromDep: true, // Suppress nested prescan
|
|
4006
4144
|
rebase, // Propagate so behind-remote deps get rebased automatically
|
|
@@ -5364,32 +5502,109 @@ export async function globalizeWorkspace(rootDir, options = {}, configOptions =
|
|
|
5364
5502
|
}
|
|
5365
5503
|
}
|
|
5366
5504
|
const wsNameSet = new Set(packages.map(p => p.name));
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5505
|
+
// Fixpoint: each promotion can expose new transitive deps (a newly-public B may
|
|
5506
|
+
// itself depend on a private C). Iterate until no further promotions happen so
|
|
5507
|
+
// chains like public A → private B → private C are fully resolved regardless of
|
|
5508
|
+
// packages[] order.
|
|
5509
|
+
{
|
|
5510
|
+
let changed = true;
|
|
5511
|
+
let guard = 0;
|
|
5512
|
+
while (changed && guard++ < 50) {
|
|
5513
|
+
changed = false;
|
|
5514
|
+
for (const pkgInfo of packages) {
|
|
5515
|
+
if (visibilityMap.get(pkgInfo.name) !== 'public')
|
|
5516
|
+
continue;
|
|
5517
|
+
const deps = { ...pkgInfo.pkg.dependencies, ...pkgInfo.pkg.devDependencies };
|
|
5518
|
+
for (const depName of Object.keys(deps || {})) {
|
|
5519
|
+
if (!wsNameSet.has(depName))
|
|
5520
|
+
continue;
|
|
5521
|
+
if (visibilityMap.get(depName) === 'public')
|
|
5522
|
+
continue;
|
|
5523
|
+
const depInfo = packages.find(p => p.name === depName);
|
|
5524
|
+
if (!depInfo)
|
|
5525
|
+
continue;
|
|
5526
|
+
console.log(colors.yellow(`⚠ Public package ${pkgInfo.name} depends on ${depName} which has no public visibility set.`));
|
|
5527
|
+
let makePublic = false;
|
|
5528
|
+
if (options.publicDeps) {
|
|
5529
|
+
console.log(colors.green(` -public-deps: auto-promoting ${depName} to public`));
|
|
5530
|
+
makePublic = true;
|
|
5531
|
+
}
|
|
5532
|
+
else if (options.dryRun) {
|
|
5533
|
+
console.log(colors.dim(` [dry-run] Would ask to make ${depName} public`));
|
|
5534
|
+
}
|
|
5535
|
+
else {
|
|
5536
|
+
makePublic = await confirm(`Make ${depName} public too?`, true);
|
|
5537
|
+
}
|
|
5538
|
+
if (makePublic) {
|
|
5539
|
+
if (!options.dryRun) {
|
|
5540
|
+
const depConfig = readConfig(depInfo.dir);
|
|
5541
|
+
depConfig.npmVisibility = 'public';
|
|
5542
|
+
writeConfig(depInfo.dir, depConfig, new Set(['npmVisibility']));
|
|
5543
|
+
console.log(colors.green(`✓ Set ${depName} to public in .globalize.json5`));
|
|
5544
|
+
}
|
|
5545
|
+
else {
|
|
5546
|
+
console.log(colors.dim(` [dry-run] Would set ${depName} npmVisibility:"public"`));
|
|
5547
|
+
}
|
|
5548
|
+
visibilityMap.set(depName, 'public');
|
|
5549
|
+
changed = true;
|
|
5550
|
+
}
|
|
5551
|
+
}
|
|
5552
|
+
}
|
|
5553
|
+
}
|
|
5554
|
+
}
|
|
5555
|
+
// Fail-loud: refuse to publish a public workspace member whose file: deps point
|
|
5556
|
+
// at non-publishable workspace siblings (private:true, noPublish:true, or still
|
|
5557
|
+
// not marked public after the prompt loop). A registry tarball declaring such
|
|
5558
|
+
// deps would 404 on a clean install. See docs/npmglobalize-transitive-workspace-deps.md.
|
|
5559
|
+
{
|
|
5560
|
+
const allMembers = resolveAllWorkspacePackages(rootDir);
|
|
5561
|
+
const memberByName = new Map();
|
|
5562
|
+
for (const m of allMembers)
|
|
5563
|
+
memberByName.set(m.name, m);
|
|
5564
|
+
const blockers = [];
|
|
5565
|
+
const filteredSet = new Set(filteredOrder);
|
|
5566
|
+
for (const pkgInfo of packages) {
|
|
5567
|
+
if (visibilityMap.get(pkgInfo.name) !== 'public')
|
|
5375
5568
|
continue;
|
|
5376
|
-
|
|
5377
|
-
if (!
|
|
5569
|
+
// Only check members that will actually be processed this run.
|
|
5570
|
+
if (!filteredSet.has(pkgInfo.name))
|
|
5378
5571
|
continue;
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5572
|
+
const deps = { ...pkgInfo.pkg.dependencies, ...pkgInfo.pkg.devDependencies };
|
|
5573
|
+
for (const [depName, depVal] of Object.entries(deps)) {
|
|
5574
|
+
if (typeof depVal !== 'string')
|
|
5575
|
+
continue;
|
|
5576
|
+
const member = memberByName.get(depName);
|
|
5577
|
+
if (!member)
|
|
5578
|
+
continue; // External dep, not workspace-internal — out of scope.
|
|
5579
|
+
if (member.pkg.private) {
|
|
5580
|
+
blockers.push({ pkg: pkgInfo.name, dep: depName, reason: `${depName} has "private": true in package.json (cannot publish)` });
|
|
5581
|
+
continue;
|
|
5582
|
+
}
|
|
5583
|
+
const depConfig = readConfig(member.dir);
|
|
5584
|
+
if (depConfig.noPublish) {
|
|
5585
|
+
blockers.push({ pkg: pkgInfo.name, dep: depName, reason: `${depName} has noPublish:true in .globalize.json5` });
|
|
5586
|
+
continue;
|
|
5587
|
+
}
|
|
5588
|
+
if (visibilityMap.get(depName) !== 'public') {
|
|
5589
|
+
blockers.push({ pkg: pkgInfo.name, dep: depName, reason: `${depName} is not marked public (set npmVisibility:"public" in its .globalize.json5, or rerun with -public-deps)` });
|
|
5590
|
+
continue;
|
|
5388
5591
|
}
|
|
5389
5592
|
}
|
|
5390
|
-
|
|
5391
|
-
|
|
5593
|
+
}
|
|
5594
|
+
if (blockers.length > 0) {
|
|
5595
|
+
console.log('');
|
|
5596
|
+
console.log(colors.red('✗ Refusing to publish: public workspace members depend on non-publishable siblings.'));
|
|
5597
|
+
console.log(colors.red(' The published tarball would 404 on a clean install.'));
|
|
5598
|
+
console.log('');
|
|
5599
|
+
for (const b of blockers) {
|
|
5600
|
+
console.log(colors.red(` ${b.pkg} → ${b.dep}: ${b.reason}`));
|
|
5392
5601
|
}
|
|
5602
|
+
console.log('');
|
|
5603
|
+
console.log(colors.dim('Fixes:'));
|
|
5604
|
+
console.log(colors.dim(' - Set npmVisibility:"public" in each offending dep\'s .globalize.json5'));
|
|
5605
|
+
console.log(colors.dim(' - Or rerun with -public-deps to auto-promote all transitive workspace deps'));
|
|
5606
|
+
console.log(colors.dim(' - Or remove npmVisibility:"public" from the consumer (keep it bundled under the workspace root)'));
|
|
5607
|
+
return { success: false, packages: [], publishOrder };
|
|
5393
5608
|
}
|
|
5394
5609
|
}
|
|
5395
5610
|
// Sync workspace-root node_modules with member package.json files. Catches
|