@bobfrankston/npmglobalize 1.0.167 → 1.0.169
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/git.d.ts +12 -4
- package/lib/git.js +29 -11
- package/lib.js +81 -61
- package/package.json +1 -1
package/lib/git.d.ts
CHANGED
|
@@ -51,10 +51,18 @@ export declare function parseVersionTag(tag: string): number[] | null;
|
|
|
51
51
|
export declare function compareVersions(a: number[], b: number[]): number;
|
|
52
52
|
/** Fix version/tag mismatches */
|
|
53
53
|
export declare function fixVersionTagMismatch(cwd: string, pkg: any, verbose?: boolean): boolean;
|
|
54
|
-
/** Wait for a package version to appear on the npm registry
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
/** Wait for a package version to appear on the npm registry.
|
|
55
|
+
* First-time publishes (brand-new package name) take much longer to
|
|
56
|
+
* propagate than version bumps — npm has no cached metadata to update,
|
|
57
|
+
* so the registry/CDN can take several minutes before the package is
|
|
58
|
+
* resolvable. We wait longer and re-probe `npm view` for the version
|
|
59
|
+
* string until it shows up (or we hit the cap). */
|
|
60
|
+
export declare function waitForNpmVersion(pkgName: string, version: string, isNewPackage?: boolean, maxWaitMs?: number): boolean;
|
|
61
|
+
/** Run npm install -g with retries for registry propagation delay.
|
|
62
|
+
* Brand-new packages (first-time publish) take much longer to become
|
|
63
|
+
* installable than version bumps, so we use longer waits and more
|
|
64
|
+
* attempts when `isNewPackage` is true. */
|
|
65
|
+
export declare function installGlobalWithRetry(pkgSpec: string, cwd: string, isNewPackage?: boolean, maxRetries?: number): {
|
|
58
66
|
success: boolean;
|
|
59
67
|
output: string;
|
|
60
68
|
stderr: string;
|
package/lib/git.js
CHANGED
|
@@ -469,11 +469,24 @@ export function fixVersionTagMismatch(cwd, pkg, verbose = false) {
|
|
|
469
469
|
return deletedAny;
|
|
470
470
|
}
|
|
471
471
|
// ── Group 5: npm install helpers (private) ──────────────────────────
|
|
472
|
-
/**
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
472
|
+
/** Reliable synchronous sleep — works regardless of stdio/TTY state.
|
|
473
|
+
* (Windows `timeout /t` exits immediately when stdio is piped, which broke
|
|
474
|
+
* the previous spawn-based sleep.) */
|
|
475
|
+
function sleepSync(ms) {
|
|
476
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
477
|
+
}
|
|
478
|
+
/** Wait for a package version to appear on the npm registry.
|
|
479
|
+
* First-time publishes (brand-new package name) take much longer to
|
|
480
|
+
* propagate than version bumps — npm has no cached metadata to update,
|
|
481
|
+
* so the registry/CDN can take several minutes before the package is
|
|
482
|
+
* resolvable. We wait longer and re-probe `npm view` for the version
|
|
483
|
+
* string until it shows up (or we hit the cap). */
|
|
484
|
+
export function waitForNpmVersion(pkgName, version, isNewPackage = false, maxWaitMs) {
|
|
485
|
+
const effectiveMaxWait = maxWaitMs ?? (isNewPackage ? 600000 : 180000);
|
|
486
|
+
const interval = isNewPackage ? 5000 : 3000;
|
|
487
|
+
const maxAttempts = Math.ceil(effectiveMaxWait / interval);
|
|
488
|
+
const suffix = isNewPackage ? ' (new package, may take several minutes)' : '';
|
|
489
|
+
process.stdout.write(`Waiting for ${pkgName}@${version} on npm registry${suffix}`);
|
|
477
490
|
for (let i = 0; i < maxAttempts; i++) {
|
|
478
491
|
const result = spawnSafe('npm', ['view', `${pkgName}@${version}`, 'version'], {
|
|
479
492
|
shell: process.platform === 'win32',
|
|
@@ -485,17 +498,22 @@ export function waitForNpmVersion(pkgName, version, maxWaitMs = 90000) {
|
|
|
485
498
|
return true;
|
|
486
499
|
}
|
|
487
500
|
process.stdout.write('.');
|
|
488
|
-
|
|
501
|
+
sleepSync(interval);
|
|
489
502
|
}
|
|
490
503
|
process.stdout.write(' timed out\n');
|
|
491
504
|
return false;
|
|
492
505
|
}
|
|
493
|
-
/** Run npm install -g with retries for registry propagation delay
|
|
494
|
-
|
|
506
|
+
/** Run npm install -g with retries for registry propagation delay.
|
|
507
|
+
* Brand-new packages (first-time publish) take much longer to become
|
|
508
|
+
* installable than version bumps, so we use longer waits and more
|
|
509
|
+
* attempts when `isNewPackage` is true. */
|
|
510
|
+
export function installGlobalWithRetry(pkgSpec, cwd, isNewPackage = false, maxRetries) {
|
|
511
|
+
const retries = maxRetries ?? (isNewPackage ? 6 : 3);
|
|
512
|
+
const delaySec = isNewPackage ? 30 : 10;
|
|
495
513
|
let result = runCommand('npm', ['install', '-g', pkgSpec], { cwd, silent: false, showCommand: true });
|
|
496
|
-
for (let attempt = 1; attempt <
|
|
497
|
-
console.log(colors.yellow(` Retrying install (attempt ${attempt + 1}/${
|
|
498
|
-
|
|
514
|
+
for (let attempt = 1; attempt < retries && !result.success; attempt++) {
|
|
515
|
+
console.log(colors.yellow(` Retrying install (attempt ${attempt + 1}/${retries}) in ${delaySec} seconds...`));
|
|
516
|
+
sleepSync(delaySec * 1000);
|
|
499
517
|
result = runCommand('npm', ['install', '-g', pkgSpec], { cwd, silent: false, showCommand: true });
|
|
500
518
|
}
|
|
501
519
|
return result;
|
package/lib.js
CHANGED
|
@@ -1365,11 +1365,24 @@ export function fixVersionTagMismatch(cwd, pkg, verbose = false) {
|
|
|
1365
1365
|
}
|
|
1366
1366
|
return deletedAny;
|
|
1367
1367
|
}
|
|
1368
|
-
/**
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1368
|
+
/** Reliable synchronous sleep — works regardless of stdio/TTY state.
|
|
1369
|
+
* (Windows `timeout /t` exits immediately when stdio is piped, which broke
|
|
1370
|
+
* the previous spawn-based sleep.) */
|
|
1371
|
+
function sleepSync(ms) {
|
|
1372
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
1373
|
+
}
|
|
1374
|
+
/** Wait for a package version to appear on the npm registry.
|
|
1375
|
+
* First-time publishes (brand-new package name) take much longer to
|
|
1376
|
+
* propagate than version bumps — npm has no cached metadata to update,
|
|
1377
|
+
* so the registry/CDN can take several minutes before the package is
|
|
1378
|
+
* resolvable. We wait longer and re-probe `npm view` for the version
|
|
1379
|
+
* string until it shows up (or we hit the cap). */
|
|
1380
|
+
function waitForNpmVersion(pkgName, version, isNewPackage = false, maxWaitMs) {
|
|
1381
|
+
const effectiveMaxWait = maxWaitMs ?? (isNewPackage ? 600000 : 180000);
|
|
1382
|
+
const interval = isNewPackage ? 5000 : 3000;
|
|
1383
|
+
const maxAttempts = Math.ceil(effectiveMaxWait / interval);
|
|
1384
|
+
const suffix = isNewPackage ? ' (new package, may take several minutes)' : '';
|
|
1385
|
+
process.stdout.write(`${timestamp()} Waiting for ${pkgName}@${version} on npm registry${suffix}`);
|
|
1373
1386
|
for (let i = 0; i < maxAttempts; i++) {
|
|
1374
1387
|
const result = spawnSafe('npm', ['view', `${pkgName}@${version}`, 'version'], {
|
|
1375
1388
|
shell: process.platform === 'win32',
|
|
@@ -1381,7 +1394,7 @@ function waitForNpmVersion(pkgName, version, maxWaitMs = 90000) {
|
|
|
1381
1394
|
return true;
|
|
1382
1395
|
}
|
|
1383
1396
|
process.stdout.write('.');
|
|
1384
|
-
|
|
1397
|
+
sleepSync(interval);
|
|
1385
1398
|
}
|
|
1386
1399
|
process.stdout.write(' timed out\n');
|
|
1387
1400
|
return false;
|
|
@@ -1687,12 +1700,17 @@ export function ensureWorkspaceDepModules(rootDir, members, verbose = false) {
|
|
|
1687
1700
|
console.error(colors.dim(r.stderr.split('\n').slice(0, 5).join('\n')));
|
|
1688
1701
|
}
|
|
1689
1702
|
}
|
|
1690
|
-
/** Run npm install -g with retries for registry propagation delay
|
|
1691
|
-
|
|
1703
|
+
/** Run npm install -g with retries for registry propagation delay.
|
|
1704
|
+
* Brand-new packages (first-time publish) take much longer to become
|
|
1705
|
+
* installable than version bumps, so we use longer waits and more
|
|
1706
|
+
* attempts when `isNewPackage` is true. */
|
|
1707
|
+
function installGlobalWithRetry(pkgSpec, cwd, isNewPackage = false, maxRetries) {
|
|
1708
|
+
const retries = maxRetries ?? (isNewPackage ? 6 : 3);
|
|
1709
|
+
const delaySec = isNewPackage ? 30 : 10;
|
|
1692
1710
|
let result = runCommand('npm', ['install', '-g', pkgSpec], { cwd, silent: false, showCommand: true });
|
|
1693
|
-
for (let attempt = 1; attempt <
|
|
1694
|
-
console.log(colors.yellow(` Retrying install (attempt ${attempt + 1}/${
|
|
1695
|
-
|
|
1711
|
+
for (let attempt = 1; attempt < retries && !result.success; attempt++) {
|
|
1712
|
+
console.log(colors.yellow(` Retrying install (attempt ${attempt + 1}/${retries}) in ${delaySec} seconds...`));
|
|
1713
|
+
sleepSync(delaySec * 1000);
|
|
1696
1714
|
result = runCommand('npm', ['install', '-g', pkgSpec], { cwd, silent: false, showCommand: true });
|
|
1697
1715
|
}
|
|
1698
1716
|
return result;
|
|
@@ -2024,9 +2042,18 @@ export function getGitStatus(cwd) {
|
|
|
2024
2042
|
remoteBranch: '',
|
|
2025
2043
|
isBehindRemote: false
|
|
2026
2044
|
};
|
|
2027
|
-
// Check if git repo
|
|
2028
|
-
|
|
2029
|
-
|
|
2045
|
+
// Check if git repo — walk up the directory tree to find the enclosing
|
|
2046
|
+
// .git, like git itself does. Previously this only looked for `.git/` at
|
|
2047
|
+
// cwd, which broke for any subdir inside a repo (e.g., a workspace
|
|
2048
|
+
// member that lives inside a parent monorepo's git tree).
|
|
2049
|
+
let gitDir;
|
|
2050
|
+
try {
|
|
2051
|
+
const result = execSync('git rev-parse --git-dir', { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }).trim();
|
|
2052
|
+
if (!result)
|
|
2053
|
+
return status;
|
|
2054
|
+
gitDir = path.isAbsolute(result) ? result : path.resolve(cwd, result);
|
|
2055
|
+
}
|
|
2056
|
+
catch {
|
|
2030
2057
|
return status;
|
|
2031
2058
|
}
|
|
2032
2059
|
status.isRepo = true;
|
|
@@ -3039,25 +3066,6 @@ export function getToolVersion() {
|
|
|
3039
3066
|
return 'unknown';
|
|
3040
3067
|
}
|
|
3041
3068
|
}
|
|
3042
|
-
/** Offer to add a bin field if missing — returns true if bin was added, false to skip/abort */
|
|
3043
|
-
async function offerAddBin(cwd, pkg) {
|
|
3044
|
-
if (pkg.bin)
|
|
3045
|
-
return true;
|
|
3046
|
-
// Determine likely entry point
|
|
3047
|
-
const mainFile = pkg.main || 'index.js';
|
|
3048
|
-
const cmdName = (pkg.name || path.basename(cwd)).replace(/^@[^/]+\//, '');
|
|
3049
|
-
console.log(colors.yellow('No bin field — this is a library, not a CLI tool.'));
|
|
3050
|
-
console.log(colors.yellow('Libraries should not be installed globally. Use npm install <name> in projects instead.'));
|
|
3051
|
-
const choice = await promptChoice(`Add bin field to make it a CLI tool?\n 1) Yes, use "${cmdName}" → "${mainFile}"\n 2) Skip global install (default)\nChoice:`, ['1', '2', '']);
|
|
3052
|
-
if (choice === '1') {
|
|
3053
|
-
pkg.bin = { [cmdName]: mainFile };
|
|
3054
|
-
writePackageJson(cwd, pkg);
|
|
3055
|
-
console.log(colors.green(`✓ Added bin: { "${cmdName}": "${mainFile}" }`));
|
|
3056
|
-
return true;
|
|
3057
|
-
}
|
|
3058
|
-
// choice is '2' or '' (default) — skip
|
|
3059
|
-
return false;
|
|
3060
|
-
}
|
|
3061
3069
|
/** Perform local-only install (npm install -g .) — extracted for reuse from git-init prompts */
|
|
3062
3070
|
async function doLocalInstall(cwd, options) {
|
|
3063
3071
|
const { dryRun = false, wsl = false } = options;
|
|
@@ -3067,12 +3075,8 @@ async function doLocalInstall(cwd, options) {
|
|
|
3067
3075
|
console.log(colors.blue(`Local install: ${pkgName}@${pkgVersion}`));
|
|
3068
3076
|
console.log(colors.dim('Skipping transform/publish — installing with file: deps as-is'));
|
|
3069
3077
|
if (!pkg.bin) {
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
console.log(colors.yellow('Skipping global install — library packages should not be installed globally.'));
|
|
3073
|
-
console.log(colors.yellow(`To use in projects: npm install ${pkgName}`));
|
|
3074
|
-
return true;
|
|
3075
|
-
}
|
|
3078
|
+
console.log(colors.dim(` (library — skipping global install)`));
|
|
3079
|
+
return true;
|
|
3076
3080
|
}
|
|
3077
3081
|
if (dryRun) {
|
|
3078
3082
|
console.log(' [dry-run] Would run: npm install -g .');
|
|
@@ -3175,7 +3179,10 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3175
3179
|
}
|
|
3176
3180
|
}
|
|
3177
3181
|
console.log('');
|
|
3178
|
-
// -local: skip all transform/publish, just install from local directory with file: deps intact
|
|
3182
|
+
// -local: skip all transform/publish, just install from local directory with file: deps intact.
|
|
3183
|
+
// -local is testing-only and must NOT mutate package.json — packages are meant to be installed
|
|
3184
|
+
// as published public packages by end users; the bin field is whatever the published shape is.
|
|
3185
|
+
// Library members (no bin) are silently skipped: nothing for global install to expose.
|
|
3179
3186
|
if (local) {
|
|
3180
3187
|
const pkg = readPackageJson(cwd);
|
|
3181
3188
|
const pkgName = pkg.name || path.basename(cwd);
|
|
@@ -3183,12 +3190,8 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3183
3190
|
console.log(colors.blue(`Local install: ${pkgName}@${pkgVersion}`));
|
|
3184
3191
|
console.log(colors.dim('Skipping transform/publish — installing with file: deps as-is'));
|
|
3185
3192
|
if (!pkg.bin) {
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
console.log(colors.yellow('Skipping global install — library packages should not be installed globally.'));
|
|
3189
|
-
console.log(colors.yellow(`To use in projects: npm install ${pkgName}`));
|
|
3190
|
-
return true;
|
|
3191
|
-
}
|
|
3193
|
+
console.log(colors.dim(` (library — skipping global install)`));
|
|
3194
|
+
return true;
|
|
3192
3195
|
}
|
|
3193
3196
|
if (dryRun) {
|
|
3194
3197
|
console.log(' [dry-run] Would run: npm install -g .');
|
|
@@ -3633,6 +3636,10 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3633
3636
|
}
|
|
3634
3637
|
// Check npm visibility and current publication status
|
|
3635
3638
|
let currentAccess = checkNpmAccess(pkg.name);
|
|
3639
|
+
// Track whether this name is brand-new on npm so post-publish waits use
|
|
3640
|
+
// longer timeouts — first-time publishes propagate slowly through the
|
|
3641
|
+
// registry/CDN even after `npm publish` reports success.
|
|
3642
|
+
const wasNewPackage = !currentAccess;
|
|
3636
3643
|
let isScoped = pkg.name.startsWith('@');
|
|
3637
3644
|
// Check if public intent was explicitly declared anywhere:
|
|
3638
3645
|
// CLI (--npm public), config file (.globalize.json5), or package.json publishConfig
|
|
@@ -4279,11 +4286,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4279
4286
|
const pkgName = pkg.name;
|
|
4280
4287
|
const pkgVersion = pkg.version;
|
|
4281
4288
|
if (!pkg.bin && (install || link || wsl)) {
|
|
4282
|
-
|
|
4283
|
-
if (!proceed) {
|
|
4284
|
-
console.log(colors.yellow('Skipping global install — library packages should not be installed globally.'));
|
|
4285
|
-
console.log(colors.yellow(`To use in projects: npm install ${pkgName}`));
|
|
4286
|
-
}
|
|
4289
|
+
console.log(colors.dim(` ${pkgName}: library — skipping global install`));
|
|
4287
4290
|
}
|
|
4288
4291
|
if (pkg.bin && (install || link || wsl)) {
|
|
4289
4292
|
if (link) {
|
|
@@ -5079,11 +5082,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
5079
5082
|
let globalInstallOk = false;
|
|
5080
5083
|
let wslInstallOk = false;
|
|
5081
5084
|
if (!updatedPkg.bin && (install || link || wsl)) {
|
|
5082
|
-
|
|
5083
|
-
if (!proceed) {
|
|
5084
|
-
console.log(colors.yellow('Skipping global install — library packages should not be installed globally.'));
|
|
5085
|
-
console.log(colors.yellow(`To use in projects: npm install ${pkgName}`));
|
|
5086
|
-
}
|
|
5085
|
+
console.log(colors.dim(` ${pkgName}: library — skipping global install`));
|
|
5087
5086
|
}
|
|
5088
5087
|
if (updatedPkg.bin && (install || link || wsl)) {
|
|
5089
5088
|
if (link) {
|
|
@@ -5127,8 +5126,8 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
5127
5126
|
else {
|
|
5128
5127
|
console.log(`${timestamp()} Installing globally from registry: ${pkgName}@${pkgVersion}...`);
|
|
5129
5128
|
if (!dryRun) {
|
|
5130
|
-
waitForNpmVersion(pkgName, pkgVersion);
|
|
5131
|
-
const installResult = installGlobalWithRetry(`${pkgName}@${pkgVersion}`, cwd);
|
|
5129
|
+
waitForNpmVersion(pkgName, pkgVersion, wasNewPackage);
|
|
5130
|
+
const installResult = installGlobalWithRetry(`${pkgName}@${pkgVersion}`, cwd, wasNewPackage);
|
|
5132
5131
|
if (installResult.success) {
|
|
5133
5132
|
globalInstallOk = true;
|
|
5134
5133
|
console.log(colors.green(`✓ Installed globally: ${pkgName}@${pkgVersion}`));
|
|
@@ -5157,7 +5156,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
5157
5156
|
const useLocalWsl = link || !!updatedPkg.workspaces;
|
|
5158
5157
|
const wslArgs = useLocalWsl ? ['npm', 'install', '-g', '.'] : ['npm', 'install', '-g', `${pkgName}@${pkgVersion}`];
|
|
5159
5158
|
if (!useLocalWsl)
|
|
5160
|
-
waitForNpmVersion(pkgName, pkgVersion);
|
|
5159
|
+
waitForNpmVersion(pkgName, pkgVersion, wasNewPackage);
|
|
5161
5160
|
console.log(`Installing in WSL${useLocalWsl ? ' (local)' : ' from registry'}: ${pkgName}@${pkgVersion}...`);
|
|
5162
5161
|
if (!dryRun) {
|
|
5163
5162
|
const wslResult = installInWsl(wslArgs, { cwd });
|
|
@@ -5289,6 +5288,26 @@ export async function globalizeWorkspace(rootDir, options = {}, configOptions =
|
|
|
5289
5288
|
console.log(`Packages (${packages.length}): ${packages.map(p => p.name).join(', ')}`);
|
|
5290
5289
|
console.log(`Publish order: ${publishOrder.join(' → ')}`);
|
|
5291
5290
|
console.log('');
|
|
5291
|
+
// Warn if -local / -install are present at the workspace root: those are
|
|
5292
|
+
// component-level concerns. In workspace mode they only act on members
|
|
5293
|
+
// that have a `bin` field; libraries are silently skipped. Telling the
|
|
5294
|
+
// user up front avoids the surprise of "I asked for install but most
|
|
5295
|
+
// packages didn't install."
|
|
5296
|
+
const installSources = [];
|
|
5297
|
+
if (options.local || configOptions.local) {
|
|
5298
|
+
const src = configOptions.local && !options.local ? '.globalize.json5' : 'CLI';
|
|
5299
|
+
installSources.push(`-local (${src})`);
|
|
5300
|
+
}
|
|
5301
|
+
if (options.install || configOptions.install) {
|
|
5302
|
+
const src = configOptions.install && !options.install ? '.globalize.json5' : 'CLI';
|
|
5303
|
+
installSources.push(`-install (${src})`);
|
|
5304
|
+
}
|
|
5305
|
+
if (installSources.length > 0) {
|
|
5306
|
+
console.log(colors.yellow(`! ${installSources.join(', ')} at workspace root — these are component-level flags.`));
|
|
5307
|
+
console.log(colors.yellow(` Only members with a \`bin\` field will install globally; libraries are skipped.`));
|
|
5308
|
+
console.log(colors.yellow(` For per-component control, run npmglobalize inside the component directory.`));
|
|
5309
|
+
console.log('');
|
|
5310
|
+
}
|
|
5292
5311
|
const rootPkg = readPackageJson(rootDir);
|
|
5293
5312
|
// Handle --cleanup: restore deps for all packages and return
|
|
5294
5313
|
if (options.cleanup) {
|
|
@@ -5432,13 +5451,14 @@ export async function globalizeWorkspace(rootDir, options = {}, configOptions =
|
|
|
5432
5451
|
}
|
|
5433
5452
|
}
|
|
5434
5453
|
}
|
|
5454
|
+
const relPath = path.relative(rootDir, pkgInfo.dir).split(path.sep).join('/') || '.';
|
|
5435
5455
|
if (reason) {
|
|
5436
5456
|
flaggedForWork.add(pkgName);
|
|
5437
|
-
console.log(` ${colors.yellow('⟳')} ${pkgName} — ${reason}`);
|
|
5457
|
+
console.log(` ${colors.yellow('⟳')} ${pkgName} — ${reason} ${colors.dim(relPath)}`);
|
|
5438
5458
|
}
|
|
5439
5459
|
else {
|
|
5440
5460
|
skipReasons.set(pkgName, 'clean, published, build fresh');
|
|
5441
|
-
console.log(` ${colors.green('✓')} ${pkgName} — skip (clean, published, build fresh)`);
|
|
5461
|
+
console.log(` ${colors.green('✓')} ${pkgName} — skip (clean, published, build fresh) ${colors.dim(relPath)}`);
|
|
5442
5462
|
}
|
|
5443
5463
|
}
|
|
5444
5464
|
console.log('');
|