@bobfrankston/npmglobalize 1.0.209 → 1.0.211
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 +904 -910
- package/lib.d.ts +3 -0
- package/lib.js +153 -59
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,910 +1,904 @@
|
|
|
1
|
-
# npmglobalize
|
|
2
|
-
|
|
3
|
-
Transform `file:` dependencies to npm versions for publishing.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
`npmglobalize` automates the workflow of publishing npm packages that use local `file:` references during development. It converts those references to proper npm versions, publishes everything in dependency order, and optionally restores the local references afterward.
|
|
8
|
-
|
|
9
|
-
## Installation
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
npm install -g @bobfrankston/npmglobalize
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Basic Usage
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
cd your-package
|
|
19
|
-
npmglobalize # Transform + publish (patch version)
|
|
20
|
-
npmglobalize --minor # Bump minor version
|
|
21
|
-
npmglobalize --major # Bump major version
|
|
22
|
-
|
|
23
|
-
# Or run from anywhere with a path
|
|
24
|
-
npmglobalize y:\path\to\your-package
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Key Features
|
|
28
|
-
|
|
29
|
-
### 🔗 Automatic Dependency Publishing (Default)
|
|
30
|
-
|
|
31
|
-
By default, `npmglobalize` ensures all `file:` dependencies are published **before** converting them:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
npmglobalize # Auto-publishes file: deps in correct order
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
If you have:
|
|
38
|
-
```
|
|
39
|
-
lxtest
|
|
40
|
-
├── file:../lxlan-node
|
|
41
|
-
│ └── file:../lxland
|
|
42
|
-
└── file:../lxland
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
It automatically:
|
|
46
|
-
1. Publishes `lxland` (root dependency)
|
|
47
|
-
2. Publishes `lxlan-node` (depends on lxlan)
|
|
48
|
-
3. Converts and publishes `lxtest`
|
|
49
|
-
|
|
50
|
-
**Settings Propagation (Default Behavior):**
|
|
51
|
-
When publishing `file:` dependencies, these settings are **automatically inherited**:
|
|
52
|
-
- `--update-deps` / `--update-major` (update dependencies)
|
|
53
|
-
- `--fix` (run npm audit fix)
|
|
54
|
-
- `--conform` (fix .gitignore/.npmignore/.gitattributes and git config)
|
|
55
|
-
- `--verbose` / `--quiet`
|
|
56
|
-
- `--force` / `--files`
|
|
57
|
-
|
|
58
|
-
**⚠️ Visibility Settings (Smart Inheritance):**
|
|
59
|
-
- `--npmVisibility` is **only inherited by NEW repositories** (never published to npm before)
|
|
60
|
-
- **Existing repositories** keep their current npm visibility (public/private) unchanged
|
|
61
|
-
- `--gitVisibility` is inherited by all dependencies
|
|
62
|
-
|
|
63
|
-
This ensures you can safely set `--npmVisibility private` as a default for new packages without accidentally changing the visibility of your existing published packages.
|
|
64
|
-
|
|
65
|
-
**Why This Matters:**
|
|
66
|
-
Once a package is published to npm (public or private), changing its visibility later requires careful consideration. This smart inheritance protects your existing packages while making new ones default to safe settings.
|
|
67
|
-
|
|
68
|
-
Example - safely publish with new packages private by default:
|
|
69
|
-
```bash
|
|
70
|
-
npmglobalize --npmVisibility private
|
|
71
|
-
# ✓ Existing npm packages: keep their current visibility
|
|
72
|
-
# ✓ New packages (never published): default to private (safe!)
|
|
73
|
-
# ✓ Regular npm dependencies (express, etc.): unchanged
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
Example with configuration file (recommended):
|
|
77
|
-
```bash
|
|
78
|
-
# In main package: create .globalize.json5 with "npmVisibility": "private"
|
|
79
|
-
npmglobalize
|
|
80
|
-
# ✓ Existing repos: publish with their current npm visibility
|
|
81
|
-
# ✓ Brand new repos: inherit private setting
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
**Skip auto-publishing** (use with caution):
|
|
85
|
-
```bash
|
|
86
|
-
npmglobalize -npd # --no-publish-deps
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### 🔍 Prescan (Default)
|
|
90
|
-
|
|
91
|
-
Before any transform/publish, `npmglobalize` walks the full `file:` dep graph and reports all problems up front (unresolvable paths, missing `package.json`, unscoped packages with private intent, etc.). This lets you fix the whole punch list at once rather than being interrupted mid-cascade.
|
|
92
|
-
|
|
93
|
-
Errors abort (unless `--force`); warnings prompt to continue.
|
|
94
|
-
|
|
95
|
-
Skip the prescan with `-no-prescan` / `-nps`.
|
|
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
|
-
|
|
119
|
-
**Force republish** all file: dependencies even if versions exist:
|
|
120
|
-
```bash
|
|
121
|
-
npmglobalize --force-publish
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### 📦 Dependency Updates
|
|
125
|
-
|
|
126
|
-
**Safe updates** (minor/patch only, respects semver):
|
|
127
|
-
```bash
|
|
128
|
-
npmglobalize --update-deps
|
|
129
|
-
```
|
|
130
|
-
- `express ^4.18.0` → `^4.21.0` ✓
|
|
131
|
-
- `lodash ^4.17.0` → `^4.17.21` ✓
|
|
132
|
-
- Won't update to `express ^5.0.0` (breaking change)
|
|
133
|
-
|
|
134
|
-
**Include major updates** (breaking changes):
|
|
135
|
-
```bash
|
|
136
|
-
npmglobalize --update-major
|
|
137
|
-
```
|
|
138
|
-
- Updates to latest including major versions
|
|
139
|
-
- Shows "(MAJOR)" indicator for breaking changes
|
|
140
|
-
|
|
141
|
-
**Note:** The `--update-deps` flag propagates to all file: dependencies, so one command updates your entire dependency tree.
|
|
142
|
-
|
|
143
|
-
### 🔒 Security Auditing
|
|
144
|
-
|
|
145
|
-
**Check vulnerabilities**:
|
|
146
|
-
```bash
|
|
147
|
-
npmglobalize # Shows audit at end
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
**Auto-fix vulnerabilities**:
|
|
151
|
-
```bash
|
|
152
|
-
npmglobalize --fix # Runs npm audit fix
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
**Disable audit**:
|
|
156
|
-
```bash
|
|
157
|
-
npmglobalize --no-fix
|
|
158
|
-
```
|
|
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
|
-
|
|
205
|
-
### 🔑 OAuth Credentials Handling
|
|
206
|
-
|
|
207
|
-
`npmglobalize` automatically detects `credentials.json` files and handles them based on OAuth app type:
|
|
208
|
-
|
|
209
|
-
- **Desktop/installed apps** (`"installed"` key in JSON): The `client_secret` is just a public app registration ID, not a real secret. Google's own docs state: *"the client_secret is obviously not treated as a secret."* These files are kept in the repo — `!credentials.json` is added to `.gitignore`/`.npmignore` to override any broader ignore patterns.
|
|
210
|
-
|
|
211
|
-
- **Web apps** (`"web"` key in JSON): The `client_secret` is a real secret. These files are automatically added to `.gitignore`/`.npmignore` to prevent accidental exposure.
|
|
212
|
-
|
|
213
|
-
This distinction also drives the **push protection auto-bypass**: when GitHub blocks a push because it detects an OAuth client secret, `npmglobalize` checks the credential file type. For installed apps, it auto-bypasses (marking as `false_positive`) since the credential is public by design.
|
|
214
|
-
|
|
215
|
-
### 🔄 File Reference Management
|
|
216
|
-
|
|
217
|
-
**Default behavior** (restore file: references after publish):
|
|
218
|
-
```bash
|
|
219
|
-
npmglobalize # Converts file: → npm, publishes, then restores file:
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
**Keep npm references** permanently:
|
|
223
|
-
```bash
|
|
224
|
-
npmglobalize --nofiles # Don't restore file: references
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
**Just transform** without publishing:
|
|
228
|
-
```bash
|
|
229
|
-
npmglobalize -np # --nopublish (formerly --apply)
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
**Restore** from backup:
|
|
233
|
-
```bash
|
|
234
|
-
npmglobalize --cleanup # Restore original file: references
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### 📝 Release Notes via `.commitmsg`
|
|
238
|
-
|
|
239
|
-
For multi-line or reusable release notes, write them to a `.commitmsg` file in the package root instead of passing them on the command line:
|
|
240
|
-
|
|
241
|
-
```bash
|
|
242
|
-
cat > .commitmsg <<'EOF'
|
|
243
|
-
Added foo feature
|
|
244
|
-
Fixed bar regression
|
|
245
|
-
EOF
|
|
246
|
-
npmglobalize
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
Behavior:
|
|
250
|
-
- If `-m` / `-message` is **not** given and `.commitmsg` exists, its contents are used as the commit message (and force a release even if the working tree is otherwise clean).
|
|
251
|
-
- After a successful `npm publish`, npmglobalize:
|
|
252
|
-
1. Appends the contents to `npmchanges.md` under a `## v<version> — <YYYY-MM-DD>` header (creating the file if needed)
|
|
253
|
-
2. Deletes `.commitmsg`
|
|
254
|
-
3. Commits both changes as `Log v<version> to npmchanges.md` and pushes
|
|
255
|
-
|
|
256
|
-
Notes:
|
|
257
|
-
- **Git/GitHub only.** npm publish does not consume git commit messages; `npmchanges.md` lives in the git repo and on GitHub but is excluded from the published npm tarball (the standard `*.md` rule keeps only `README.md`).
|
|
258
|
-
- If both `-m` and `.commitmsg` are present, `-m` wins and `.commitmsg` is left alone (not consumed).
|
|
259
|
-
- If publish fails, `.commitmsg` is preserved for the next attempt.
|
|
260
|
-
- `.commitmsg` is auto-added to `.npmignore` (security pattern) so it never leaks into the tarball.
|
|
261
|
-
|
|
262
|
-
### 🔧 Git Integration & Error Recovery
|
|
263
|
-
|
|
264
|
-
**Automatic tag conflict resolution**:
|
|
265
|
-
```bash
|
|
266
|
-
npmglobalize --fix-tags # Auto-fix version/tag mismatches
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
When a previous publish fails, git tags may conflict with package.json version. The `--fix-tags` option (or automatic detection) will clean up these conflicts.
|
|
270
|
-
|
|
271
|
-
**Automatic rebase** when local is behind remote:
|
|
272
|
-
```bash
|
|
273
|
-
npmglobalize --rebase # Auto-rebase if behind remote
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
For single-developer projects, this safely pulls remote changes before publishing.
|
|
277
|
-
|
|
278
|
-
**Failed publish recovery**: If a previous run bumped the version locally but the publish or push failed (e.g., network error, push protection), `npmglobalize` detects this automatically on the next run. It checks whether the current version actually exists on npm — if not, it republishes without re-bumping the version. Unpushed commits from a failed push are also pushed automatically.
|
|
279
|
-
|
|
280
|
-
**GitHub Push Protection (GH013)**: When GitHub's secret scanning blocks a push, `npmglobalize` detects the error and checks whether the flagged secrets are actually safe. For Google OAuth desktop/installed app credentials (where the "client_secret" is just a public app identifier, not a real secret), it automatically bypasses push protection via the GitHub API (`gh` CLI required) and retries the push. For other secret types, it displays the unblock URLs with guidance.
|
|
281
|
-
|
|
282
|
-
**Note:** This tool is designed for single-developer, single-branch workflows where automatic rebase and tag cleanup are safe operations.
|
|
283
|
-
|
|
284
|
-
### 📂 Git Repository Setup
|
|
285
|
-
|
|
286
|
-
**Change visibility of an existing repo:**
|
|
287
|
-
```bash
|
|
288
|
-
npmglobalize -git public # Makes the GitHub repo public (with confirmation)
|
|
289
|
-
npmglobalize -git private # Makes the GitHub repo private
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
### Missing local `.git` (adopt vs fresh init)
|
|
293
|
-
|
|
294
|
-
When npmglobalize runs in a directory with no `.git`, it first checks for a
|
|
295
|
-
reachable `repository.url` in `package.json`. If found, it offers two paths:
|
|
296
|
-
|
|
297
|
-
```
|
|
298
|
-
How would you like to set up git?
|
|
299
|
-
1) Adopt history from existing remote (recommended)
|
|
300
|
-
2) Initialize fresh git repository
|
|
301
|
-
a) Adopt ALL (don't ask again for remaining deps)
|
|
302
|
-
3) Use local install only (skip git/publish)
|
|
303
|
-
4) Abort
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
**Adopt** runs:
|
|
307
|
-
|
|
308
|
-
```bash
|
|
309
|
-
git init
|
|
310
|
-
git remote add origin <package.json repository.url>
|
|
311
|
-
git fetch origin
|
|
312
|
-
# Point HEAD at origin/<defaultBranch> without touching the working tree
|
|
313
|
-
git update-ref refs/heads/<branch> origin/<branch>
|
|
314
|
-
git symbolic-ref HEAD refs/heads/<branch>
|
|
315
|
-
git reset # mixed: refresh index, keep working tree
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
After adoption, your local files appear as **uncommitted changes on top of the
|
|
319
|
-
remote's HEAD** — `git status` shows the drift, and npmglobalize's normal flow
|
|
320
|
-
will commit them on the next publish. **No force-push is required**, and the
|
|
321
|
-
remote's history is preserved.
|
|
322
|
-
|
|
323
|
-
If the remote is not reachable (no `repository` field, network/auth failure,
|
|
324
|
-
deleted repo), the original "Initialize fresh git repository" prompt is shown
|
|
325
|
-
instead.
|
|
326
|
-
|
|
327
|
-
CLI flags:
|
|
328
|
-
|
|
329
|
-
- `-init` — auto path; adopts if a reachable remote is found, otherwise falls
|
|
330
|
-
back to fresh `git init` + `gh repo create`.
|
|
331
|
-
- `-adopt` — **strict**: aborts if no reachable remote in `package.json.repository`.
|
|
332
|
-
Use this when you want to be sure no new GitHub repo is created (e.g. in
|
|
333
|
-
scripts, or when re-attaching a tree of packages to existing repos).
|
|
334
|
-
|
|
335
|
-
When initializing a new repository with `--init`, npmglobalize automatically sets up:
|
|
336
|
-
|
|
337
|
-
**File structure** (per programming.md standards):
|
|
338
|
-
- `.gitignore` - Node.js best practices (node_modules, secrets, certificates, etc.)
|
|
339
|
-
- `.npmignore` - Publishing filters (excludes .git, tests, source files, etc.)
|
|
340
|
-
- **noEmit projects:** `*.ts`, `*.map`, and `tsconfig.json` are kept (not ignored) since TS files are the runtime files
|
|
341
|
-
- `.gitattributes` - Forces LF line endings for all text files
|
|
342
|
-
|
|
343
|
-
**Git configuration** (ensures cross-platform compatibility):
|
|
344
|
-
```bash
|
|
345
|
-
git config core.autocrlf false # Disable CRLF conversion
|
|
346
|
-
git config core.eol lf # Force LF line endings
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
This ensures consistent line endings across Windows, macOS, and Linux, preventing "modified file" issues caused by line ending differences.
|
|
350
|
-
|
|
351
|
-
### 🔍
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
-
|
|
411
|
-
-
|
|
412
|
-
-
|
|
413
|
-
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
-
|
|
429
|
-
-
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
###
|
|
433
|
-
```
|
|
434
|
-
-
|
|
435
|
-
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
that
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
-
|
|
489
|
-
-
|
|
490
|
-
-
|
|
491
|
-
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
-
|
|
497
|
-
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
```
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
```
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
```
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
"
|
|
555
|
-
"
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
"
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
npmglobalize
|
|
648
|
-
```
|
|
649
|
-
|
|
650
|
-
###
|
|
651
|
-
```bash
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
**
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
**
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
- `-
|
|
777
|
-
- `-
|
|
778
|
-
- `-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
```
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
#
|
|
820
|
-
npmglobalize
|
|
821
|
-
|
|
822
|
-
#
|
|
823
|
-
npmglobalize
|
|
824
|
-
|
|
825
|
-
#
|
|
826
|
-
npmglobalize -
|
|
827
|
-
|
|
828
|
-
#
|
|
829
|
-
npmglobalize -
|
|
830
|
-
|
|
831
|
-
#
|
|
832
|
-
npmglobalize -
|
|
833
|
-
|
|
834
|
-
#
|
|
835
|
-
npmglobalize -
|
|
836
|
-
|
|
837
|
-
#
|
|
838
|
-
npmglobalize -
|
|
839
|
-
|
|
840
|
-
#
|
|
841
|
-
npmglobalize -
|
|
842
|
-
|
|
843
|
-
#
|
|
844
|
-
npmglobalize -
|
|
845
|
-
|
|
846
|
-
#
|
|
847
|
-
npmglobalize -
|
|
848
|
-
|
|
849
|
-
#
|
|
850
|
-
npmglobalize -
|
|
851
|
-
|
|
852
|
-
#
|
|
853
|
-
npmglobalize -
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
```
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
This ensures you never accidentally run outdated code when the TypeScript source has changed.
|
|
907
|
-
|
|
908
|
-
## License
|
|
909
|
-
|
|
910
|
-
MIT
|
|
1
|
+
# npmglobalize
|
|
2
|
+
|
|
3
|
+
Transform `file:` dependencies to npm versions for publishing.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`npmglobalize` automates the workflow of publishing npm packages that use local `file:` references during development. It converts those references to proper npm versions, publishes everything in dependency order, and optionally restores the local references afterward.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @bobfrankston/npmglobalize
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Basic Usage
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
cd your-package
|
|
19
|
+
npmglobalize # Transform + publish (patch version)
|
|
20
|
+
npmglobalize --minor # Bump minor version
|
|
21
|
+
npmglobalize --major # Bump major version
|
|
22
|
+
|
|
23
|
+
# Or run from anywhere with a path
|
|
24
|
+
npmglobalize y:\path\to\your-package
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Key Features
|
|
28
|
+
|
|
29
|
+
### 🔗 Automatic Dependency Publishing (Default)
|
|
30
|
+
|
|
31
|
+
By default, `npmglobalize` ensures all `file:` dependencies are published **before** converting them:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npmglobalize # Auto-publishes file: deps in correct order
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
If you have:
|
|
38
|
+
```
|
|
39
|
+
lxtest
|
|
40
|
+
├── file:../lxlan-node
|
|
41
|
+
│ └── file:../lxland
|
|
42
|
+
└── file:../lxland
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
It automatically:
|
|
46
|
+
1. Publishes `lxland` (root dependency)
|
|
47
|
+
2. Publishes `lxlan-node` (depends on lxlan)
|
|
48
|
+
3. Converts and publishes `lxtest`
|
|
49
|
+
|
|
50
|
+
**Settings Propagation (Default Behavior):**
|
|
51
|
+
When publishing `file:` dependencies, these settings are **automatically inherited**:
|
|
52
|
+
- `--update-deps` / `--update-major` (update dependencies)
|
|
53
|
+
- `--fix` (run npm audit fix)
|
|
54
|
+
- `--conform` (fix .gitignore/.npmignore/.gitattributes and git config)
|
|
55
|
+
- `--verbose` / `--quiet`
|
|
56
|
+
- `--force` / `--files`
|
|
57
|
+
|
|
58
|
+
**⚠️ Visibility Settings (Smart Inheritance):**
|
|
59
|
+
- `--npmVisibility` is **only inherited by NEW repositories** (never published to npm before)
|
|
60
|
+
- **Existing repositories** keep their current npm visibility (public/private) unchanged
|
|
61
|
+
- `--gitVisibility` is inherited by all dependencies
|
|
62
|
+
|
|
63
|
+
This ensures you can safely set `--npmVisibility private` as a default for new packages without accidentally changing the visibility of your existing published packages.
|
|
64
|
+
|
|
65
|
+
**Why This Matters:**
|
|
66
|
+
Once a package is published to npm (public or private), changing its visibility later requires careful consideration. This smart inheritance protects your existing packages while making new ones default to safe settings.
|
|
67
|
+
|
|
68
|
+
Example - safely publish with new packages private by default:
|
|
69
|
+
```bash
|
|
70
|
+
npmglobalize --npmVisibility private
|
|
71
|
+
# ✓ Existing npm packages: keep their current visibility
|
|
72
|
+
# ✓ New packages (never published): default to private (safe!)
|
|
73
|
+
# ✓ Regular npm dependencies (express, etc.): unchanged
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Example with configuration file (recommended):
|
|
77
|
+
```bash
|
|
78
|
+
# In main package: create .globalize.json5 with "npmVisibility": "private"
|
|
79
|
+
npmglobalize
|
|
80
|
+
# ✓ Existing repos: publish with their current npm visibility
|
|
81
|
+
# ✓ Brand new repos: inherit private setting
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Skip auto-publishing** (use with caution):
|
|
85
|
+
```bash
|
|
86
|
+
npmglobalize -npd # --no-publish-deps
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 🔍 Prescan (Default)
|
|
90
|
+
|
|
91
|
+
Before any transform/publish, `npmglobalize` walks the full `file:` dep graph and reports all problems up front (unresolvable paths, missing `package.json`, unscoped packages with private intent, etc.). This lets you fix the whole punch list at once rather than being interrupted mid-cascade.
|
|
92
|
+
|
|
93
|
+
Errors abort (unless `--force`); warnings prompt to continue.
|
|
94
|
+
|
|
95
|
+
Skip the prescan with `-no-prescan` / `-nps`.
|
|
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
|
+
|
|
119
|
+
**Force republish** all file: dependencies even if versions exist:
|
|
120
|
+
```bash
|
|
121
|
+
npmglobalize --force-publish
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 📦 Dependency Updates
|
|
125
|
+
|
|
126
|
+
**Safe updates** (minor/patch only, respects semver):
|
|
127
|
+
```bash
|
|
128
|
+
npmglobalize --update-deps
|
|
129
|
+
```
|
|
130
|
+
- `express ^4.18.0` → `^4.21.0` ✓
|
|
131
|
+
- `lodash ^4.17.0` → `^4.17.21` ✓
|
|
132
|
+
- Won't update to `express ^5.0.0` (breaking change)
|
|
133
|
+
|
|
134
|
+
**Include major updates** (breaking changes):
|
|
135
|
+
```bash
|
|
136
|
+
npmglobalize --update-major
|
|
137
|
+
```
|
|
138
|
+
- Updates to latest including major versions
|
|
139
|
+
- Shows "(MAJOR)" indicator for breaking changes
|
|
140
|
+
|
|
141
|
+
**Note:** The `--update-deps` flag propagates to all file: dependencies, so one command updates your entire dependency tree.
|
|
142
|
+
|
|
143
|
+
### 🔒 Security Auditing
|
|
144
|
+
|
|
145
|
+
**Check vulnerabilities**:
|
|
146
|
+
```bash
|
|
147
|
+
npmglobalize # Shows audit at end
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Auto-fix vulnerabilities**:
|
|
151
|
+
```bash
|
|
152
|
+
npmglobalize --fix # Runs npm audit fix
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Disable audit**:
|
|
156
|
+
```bash
|
|
157
|
+
npmglobalize --no-fix
|
|
158
|
+
```
|
|
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
|
+
|
|
205
|
+
### 🔑 OAuth Credentials Handling
|
|
206
|
+
|
|
207
|
+
`npmglobalize` automatically detects `credentials.json` files and handles them based on OAuth app type:
|
|
208
|
+
|
|
209
|
+
- **Desktop/installed apps** (`"installed"` key in JSON): The `client_secret` is just a public app registration ID, not a real secret. Google's own docs state: *"the client_secret is obviously not treated as a secret."* These files are kept in the repo — `!credentials.json` is added to `.gitignore`/`.npmignore` to override any broader ignore patterns.
|
|
210
|
+
|
|
211
|
+
- **Web apps** (`"web"` key in JSON): The `client_secret` is a real secret. These files are automatically added to `.gitignore`/`.npmignore` to prevent accidental exposure.
|
|
212
|
+
|
|
213
|
+
This distinction also drives the **push protection auto-bypass**: when GitHub blocks a push because it detects an OAuth client secret, `npmglobalize` checks the credential file type. For installed apps, it auto-bypasses (marking as `false_positive`) since the credential is public by design.
|
|
214
|
+
|
|
215
|
+
### 🔄 File Reference Management
|
|
216
|
+
|
|
217
|
+
**Default behavior** (restore file: references after publish):
|
|
218
|
+
```bash
|
|
219
|
+
npmglobalize # Converts file: → npm, publishes, then restores file:
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Keep npm references** permanently:
|
|
223
|
+
```bash
|
|
224
|
+
npmglobalize --nofiles # Don't restore file: references
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Just transform** without publishing:
|
|
228
|
+
```bash
|
|
229
|
+
npmglobalize -np # --nopublish (formerly --apply)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
**Restore** from backup:
|
|
233
|
+
```bash
|
|
234
|
+
npmglobalize --cleanup # Restore original file: references
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### 📝 Release Notes via `.commitmsg`
|
|
238
|
+
|
|
239
|
+
For multi-line or reusable release notes, write them to a `.commitmsg` file in the package root instead of passing them on the command line:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
cat > .commitmsg <<'EOF'
|
|
243
|
+
Added foo feature
|
|
244
|
+
Fixed bar regression
|
|
245
|
+
EOF
|
|
246
|
+
npmglobalize
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Behavior:
|
|
250
|
+
- If `-m` / `-message` is **not** given and `.commitmsg` exists, its contents are used as the commit message (and force a release even if the working tree is otherwise clean).
|
|
251
|
+
- After a successful `npm publish`, npmglobalize:
|
|
252
|
+
1. Appends the contents to `npmchanges.md` under a `## v<version> — <YYYY-MM-DD>` header (creating the file if needed)
|
|
253
|
+
2. Deletes `.commitmsg`
|
|
254
|
+
3. Commits both changes as `Log v<version> to npmchanges.md` and pushes
|
|
255
|
+
|
|
256
|
+
Notes:
|
|
257
|
+
- **Git/GitHub only.** npm publish does not consume git commit messages; `npmchanges.md` lives in the git repo and on GitHub but is excluded from the published npm tarball (the standard `*.md` rule keeps only `README.md`).
|
|
258
|
+
- If both `-m` and `.commitmsg` are present, `-m` wins and `.commitmsg` is left alone (not consumed).
|
|
259
|
+
- If publish fails, `.commitmsg` is preserved for the next attempt.
|
|
260
|
+
- `.commitmsg` is auto-added to `.npmignore` (security pattern) so it never leaks into the tarball.
|
|
261
|
+
|
|
262
|
+
### 🔧 Git Integration & Error Recovery
|
|
263
|
+
|
|
264
|
+
**Automatic tag conflict resolution**:
|
|
265
|
+
```bash
|
|
266
|
+
npmglobalize --fix-tags # Auto-fix version/tag mismatches
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
When a previous publish fails, git tags may conflict with package.json version. The `--fix-tags` option (or automatic detection) will clean up these conflicts.
|
|
270
|
+
|
|
271
|
+
**Automatic rebase** when local is behind remote:
|
|
272
|
+
```bash
|
|
273
|
+
npmglobalize --rebase # Auto-rebase if behind remote
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
For single-developer projects, this safely pulls remote changes before publishing.
|
|
277
|
+
|
|
278
|
+
**Failed publish recovery**: If a previous run bumped the version locally but the publish or push failed (e.g., network error, push protection), `npmglobalize` detects this automatically on the next run. It checks whether the current version actually exists on npm — if not, it republishes without re-bumping the version. Unpushed commits from a failed push are also pushed automatically.
|
|
279
|
+
|
|
280
|
+
**GitHub Push Protection (GH013)**: When GitHub's secret scanning blocks a push, `npmglobalize` detects the error and checks whether the flagged secrets are actually safe. For Google OAuth desktop/installed app credentials (where the "client_secret" is just a public app identifier, not a real secret), it automatically bypasses push protection via the GitHub API (`gh` CLI required) and retries the push. For other secret types, it displays the unblock URLs with guidance.
|
|
281
|
+
|
|
282
|
+
**Note:** This tool is designed for single-developer, single-branch workflows where automatic rebase and tag cleanup are safe operations.
|
|
283
|
+
|
|
284
|
+
### 📂 Git Repository Setup
|
|
285
|
+
|
|
286
|
+
**Change visibility of an existing repo:**
|
|
287
|
+
```bash
|
|
288
|
+
npmglobalize -git public # Makes the GitHub repo public (with confirmation)
|
|
289
|
+
npmglobalize -git private # Makes the GitHub repo private
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### Missing local `.git` (adopt vs fresh init)
|
|
293
|
+
|
|
294
|
+
When npmglobalize runs in a directory with no `.git`, it first checks for a
|
|
295
|
+
reachable `repository.url` in `package.json`. If found, it offers two paths:
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
How would you like to set up git?
|
|
299
|
+
1) Adopt history from existing remote (recommended)
|
|
300
|
+
2) Initialize fresh git repository
|
|
301
|
+
a) Adopt ALL (don't ask again for remaining deps)
|
|
302
|
+
3) Use local install only (skip git/publish)
|
|
303
|
+
4) Abort
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**Adopt** runs:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
git init
|
|
310
|
+
git remote add origin <package.json repository.url>
|
|
311
|
+
git fetch origin
|
|
312
|
+
# Point HEAD at origin/<defaultBranch> without touching the working tree
|
|
313
|
+
git update-ref refs/heads/<branch> origin/<branch>
|
|
314
|
+
git symbolic-ref HEAD refs/heads/<branch>
|
|
315
|
+
git reset # mixed: refresh index, keep working tree
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
After adoption, your local files appear as **uncommitted changes on top of the
|
|
319
|
+
remote's HEAD** — `git status` shows the drift, and npmglobalize's normal flow
|
|
320
|
+
will commit them on the next publish. **No force-push is required**, and the
|
|
321
|
+
remote's history is preserved.
|
|
322
|
+
|
|
323
|
+
If the remote is not reachable (no `repository` field, network/auth failure,
|
|
324
|
+
deleted repo), the original "Initialize fresh git repository" prompt is shown
|
|
325
|
+
instead.
|
|
326
|
+
|
|
327
|
+
CLI flags:
|
|
328
|
+
|
|
329
|
+
- `-init` — auto path; adopts if a reachable remote is found, otherwise falls
|
|
330
|
+
back to fresh `git init` + `gh repo create`.
|
|
331
|
+
- `-adopt` — **strict**: aborts if no reachable remote in `package.json.repository`.
|
|
332
|
+
Use this when you want to be sure no new GitHub repo is created (e.g. in
|
|
333
|
+
scripts, or when re-attaching a tree of packages to existing repos).
|
|
334
|
+
|
|
335
|
+
When initializing a new repository with `--init`, npmglobalize automatically sets up:
|
|
336
|
+
|
|
337
|
+
**File structure** (per programming.md standards):
|
|
338
|
+
- `.gitignore` - Node.js best practices (node_modules, secrets, certificates, etc.)
|
|
339
|
+
- `.npmignore` - Publishing filters (excludes .git, tests, source files, etc.)
|
|
340
|
+
- **noEmit projects:** `*.ts`, `*.map`, and `tsconfig.json` are kept (not ignored) since TS files are the runtime files
|
|
341
|
+
- `.gitattributes` - Forces LF line endings for all text files
|
|
342
|
+
|
|
343
|
+
**Git configuration** (ensures cross-platform compatibility):
|
|
344
|
+
```bash
|
|
345
|
+
git config core.autocrlf false # Disable CRLF conversion
|
|
346
|
+
git config core.eol lf # Force LF line endings
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
This ensures consistent line endings across Windows, macOS, and Linux, preventing "modified file" issues caused by line ending differences.
|
|
350
|
+
|
|
351
|
+
### 🔍 HEAD not on a branch — repaired automatically
|
|
352
|
+
|
|
353
|
+
Git leaves HEAD "detached" (pointing at a commit rather than at a branch) after checking out a
|
|
354
|
+
commit or a tag, and — far more often in practice — after a `git pull --rebase` that was
|
|
355
|
+
interrupted. The rebase applies its commits, then stops before moving the branch ref, so the
|
|
356
|
+
work exists on a real commit that no branch names.
|
|
357
|
+
|
|
358
|
+
npmglobalize repairs that itself rather than stopping and asking you to type git commands. The
|
|
359
|
+
working tree is authoritative, so it:
|
|
360
|
+
|
|
361
|
+
1. Clears the unfinished rebase / cherry-pick / revert bookkeeping with `--quit`, which keeps
|
|
362
|
+
HEAD and the working tree exactly as they are (unlike `--abort`, which would throw away the
|
|
363
|
+
commits the interrupted run produced).
|
|
364
|
+
2. Moves the branch to the current commit with `git checkout -B <branch>`, and restores its
|
|
365
|
+
upstream tracking.
|
|
366
|
+
3. Prints what moved, naming the branch and both commits:
|
|
367
|
+
|
|
368
|
+
```
|
|
369
|
+
⚠ HEAD is not on a branch — left by an unfinished rebase, at 344431e "Restore file: dependencies"
|
|
370
|
+
Cleared the unfinished rebase (its commits are kept)
|
|
371
|
+
✓ On branch main at 344431e "Restore file: dependencies"
|
|
372
|
+
Branch main previously pointed at f3ec2e6 "Pre-release commit", which is not in this history.
|
|
373
|
+
To put main back there: git reset --hard f3ec2e6
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
The branch it picks is, in order: the branch the unfinished rebase was rebuilding (from the
|
|
377
|
+
rebase state), the remote's default branch (`origin/HEAD`), or whichever of `main` / `master`
|
|
378
|
+
the repository actually has. It is never assumed to be `master`.
|
|
379
|
+
|
|
380
|
+
**The one case it refuses:** files with unresolved conflict markers. There the working tree is
|
|
381
|
+
not what you meant to publish, so npmglobalize lists the conflicted files and stops.
|
|
382
|
+
|
|
383
|
+
## Command Reference
|
|
384
|
+
|
|
385
|
+
<!-- NOTE: Keep this in sync with the -help output in cli.ts printHelp().
|
|
386
|
+
The README expands on options with examples and context;
|
|
387
|
+
cli.ts is the concise quick-reference. Update both when adding/changing flags.
|
|
388
|
+
Both -flag and --flag are accepted; single-dash is shown as primary. -->
|
|
389
|
+
|
|
390
|
+
### Release Options
|
|
391
|
+
```
|
|
392
|
+
-patch Bump patch version (default)
|
|
393
|
+
-minor Bump minor version
|
|
394
|
+
-major Bump major version
|
|
395
|
+
-nopublish, -np Just transform, don't publish (persisted to config)
|
|
396
|
+
-cleanup Restore file: dependencies from .dependencies backup
|
|
397
|
+
-m, -message <msg> Custom commit message (forces release even without changes)
|
|
398
|
+
If -m not given, a `.commitmsg` file (if present) is used instead.
|
|
399
|
+
See "Release Notes via .commitmsg" below.
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Dependency Options
|
|
403
|
+
```
|
|
404
|
+
-update-deps, -ud Update package.json to latest versions (safe: minor/patch)
|
|
405
|
+
-update-major Allow major version updates (breaking changes)
|
|
406
|
+
-publish-deps Auto-publish file: dependencies (default)
|
|
407
|
+
-pd Like -publish-deps, plus auto-yes to dep-cascade prompts (private only)
|
|
408
|
+
-no-publish-deps, -npd Skip auto-publishing file: dependencies
|
|
409
|
+
-no-prescan, -nps Skip upfront dep-graph prescan
|
|
410
|
+
-force-publish Republish dependencies even if version exists
|
|
411
|
+
-fix Run npm audit fix after transformation
|
|
412
|
+
-no-fix Don't run npm audit
|
|
413
|
+
-no-use-paths, -nup Declare package standalone; do not resolve file: deps
|
|
414
|
+
from sibling checkouts (see Configuration File)
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Install Options
|
|
418
|
+
```
|
|
419
|
+
-install, -i Install globally after publish (from registry)
|
|
420
|
+
-link Install globally via symlink (npm install -g .)
|
|
421
|
+
-local Local install only — skip transform/publish, just npm install -g .
|
|
422
|
+
-wsl Also install in WSL
|
|
423
|
+
-once Don't persist flags to .globalize.json5
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Mode Options
|
|
427
|
+
```
|
|
428
|
+
-files Keep file: paths after publish (default)
|
|
429
|
+
-nofiles Keep npm versions permanently
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### Git/npm Visibility
|
|
433
|
+
```
|
|
434
|
+
-git private Set GitHub repo to private (default for new repos)
|
|
435
|
+
-git public Set GitHub repo to public (requires confirmation)
|
|
436
|
+
Works on both new and existing repos
|
|
437
|
+
-npm <values> Comma-separated list of npm options:
|
|
438
|
+
private (default) | public — package visibility
|
|
439
|
+
ts — keep .ts source (and *.map,
|
|
440
|
+
tsconfig.json) in npm tarball
|
|
441
|
+
nts — exclude .ts source
|
|
442
|
+
(default for non-noEmit projects)
|
|
443
|
+
Example: -npm public,ts
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
`ts` is already the default on git (source is tracked). Pass `-npm ts` to ship
|
|
447
|
+
the same files to npm — useful for debugging installed packages or "source on
|
|
448
|
+
demand" packages. `noEmit` projects automatically ship `.ts` files (they *are*
|
|
449
|
+
the runtime); pass `-npm nts` to override. `allowTs` persists to
|
|
450
|
+
`.globalize.json5`.
|
|
451
|
+
|
|
452
|
+
### Workspace Options
|
|
453
|
+
```
|
|
454
|
+
-w, -workspace <pkg> Filter to specific package (repeatable)
|
|
455
|
+
-no-workspace Disable workspace mode at a workspace root
|
|
456
|
+
-continue-on-error Continue if a package fails in workspace mode
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Workspace mode is auto-detected when run from a root with `"private": true` and a `workspaces` field.
|
|
460
|
+
|
|
461
|
+
### Other Options
|
|
462
|
+
```
|
|
463
|
+
-init Initialize git/npm if needed (creates .gitignore, .npmignore,
|
|
464
|
+
.gitattributes, and configures git for LF line endings).
|
|
465
|
+
If package.json.repository.url is reachable, adopts its
|
|
466
|
+
history instead of creating a fresh repo.
|
|
467
|
+
-adopt Strict adopt: require a reachable git remote in
|
|
468
|
+
package.json.repository. Abort if probe fails. Skips prompt.
|
|
469
|
+
-strict-imports, -import-check
|
|
470
|
+
Opt in to scanning .ts/.js source for imports of packages not
|
|
471
|
+
declared in any dependencies bucket. Off by default — the
|
|
472
|
+
always-declare style this enforces doesn't hold in monorepos
|
|
473
|
+
that rely on workspace cross-refs or the -public-deps cascade
|
|
474
|
+
(those produce noisy prompts that get dismissed reflexively,
|
|
475
|
+
which defeats the safety purpose). Use only on packages where
|
|
476
|
+
every import is meant to be a direct package.json declaration.
|
|
477
|
+
When it does fire, it catches silent runtime failures —
|
|
478
|
+
ERR_MODULE_NOT_FOUND on a clean install of a published tarball
|
|
479
|
+
that was resolving via an ambient parent/global node_modules
|
|
480
|
+
at dev time.
|
|
481
|
+
-force Continue despite git errors
|
|
482
|
+
-dry-run Preview what would happen
|
|
483
|
+
-quiet Suppress npm warnings (default)
|
|
484
|
+
-verbose Show detailed output
|
|
485
|
+
-conform Update .gitignore/.npmignore/.gitattributes to best practices
|
|
486
|
+
and configure git for LF line endings (fixes existing repos)
|
|
487
|
+
For noEmit projects: removes *.ts/*.map/tsconfig.json from .npmignore
|
|
488
|
+
-asis Skip ignore file checks (or set "asis": true in .globalize.json5)
|
|
489
|
+
-fix-tags Automatically fix version/tag mismatches
|
|
490
|
+
-rebase Automatically rebase if local is behind remote
|
|
491
|
+
-clean-nested-modules, -clean-nested
|
|
492
|
+
Before npm pack, wipe node_modules/ inside each file: dep
|
|
493
|
+
target. Fixes arborist "Cannot read properties of null"
|
|
494
|
+
crashes caused by sibling file: deps with nested
|
|
495
|
+
node_modules. Suggested automatically when the error hits.
|
|
496
|
+
-ts7-report, -deprecation-report
|
|
497
|
+
Report-only: scan this package and its file: deps for
|
|
498
|
+
compilerOptions removed in TypeScript 7 and list a migration
|
|
499
|
+
to-do. Writes nothing.
|
|
500
|
+
-tsfix, -ts7-fix
|
|
501
|
+
One-off utility, NOT part of the main flow: apply the TS7
|
|
502
|
+
tsconfig migration to a package (and its file: deps) and exit —
|
|
503
|
+
no build, no commit, no push, no publish. Intended as a
|
|
504
|
+
temporary tool for fixing a local/subdirectory tsconfig in
|
|
505
|
+
place. The normal release flow already applies the same
|
|
506
|
+
migration automatically when a build hits a TS7 deprecation
|
|
507
|
+
error, so day-to-day you never need this flag.
|
|
508
|
+
npmglobalize <path> -tsfix
|
|
509
|
+
-show Show package.json dependency changes
|
|
510
|
+
-package, -pkg Update package.json scripts to use npmglobalize (see below)
|
|
511
|
+
-h, -help Show help
|
|
512
|
+
-v, -version Show version
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
## Using in package.json
|
|
516
|
+
|
|
517
|
+
You can wire npmglobalize into your package.json `scripts` so that `npm run release` handles publishing:
|
|
518
|
+
|
|
519
|
+
```json
|
|
520
|
+
{
|
|
521
|
+
"scripts": {
|
|
522
|
+
"release": "npmglobalize"
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
The `--package` (`-pkg`) flag does this automatically — it adds a `release` script (renaming any existing `release`/`installer` scripts to `old-release`/`old-installer`):
|
|
528
|
+
|
|
529
|
+
```bash
|
|
530
|
+
npmglobalize --package # Sets up "release": "npmglobalize" in package.json
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
After that, publishing is just:
|
|
534
|
+
```bash
|
|
535
|
+
npm run release # Same as running npmglobalize directly
|
|
536
|
+
npm run release -- --minor # Pass flags through
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
You can also combine it with `.globalize.json5` for persistent options so `npm run release` always uses your preferred settings (install, visibility, etc.).
|
|
540
|
+
|
|
541
|
+
## Configuration File
|
|
542
|
+
|
|
543
|
+
Settings can be saved in `.globalize.json5`:
|
|
544
|
+
|
|
545
|
+
```json5
|
|
546
|
+
{
|
|
547
|
+
// npmglobalize configuration (JSON5 format)
|
|
548
|
+
"bump": "patch", // Version bump type
|
|
549
|
+
"install": true, // Auto-install globally
|
|
550
|
+
"wsl": false, // Also install in WSL
|
|
551
|
+
"fix": true, // Auto-run npm audit fix
|
|
552
|
+
"verbose": false, // Show detailed output
|
|
553
|
+
"gitVisibility": "private",
|
|
554
|
+
"npmVisibility": "public",
|
|
555
|
+
"usePaths": true // Resolve file: deps from sibling checkouts (see below)
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
Configuration persists across runs. CLI flags override config file.
|
|
560
|
+
|
|
561
|
+
### `usePaths` — Standalone packages
|
|
562
|
+
|
|
563
|
+
Default: `true`. Set to `false` (or pass `-no-use-paths` / `-nup`) to mark a
|
|
564
|
+
package as **standalone** — one that should be publishable/installable on a
|
|
565
|
+
machine that does not have sibling `file:` dep checkouts available. Example:
|
|
566
|
+
a backup/recovery utility you want to `npm install -g` on any host.
|
|
567
|
+
|
|
568
|
+
Currently this setting is **declarative** — it is parsed, persisted to
|
|
569
|
+
`.globalize.json5`, and surfaced in the settings banner, but the tool does
|
|
570
|
+
not yet change its behavior based on it. Behavior wiring (e.g. resolving
|
|
571
|
+
`file:` deps to the latest published npm version instead of walking siblings)
|
|
572
|
+
is planned.
|
|
573
|
+
|
|
574
|
+
### `upstream` — who consumes this package
|
|
575
|
+
|
|
576
|
+
**Experimental.** Bookkeeping, not a setting. When a package with `file:` deps
|
|
577
|
+
publishes, npmglobalize appends an entry to each **dependency's**
|
|
578
|
+
`.globalize.json5`:
|
|
579
|
+
|
|
580
|
+
```json5
|
|
581
|
+
{
|
|
582
|
+
"install": true,
|
|
583
|
+
|
|
584
|
+
// FYI: packages that depend on this one (immediate consumers,
|
|
585
|
+
// recorded when each of them publishes). Nothing is updated
|
|
586
|
+
// automatically; follow each path's own .globalize.json5 to
|
|
587
|
+
// walk further out.
|
|
588
|
+
"upstream": [
|
|
589
|
+
{"path":"Y:\\dev\\utils\\winpos","version":"2.0.51","updated":"2026-08-09"},
|
|
590
|
+
],
|
|
591
|
+
}
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
So publishing `winpos` (which has `"@bobfrankston/msger": "file:../msgx/msger"`)
|
|
595
|
+
records winpos in **msger's** config. Each entry carries the consumer's
|
|
596
|
+
checkout path, its version at the time, and the date.
|
|
597
|
+
|
|
598
|
+
Only **immediate** consumers are recorded. A full consumer tree is a walk, not
|
|
599
|
+
a copy: follow each entry's path and read that package's own `upstream` list.
|
|
600
|
+
That keeps each file small and self-maintaining — no package has to know about
|
|
601
|
+
anything beyond its own direct consumers.
|
|
602
|
+
|
|
603
|
+
The list is **FYI** — it is recorded, preserved across publishes, and printed
|
|
604
|
+
in the Release Summary of the package that owns it. Nothing is rebuilt,
|
|
605
|
+
republished, or reinstalled on its behalf. An entry appears the first time a
|
|
606
|
+
consumer publishes, and is refreshed in place on every publish after that.
|
|
607
|
+
|
|
608
|
+
#### Scope and limits
|
|
609
|
+
|
|
610
|
+
**`file:` deps only.** A dependency referenced by npm version (`"^0.1.39"`)
|
|
611
|
+
is never recorded, because npmglobalize has no checkout path for it — it
|
|
612
|
+
consumes the published tarball, not a sibling directory. To have a consumer
|
|
613
|
+
show up in a library's list, that consumer must reference it as
|
|
614
|
+
`file:../<lib>`.
|
|
615
|
+
|
|
616
|
+
**Usually not committed.** The entry is written into the dependency's own
|
|
617
|
+
checkout, and npmglobalize then tries to commit it there
|
|
618
|
+
(`Record upstream <consumer>@<version>`) and push if that repo has a remote.
|
|
619
|
+
In practice that commit is usually skipped: `.globalize.json5` is in the
|
|
620
|
+
standard ignore template, so most repos ignore it and the entry stays as
|
|
621
|
+
untracked local state. That is a reasonable failure mode for an experimental
|
|
622
|
+
mechanism — the list rewrites itself on every publish, so there is nothing to
|
|
623
|
+
merge and nothing to reconcile between machines. It also means the list is
|
|
624
|
+
**per-machine**, not shared history.
|
|
625
|
+
|
|
626
|
+
When the commit does happen (a repo that tracks its `.globalize.json5`, as
|
|
627
|
+
npmglobalize itself does), only that one file is staged and committed by
|
|
628
|
+
pathspec — anything else the dependency had staged or modified is left exactly
|
|
629
|
+
as it was. A dependency that isn't a git repo is written and skipped the same
|
|
630
|
+
way.
|
|
631
|
+
|
|
632
|
+
The alternative would be recording this in `package.json`, which is tracked
|
|
633
|
+
and published — every consumer of a library would then download that library's
|
|
634
|
+
list of local checkout paths in its tarball. Keeping it in `.globalize.json5`
|
|
635
|
+
keeps it out of the package entirely.
|
|
636
|
+
|
|
637
|
+
## Common Workflows
|
|
638
|
+
|
|
639
|
+
### Standard Release
|
|
640
|
+
```bash
|
|
641
|
+
npmglobalize --install # Publish + install globally
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
### Release with Dependency Chain
|
|
645
|
+
```bash
|
|
646
|
+
cd my-app # Has file: deps
|
|
647
|
+
npmglobalize # Publishes all deps automatically
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
### Safe Dependency Updates
|
|
651
|
+
```bash
|
|
652
|
+
npmglobalize --update-deps # Update to latest safe versions
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
### Security Fixes
|
|
656
|
+
```bash
|
|
657
|
+
npmglobalize --fix # Fix vulnerabilities + release
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
### Force Update Everything
|
|
661
|
+
```bash
|
|
662
|
+
npmglobalize --force-publish --update-major
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
### Preview Changes
|
|
666
|
+
```bash
|
|
667
|
+
npmglobalize --dry-run # See what would happen
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
## How It Works
|
|
671
|
+
|
|
672
|
+
1. **Validates** package.json and git status
|
|
673
|
+
2. **Checks** if current version is on npm (recovers from failed publishes)
|
|
674
|
+
3. **Updates dependencies** (if `--update-deps`)
|
|
675
|
+
4. **Builds `file:` deps in topological order**, then the target itself, so consumers' `tsc` reads up-to-date `.d.ts` from sibling checkouts whose source has changed (see [Build Cascade](#build-cascade))
|
|
676
|
+
5. **Publishes file: dependencies** (if needed)
|
|
677
|
+
6. **Backs up** original file: references to `.dependencies`
|
|
678
|
+
7. **Converts** `file:` → npm version references
|
|
679
|
+
8. **Commits** changes
|
|
680
|
+
9. **Bumps** version (using npm version) — skipped if recovering a failed publish
|
|
681
|
+
10. **Publishes** to npm
|
|
682
|
+
11. **Pushes** to git (with push-protection detection and auto-bypass)
|
|
683
|
+
12. **Installs** globally (if `--install`)
|
|
684
|
+
13. **Restores** file: references (if `--files`, default)
|
|
685
|
+
14. **Runs audit** (shows security status)
|
|
686
|
+
|
|
687
|
+
## Operational Details
|
|
688
|
+
|
|
689
|
+
### Build Cascade
|
|
690
|
+
|
|
691
|
+
Before transforming or publishing anything, `npmglobalize` builds `file:` dependencies in topological order — deps before consumers — and then builds the target itself. This guarantees the target's `tsc` reads up-to-date `.d.ts` and `.js` from sibling checkouts even when a dep's source has changed since its last build.
|
|
692
|
+
|
|
693
|
+
For each project visited (the target and every transitive `file:` dep):
|
|
694
|
+
|
|
695
|
+
- If `tsconfig.json` is missing or has `"noEmit": true` → **skip** (not a TypeScript build), unless the `build` script runs `importgen` — a plain-JS browser app still needs its import map regenerated.
|
|
696
|
+
- If `tsconfig.json` exists but `package.json` has no `"build"` script → **prompt** to add `"build": "tsc"` (plus a `tsc -p <dir>` per sub-project — see below). Decline and that project is skipped.
|
|
697
|
+
- Otherwise → run `npm run build`. A failure halts the cascade unless `-force` is passed.
|
|
698
|
+
|
|
699
|
+
Cycle-safe via a shared visited set; each project is built at most once per run.
|
|
700
|
+
|
|
701
|
+
This complements the existing publish cascade (which ensures version refs are correct) by closing the build-freshness gap that `npm install` alone left open.
|
|
702
|
+
|
|
703
|
+
#### Import maps (`importgen`) as a build step
|
|
704
|
+
|
|
705
|
+
Browser projects that use [`importgen`](https://www.npmjs.com/package/@bobfrankston/importgen) have historically regenerated their import map from `.vscode/tasks.json`, which only runs when VS Code opens the folder — so a command-line or CI build could publish a stale map. `npmglobalize` treats the import map as a build product and moves the step into the package's own `build` script, where every build path picks it up.
|
|
706
|
+
|
|
707
|
+
Before building each project, it checks whether the project is an importgen project, in this order:
|
|
708
|
+
|
|
709
|
+
1. `.vscode/tasks.json` has a task whose `command` is `importgen` (the HTML file, if the task names one, is reused).
|
|
710
|
+
2. A root-level `.htm`/`.html` file already contains a `<script type="importmap">` block (`index.html`, `default.html`, `default.htm` are checked first, so a stray `temp.htm` doesn't win).
|
|
711
|
+
3. `importgen` is in `dependencies`/`devDependencies` **and** the project has one of those HTML files — the HTML requirement keeps packages that merely *use* importgen as a library from matching.
|
|
712
|
+
|
|
713
|
+
If a signal matches and the `build` script doesn't already run `importgen`, you're prompted to rewrite it — e.g. `"build": "tsc"` → `"build": "importgen default.htm && tsc"`. The HTML file is named explicitly so importgen doesn't have to guess. Decline and `"importgen": false` is written to `.globalize.json5`, which suppresses the prompt for good; `-noimportgen` does the same from the command line.
|
|
714
|
+
|
|
715
|
+
Once wired, the freshness check gains a second condition: a project whose build runs importgen is only considered up to date if the generated HTML is at least as new as `package.json`. Adding a dependency changes the import map without touching any `.ts` file, which the source-vs-output comparison alone would miss.
|
|
716
|
+
|
|
717
|
+
#### Sub-projects (a second `tsconfig.json` in a sub-directory)
|
|
718
|
+
|
|
719
|
+
A package can hold more than one TypeScript project. The common shape is a service worker in `Sw/` with its own `tsconfig.json` (`lib: ["WebWorker"]`, its own `outDir`) that the root `tsconfig.json` lists under `exclude` — so a bare `"build": "tsc"` compiles everything *except* the service worker, and the stale `sw2.js` ships. As with import maps, these have historically been built only by a second `.vscode/tasks.json` watcher, which runs on folder open and nowhere else.
|
|
720
|
+
|
|
721
|
+
Before building, `npmglobalize` looks for sub-projects, in this order:
|
|
722
|
+
|
|
723
|
+
1. `.vscode/tasks.json` has a task that runs `tsc` with `"options": { "cwd": "${workspaceFolder}/Sw" }` or with `-p`/`--project` naming a sub-directory (authoritative — it's how the project is actually built today; the task's `label` is quoted back in the prompt).
|
|
724
|
+
2. An immediate sub-directory containing its own `tsconfig.json`. Build output and vendored trees (`node_modules`, `prev`, `dist`, `built`, `out`, `wwwroot`, `coverage`, `temp`, `preflight`, dot-directories) are never scanned.
|
|
725
|
+
|
|
726
|
+
Nothing is detected when the root `tsconfig.json` uses project `references` — that build graph belongs to `tsc -b` and isn't second-guessed.
|
|
727
|
+
|
|
728
|
+
When the `build` script is `tsc`-driven and doesn't already compile a detected sub-project, you're prompted to append it — `"build": "tsc"` → `"build": "tsc && tsc -p Sw"`, or for an importgen project `"build": "importgen default.htm && tsc && tsc -p Sw"`. Already-wired scripts are recognized in any of their spellings (`tsc -p Sw`, `tsc --project ./Sw/tsconfig.json`, `cd Sw && tsc`), and the directory is emitted with its real on-disk casing so the script still works on WSL and CI. Decline and `"subProjects": false` is written to `.globalize.json5`, suppressing the prompt for good; a `"subProjects": ["Sw"]` array pins the list instead of detecting it.
|
|
729
|
+
|
|
730
|
+
Sub-projects the build actually compiles are also folded into the freshness check — the package rebuilds when a sub-project's sources are newer than its output. A sub-project that emits *outside* its own directory (`"outDir": ".."`, the usual service-worker case) always reports stale: comparing its sources against the whole package would prove nothing.
|
|
731
|
+
|
|
732
|
+
#### TypeScript 6 `types` auto-fix
|
|
733
|
+
|
|
734
|
+
TypeScript 6 dropped the legacy behavior of auto-including every installed `@types/*` package. A `tsconfig.json` with no explicit `compilerOptions.types` then loses the Node globals (`process`, `Buffer`, …) and the build fails with `TS2591`.
|
|
735
|
+
|
|
736
|
+
Before each build, when the global `tsc` is version 6 or newer, `npmglobalize` patches the project's `tsconfig.json` to add an explicit `types` list — enumerating the installed `@types/*` packages (e.g. `"types": ["node", …]`) — restoring the old behavior. The edit is:
|
|
737
|
+
|
|
738
|
+
- **Conservative** — only applied when `compilerOptions.types` is **absent**, `node_modules/@types/node` is actually installed, and there is no `extends` (whose merged `types` can't be seen). An explicit `types` you already set is never overridden.
|
|
739
|
+
- **Format-preserving** — the single `types` key is inserted into the existing `compilerOptions` block; comments, ordering, and indentation are left intact.
|
|
740
|
+
- **Idempotent** — once the list is present, subsequent runs skip it.
|
|
741
|
+
|
|
742
|
+
If the patch can't be applied for some reason and the build still fails with `TS2591`, the failure summary prints a hint to add `"types": ["node"]` manually.
|
|
743
|
+
|
|
744
|
+
### The `.dependencies` Backup (Internal/Transient)
|
|
745
|
+
|
|
746
|
+
**You should never see `.dependencies` in your `package.json` under normal operation.** It is a temporary internal backup that exists only during the brief publish cycle and is removed automatically when the cycle completes.
|
|
747
|
+
|
|
748
|
+
During publishing, `npmglobalize` temporarily replaces `file:` references with npm version strings. The original `file:` entries are stashed in `.dependencies` (and `.devDependencies`, etc.) so they can be restored afterward. Once the publish succeeds and `file:` paths are restored, `.dependencies` is deleted. A normal run leaves no trace of it.
|
|
749
|
+
|
|
750
|
+
**If you see `.dependencies` in your `package.json`, something went wrong** — the tool crashed, was killed, or the publish failed partway through. It is not a feature to rely on or edit manually.
|
|
751
|
+
|
|
752
|
+
**Recovery:**
|
|
753
|
+
- **Re-run `npmglobalize`**: It detects leftover `.dependencies`, restores the originals, and continues normally. Self-healing is automatic.
|
|
754
|
+
- **Manual restore**: `npmglobalize -cleanup` restores file: deps and removes the `.dependencies` backup.
|
|
755
|
+
|
|
756
|
+
**Why a persistent backup?** If the tool crashes hard (killed process, power failure, npm timeout), there's no cleanup code to run. The backup in `package.json` survives because it was written before the risky operations began. The next run self-heals.
|
|
757
|
+
|
|
758
|
+
### Flag Conventions
|
|
759
|
+
|
|
760
|
+
Both `-flag` and `--flag` are accepted. Single-dash is the primary convention:
|
|
761
|
+
```bash
|
|
762
|
+
npmglobalize -patch # same as --patch
|
|
763
|
+
npmglobalize -np # same as --nopublish
|
|
764
|
+
npmglobalize -local # same as --local
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
### Persistent vs One-Shot Flags
|
|
768
|
+
|
|
769
|
+
Some flags are **persisted** to `.globalize.json5` when set from the CLI:
|
|
770
|
+
- `-install`, `-link`, `-wsl`, `-files`, `-fix` — install/build preferences
|
|
771
|
+
- `-np` (noPublish) — once set, prevents accidental publishes
|
|
772
|
+
- `-local` — remembers "this project is local-only"
|
|
773
|
+
- `-git`/`-npm` visibility
|
|
774
|
+
|
|
775
|
+
Other flags are **one-shot** (never persisted):
|
|
776
|
+
- `-cleanup`, `-init`, `-dry-run`, `-message` — situational actions
|
|
777
|
+
- `-update-deps`, `-update-major`, `-force-publish` — explicit per-run choices
|
|
778
|
+
- `-conform`, `-asis` — one-time fixes
|
|
779
|
+
|
|
780
|
+
Use `-once` to prevent any flag from persisting on that run:
|
|
781
|
+
```bash
|
|
782
|
+
npmglobalize -np -once # No-publish this run only, don't remember it
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
### Local Install (`-local`)
|
|
786
|
+
|
|
787
|
+
Skip all transform/publish logic and just run `npm install -g .` with `file:` deps as-is. Use this when you want to install a CLI tool locally for your own use without publishing anything:
|
|
788
|
+
|
|
789
|
+
```bash
|
|
790
|
+
npmglobalize -local # Install globally from local directory
|
|
791
|
+
npmglobalize -local -wsl # Also install in WSL
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
This is useful for:
|
|
795
|
+
- Development tools you don't publish
|
|
796
|
+
- Testing a CLI before publishing
|
|
797
|
+
- Projects with `file:` deps that should stay as-is
|
|
798
|
+
|
|
799
|
+
### Private Packages
|
|
800
|
+
|
|
801
|
+
Packages with `"private": true` in `package.json` skip the npm publish step. Dependencies are still transformed and restored — the publish is the only thing skipped.
|
|
802
|
+
|
|
803
|
+
## Version Checking
|
|
804
|
+
|
|
805
|
+
When publishing file: dependencies, checks if each version exists on npm:
|
|
806
|
+
- ✅ Exists → Skip, use existing version
|
|
807
|
+
- ❌ Missing → Publish it first
|
|
808
|
+
- 🔄 Force → Use `--force-publish` to republish
|
|
809
|
+
|
|
810
|
+
## Examples
|
|
811
|
+
|
|
812
|
+
```bash
|
|
813
|
+
# Basic release
|
|
814
|
+
npmglobalize
|
|
815
|
+
|
|
816
|
+
# Run on a different project
|
|
817
|
+
npmglobalize y:\dev\myproject
|
|
818
|
+
|
|
819
|
+
# Auto-fix tag conflicts and rebase
|
|
820
|
+
npmglobalize -fix-tags -rebase
|
|
821
|
+
|
|
822
|
+
# Release with updates and security fixes
|
|
823
|
+
npmglobalize -update-deps -fix
|
|
824
|
+
|
|
825
|
+
# Just update package.json, don't publish
|
|
826
|
+
npmglobalize -np -update-deps
|
|
827
|
+
|
|
828
|
+
# Force republish all dependencies
|
|
829
|
+
npmglobalize -force-publish -update-major
|
|
830
|
+
|
|
831
|
+
# Release + install on Windows and WSL (from registry)
|
|
832
|
+
npmglobalize -install -wsl
|
|
833
|
+
|
|
834
|
+
# Release + link on Windows and WSL (symlink)
|
|
835
|
+
npmglobalize -link -wsl
|
|
836
|
+
|
|
837
|
+
# Install locally without publishing (file: deps stay as-is)
|
|
838
|
+
npmglobalize -local
|
|
839
|
+
|
|
840
|
+
# Restore original file: references
|
|
841
|
+
npmglobalize -cleanup
|
|
842
|
+
|
|
843
|
+
# Initialize git (adopt existing remote if reachable, else fresh) + release
|
|
844
|
+
npmglobalize -init
|
|
845
|
+
|
|
846
|
+
# Strict adopt: re-attach to remote in package.json.repository (or abort)
|
|
847
|
+
npmglobalize -adopt
|
|
848
|
+
|
|
849
|
+
# Migrate package.json scripts to use npmglobalize
|
|
850
|
+
npmglobalize -package
|
|
851
|
+
|
|
852
|
+
# Preview what would happen
|
|
853
|
+
npmglobalize -dry-run -verbose
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
## Authentication
|
|
857
|
+
|
|
858
|
+
Requires npm authentication:
|
|
859
|
+
```bash
|
|
860
|
+
npm login
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
Check authentication:
|
|
864
|
+
```bash
|
|
865
|
+
npm whoami
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
## Development
|
|
869
|
+
|
|
870
|
+
### Build Check
|
|
871
|
+
|
|
872
|
+
`npmglobalize` includes automatic build verification to ensure TypeScript files are compiled before execution. This prevents runtime errors from outdated JavaScript files.
|
|
873
|
+
|
|
874
|
+
**How it works:**
|
|
875
|
+
- When you run `npmglobalize`, it automatically checks if `.js` files are newer than their `.ts` sources
|
|
876
|
+
- If `.js` files are missing or outdated, execution stops with an error
|
|
877
|
+
- The check is skipped if `noEmit: true` is set in `tsconfig.json`
|
|
878
|
+
|
|
879
|
+
**Building the project:**
|
|
880
|
+
```bash
|
|
881
|
+
npm run build # Compile TypeScript files
|
|
882
|
+
npm run watch # Watch mode for development
|
|
883
|
+
npm run check # Manually verify build status
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
**Bypassing the check:**
|
|
887
|
+
```bash
|
|
888
|
+
npmglobalize --force # Skip build check (not recommended)
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
**Error example:**
|
|
892
|
+
```
|
|
893
|
+
❌ Error: TypeScript files not compiled
|
|
894
|
+
cli.js is older than cli.ts
|
|
895
|
+
|
|
896
|
+
Please run: npm run build
|
|
897
|
+
Or use --force to skip this check
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
This ensures you never accidentally run outdated code when the TypeScript source has changed.
|
|
901
|
+
|
|
902
|
+
## License
|
|
903
|
+
|
|
904
|
+
MIT
|