Nub 0.8.0 reworks environment-file configuration, ships a round of package-manager correctness fixes, and adds garbage collection for the package store.
Important
Two changes are worth knowing before upgrading:
Breaking changes
File paths in envFile go in an array. "envFile": ".env" is now an error naming [".env"]; a string value is reserved for mode names, and "varlock" is the only one. This makes envFile consistent with the other list-valued config fields. (#735)
A declared envFile now displaces a .env.schema hand-over, at every scope. A .env.schema file decides the environment only when no envFile is declared. Previously the two together were a hard error, and envFile: false in a schema project loaded the schema anyway. Now declared intent wins: envFile: false (or --no-env-file) loads nothing, an envFile list loads those files, and envFile: "varlock" keeps the hand-over explicitly. This applies to a global-scope envFile too, so a machine-wide envFile: false empties a schema project unless the project declares "varlock". (#735, #774)
An unreadable tsconfig.json now refuses the run. A project tsconfig whose extends target is missing previously ran under options its author never wrote — extends is where strict, target, and path aliases usually live. Nub now stops with the read error, as tsc does (TS5083). Fix the config, or run with --node. A dependency's own unreadable tsconfig is still salvaged rather than fatal. (#778, #768)
Another package manager's config no longer directs the node_modules layout. The layout is configured in nub.jsonc (install.linker, install.publicHoist) under every project identity. The last two branded exceptions are gone: pnpm 11's pnpm-workspace.yaml layout keys are no longer read, and npm's install-strategy=nested no longer aborts the install. (#698)
Version-gated feature flags moved out of NODE_OPTIONS. NODE_OPTIONS is inherited by the whole process subtree, and flags matched to the host Node aborted any descendant on an older Node — Electron apps exited at startup. Feature flags now ride argv instead. Cost: a tool that spawns Node by absolute path (bypassing the shim) loses the version-gated features, though it keeps the preload and source-map remapping. (#777)
Package manager
| Area | What changed | PR |
|---|---|---|
| Optional dependencies | A build failure in an optionalDependencies entry skips the package with a warning instead of failing the install, matching npm and pnpm.
| #737 |
| Store maintenance | nub store prune now garbage-collects the global virtual store and extracted-tree tiers, not just the content store. Installs register the project with the store; a project whose last install predates 0.8.0 registers on its next install.
| #720 |
nub outdated
| minimumReleaseAge now applies to both reported columns, so the report no longer offers upgrades nub update would decline. A held version is marked with the time it becomes installable, and a project whose only pending upgrade is held exits 0.
| #743 |
| Release-age policy | A frozen install revalidates the release policy only when the age gate actually moved. | #710 |
| Relocated stores | A store-dir override now moves every store tier together, so phantom-dependency protection keeps working and the machine's default store stays untouched.
| #644 |
.bin wrappers
| A package whose bin shares its interpreter's name (for example the node npm package) no longer produces a wrapper that resolves to itself — nub install node used to hang.
| #741 |
| Workspaces | Workspace aliases and relative-path workspace specs resolve. | #717 |
| Workspaces | A member-scoped nub update resolves in the workspace-root frame.
| #754 |
| Workspaces | The disk-materialize eject applies in workspace projects too. | #736 |
| Workspaces | nub config init writes nub.jsonc at the workspace root.
| #714 |
| Lockfiles | An empty importer reads as drift, not as a specifier-less lockfile format. | #763 |
| Linker | Hidden-hoist names are claimed shallowest-first, matching pnpm's depth ordering — 1.0.0 no longer beats 2.0.0 on lexicographic accident.
| #775 |
| Resolution | Peer-context convergence is sized by graph. | #716 |
| Registry | A stalled packument fetch is bounded and surfaced instead of hanging the install. | #723 |
| Configuration | NUB_CACHE_DIR moves the engine cache, and nub config get/list report values set through the environment.
| #740 |
| Configuration | A settings default with a namespace resolves against the active embedder. | #780 |
| Fresh installs | The shim directories honor XDG_DATA_HOME on a fresh install; an existing ~/.nub install keeps its path.
| #752 |
| Source builds | A source build (Homebrew and friends) now fails loudly when a build prerequisite is missing instead of shipping a quietly degraded binary. | #761 |
Runtime
| Area | What changed | PR |
|---|---|---|
import defer
| Deferred module evaluation is enabled on Node 26.4+, where Node wires the defer phase through. | #770 |
| Loaders | A user loader delivered through NODE_OPTIONS is seen by the CommonJS-sync guard.
| #742 |
fetch cache
| Cache-evict builtins load synchronously, closing a startup race. | #707 |
| Types | @nubjs/types covers the six polyfilled proposals an es2024 lib can't reach.
| #734 |
| Windows | nub.exe reserves an 8 MB main-thread stack, matching node.exe.
| #701 |
CLI
- A help flag after the verb works in the
pmandnodecommand groups. (#739) - Ctrl-C signals every concurrent child of
nub run, and--colortakes effect. (#744)
What's Changed
- fix(config): write nub.jsonc at the workspace root by @colinhacks in #714
- pm: allow deep peer-context convergence by @colinhacks in #716
- cli: reserve an 8 MB main-thread stack for
nub.exeon Windows by @pullfrog[bot] in #701 - runtime: fetch cache-evict builtins synchronously by @pullfrog[bot] in #707
- fix(pm): make layout nub's own axis, with nub.jsonc as its canonical surface by @colinhacks in #698
- fix(pm): resolve workspace aliases and relative-path workspace specs by @colinhacks in #717
- fix(registry): bound and surface a stalled packument fetch by @colinhacks in #723
- types: cover the six polyfilled proposals an es2024 lib can't reach by @colinhacks in #734
- runtime: let envFile take precedence over the Varlock hand-over by @colinhacks in #735
- wiki: adopt lat.md as the corpus knowledge graph by @colinhacks in #725
- fix(pm): apply the disk-materialize eject in workspace projects too by @colinhacks in #736
- fix(runtime): see a NODE_OPTIONS-delivered user loader in the commonjs-sync guard by @colinhacks in #742
- fix(pm): move the phantom sidecar tier with a store-dir override by @jdalton in #644
- cli: parse a help flag after the verb in the pm and node groups by @colinhacks in #739
- Harden source builds of nub against silent degradation by @colinhacks in #761
- docs: correct the env-precedence attribution — the ecosystem is split, not a consensus by @colinhacks in #762
- fix(linker): never let a .bin wrapper resolve to itself by @colinhacks in #741
- lockfile: treat an empty importer as drift, not as a specifier-less format by @colinhacks in #763
- pm: garbage-collect the virtual store and extracted-tree tiers by @colinhacks in #720
- run: signal every concurrent child on Ctrl-C, and make --color take effect by @colinhacks in #744
- fix(tsconfig): keep the local compilerOptions when an extends target is missing by @colinhacks in #768
- fix(pm): resolve a member-scoped update in the workspace-root frame by @colinhacks in #754
- fix(install): an optional dependency's build failure no longer fails the install by @colinhacks in #737
- fix(install): only revalidate release policy when the age gate moved by @candrewlee14 in #710
- outdated: apply minimumReleaseAge to both reported versions by @colinhacks in #743
- pm: honor NUB_CACHE_DIR for the engine cache, and report the env tier in nub config by @colinhacks in #740
- runtime: let a global envFile displace a .env.schema too by @colinhacks in #774
- linker: claim hidden-hoist names shallowest-first, matching pnpm's depth ordering by @colinhacks in #775
- fix(tsconfig): refuse the run when the project's tsconfig cannot be read by @colinhacks in #778
- pm/node: honor XDG_DATA_HOME for the shim dirs on a fresh install by @colinhacks in #752
- node: auto-unflag
import deferon Node 26.4+ by @colinhacks in #770 - settings: resolve the namespace in a default against the active embedder by @colinhacks in #780
- node: keep version-gated flags out of NODE_OPTIONS so Electron survives by @colinhacks in #777
- tests: make recursive clones skip the node-suite submodule by @colinhacks in #791
New Contributors
- @candrewlee14 made their first contribution in #710
Full Changelog: v0.7.5...v0.8.0