@bobfrankston/npmglobalize 1.0.148 → 1.0.150
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 +22 -0
- package/ignorepatterns.json5 +1 -1
- package/lib.d.ts +8 -5
- package/lib.js +210 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -94,6 +94,28 @@ Errors abort (unless `--force`); warnings prompt to continue.
|
|
|
94
94
|
|
|
95
95
|
Skip the prescan with `-no-prescan` / `-nps`.
|
|
96
96
|
|
|
97
|
+
#### Workspace skip prescan
|
|
98
|
+
|
|
99
|
+
In workspace mode, before processing begins, each package is also checked to decide whether it actually needs work. A package is **skipped** (no rebuild, no version bump, no publish) only if **all** of these are true:
|
|
100
|
+
|
|
101
|
+
1. **Working tree clean** — `git status --porcelain .` reports no uncommitted changes for that package's directory.
|
|
102
|
+
2. **Version already on npm** — the version in its `package.json` is published to the registry, and no non-bookkeeping commit (anything outside "Pre-release commit" / "Restore file: dependencies" / "Pre-version cleanup" / "Untrack node_modules") is newer than that version's publish timestamp.
|
|
103
|
+
3. **Build is fresh** — every `.ts` source file (excluding `.d.ts`) has a sibling `.js` whose mtime is ≥ the `.ts` mtime. A missing `.js` or an older `.js` counts as stale.
|
|
104
|
+
4. **No sibling workspace `file:` dep flagged for work** — if another workspace package depends on this one via `file:`, and that dep is being updated in this run, this one is also processed (propagates through the graph in topological order).
|
|
105
|
+
|
|
106
|
+
If any condition fails, the package is processed. The prescan prints one line per package with `⟳` for "will process" (and the reason) or `✓` for "skip". Example:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
⟳ mlproc — uncommitted changes
|
|
110
|
+
⟳ pzip — stale build (index.ts newer than index.js)
|
|
111
|
+
⟳ stage — dep pzip is being updated
|
|
112
|
+
✓ puller — skip (clean, published, build fresh)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The summary row for a skipped package shows `– name v1.0.0 (skipped — already up to date)`.
|
|
116
|
+
|
|
117
|
+
Use `--force` or `--force-publish` to bypass the skip prescan and process every package.
|
|
118
|
+
|
|
97
119
|
**Force republish** all file: dependencies even if versions exist:
|
|
98
120
|
```bash
|
|
99
121
|
npmglobalize --force-publish
|
package/ignorepatterns.json5
CHANGED
package/lib.d.ts
CHANGED
|
@@ -115,6 +115,7 @@ interface WorkspacePackageResult {
|
|
|
115
115
|
success: boolean;
|
|
116
116
|
version?: string;
|
|
117
117
|
error?: string;
|
|
118
|
+
skipped?: boolean;
|
|
118
119
|
}
|
|
119
120
|
/** Aggregate result from workspace orchestration */
|
|
120
121
|
export interface WorkspaceResult {
|
|
@@ -188,13 +189,15 @@ export declare function buildDependencyGraph(packages: Array<{
|
|
|
188
189
|
/** Topological sort with cycle detection. Returns package names in dependency order. */
|
|
189
190
|
export declare function topologicalSort(graph: Map<string, Set<string>>): string[];
|
|
190
191
|
/** Transform file: dependencies to npm versions */
|
|
192
|
+
export type UnpublishedDep = {
|
|
193
|
+
name: string;
|
|
194
|
+
version: string;
|
|
195
|
+
path: string;
|
|
196
|
+
reason: 'new' | 'update';
|
|
197
|
+
};
|
|
191
198
|
export declare function transformDeps(pkg: any, baseDir: string, verbose?: boolean, forcePublish?: boolean): {
|
|
192
199
|
transformed: boolean;
|
|
193
|
-
unpublished:
|
|
194
|
-
name: string;
|
|
195
|
-
version: string;
|
|
196
|
-
path: string;
|
|
197
|
-
}>;
|
|
200
|
+
unpublished: UnpublishedDep[];
|
|
198
201
|
};
|
|
199
202
|
/** A problem discovered by the prescan. */
|
|
200
203
|
export interface PrescanIssue {
|
package/lib.js
CHANGED
|
@@ -810,6 +810,49 @@ function hasUnpublishedTransitiveDeps(packageName, pkg, baseDir, verbose) {
|
|
|
810
810
|
}
|
|
811
811
|
return false;
|
|
812
812
|
}
|
|
813
|
+
/** Walk a dir and return true if any `.ts` source has mtime newer than its sibling `.js`
|
|
814
|
+
* (or the `.js` is missing). Skips node_modules/prev/cruft/.git and declaration files. */
|
|
815
|
+
function hasStaleBuild(rootDir) {
|
|
816
|
+
const SKIP = new Set(['node_modules', 'prev', 'cruft', '.git']);
|
|
817
|
+
function walk(dir) {
|
|
818
|
+
let entries;
|
|
819
|
+
try {
|
|
820
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
821
|
+
}
|
|
822
|
+
catch {
|
|
823
|
+
return { stale: false };
|
|
824
|
+
}
|
|
825
|
+
for (const e of entries) {
|
|
826
|
+
const full = path.join(dir, e.name);
|
|
827
|
+
if (e.isDirectory()) {
|
|
828
|
+
if (SKIP.has(e.name.toLowerCase()))
|
|
829
|
+
continue;
|
|
830
|
+
const sub = walk(full);
|
|
831
|
+
if (sub.stale)
|
|
832
|
+
return sub;
|
|
833
|
+
}
|
|
834
|
+
else if (e.isFile() && e.name.endsWith('.ts') && !e.name.endsWith('.d.ts')) {
|
|
835
|
+
const jsPath = full.slice(0, -3) + '.js';
|
|
836
|
+
try {
|
|
837
|
+
const tsStat = fs.statSync(full);
|
|
838
|
+
let jsStat;
|
|
839
|
+
try {
|
|
840
|
+
jsStat = fs.statSync(jsPath);
|
|
841
|
+
}
|
|
842
|
+
catch {
|
|
843
|
+
return { stale: true, example: `${e.name} (no .js)` };
|
|
844
|
+
}
|
|
845
|
+
if (tsStat.mtimeMs > jsStat.mtimeMs) {
|
|
846
|
+
return { stale: true, example: `${e.name} newer than ${path.basename(jsPath)}` };
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
catch { /* unreadable — ignore */ }
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return { stale: false };
|
|
853
|
+
}
|
|
854
|
+
return walk(rootDir);
|
|
855
|
+
}
|
|
813
856
|
/** Check if local package directory has changes newer than the npm-published version.
|
|
814
857
|
* Detects uncommitted changes or commits made after the version was published. */
|
|
815
858
|
function hasLocalChanges(packageName, version, targetPath, verbose) {
|
|
@@ -863,7 +906,6 @@ function hasLocalChanges(packageName, version, targetPath, verbose) {
|
|
|
863
906
|
return false;
|
|
864
907
|
}
|
|
865
908
|
}
|
|
866
|
-
/** Transform file: dependencies to npm versions */
|
|
867
909
|
export function transformDeps(pkg, baseDir, verbose = false, forcePublish = false) {
|
|
868
910
|
let transformed = false;
|
|
869
911
|
const unpublished = [];
|
|
@@ -894,7 +936,7 @@ export function transformDeps(pkg, baseDir, verbose = false, forcePublish = fals
|
|
|
894
936
|
// Check if this version exists on npm (or if force publish)
|
|
895
937
|
const versionExists = forcePublish ? false : checkVersionExists(name, targetVersion);
|
|
896
938
|
if (!versionExists) {
|
|
897
|
-
unpublished.push({ name, version: targetVersion, path: targetPath });
|
|
939
|
+
unpublished.push({ name, version: targetVersion, path: targetPath, reason: forcePublish ? 'update' : 'new' });
|
|
898
940
|
if (forcePublish) {
|
|
899
941
|
console.log(colors.yellow(` ⟳ ${name}@${targetVersion} will be republished (--force-publish)`));
|
|
900
942
|
}
|
|
@@ -908,15 +950,15 @@ export function transformDeps(pkg, baseDir, verbose = false, forcePublish = fals
|
|
|
908
950
|
}
|
|
909
951
|
// Check transitive file: deps — if any are unpublished, this dep needs republishing
|
|
910
952
|
if (hasUnpublishedTransitiveDeps(name, targetPkg, targetPath, verbose)) {
|
|
911
|
-
unpublished.push({ name, version: targetVersion, path: targetPath });
|
|
953
|
+
unpublished.push({ name, version: targetVersion, path: targetPath, reason: 'update' });
|
|
912
954
|
console.log(colors.yellow(` ⟳ ${name}@${targetVersion} has unpublished transitive deps — will republish`));
|
|
913
955
|
}
|
|
914
956
|
else if (hasLocalChanges(name, targetVersion, targetPath, verbose)) {
|
|
915
|
-
unpublished.push({ name, version: targetVersion, path: targetPath });
|
|
957
|
+
unpublished.push({ name, version: targetVersion, path: targetPath, reason: 'update' });
|
|
916
958
|
console.log(colors.yellow(` ⟳ ${name}@${targetVersion} has local changes not on npm — will republish`));
|
|
917
959
|
}
|
|
918
960
|
else if (checkIgnoreFiles(targetPath, { verbose }).securityChanges.length > 0) {
|
|
919
|
-
unpublished.push({ name, version: targetVersion, path: targetPath });
|
|
961
|
+
unpublished.push({ name, version: targetVersion, path: targetPath, reason: 'update' });
|
|
920
962
|
console.log(colors.yellow(` ⟳ ${name}@${targetVersion} has missing ignore patterns — will republish`));
|
|
921
963
|
}
|
|
922
964
|
}
|
|
@@ -2349,6 +2391,13 @@ export async function initGit(cwd, visibility, dryRun) {
|
|
|
2349
2391
|
if (staged.status !== 0) {
|
|
2350
2392
|
runCommandOrThrow('git', ['commit', '-m', 'Initial commit'], { cwd });
|
|
2351
2393
|
}
|
|
2394
|
+
// Guarantee HEAD exists — `gh repo create --push` refuses an empty repo.
|
|
2395
|
+
// Can happen if the working tree had nothing to stage (everything ignored, etc.).
|
|
2396
|
+
const hasHead = spawnSafe('git', ['rev-parse', '--verify', 'HEAD'], { cwd, stdio: 'pipe' });
|
|
2397
|
+
if (hasHead.status !== 0) {
|
|
2398
|
+
console.log(colors.yellow(' No commits yet — creating an empty initial commit so --push can proceed.'));
|
|
2399
|
+
runCommandOrThrow('git', ['commit', '--allow-empty', '-m', 'Initial commit'], { cwd });
|
|
2400
|
+
}
|
|
2352
2401
|
// Create GitHub repo (or link to existing one)
|
|
2353
2402
|
const visFlag = visibility === 'private' ? '--private' : '--public';
|
|
2354
2403
|
const createResult = runCommand('gh', ['repo', 'create', repoName, visFlag, '--source=.', '--push'], { cwd, silent: true });
|
|
@@ -3137,9 +3186,6 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3137
3186
|
}
|
|
3138
3187
|
// Don't set "private": true in package.json - that blocks all publishing
|
|
3139
3188
|
console.log(`Package '${pkg.name}' will publish as PRIVATE (restricted access).`);
|
|
3140
|
-
if (!currentAccess) {
|
|
3141
|
-
console.log(colors.dim(` First publish - requires paid npm account`));
|
|
3142
|
-
}
|
|
3143
3189
|
}
|
|
3144
3190
|
else if (effectiveNpmVisibility === 'public') {
|
|
3145
3191
|
// User explicitly wants public (or confirmed via prompt)
|
|
@@ -3337,21 +3383,59 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3337
3383
|
}
|
|
3338
3384
|
console.log('');
|
|
3339
3385
|
}
|
|
3340
|
-
//
|
|
3386
|
+
// Publish/update dependencies.
|
|
3387
|
+
// - reason='update': already on npm but stale (local changes, broken transitive, missing ignore) — auto-publish always
|
|
3388
|
+
// - reason='new' : version not on npm yet — auto-publish if --publish-deps, else prompt
|
|
3341
3389
|
if (transformResult.unpublished.length > 0 && !noPublish) {
|
|
3390
|
+
const updateDepsList = transformResult.unpublished.filter(d => d.reason === 'update');
|
|
3391
|
+
const newDepsList = transformResult.unpublished.filter(d => d.reason === 'new');
|
|
3342
3392
|
console.log('');
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3393
|
+
if (updateDepsList.length > 0) {
|
|
3394
|
+
console.log(colors.yellow('Dependencies to update (already on npm, auto-republishing):'));
|
|
3395
|
+
for (const { name, version, path } of updateDepsList) {
|
|
3396
|
+
console.log(colors.yellow(` ${name}@${version} (${path})`));
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
if (newDepsList.length > 0) {
|
|
3400
|
+
console.log(colors.yellow('New dependencies to publish:'));
|
|
3401
|
+
for (const { name, version, path } of newDepsList) {
|
|
3402
|
+
console.log(colors.yellow(` ${name}@${version} (${path})`));
|
|
3403
|
+
}
|
|
3346
3404
|
}
|
|
3347
3405
|
console.log('');
|
|
3348
3406
|
console.log('Dependency tree (✓ = on npm, ✗ = needs publishing):');
|
|
3349
3407
|
printDepTree(cwd);
|
|
3350
3408
|
console.log('');
|
|
3351
|
-
|
|
3409
|
+
// Decide which deps to publish now.
|
|
3410
|
+
// Updates always run. New deps run if --publish-deps; otherwise prompt.
|
|
3411
|
+
let depsToPublish = [...updateDepsList];
|
|
3412
|
+
let proceedWithNew = newDepsList.length === 0 || publishDeps;
|
|
3413
|
+
if (newDepsList.length > 0 && !publishDeps) {
|
|
3414
|
+
console.log(colors.yellow('Options for NEW dependencies:'));
|
|
3415
|
+
console.log(colors.yellow(' 1. Publish them manually first'));
|
|
3416
|
+
console.log(colors.yellow(' 2. Use --publish-deps (-pd) to publish them automatically'));
|
|
3417
|
+
console.log(colors.yellow(' 3. Use -npd (--no-publish-deps) to skip dependency publishing'));
|
|
3418
|
+
console.log(colors.dim(' (--force controls error-continuation only; it does not imply consent to publish new deps)'));
|
|
3419
|
+
console.log('');
|
|
3420
|
+
if (publishDepsYes) {
|
|
3421
|
+
proceedWithNew = true;
|
|
3422
|
+
}
|
|
3423
|
+
else {
|
|
3424
|
+
proceedWithNew = await confirm('Publish new dependencies now?', false);
|
|
3425
|
+
if (!proceedWithNew) {
|
|
3426
|
+
const newList = newDepsList.map(d => `${d.name}@${d.version}`).join(', ');
|
|
3427
|
+
recordBuildIssue(pkg.name || cwd, 'error', `Declined publishing new deps (${newList}). Rerun with -pd to auto-publish, or publish them manually first.`);
|
|
3428
|
+
return false;
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
if (proceedWithNew) {
|
|
3433
|
+
depsToPublish.push(...newDepsList);
|
|
3434
|
+
}
|
|
3435
|
+
if (depsToPublish.length > 0) {
|
|
3352
3436
|
const action = forcePublish ? 'Publishing/updating' : 'Publishing';
|
|
3353
|
-
console.log(`${action} file: dependencies first
|
|
3354
|
-
for (const { name, version, path } of
|
|
3437
|
+
console.log(`${action} file: dependencies first...`);
|
|
3438
|
+
for (const { name, version, path } of depsToPublish) {
|
|
3355
3439
|
console.log('');
|
|
3356
3440
|
console.log(colors.yellow(`━━━ Publishing ${name}@${version} ━━━`));
|
|
3357
3441
|
if (!dryRun) {
|
|
@@ -3393,20 +3477,6 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3393
3477
|
console.log('');
|
|
3394
3478
|
console.log(colors.green('✓ All dependencies published'));
|
|
3395
3479
|
}
|
|
3396
|
-
else {
|
|
3397
|
-
console.log(colors.yellow('Options:'));
|
|
3398
|
-
console.log(colors.yellow(' 1. Publish them manually first'));
|
|
3399
|
-
console.log(colors.yellow(' 2. Use --publish-deps to publish them automatically'));
|
|
3400
|
-
console.log(colors.yellow(' 3. Use --force to continue anyway (NOT RECOMMENDED)'));
|
|
3401
|
-
console.log(colors.yellow(' 4. Use -npd (--no-publish-deps) to skip dependency publishing'));
|
|
3402
|
-
console.log('');
|
|
3403
|
-
if (!force && !publishDepsYes) {
|
|
3404
|
-
const shouldContinue = await confirm('Continue with unpublished dependencies?', false);
|
|
3405
|
-
if (!shouldContinue) {
|
|
3406
|
-
return false;
|
|
3407
|
-
}
|
|
3408
|
-
}
|
|
3409
|
-
}
|
|
3410
3480
|
}
|
|
3411
3481
|
if (!dryRun) {
|
|
3412
3482
|
writePackageJson(cwd, pkg);
|
|
@@ -3878,7 +3948,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3878
3948
|
}
|
|
3879
3949
|
catch (error) {
|
|
3880
3950
|
let autoFixed = false;
|
|
3881
|
-
console.error(colors.red(
|
|
3951
|
+
console.error(colors.red(`ERROR: Version bump failed in ${cwd} (${pkg.name || '<unnamed>'}):`), error.message);
|
|
3882
3952
|
// Show additional error details if available
|
|
3883
3953
|
if (error.stderr || error.stdout || error.code) {
|
|
3884
3954
|
if (error.stderr)
|
|
@@ -4035,8 +4105,39 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4035
4105
|
return false;
|
|
4036
4106
|
}
|
|
4037
4107
|
}
|
|
4108
|
+
else if (/paths are ignored by one of your \.gitignore files/i.test(combinedOutput)) {
|
|
4109
|
+
// git add refused to stage files (usually package.json) because a
|
|
4110
|
+
// .gitignore rule matches. Ask git which exact line is to blame.
|
|
4111
|
+
const ignoredMatch = combinedOutput.match(/paths are ignored by one of your \.gitignore files:\s*\n([\s\S]*?)(?:hint:|$)/i);
|
|
4112
|
+
const ignoredFiles = (ignoredMatch ? ignoredMatch[1] : '')
|
|
4113
|
+
.split('\n').map(s => s.trim()).filter(Boolean);
|
|
4114
|
+
console.error(colors.red(`\n.gitignore in ${cwd} is blocking required file(s):`));
|
|
4115
|
+
for (const f of ignoredFiles) {
|
|
4116
|
+
const chk = spawnSafe('git', ['-C', cwd, 'check-ignore', '-v', f], { encoding: 'utf-8', stdio: 'pipe' });
|
|
4117
|
+
if (chk.status === 0 && chk.stdout?.trim()) {
|
|
4118
|
+
// Output format: "<source>:<line>:<pattern>\t<file>"
|
|
4119
|
+
const m = chk.stdout.trim().match(/^(.+?):(\d+):(.+?)\t(.+)$/);
|
|
4120
|
+
if (m) {
|
|
4121
|
+
const [, src, line, pattern, file] = m;
|
|
4122
|
+
console.error(colors.red(` ${file} — matched by ${src}:${line} pattern: ${pattern}`));
|
|
4123
|
+
if (pattern.trim() === '*') {
|
|
4124
|
+
console.error(colors.yellow(` → Line ${line} is a bare "*" which ignores EVERY file. Delete it.`));
|
|
4125
|
+
}
|
|
4126
|
+
}
|
|
4127
|
+
else {
|
|
4128
|
+
console.error(colors.red(` ${f}`));
|
|
4129
|
+
console.error(colors.dim(` ${chk.stdout.trim()}`));
|
|
4130
|
+
}
|
|
4131
|
+
}
|
|
4132
|
+
else {
|
|
4133
|
+
console.error(colors.red(` ${f}`));
|
|
4134
|
+
}
|
|
4135
|
+
}
|
|
4136
|
+
console.error(colors.yellow(`\nEdit ${path.join(cwd, '.gitignore')} to remove/narrow the blocking pattern,`));
|
|
4137
|
+
console.error(colors.yellow(`or exempt with an explicit negation, e.g.: !package.json`));
|
|
4138
|
+
}
|
|
4038
4139
|
else if (error.message?.includes('unknown git error')) {
|
|
4039
|
-
console.error(colors.yellow(
|
|
4140
|
+
console.error(colors.yellow(`Unknown git error in ${cwd} — check git hooks, signing, or permissions`));
|
|
4040
4141
|
}
|
|
4041
4142
|
else if (combinedOutput.includes('gh013') || combinedOutput.includes('push protection') || combinedOutput.includes('push declined due to repository rule')) {
|
|
4042
4143
|
// GitHub push protection blocked a push (from postversion script)
|
|
@@ -4607,9 +4708,84 @@ export async function globalizeWorkspace(rootDir, options = {}, configOptions =
|
|
|
4607
4708
|
}
|
|
4608
4709
|
}
|
|
4609
4710
|
}
|
|
4711
|
+
// Prescan: decide which packages actually need processing so we don't waste
|
|
4712
|
+
// time rebuilding+republishing ones with no relevant changes.
|
|
4713
|
+
// A package is SKIPPED only if ALL of:
|
|
4714
|
+
// - working tree clean (no uncommitted changes)
|
|
4715
|
+
// - current version is on npm AND no commits newer than its publish time
|
|
4716
|
+
// - build not stale (every .ts source has a .js no older than it)
|
|
4717
|
+
// - no sibling workspace file: dep was itself flagged for processing
|
|
4718
|
+
const flaggedForWork = new Set();
|
|
4719
|
+
const skipReasons = new Map();
|
|
4720
|
+
if (!options.force && !options.forcePublish) {
|
|
4721
|
+
console.log(colors.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
4722
|
+
console.log(` Prescan: checking ${filteredOrder.length} package(s)...`);
|
|
4723
|
+
console.log(colors.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
4724
|
+
const wsDeps = new Map(); // pkg → workspace-sibling file: deps
|
|
4725
|
+
for (const pkgInfo of packages) {
|
|
4726
|
+
const sibs = new Set();
|
|
4727
|
+
const deps = { ...pkgInfo.pkg.dependencies, ...pkgInfo.pkg.devDependencies };
|
|
4728
|
+
for (const [dn, dv] of Object.entries(deps)) {
|
|
4729
|
+
if (typeof dv === 'string' && dv.startsWith('file:') && wsNameSet.has(dn)) {
|
|
4730
|
+
sibs.add(dn);
|
|
4731
|
+
}
|
|
4732
|
+
}
|
|
4733
|
+
wsDeps.set(pkgInfo.name, sibs);
|
|
4734
|
+
}
|
|
4735
|
+
for (const pkgName of filteredOrder) {
|
|
4736
|
+
const pkgInfo = packages.find(p => p.name === pkgName);
|
|
4737
|
+
if (!pkgInfo)
|
|
4738
|
+
continue;
|
|
4739
|
+
let reason;
|
|
4740
|
+
const status = spawnSafe('git', ['-C', pkgInfo.dir, 'status', '--porcelain', '.'], { encoding: 'utf-8', stdio: 'pipe', shell: true });
|
|
4741
|
+
if (status.status === 0 && status.stdout.trim()) {
|
|
4742
|
+
reason = 'uncommitted changes';
|
|
4743
|
+
}
|
|
4744
|
+
else {
|
|
4745
|
+
const version = pkgInfo.pkg.version;
|
|
4746
|
+
if (!checkVersionExists(pkgInfo.name, version)) {
|
|
4747
|
+
reason = `v${version} not on npm`;
|
|
4748
|
+
}
|
|
4749
|
+
else if (hasLocalChanges(pkgInfo.name, version, pkgInfo.dir, false)) {
|
|
4750
|
+
reason = 'commits since last publish';
|
|
4751
|
+
}
|
|
4752
|
+
else {
|
|
4753
|
+
const stale = hasStaleBuild(pkgInfo.dir);
|
|
4754
|
+
if (stale.stale) {
|
|
4755
|
+
reason = `stale build (${stale.example})`;
|
|
4756
|
+
}
|
|
4757
|
+
else {
|
|
4758
|
+
const flaggedSib = [...(wsDeps.get(pkgName) || [])].find(s => flaggedForWork.has(s));
|
|
4759
|
+
if (flaggedSib)
|
|
4760
|
+
reason = `dep ${flaggedSib} is being updated`;
|
|
4761
|
+
}
|
|
4762
|
+
}
|
|
4763
|
+
}
|
|
4764
|
+
if (reason) {
|
|
4765
|
+
flaggedForWork.add(pkgName);
|
|
4766
|
+
console.log(` ${colors.yellow('⟳')} ${pkgName} — ${reason}`);
|
|
4767
|
+
}
|
|
4768
|
+
else {
|
|
4769
|
+
skipReasons.set(pkgName, 'clean, published, build fresh');
|
|
4770
|
+
console.log(` ${colors.green('✓')} ${pkgName} — skip (clean, published, build fresh)`);
|
|
4771
|
+
}
|
|
4772
|
+
}
|
|
4773
|
+
console.log('');
|
|
4774
|
+
if (flaggedForWork.size === 0) {
|
|
4775
|
+
console.log(colors.green('Nothing to do — all packages up to date.'));
|
|
4776
|
+
console.log('');
|
|
4777
|
+
return { success: true, packages: [], publishOrder };
|
|
4778
|
+
}
|
|
4779
|
+
console.log(colors.dim(`Prescan: ${flaggedForWork.size} to process, ${skipReasons.size} skip. (Use --force to process all.)`));
|
|
4780
|
+
console.log('');
|
|
4781
|
+
}
|
|
4610
4782
|
// Process each package in dependency order
|
|
4611
4783
|
const results = [];
|
|
4612
4784
|
for (const pkgName of filteredOrder) {
|
|
4785
|
+
if (skipReasons.has(pkgName)) {
|
|
4786
|
+
results.push({ name: pkgName, dir: packages.find(p => p.name === pkgName).dir, success: true, version: packages.find(p => p.name === pkgName).pkg.version, skipped: true });
|
|
4787
|
+
continue;
|
|
4788
|
+
}
|
|
4613
4789
|
const pkgInfo = packages.find(p => p.name === pkgName);
|
|
4614
4790
|
if (!pkgInfo)
|
|
4615
4791
|
continue;
|
|
@@ -4662,10 +4838,11 @@ export async function globalizeWorkspace(rootDir, options = {}, configOptions =
|
|
|
4662
4838
|
console.log(allSuccess ? colors.green('✓ Workspace Summary') : colors.red('✗ Workspace Summary'));
|
|
4663
4839
|
console.log(colors.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
4664
4840
|
for (const r of results) {
|
|
4665
|
-
const status = r.success ? colors.green('✓') : colors.red('✗');
|
|
4841
|
+
const status = r.skipped ? colors.dim('–') : (r.success ? colors.green('✓') : colors.red('✗'));
|
|
4666
4842
|
const ver = r.version ? ` v${r.version}` : '';
|
|
4667
4843
|
const err = r.error ? colors.red(` (${r.error})`) : '';
|
|
4668
|
-
|
|
4844
|
+
const tag = r.skipped ? colors.dim(' (skipped — already up to date)') : '';
|
|
4845
|
+
console.log(` ${status} ${r.name}${ver}${err}${tag}`);
|
|
4669
4846
|
}
|
|
4670
4847
|
console.log(colors.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
4671
4848
|
console.log('');
|