@bobfrankston/npmglobalize 1.0.185 → 1.0.186

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 +1 -0
  2. package/lib.js +60 -0
  3. package/package.json +1 -1
package/lib.d.ts CHANGED
@@ -296,6 +296,7 @@ export declare function parseVersionTag(tag: string): number[] | null;
296
296
  export declare function compareVersions(a: number[], b: number[]): number;
297
297
  /** Fix version/tag mismatches */
298
298
  export declare function fixVersionTagMismatch(cwd: string, pkg: any, verbose?: boolean): boolean;
299
+ export declare function printPnpmSuggestionSummary(): void;
299
300
  /** Return declared deps (dependencies + devDependencies) that don't resolve from
300
301
  * `pkgDir`. Skips `workspace:`/`link:` specs (handled by workspace tooling /
301
302
  * rarely used). `file:` deps are checked: npm installs them as junctions
package/lib.js CHANGED
@@ -29,6 +29,7 @@ function spawnSafe(cmd, args, options = {}) {
29
29
  return spawnSync(cmd, args, opts);
30
30
  }
31
31
  import readline from 'readline';
32
+ import { styleText } from 'util';
32
33
  import libversion from 'libnpmversion';
33
34
  import JSON5 from 'json5';
34
35
  import { fileURLToPath } from 'url';
@@ -1666,6 +1667,59 @@ export function fixVersionTagMismatch(cwd, pkg, verbose = false) {
1666
1667
  function sleepSync(ms) {
1667
1668
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
1668
1669
  }
1670
+ /** User-requested pnpm-eval hook (2026-05-14). The first local `npm install`
1671
+ * failure in a process prints a yellow-on-red banner and pauses 60s so the
1672
+ * user can read it. Every failure (first and subsequent) is captured in
1673
+ * `_pnpmFailureContexts` so `printPnpmSuggestionSummary()` can re-emit the
1674
+ * banner — listing all failure sites — at end-of-run, in case the live
1675
+ * banner scrolled past. Remove once pnpm-vs-npm decision is made. */
1676
+ let _pnpmSuggestionShown = false;
1677
+ const _pnpmFailureContexts = [];
1678
+ function _pnpmBanner(contexts) {
1679
+ const width = 76;
1680
+ const wrap = (s) => styleText(['yellow', 'bgRed', 'bold'], ' ' + s.padEnd(width - 2) + ' ');
1681
+ const body = [
1682
+ '',
1683
+ contexts.length > 1
1684
+ ? `npm install FAILED -- ${contexts.length} sites -- consider trying pnpm`
1685
+ : 'npm install FAILED -- consider trying pnpm',
1686
+ '',
1687
+ ];
1688
+ for (const c of contexts) {
1689
+ const label = ` - ${c}`;
1690
+ body.push(label.length > width - 2 ? label.slice(0, width - 5) + '...' : label);
1691
+ }
1692
+ body.push('', 'pnpm uses a content-addressed store with strict node_modules and', 'much faster installs. Commands mirror npm; great workspace support.', '', 'Try: npm i -g pnpm && pnpm import && pnpm install', '');
1693
+ return body.map(wrap);
1694
+ }
1695
+ async function suggestPnpmOnInstallFailure(context) {
1696
+ _pnpmFailureContexts.push(context);
1697
+ if (_pnpmSuggestionShown)
1698
+ return;
1699
+ _pnpmSuggestionShown = true;
1700
+ const lines = _pnpmBanner([context]);
1701
+ console.error('');
1702
+ for (const l of lines)
1703
+ console.error(l);
1704
+ console.error(styleText(['yellow', 'bgRed', 'bold'], ' ' + 'Pausing 60s so you can read this. Ctrl+C to abort.'.padEnd(74) + ' '));
1705
+ console.error('');
1706
+ await new Promise(resolve => setTimeout(resolve, 60_000));
1707
+ }
1708
+ /** Re-emit the pnpm banner at end-of-run if any `npm install` failed.
1709
+ * No-op when nothing failed. Safe to call from multiple summary paths —
1710
+ * guarded so the summary banner prints at most once per process. */
1711
+ let _pnpmSummaryPrinted = false;
1712
+ export function printPnpmSuggestionSummary() {
1713
+ if (_pnpmSummaryPrinted)
1714
+ return;
1715
+ if (_pnpmFailureContexts.length === 0)
1716
+ return;
1717
+ _pnpmSummaryPrinted = true;
1718
+ console.error('');
1719
+ for (const l of _pnpmBanner(_pnpmFailureContexts))
1720
+ console.error(l);
1721
+ console.error('');
1722
+ }
1669
1723
  /** Wait for a package version to appear on the npm registry.
1670
1724
  * First-time publishes (brand-new package name) take much longer to
1671
1725
  * propagate than version bumps — npm has no cached metadata to update,
@@ -1828,6 +1882,7 @@ export async function ensureFileDepModules(cwd, verbose = false, visited = new S
1828
1882
  console.error(colors.red(` ✗ npm install failed in ${abs}`));
1829
1883
  if (r.stderr)
1830
1884
  console.error(colors.dim(r.stderr.split('\n').slice(0, 5).join('\n')));
1885
+ await suggestPnpmOnInstallFailure(`ensureFileDepModules (cwd): ${abs}`);
1831
1886
  }
1832
1887
  }
1833
1888
  for (const key of ['dependencies', 'devDependencies']) {
@@ -1865,6 +1920,7 @@ export async function ensureFileDepModules(cwd, verbose = false, visited = new S
1865
1920
  console.error(colors.red(` ✗ npm install failed in ${target}`));
1866
1921
  if (r.stderr)
1867
1922
  console.error(colors.dim(r.stderr.split('\n').slice(0, 5).join('\n')));
1923
+ await suggestPnpmOnInstallFailure(`ensureFileDepModules (file: dep ${name}): ${target}`);
1868
1924
  }
1869
1925
  }
1870
1926
  await ensureFileDepModules(target, verbose, visited);
@@ -1993,6 +2049,7 @@ export async function ensureWorkspaceDepModules(rootDir, members, verbose = fals
1993
2049
  console.error(colors.red(` ✗ npm install failed in ${root}`));
1994
2050
  if (r.stderr)
1995
2051
  console.error(colors.dim(r.stderr.split('\n').slice(0, 5).join('\n')));
2052
+ await suggestPnpmOnInstallFailure(`ensureWorkspaceDepModules: ${root}`);
1996
2053
  }
1997
2054
  }
1998
2055
  /** Run npm install -g with retries for registry propagation delay.
@@ -4788,6 +4845,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
4788
4845
  console.log(` ${colors.yellow('!')} ${s}`);
4789
4846
  console.log(colors.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
4790
4847
  console.log('');
4848
+ printPnpmSuggestionSummary();
4791
4849
  return true;
4792
4850
  }
4793
4851
  // Skip if private
@@ -5901,6 +5959,7 @@ export async function globalize(cwd, options = {}, configOptions = {}) {
5901
5959
  }
5902
5960
  console.log(colors.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
5903
5961
  console.log('');
5962
+ printPnpmSuggestionSummary();
5904
5963
  // Only show "To use run" message if package provides commands (has bin field)
5905
5964
  if (finalPkg.bin) {
5906
5965
  const commandName = finalPkg.name.includes('/') ? finalPkg.name.split('/')[1] : finalPkg.name;
@@ -6254,6 +6313,7 @@ export async function globalizeWorkspace(rootDir, options = {}, configOptions =
6254
6313
  }
6255
6314
  console.log(colors.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
6256
6315
  console.log('');
6316
+ printPnpmSuggestionSummary();
6257
6317
  // Global install of the workspace root (monorepo CLI)
6258
6318
  const { install = false, link = false, wsl = false, dryRun = false, verbose = false } = options;
6259
6319
  const rootPkgFinal = readPackageJson(rootDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/npmglobalize",
3
- "version": "1.0.185",
3
+ "version": "1.0.186",
4
4
  "description": "Transform file: dependencies to npm versions for publishing",
5
5
  "main": "index.js",
6
6
  "type": "module",