@bobfrankston/npmglobalize 1.0.198 โ†’ 1.0.200

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/README.md +84 -15
  2. package/lib.js +40 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -157,6 +157,51 @@ npmglobalize --fix # Runs npm audit fix
157
157
  npmglobalize --no-fix
158
158
  ```
159
159
 
160
+ ### ๐Ÿงฉ Install Scripts (npm `allowScripts`)
161
+
162
+ npm 11.17+ skips install-time lifecycle scripts (`preinstall`/`install`/
163
+ `postinstall`) for packages that aren't on an allowlist. A **global** install
164
+ has no project `package.json` to record approvals in, so without help a
165
+ postinstall you wrote yourself silently never runs while the install still
166
+ exits 0 โ€” e.g. `@bobfrankston/msger`'s postinstall copies its native binary
167
+ into the per-user bin dir its launcher reads from, and skipping it leaves a
168
+ stale exe behind a green checkmark.
169
+
170
+ npmglobalize therefore allowlists **your own packages** on the global install
171
+ it performs โ€” it just built and published them from your source, so they're
172
+ trusted โ€” and leaves third-party packages gated:
173
+
174
+ ```
175
+ > npm install -g @bobfrankston/winpos@2.0.51 --allow-scripts @bobfrankston/winpos,@bobfrankston/msger,@bobfrankston/msgcommon
176
+ ```
177
+
178
+ Anything npm skips is reported rather than buried in the captured output:
179
+
180
+ ```
181
+ ยท Skipped koffi@2.16.3 install script โ€” prebuilt-binary fetcher, the package ships binaries; normally harmless.
182
+ โš  npm skipped install scripts for third-party packages: sharp@0.33.0
183
+ sharp@0.33.0 (install: node install/check)
184
+ These may be broken at runtime โ€” no prebuilt fallback recognized.
185
+ To run them anyway: npm install -g @bobfrankston/winpos@2.0.51 --allow-scripts @bobfrankston/winpos,@bobfrankston/msger,koffi,sharp
186
+ ```
187
+
188
+ Two details worth knowing:
189
+
190
+ - **Prebuilt fetchers are called out separately.** npm names the skipped
191
+ script, so a runner like `cnoke`, `prebuild-install`, `node-gyp-build`,
192
+ `prebuildify` or `napi-postinstall` is recognized as one that only compiles
193
+ when no prebuild matches. Those packages ship working binaries โ€” koffi
194
+ carries `build/koffi/<platform>/koffi.node` for every platform it supports โ€”
195
+ so the skip gets an informational note, not a warning.
196
+ - **The suggested re-run command includes your own packages.** npm takes the
197
+ allowlist from the first source that has one rather than merging sources, so
198
+ a command naming only the third-party package would silently re-gate yours.
199
+ Always run the full list.
200
+
201
+ An own-scope package showing up as skipped is reported louder โ€” that means the
202
+ allowlist missed it, usually a transitive dep not present in the local
203
+ `node_modules` tree.
204
+
160
205
  ### ๐Ÿ”‘ OAuth Credentials Handling
161
206
 
162
207
  `npmglobalize` automatically detects `credentials.json` files and handles them based on OAuth app type:
@@ -534,8 +579,9 @@ is planned.
534
579
 
535
580
  ### `upstream` โ€” who consumes this package
536
581
 
537
- Bookkeeping, not a setting. When a package with `file:` deps publishes,
538
- npmglobalize appends an entry to each **dependency's** `.globalize.json5`:
582
+ **Experimental.** Bookkeeping, not a setting. When a package with `file:` deps
583
+ publishes, npmglobalize appends an entry to each **dependency's**
584
+ `.globalize.json5`:
539
585
 
540
586
  ```json5
541
587
  {
@@ -557,19 +603,42 @@ checkout path, its version at the time, and the date.
557
603
 
558
604
  Only **immediate** consumers are recorded. A full consumer tree is a walk, not
559
605
  a copy: follow each entry's path and read that package's own `upstream` list.
560
-
561
- The list is **FYI for now** โ€” it is recorded, preserved across publishes, and
562
- printed in the Release Summary of the package that owns it. Nothing is
563
- rebuilt, republished, or reinstalled on its behalf. An entry is added the
564
- first time a consumer publishes after this feature landed, and refreshed in
565
- place on every publish after that.
566
-
567
- The entry is written into the dependency's own checkout, then committed there
568
- (`Record upstream <consumer>@<version>`) and pushed if that repo has a remote.
569
- Only `.globalize.json5` is staged and committed, by pathspec โ€” anything else
570
- the dependency had staged or modified is left exactly as it was. If the
571
- dependency isn't a git repo, or ignores `.globalize.json5`, the file is still
572
- written and the commit is skipped.
606
+ That keeps each file small and self-maintaining โ€” no package has to know about
607
+ anything beyond its own direct consumers.
608
+
609
+ The list is **FYI** โ€” it is recorded, preserved across publishes, and printed
610
+ in the Release Summary of the package that owns it. Nothing is rebuilt,
611
+ republished, or reinstalled on its behalf. An entry appears the first time a
612
+ consumer publishes, and is refreshed in place on every publish after that.
613
+
614
+ #### Scope and limits
615
+
616
+ **`file:` deps only.** A dependency referenced by npm version (`"^0.1.39"`)
617
+ is never recorded, because npmglobalize has no checkout path for it โ€” it
618
+ consumes the published tarball, not a sibling directory. To have a consumer
619
+ show up in a library's list, that consumer must reference it as
620
+ `file:../<lib>`.
621
+
622
+ **Usually not committed.** The entry is written into the dependency's own
623
+ checkout, and npmglobalize then tries to commit it there
624
+ (`Record upstream <consumer>@<version>`) and push if that repo has a remote.
625
+ In practice that commit is usually skipped: `.globalize.json5` is in the
626
+ standard ignore template, so most repos ignore it and the entry stays as
627
+ untracked local state. That is a reasonable failure mode for an experimental
628
+ mechanism โ€” the list rewrites itself on every publish, so there is nothing to
629
+ merge and nothing to reconcile between machines. It also means the list is
630
+ **per-machine**, not shared history.
631
+
632
+ When the commit does happen (a repo that tracks its `.globalize.json5`, as
633
+ npmglobalize itself does), only that one file is staged and committed by
634
+ pathspec โ€” anything else the dependency had staged or modified is left exactly
635
+ as it was. A dependency that isn't a git repo is written and skipped the same
636
+ way.
637
+
638
+ The alternative would be recording this in `package.json`, which is tracked
639
+ and published โ€” every consumer of a library would then download that library's
640
+ list of local checkout paths in its tarball. Keeping it in `.globalize.json5`
641
+ keeps it out of the package entirely.
573
642
 
574
643
  ## Common Workflows
575
644
 
package/lib.js CHANGED
@@ -3514,28 +3514,58 @@ function allowOwnScriptsArgs(dir) {
3514
3514
  const own = ownScopePackages(dir);
3515
3515
  return own.length ? ['--allow-scripts', own.join(',')] : [];
3516
3516
  }
3517
+ /** Install-script runners that fetch or select a prebuilt binary and only
3518
+ * compile when no prebuild matches the platform. A package using one of these
3519
+ * ships working binaries in its tarball, so a skipped script is very rarely
3520
+ * the thing that breaks it โ€” koffi (cnoke) is the standing example: it carries
3521
+ * build/koffi/<platform>/koffi.node for every platform it supports. */
3522
+ const PREBUILT_SCRIPT_RUNNERS = /\b(prebuild-install|node-gyp-build|prebuildify|cnoke|napi-postinstall)\b/;
3517
3523
  /** A skipped postinstall can leave a package broken at runtime (puppeteer
3518
3524
  * without its browser, native modules unbuilt), so surface the warning
3519
3525
  * instead of burying it in the captured output. Own-scope packages are
3520
3526
  * reported separately and louder: those were supposed to be allowed, so
3521
- * one showing up here means the allowlist missed it. */
3522
- function warnSkippedScripts(combined, allowed = []) {
3523
- const pkgs = [...combined.matchAll(/^npm warn allow-scripts\s+(\S+@[^\s(]+) \(/gm)];
3527
+ * one showing up here means the allowlist missed it.
3528
+ *
3529
+ * npm names the script it skipped in the warn line โ€” "(install: node
3530
+ * src/cnoke/cnoke.js ... --prebuild)" โ€” so the report can say WHICH script
3531
+ * and whether it's the kind that has a prebuilt fallback, rather than
3532
+ * flatly claiming every skip may have broken something. */
3533
+ function warnSkippedScripts(combined, allowed = [], pkgSpec) {
3534
+ const pkgs = [...combined.matchAll(/^npm warn allow-scripts\s+(\S+@[^\s(]+) \(([^)]*)\)/gm)]
3535
+ .map(m => ({ spec: m[1], detail: m[2] }));
3524
3536
  if (!pkgs.length)
3525
3537
  return;
3526
3538
  const nameOf = (spec) => spec.replace(/@[^@]+$/, '');
3527
3539
  const first = allowed[0] || '';
3528
3540
  const scope = first.startsWith('@') ? first.slice(0, first.indexOf('/') + 1) : '';
3529
- const ours = scope ? pkgs.filter(m => nameOf(m[1]).startsWith(scope)) : [];
3530
- const others = pkgs.filter(m => !ours.includes(m));
3541
+ const ours = scope ? pkgs.filter(p => nameOf(p.spec).startsWith(scope)) : [];
3542
+ const others = pkgs.filter(p => !ours.includes(p));
3531
3543
  if (ours.length) {
3532
- console.log(colors.red(` โš  Own-scope install scripts still skipped: ${ours.map(m => m[1]).join(', ')}`));
3544
+ console.log(colors.red(` โš  Own-scope install scripts still skipped: ${ours.map(p => p.spec).join(', ')}`));
3533
3545
  console.log(colors.red(` npmglobalize allowlisted ${allowed.length} own package(s) but missed these โ€” likely transitive deps not present in the local node_modules tree.`));
3534
3546
  }
3535
3547
  if (others.length) {
3536
- const names = [...new Set(others.map(m => nameOf(m[1])))];
3537
- console.log(colors.yellow(` โš  npm skipped install scripts for third-party packages: ${others.map(m => m[1]).join(', ')}`));
3538
- console.log(colors.yellow(` Package may be broken at runtime. Allow with: npm install -g <pkg> --allow-scripts ${names.join(',')}`));
3548
+ const prebuilt = others.filter(p => PREBUILT_SCRIPT_RUNNERS.test(p.detail));
3549
+ const unknown = others.filter(p => !prebuilt.includes(p));
3550
+ for (const p of prebuilt) {
3551
+ const script = p.detail.split(':')[0];
3552
+ console.log(colors.dim(` ยท Skipped ${p.spec} ${script} script โ€” prebuilt-binary fetcher, the package ships binaries; normally harmless.`));
3553
+ }
3554
+ if (unknown.length) {
3555
+ console.log(colors.yellow(` โš  npm skipped install scripts for third-party packages: ${unknown.map(p => p.spec).join(', ')}`));
3556
+ for (const p of unknown)
3557
+ console.log(colors.yellow(` ${p.spec} (${p.detail})`));
3558
+ console.log(colors.yellow(` These may be broken at runtime โ€” no prebuilt fallback recognized.`));
3559
+ }
3560
+ // The re-run command must carry the OWN packages too: npm takes the
3561
+ // allowlist from the first source that has one, so a command listing
3562
+ // only the third-party name silently re-gates ours (msger's postinstall
3563
+ // copies its native binary โ€” losing it is the failure this all exists
3564
+ // to prevent). Print the whole list, and the real spec rather than a
3565
+ // <pkg> placeholder nobody can paste.
3566
+ const rerun = [...allowed, ...new Set(others.map(p => nameOf(p.spec)))].join(',');
3567
+ const target = pkgSpec ?? '<pkg>';
3568
+ console.log(colors.dim(` To run them anyway: npm install -g ${target} --allow-scripts ${rerun}`));
3539
3569
  }
3540
3570
  }
3541
3571
  /** Run npm install -g with retries for registry propagation delay.
@@ -3568,7 +3598,7 @@ async function installGlobalWithRetry(pkgSpec, cwd, isNewPackage = false, maxRet
3568
3598
  const added = combined.match(/^(?:added|changed|removed) \d+ packages?.*$/m);
3569
3599
  if (added)
3570
3600
  console.log(colors.dim(` ${added[0]}`));
3571
- warnSkippedScripts(combined, allowArgs.length ? allowArgs[1].split(',') : []);
3601
+ warnSkippedScripts(combined, allowArgs.length ? allowArgs[1].split(',') : [], pkgSpec);
3572
3602
  // A truncated puppeteer browser extraction exits 0, so the install
3573
3603
  // LOOKS fine while the cache is poisoned for every future install.
3574
3604
  // Verify now, while the leftover archive still allows a repair.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/npmglobalize",
3
- "version": "1.0.198",
3
+ "version": "1.0.200",
4
4
  "description": "Transform file: dependencies to npm versions for publishing",
5
5
  "main": "index.js",
6
6
  "type": "module",