@bobfrankston/npmglobalize 1.0.152 → 1.0.154
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 +5 -0
- package/cli.js +2 -1
- package/lib.d.ts +6 -0
- package/lib.js +92 -13
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -350,6 +350,11 @@ Workspace mode is auto-detected when run from a root with `"private": true` and
|
|
|
350
350
|
-asis Skip ignore file checks (or set "asis": true in .globalize.json5)
|
|
351
351
|
-fix-tags Automatically fix version/tag mismatches
|
|
352
352
|
-rebase Automatically rebase if local is behind remote
|
|
353
|
+
-clean-nested-modules, -clean-nested
|
|
354
|
+
Before npm pack, wipe node_modules/ inside each file: dep
|
|
355
|
+
target. Fixes arborist "Cannot read properties of null"
|
|
356
|
+
crashes caused by sibling file: deps with nested
|
|
357
|
+
node_modules. Suggested automatically when the error hits.
|
|
353
358
|
-show Show package.json dependency changes
|
|
354
359
|
-package, -pkg Update package.json scripts to use npmglobalize (see below)
|
|
355
360
|
-h, -help Show help
|
package/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* npmglobalize CLI - Transform file: dependencies to npm versions for publishing
|
|
4
4
|
*/
|
|
5
|
-
import { globalize, globalizeWorkspace, installCleanupHandlers, readConfig, readPackageJson, readUserNpmConfig, writeConfig, writePackageJson, confirm, getBuildIssues, clearBuildIssues, recordBuildIssue, extractFirstTscError } from './lib.js';
|
|
5
|
+
import { globalize, globalizeWorkspace, installCleanupHandlers, readConfig, readPackageJson, readUserNpmConfig, writeConfig, writePackageJson, confirm, getBuildIssues, clearBuildIssues, recordBuildIssue, extractFirstTscError, ensureFileDepModules } from './lib.js';
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import { styleText } from 'util';
|
|
@@ -423,6 +423,7 @@ export async function main() {
|
|
|
423
423
|
}
|
|
424
424
|
}
|
|
425
425
|
if (pkg.scripts?.build) {
|
|
426
|
+
ensureFileDepModules(cwd, !!cliOptions.verbose);
|
|
426
427
|
const { spawnSync } = await import('child_process');
|
|
427
428
|
console.log(`Building ${cwd}...`);
|
|
428
429
|
const buildResult = spawnSync('npm', ['run', 'build'], {
|
package/lib.d.ts
CHANGED
|
@@ -234,6 +234,12 @@ export declare function parseVersionTag(tag: string): number[] | null;
|
|
|
234
234
|
export declare function compareVersions(a: number[], b: number[]): number;
|
|
235
235
|
/** Fix version/tag mismatches */
|
|
236
236
|
export declare function fixVersionTagMismatch(cwd: string, pkg: any, verbose?: boolean): boolean;
|
|
237
|
+
/** Walk `file:` deps transitively and run `npm install` in any target whose
|
|
238
|
+
* package.json declares deps but has no `node_modules/`. Recovers from prior
|
|
239
|
+
* aborted runs (or pre-1.0.153 `--clean-nested-modules` damage) so the
|
|
240
|
+
* upcoming build/pack can resolve transitive package imports.
|
|
241
|
+
* Cycle-safe via the shared `visited` set. */
|
|
242
|
+
export declare function ensureFileDepModules(cwd: string, verbose?: boolean, visited?: Set<string>): void;
|
|
237
243
|
/** Run a command and return success status */
|
|
238
244
|
export declare function runCommand(cmd: string, args: string[], options?: {
|
|
239
245
|
silent?: boolean;
|
package/lib.js
CHANGED
|
@@ -1262,12 +1262,10 @@ function waitForNpmVersion(pkgName, version, maxWaitMs = 90000) {
|
|
|
1262
1262
|
process.stdout.write(' timed out\n');
|
|
1263
1263
|
return false;
|
|
1264
1264
|
}
|
|
1265
|
-
/** Delete `node_modules/` inside each `file:` dep target.
|
|
1266
|
-
* Works around arborist crashes during `npm pack` when sibling `file:` deps
|
|
1267
|
-
* have their own populated `node_modules/`. Returns the names of cleaned deps. */
|
|
1268
1265
|
function cleanNestedDepModules(pkg, cwd, verbose) {
|
|
1269
|
-
const
|
|
1266
|
+
const stashed = [];
|
|
1270
1267
|
const seen = new Set();
|
|
1268
|
+
const suffix = `.npmglobalize-stash-${process.pid}`;
|
|
1271
1269
|
for (const key of ['.dependencies', '.devDependencies', 'dependencies', 'devDependencies']) {
|
|
1272
1270
|
const deps = pkg?.[key];
|
|
1273
1271
|
if (!deps || typeof deps !== 'object')
|
|
@@ -1282,18 +1280,90 @@ function cleanNestedDepModules(pkg, cwd, verbose) {
|
|
|
1282
1280
|
if (!fs.existsSync(nm))
|
|
1283
1281
|
continue;
|
|
1284
1282
|
seen.add(name);
|
|
1283
|
+
const backup = nm + suffix;
|
|
1285
1284
|
try {
|
|
1286
|
-
fs.
|
|
1287
|
-
|
|
1285
|
+
if (fs.existsSync(backup))
|
|
1286
|
+
fs.rmSync(backup, { recursive: true, force: true });
|
|
1287
|
+
fs.renameSync(nm, backup);
|
|
1288
|
+
stashed.push({ name, nm, backup });
|
|
1288
1289
|
if (verbose)
|
|
1289
|
-
console.log(colors.dim(`
|
|
1290
|
+
console.log(colors.dim(` stashed ${nm} -> ${path.basename(backup)}`));
|
|
1290
1291
|
}
|
|
1291
1292
|
catch (e) {
|
|
1292
|
-
console.error(colors.yellow(` warning: could not
|
|
1293
|
+
console.error(colors.yellow(` warning: could not stash ${nm}: ${e.message}`));
|
|
1293
1294
|
}
|
|
1294
1295
|
}
|
|
1295
1296
|
}
|
|
1296
|
-
return
|
|
1297
|
+
return stashed;
|
|
1298
|
+
}
|
|
1299
|
+
/** Restore `node_modules/` previously moved aside by `cleanNestedDepModules`.
|
|
1300
|
+
* Safe to call multiple times; missing backups are skipped. */
|
|
1301
|
+
function restoreNestedDepModules(stashed, verbose) {
|
|
1302
|
+
for (const s of stashed) {
|
|
1303
|
+
try {
|
|
1304
|
+
if (!fs.existsSync(s.backup))
|
|
1305
|
+
continue;
|
|
1306
|
+
if (fs.existsSync(s.nm))
|
|
1307
|
+
fs.rmSync(s.nm, { recursive: true, force: true });
|
|
1308
|
+
fs.renameSync(s.backup, s.nm);
|
|
1309
|
+
if (verbose)
|
|
1310
|
+
console.log(colors.dim(` restored ${s.nm}`));
|
|
1311
|
+
}
|
|
1312
|
+
catch (e) {
|
|
1313
|
+
console.error(colors.yellow(` warning: could not restore ${s.nm}: ${e.message}`));
|
|
1314
|
+
console.error(colors.yellow(` backup remains at ${s.backup} — restore manually`));
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
/** Walk `file:` deps transitively and run `npm install` in any target whose
|
|
1319
|
+
* package.json declares deps but has no `node_modules/`. Recovers from prior
|
|
1320
|
+
* aborted runs (or pre-1.0.153 `--clean-nested-modules` damage) so the
|
|
1321
|
+
* upcoming build/pack can resolve transitive package imports.
|
|
1322
|
+
* Cycle-safe via the shared `visited` set. */
|
|
1323
|
+
export function ensureFileDepModules(cwd, verbose = false, visited = new Set()) {
|
|
1324
|
+
const abs = path.resolve(cwd);
|
|
1325
|
+
if (visited.has(abs))
|
|
1326
|
+
return;
|
|
1327
|
+
visited.add(abs);
|
|
1328
|
+
let pkg;
|
|
1329
|
+
try {
|
|
1330
|
+
pkg = readPackageJson(cwd);
|
|
1331
|
+
}
|
|
1332
|
+
catch {
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
for (const key of ['dependencies', 'devDependencies']) {
|
|
1336
|
+
const deps = pkg?.[key];
|
|
1337
|
+
if (!deps || typeof deps !== 'object')
|
|
1338
|
+
continue;
|
|
1339
|
+
for (const [name, spec] of Object.entries(deps)) {
|
|
1340
|
+
if (typeof spec !== 'string' || !spec.startsWith('file:'))
|
|
1341
|
+
continue;
|
|
1342
|
+
const target = path.resolve(cwd, spec.slice('file:'.length));
|
|
1343
|
+
if (!fs.existsSync(path.join(target, 'package.json')))
|
|
1344
|
+
continue;
|
|
1345
|
+
let targetPkg;
|
|
1346
|
+
try {
|
|
1347
|
+
targetPkg = readPackageJson(target);
|
|
1348
|
+
}
|
|
1349
|
+
catch {
|
|
1350
|
+
continue;
|
|
1351
|
+
}
|
|
1352
|
+
const hasDeps = (targetPkg?.dependencies && Object.keys(targetPkg.dependencies).length > 0) ||
|
|
1353
|
+
(targetPkg?.devDependencies && Object.keys(targetPkg.devDependencies).length > 0);
|
|
1354
|
+
const nm = path.join(target, 'node_modules');
|
|
1355
|
+
if (hasDeps && !fs.existsSync(nm)) {
|
|
1356
|
+
console.log(colors.yellow(`↻ restoring node_modules in ${name} (${target})`));
|
|
1357
|
+
const r = runCommand('npm', ['install'], { cwd: target, silent: !verbose });
|
|
1358
|
+
if (!r.success) {
|
|
1359
|
+
console.error(colors.red(` ✗ npm install failed in ${target}`));
|
|
1360
|
+
if (r.stderr)
|
|
1361
|
+
console.error(colors.dim(r.stderr.split('\n').slice(0, 5).join('\n')));
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
ensureFileDepModules(target, verbose, visited);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1297
1367
|
}
|
|
1298
1368
|
/** Run npm install -g with retries for registry propagation delay */
|
|
1299
1369
|
function installGlobalWithRetry(pkgSpec, cwd, maxRetries = 3) {
|
|
@@ -3041,6 +3111,8 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
3041
3111
|
const pkg = readPackageJson(cwd);
|
|
3042
3112
|
// Run build step if package.json has a build script (skip if CLI already built)
|
|
3043
3113
|
if (pkg.scripts?.build && !options._fromCli) {
|
|
3114
|
+
if (!dryRun)
|
|
3115
|
+
ensureFileDepModules(cwd, verbose);
|
|
3044
3116
|
console.log(`${timestamp()} Running build...`);
|
|
3045
3117
|
if (!dryRun) {
|
|
3046
3118
|
// Always capture output so we can extract tsc errors for the summary
|
|
@@ -4270,11 +4342,14 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4270
4342
|
if (verbose) {
|
|
4271
4343
|
console.log(colors.green(`✓ Authenticated as ${authStatus.username}`));
|
|
4272
4344
|
}
|
|
4273
|
-
// Optionally
|
|
4345
|
+
// Optionally stash nested node_modules in file: dep targets before pack
|
|
4346
|
+
// (arborist crashes when sibling file: deps have populated node_modules).
|
|
4347
|
+
// Restored immediately after pack so symlinked file: deps stay runnable.
|
|
4348
|
+
let stashedDepModules = [];
|
|
4274
4349
|
if (options.cleanNestedModules) {
|
|
4275
|
-
|
|
4276
|
-
if (
|
|
4277
|
-
console.log(colors.yellow(` Cleaned node_modules in ${
|
|
4350
|
+
stashedDepModules = cleanNestedDepModules(pkg, cwd, verbose);
|
|
4351
|
+
if (stashedDepModules.length > 0) {
|
|
4352
|
+
console.log(colors.yellow(` Cleaned node_modules in ${stashedDepModules.length} file: dep target(s): ${stashedDepModules.map(s => s.name).join(', ')}`));
|
|
4278
4353
|
}
|
|
4279
4354
|
else if (verbose) {
|
|
4280
4355
|
console.log(colors.dim(' --clean-nested-modules: nothing to clean'));
|
|
@@ -4282,6 +4357,10 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
|
|
|
4282
4357
|
}
|
|
4283
4358
|
// Create tarball first
|
|
4284
4359
|
const packResult = runCommand('npm', ['pack'], { cwd, silent: true });
|
|
4360
|
+
// Restore stashed node_modules now that pack is done — must happen
|
|
4361
|
+
// before any subsequent install -g symlinks to these dep targets.
|
|
4362
|
+
if (stashedDepModules.length > 0)
|
|
4363
|
+
restoreNestedDepModules(stashedDepModules, verbose);
|
|
4285
4364
|
if (!packResult.success) {
|
|
4286
4365
|
const d = diagnoseNpmPackFailure(cwd, packResult.output, packResult.stderr, pkg);
|
|
4287
4366
|
console.error(colors.red(`ERROR: ${d.summary}`));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/npmglobalize",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.154",
|
|
4
4
|
"description": "Transform file: dependencies to npm versions for publishing",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@bobfrankston/freezepak": "^0.1.7",
|
|
35
35
|
"@bobfrankston/importgen": "^0.1.34",
|
|
36
|
+
"@bobfrankston/npmglobalize": "^1.0.153",
|
|
36
37
|
"@bobfrankston/themecolors": "^0.1.5",
|
|
37
38
|
"@bobfrankston/userconfig": "^1.0.7",
|
|
38
39
|
"@npmcli/package-json": "^7.0.4",
|
|
@@ -48,6 +49,7 @@
|
|
|
48
49
|
".dependencies": {
|
|
49
50
|
"@bobfrankston/freezepak": "file:../freezepak",
|
|
50
51
|
"@bobfrankston/importgen": "file:../importgen",
|
|
52
|
+
"@bobfrankston/npmglobalize": "^1.0.153",
|
|
51
53
|
"@bobfrankston/themecolors": "file:../themecolors",
|
|
52
54
|
"@bobfrankston/userconfig": "file:../userconfig",
|
|
53
55
|
"@npmcli/package-json": "^7.0.4",
|
|
@@ -61,6 +63,7 @@
|
|
|
61
63
|
"dependencies": {
|
|
62
64
|
"@bobfrankston/freezepak": "^0.1.7",
|
|
63
65
|
"@bobfrankston/importgen": "^0.1.34",
|
|
66
|
+
"@bobfrankston/npmglobalize": "^1.0.153",
|
|
64
67
|
"@bobfrankston/themecolors": "^0.1.5",
|
|
65
68
|
"@bobfrankston/userconfig": "^1.0.7",
|
|
66
69
|
"@npmcli/package-json": "^7.0.4",
|