@bobfrankston/npmglobalize 1.0.155 → 1.0.157

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.
Files changed (3) hide show
  1. package/lib.d.ts +12 -3
  2. package/lib.js +70 -7
  3. package/package.json +1 -1
package/lib.d.ts CHANGED
@@ -244,9 +244,9 @@ export declare function compareVersions(a: number[], b: number[]): number;
244
244
  /** Fix version/tag mismatches */
245
245
  export declare function fixVersionTagMismatch(cwd: string, pkg: any, verbose?: boolean): boolean;
246
246
  /** Walk `file:` deps transitively and run `npm install` in any target whose
247
- * package.json declares deps but has no `node_modules/`. Recovers from prior
248
- * aborted runs (or pre-1.0.153 `--clean-nested-modules` damage) so the
249
- * upcoming build/pack can resolve transitive package imports.
247
+ * package.json declares deps but has no `node_modules/`. Also covers `cwd`
248
+ * itself on the first call, so a fresh clone with no `node_modules/` gets
249
+ * installed before the upcoming build/pack tries to resolve imports.
250
250
  * Cycle-safe via the shared `visited` set. */
251
251
  export declare function ensureFileDepModules(cwd: string, verbose?: boolean, visited?: Set<string>): void;
252
252
  /** Run a command and return success status */
@@ -260,6 +260,15 @@ export declare function runCommand(cmd: string, args: string[], options?: {
260
260
  output: string;
261
261
  stderr: string;
262
262
  };
263
+ /** Install a package globally in WSL, auto-fixing the root-owned /usr/local/lib/node_modules
264
+ * EACCES case by switching npm to a user prefix (~/.npm-global) and retrying once.
265
+ * Output is captured (so we can scan for the error) and then mirrored to the terminal. */
266
+ export declare function installInWsl(wslArgs: string[], opts?: {
267
+ cwd?: string;
268
+ }): {
269
+ success: boolean;
270
+ fixed: boolean;
271
+ };
263
272
  /** Run a command and throw on failure */
264
273
  export declare function runCommandOrThrow(cmd: string, args: string[], options?: {
265
274
  silent?: boolean;
package/lib.js CHANGED
@@ -1323,9 +1323,9 @@ function restoreNestedDepModules(stashed, verbose) {
1323
1323
  }
1324
1324
  }
1325
1325
  /** Walk `file:` deps transitively and run `npm install` in any target whose
1326
- * package.json declares deps but has no `node_modules/`. Recovers from prior
1327
- * aborted runs (or pre-1.0.153 `--clean-nested-modules` damage) so the
1328
- * upcoming build/pack can resolve transitive package imports.
1326
+ * package.json declares deps but has no `node_modules/`. Also covers `cwd`
1327
+ * itself on the first call, so a fresh clone with no `node_modules/` gets
1328
+ * installed before the upcoming build/pack tries to resolve imports.
1329
1329
  * Cycle-safe via the shared `visited` set. */
1330
1330
  export function ensureFileDepModules(cwd, verbose = false, visited = new Set()) {
1331
1331
  const abs = path.resolve(cwd);
@@ -1339,6 +1339,26 @@ export function ensureFileDepModules(cwd, verbose = false, visited = new Set())
1339
1339
  catch {
1340
1340
  return;
1341
1341
  }
1342
+ const cwdHasDeps = (pkg?.dependencies && Object.keys(pkg.dependencies).length > 0) ||
1343
+ (pkg?.devDependencies && Object.keys(pkg.devDependencies).length > 0);
1344
+ if (cwdHasDeps && !fs.existsSync(path.join(abs, 'node_modules'))) {
1345
+ const lock = path.join(abs, 'package-lock.json');
1346
+ if (fs.existsSync(lock)) {
1347
+ if (verbose)
1348
+ console.log(colors.dim(` removing stale ${lock}`));
1349
+ try {
1350
+ fs.rmSync(lock, { force: true });
1351
+ }
1352
+ catch { /* best-effort */ }
1353
+ }
1354
+ console.log(colors.yellow(`↻ installing node_modules in ${pkg?.name || abs}`));
1355
+ const r = runCommand('npm', ['install'], { cwd: abs, silent: !verbose });
1356
+ if (!r.success) {
1357
+ console.error(colors.red(` ✗ npm install failed in ${abs}`));
1358
+ if (r.stderr)
1359
+ console.error(colors.dim(r.stderr.split('\n').slice(0, 5).join('\n')));
1360
+ }
1361
+ }
1342
1362
  for (const key of ['dependencies', 'devDependencies']) {
1343
1363
  const deps = pkg?.[key];
1344
1364
  if (!deps || typeof deps !== 'object')
@@ -1360,6 +1380,15 @@ export function ensureFileDepModules(cwd, verbose = false, visited = new Set())
1360
1380
  (targetPkg?.devDependencies && Object.keys(targetPkg.devDependencies).length > 0);
1361
1381
  const nm = path.join(target, 'node_modules');
1362
1382
  if (hasDeps && !fs.existsSync(nm)) {
1383
+ const lock = path.join(target, 'package-lock.json');
1384
+ if (fs.existsSync(lock)) {
1385
+ if (verbose)
1386
+ console.log(colors.dim(` removing stale ${lock}`));
1387
+ try {
1388
+ fs.rmSync(lock, { force: true });
1389
+ }
1390
+ catch { /* best-effort */ }
1391
+ }
1363
1392
  console.log(colors.yellow(`↻ restoring node_modules in ${name} (${target})`));
1364
1393
  const r = runCommand('npm', ['install'], { cwd: target, silent: !verbose });
1365
1394
  if (!r.success) {
@@ -1420,6 +1449,40 @@ export function runCommand(cmd, args, options = {}) {
1420
1449
  return { success: false, output: '', stderr: error.message };
1421
1450
  }
1422
1451
  }
1452
+ /** Install a package globally in WSL, auto-fixing the root-owned /usr/local/lib/node_modules
1453
+ * EACCES case by switching npm to a user prefix (~/.npm-global) and retrying once.
1454
+ * Output is captured (so we can scan for the error) and then mirrored to the terminal. */
1455
+ export function installInWsl(wslArgs, opts = {}) {
1456
+ console.log(colors.cyan(`> wsl ${wslArgs.join(' ')}`));
1457
+ let result = runCommand('wsl', wslArgs, { cwd: opts.cwd, silent: true });
1458
+ if (result.output)
1459
+ process.stdout.write(result.output);
1460
+ if (result.stderr)
1461
+ process.stderr.write(result.stderr);
1462
+ if (result.success)
1463
+ return { success: true, fixed: false };
1464
+ const combined = (result.output || '') + '\n' + (result.stderr || '');
1465
+ const permIssue = /EACCES/.test(combined) && /\/usr\/(?:local\/)?lib\/node_modules/.test(combined);
1466
+ if (!permIssue)
1467
+ return { success: false, fixed: false };
1468
+ console.log(colors.yellow('Detected root-owned npm prefix in WSL — switching to ~/.npm-global and retrying...'));
1469
+ const fix = `set -e; npm config set prefix "$HOME/.npm-global"; mkdir -p "$HOME/.npm-global/bin"; if ! grep -q '\\.npm-global/bin' "$HOME/.bashrc" 2>/dev/null; then printf '\\n# npm user-prefix bin (added by npmglobalize)\\nexport PATH="$HOME/.npm-global/bin:$PATH"\\n' >> "$HOME/.bashrc"; fi`;
1470
+ const fixResult = runCommand('wsl', ['bash', '-lc', fix], { silent: true });
1471
+ if (!fixResult.success) {
1472
+ console.error(colors.red('Failed to apply WSL npm prefix fix:'));
1473
+ if (fixResult.stderr)
1474
+ process.stderr.write(fixResult.stderr);
1475
+ return { success: false, fixed: false };
1476
+ }
1477
+ console.log(colors.green('✓ WSL npm prefix set to ~/.npm-global; PATH appended to ~/.bashrc'));
1478
+ console.log(colors.cyan(`> wsl ${wslArgs.join(' ')}`));
1479
+ result = runCommand('wsl', wslArgs, { cwd: opts.cwd, silent: true });
1480
+ if (result.output)
1481
+ process.stdout.write(result.output);
1482
+ if (result.stderr)
1483
+ process.stderr.write(result.stderr);
1484
+ return { success: result.success, fixed: result.success };
1485
+ }
1423
1486
  /** Diagnose common build/version failure patterns and print actionable hints.
1424
1487
  * Returns true if a diagnosis was printed. */
1425
1488
  function diagnoseBuildFailure(errorText, cwd) {
@@ -4672,10 +4735,10 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
4672
4735
  waitForNpmVersion(pkgName, pkgVersion);
4673
4736
  console.log(`Installing in WSL${useLocalWsl ? ' (local)' : ' from registry'}: ${pkgName}@${pkgVersion}...`);
4674
4737
  if (!dryRun) {
4675
- const wslResult = runCommand('wsl', wslArgs, { cwd, silent: false, showCommand: true });
4738
+ const wslResult = installInWsl(wslArgs, { cwd });
4676
4739
  if (wslResult.success) {
4677
4740
  wslInstallOk = true;
4678
- console.log(colors.green(`✓ Installed in WSL: ${pkgName}@${pkgVersion}`));
4741
+ console.log(colors.green(`✓ Installed in WSL: ${pkgName}@${pkgVersion}${wslResult.fixed ? ' (after prefix fix)' : ''}`));
4679
4742
  }
4680
4743
  else {
4681
4744
  console.error(colors.yellow('✗ WSL install failed (is npm installed in WSL?)'));
@@ -5076,9 +5139,9 @@ export async function globalizeWorkspace(rootDir, options = {}, configOptions =
5076
5139
  if (wsl) {
5077
5140
  console.log(`Installing ${pkgName} in WSL (local)...`);
5078
5141
  if (!dryRun) {
5079
- const wslResult = runCommand('wsl', ['npm', 'install', '-g', '.'], { cwd: rootDir, silent: false, showCommand: true });
5142
+ const wslResult = installInWsl(['npm', 'install', '-g', '.'], { cwd: rootDir });
5080
5143
  if (wslResult.success) {
5081
- console.log(colors.green(`✓ Installed in WSL: ${pkgName}@${pkgVersion}`));
5144
+ console.log(colors.green(`✓ Installed in WSL: ${pkgName}@${pkgVersion}${wslResult.fixed ? ' (after prefix fix)' : ''}`));
5082
5145
  }
5083
5146
  else {
5084
5147
  console.error(colors.yellow('✗ WSL install failed (is npm installed in WSL?)'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/npmglobalize",
3
- "version": "1.0.155",
3
+ "version": "1.0.157",
4
4
  "description": "Transform file: dependencies to npm versions for publishing",
5
5
  "main": "index.js",
6
6
  "type": "module",