Nub 0.10.0 brings dependency build scripts and --filter in line with pnpm 12, writes pnpm lockfiles that pnpm accepts in more layouts, and backfills util.debounce, util.throttle and the JSON.parse options proposal.
[!IMPORTANT] Five changes can surface in an existing project:
- An unapproved dependency build script fails the install with
ERR_NUB_IGNORED_BUILDSand exit 1.strictDepBuilds=falserestores the warning (#1050)- A git dependency with a
preparescript builds only when approved, and otherwise fails withERR_NUB_GIT_DEP_PREPARE_NOT_ALLOWED(#1045)--filter ./packagesselects only a project whose directory ispackages. Use./packages/*or./packages/**for the projects under it (#1056)- A
--filterthat selects no project exits 0, and--fail-if-no-matchexits 1 (#1064)nub rebuild <pkg>no longer approves the package it names (#1050)
Dependency build scripts
An install that meets a build script nobody has approved now fails, as under pnpm 12. It fails last, once node_modules is linked and the approved builds and the project's own scripts have run. Global installs and nubx stay non-strict.
:::console
$ nub install
dependencies:
+ core-js@3.40.0
ERR_NUB_IGNORED_BUILDS
× Ignored build scripts: core-js@3.40.0
help: Run `nub approve-builds` to pick which dependencies may run their
build scripts, or set `strictDepBuilds=false` to install without them.
| Area | What changed | Link |
|---|---|---|
| Denials | nub approve-builds '!pkg' and --allow-build='!pkg' record a denial, and --allow-build=pkg refuses a package the project denies. An explicit false outranks dangerouslyAllowAllBuilds. |
#1050 |
| Settings | dangerouslyAllowAllBuilds and strictDepBuilds are read from .npmrc, the environment and, in a pnpm project, pnpm-workspace.yaml. |
#1050 |
nub rebuild |
Reports unapproved packages instead of passing over them. A filtered rebuild works in a shared-lockfile workspace, where it did nothing. | #1050 |
| Member approvals | nub rebuild, nub ignored-builds and nub approve-builds read the same approvals as nub install, workspace members' included. |
#1050 |
| Git dependency approval | Approved by <name>@git+<repository> or the same key with #<commit>. A bare name, name@version or * does not approve one, and a denial installs it unbuilt. |
#1045 |
| Git dependency builds | A hosted dependency such as rimraf@github:isaacs/rimraf#v6.1.3 is now built. It linked with no dist/. The build no longer needs cp, which stock Windows lacks. |
#1045, fixes #885, #886 |
| Project lifecycle stages | Project scripts follow pnpm 12's stage lists in every project: install adds preprepare and postprepare, remove runs the uninstall stages, and --prod skips the prepare family. |
#1052 |
Workspace filters
Nub now uses pnpm 12's selector grammar and directory globs on every workspace command, including nub run (#1056).
:::diff
- nub run --filter ./packages build
+ nub run --filter './packages/*' build
| Area | What changed | Link |
|---|---|---|
| Empty selection | Does nothing and exits 0. install, remove and update print Scope: 0 of N workspace projects, and nub ci still cleans node_modules first, as pnpm's does. |
#1064 |
| Workspace root | run, exec, add and test drop the root from a selection that names no project. Other commands keep it. |
#1056 |
| Repeated filtered install | Reports up to date. It reinstalled and reran lifecycle scripts. | #1055 |
recursiveInstall |
Read from pnpm-workspace.yaml in a pnpm project. |
#1055 |
| Flags before the verb | --filter-prod, -C and --dir work there. |
#1064 |
nub ls in a member |
Lists that member. | #1064 |
Lockfile compatibility
| Area | What changed | Link |
|---|---|---|
| Per-project pnpm lockfiles | Under shared-workspace-lockfile=false, a member's lockfile matches pnpm 10's bytes on the tested workspaces. pnpm's --frozen-lockfile rejected the old form. |
#1060 |
| Frozen installs of those trees | nub install --frozen-lockfile passes on a tree Nub or pnpm 10 wrote, and a plain install keeps a member's locked versions. It failed on the first and re-resolved on the second. |
#1062 |
| Rebuild in those trees | nub rebuild finds a member's packages in the root store. It built nothing and exited 0. |
#1058 |
| pnpm's env document | pnpm-lock.yaml records a pinned pnpm in its leading document, so pnpm 11 and 12 accept Nub's lockfile under --frozen-lockfile, including after nub pm use pnpm. |
#1054 |
Local paths in pnpm-lock.yaml |
link:./x is written link:x and file:./x is written file:x, and empty packages: and snapshots: are omitted. A package spelled with ./ relinks on its first install after upgrading. |
#1079 |
A file: dependency outside the workspace |
A member's file: directory above the workspace root installs under nub ci and --frozen-lockfile. It failed with is not a directory. |
#1004 |
package-lock.json |
An optional dependency of a dev dependency is flagged dev and optional. It was devOptional. |
#1088, fixes #1087 |
| Optional peers | Every resolved optional peer is linked next to its package, a plain install repairs an existing nub.lock, and bun.lock records peers as bun writes them. |
#1040, fixes #937 |
| Catalog updates | nub update -r raises a ^ or ~ catalog entry to the version it resolved, and a catalog-only lockfile change is written instead of skipped. |
#1057 |
Overrides on catalog: |
A range-keyed override matches a catalog: dependency by its reference, as under pnpm. |
#1039, fixes #771 |
Package manager
| Area | What changed | Link |
|---|---|---|
nub pack and nub publish |
Never pack a file from outside the package. A symlinked or absolute main, or a symlinked README, put that file in the tarball. |
#1036 |
workspace: specs |
Both commands resolve workspace: and catalog: specs and apply publishConfig. The specs shipped verbatim, which made the package uninstallable. |
#1036, fixes #817 |
nub update at a workspace root |
Keeps members' locked versions. It re-resolved every member and bumped packages nobody named. | #1043, fixes #1001 |
patchedDependencies |
Git renames, header-only deletions, empty new files and mode changes apply as under pnpm 11. A patched executable keeps its mode. | #1037, fixes #844, #845 |
nub patch-commit |
Replaces the patch file with one consolidated patch instead of appending a section per round. nub patch resolves its target through the lockfile. |
#1010 |
Workspace .npmrc |
A command run in a member reads the root's registry and auth. The member's file layers on top under pnpm 10, Yarn 1 and a Nub project, and is not read under pnpm 11, npm or Bun. | #1051 |
| DNS | Hosts resolve through the OS with an in-process cache, so VPN-scoped DNS, /etc/resolver/* and Windows NRPT apply. |
#953, fixes #942 |
| Broken downloads | A tarball whose connection breaks off mid-body is downloaded again. It failed the install. | #1078 |
| Failed installs | An install that fails after a virtual-store mode change keeps node_modules. It removed the tree before resolving. |
#1035, fixes #841 |
--offline |
Refuses an uncached full packument fetch, which went to the network. | #1059 |
file: dependencies on macOS |
Two projects with a file: dependency at the same relative path get their own files. They shared whichever installed first, and a tarball rebuilt in place stayed stale. |
#995 |
| Overrides to a local copy | A registry package whose dependency is overridden to a file: path is ejected into the project. The link dangled and require failed with Cannot find module. |
#1080 |
| Store locks on NFS | Prune, maintenance and GC lock files open readable, so shared locks work on Linux NFS. They failed with EBADF. |
#1046, fixes #1028 |
| Add-time refusals | --allow-similar-name and --allow-new-name each clear their own check, and every refusal names its flag and allowedUnpopularPackages. |
#1074 |
| Aliases and bins | A dependency under its own name keeps its bin over an alias of the same package, so tsc stays with typescript when typescript5 is also installed. |
#1074 |
| Remix 3 | ^3.0.0, 3.1.0 and >=2 keep the shared store. Only a spec that may select a 3.0.0 prerelease gets a project-local store. |
#1042, fixes #927 |
pnpm 12 through nub pm shim |
The pnpm and pnpx shims run pnpm 12. They failed with SyntaxError: Unexpected identifier 'is'. |
#983 |
Mismatched nub pm shim projects |
Registry and account commands such as npm login pass through to the system manager instead of being refused. |
#1021 |
nub pm use nub |
The Turborepo heads-up says which versions recognize Nub. | #1034 |
| Peer placements | A package with peer-dependent copies reuses its file index and verified store read. Remapping across eight peer contexts went from 1.647 ms to 1.411 ms in a stage benchmark on Linux x86-64. | #984 |
Runtime
| Area | What changed | Link |
|---|---|---|
util.debounce, util.throttle |
Backfilled from Node 26.10 on older supported versions, and typed in @nubjs/types. |
#974 |
JSON.parse options |
JSON.parse(text, { freeze, preferNullPrototype }) is polyfilled from the Stage 2.7 proposal, and typed in @nubjs/types. |
#992 |
| YAML and TOML imports | Values keep NaN, -0, dates and own __proto__ keys. A TOML date is a Date, and a TOML integer past 2^53 is a BigInt. It threw on import. |
#1067 |
UV_THREADPOOL_SIZE |
Assigning it in process.env before the first pool use sizes the pool, as under Node. Nub's preload had already built the pool. |
#1013 |
| Temporal | The polyfilled global has the spec's property attributes. It was a frozen module namespace. | #1015 |
require() of a replaced package |
@js-temporal/polyfill, urlpattern-polyfill and abort-controller load the installed package. They failed with ENOENT on Node 22.15+. |
#1061 |
| Decorated files | 400 decorated ESM files on Node 26.11 start in 0.18s, down from 0.99s, on macOS arm64 with a warm transpile cache. | #796, fixes #795 |
| Startup | Augmented startup no longer loads internal/file on Node 20+. |
#556 |
| Entry tsconfig check | The check follows the entry's real path and skips a dependency's own config. A workspace bin run through its node_modules link failed before starting. |
#1041, fixes #1032 |
| Unreadable tsconfig | The diagnostic names the OS error. It read the same as a missing file. | #899 |
| Varlock | Nub stands down when Varlock already resolved the schema or is the program being launched. varlock run --path ./config exited 1. |
#724 |
| Terminal jobs | A child that stops on its first terminal read resumes after the foreground handoff. It stayed stopped. | #998 |
| Test and watch modes | --test=<value>, --watch=<value>, --watch_path and a watch flag in NODE_OPTIONS stay out of the children they start, which could recurse without end. nodeOptions in nub.jsonc refuses a watch flag. |
#1075, #1086 |
Node discovery and provisioning
| Area | What changed | Link |
|---|---|---|
latest and other aliases |
Aliases and partial pins select releases only. A .nvmrc of latest failed with a 404 once nodejs.org listed a 27.0.0 alpha. |
#1033 |
| Version-manager shims | The version behind a mise, asdf, nodenv or Volta shim is read on every launch. A cached version passed Node 26 flags to Node 24, which exited 9. | #1049 |
| Launch loops | A PATH node that hands the launch back to Nub, or links back to it, is skipped. Any chain of Nub processes stops at 64 with ERR_NUB_LAUNCH_LOOP. |
#1044, #1063, #1073, fixes #832 |
| Windows runtime cache | Validates the cache directory rather than every ancestor. A broad grant on the user's profile made each command print warnings and run without the runtime. | #1047, fixes #908 |
Install and upgrade
| Area | What changed | Link |
|---|---|---|
nub upgrade under mise |
Detects a mise install and runs mise upgrade --bump nub. It took the npm channel, which planted a second global copy. |
#571 |
| Release assets | Each stable release carries packslip.sigstore.json, a signed manifest of its archives, so mise can install Nub straight from the release. |
#982 |
Docs, testing and internals
- Every docs page was restructured and tightened (#994).
- Integration suites run against an offline Verdaccio registry with committed fixtures (#765).
- CI's macOS jobs moved to
macos-15after GitHub retiredmacos-14(4a599b6). - Only the approved release run can write a stable tag (#1017).
What's Changed
- release: only the approved run can write a stable tag by @colinhacks in https://github.com/nubjs/nub/pull/1017
- release: the approval summary says to run stager, not stager approve by @colinhacks in https://github.com/nubjs/nub/pull/1018
- Resume stopped terminal jobs and isolate the Windows cache gate by @pullfrog[bot] in https://github.com/nubjs/nub/pull/998
- pm shim: let registry and account verbs through in a mismatched project by @colinhacks in https://github.com/nubjs/nub/pull/1021
- runtime: build libuv's threadpool at the first loop turn, so a program can size its own by @colinhacks in https://github.com/nubjs/nub/pull/1013
- aube-linker: interpreter-preference test no longer symlinks /bin/echo by @colinhacks in https://github.com/nubjs/nub/pull/1038
- node: resolve latest and other aliases to a release, never a prerelease by @colinhacks in https://github.com/nubjs/nub/pull/1033
- tests: node shim check accepts the copy fallback across filesystems by @colinhacks in https://github.com/nubjs/nub/pull/1048
- runtime: memoize vendored-package resolution in resolveSpec by @pullfrog[bot] in https://github.com/nubjs/nub/pull/796
- pm use nub: say which turbo versions recognize nub by @colinhacks in https://github.com/nubjs/nub/pull/1034
- pm: open store lock files readable so shared locks work on NFS by @colinhacks in https://github.com/nubjs/nub/pull/1046
- fix(pm): support pnpm 12 shims by @afonsojramos in https://github.com/nubjs/nub/pull/983
- resolver: match overrides against a catalog: dependency's reference, as pnpm does by @colinhacks in https://github.com/nubjs/nub/pull/1039
- lockfile: rebase only link: versions from a pnpm importer by @Arthur031221 in https://github.com/nubjs/nub/pull/1004
- pm: resolve workspace: specs in pack and publish manifests by @colinhacks in https://github.com/nubjs/nub/pull/1036
- run: check the tsconfig of the entry's real directory, and skip a dependency's own by @colinhacks in https://github.com/nubjs/nub/pull/1041
- pm: resolve DNS through the OS with an in-process cache by @meoyawn in https://github.com/nubjs/nub/pull/953
- pm: apply git rename, header-only delete, empty-file and mode patches by @colinhacks in https://github.com/nubjs/nub/pull/1037
- pm: eject the store only for the Remix 3.0.0 prereleases by @colinhacks in https://github.com/nubjs/nub/pull/1042
- pm: key the macOS extracted-tree cache by package content by @colinhacks in https://github.com/nubjs/nub/pull/995
- pm_shim tests: keep an installed Nub shim off the inherited PATH by @colinhacks in https://github.com/nubjs/nub/pull/1053
- update: keep members' locked versions when updating at the workspace root by @colinhacks in https://github.com/nubjs/nub/pull/1043
- pm: link every resolved optional peer of a package by @colinhacks in https://github.com/nubjs/nub/pull/1040
- update: floor-bump catalog entries, and record catalog-only lockfile changes by @colinhacks in https://github.com/nubjs/nub/pull/1057
- pm: run pnpm's project lifecycle stages on install, update and remove by @colinhacks in https://github.com/nubjs/nub/pull/1052
- node discovery: skip a PATH shim that hands the launch back to nub by @colinhacks in https://github.com/nubjs/nub/pull/1044
- runtime: describe the
nubcondition as an export condition, not a runtime key by @colinhacks in https://github.com/nubjs/nub/pull/990 - codex: raise hook child descriptor limit by @jdalton in https://github.com/nubjs/nub/pull/985
- test: isolate real-app matrix from release-age gate by @pullfrog[bot] in https://github.com/nubjs/nub/pull/978
- release: sign a packslip for each stable release by @jdx in https://github.com/nubjs/nub/pull/982
- pm: build git deps only when allowlisted, as pnpm 12; prepare without cp; prepare hosted-git archives by @colinhacks in https://github.com/nubjs/nub/pull/1045
- runtime cache: validate only the Windows cache directory, not its ancestors by @colinhacks in https://github.com/nubjs/nub/pull/1047
- ci: isolate Windows compile fixture caches by @pullfrog[bot] in https://github.com/nubjs/nub/pull/1023
- fix(runtime): normalize Temporal namespace property attributes by @afonsojramos in https://github.com/nubjs/nub/pull/1015
- runtime: let require() of a clobbered package load the installed package by @colinhacks in https://github.com/nubjs/nub/pull/1061
- ci: run the Rust jobs when the published schema or skill.md changes by @pullfrog[bot] in https://github.com/nubjs/nub/pull/652
- runtime: polyfill the JSON.parse options proposal (Stage 2.7) by @colinhacks in https://github.com/nubjs/nub/pull/992
- test(pm): drop pre-#663 workarounds for the root-manifest cache flake by @colinhacks in https://github.com/nubjs/nub/pull/1069
- bench(install): record tool versions in results; fix --cwd; re-measure bun 1.3.14 by @colinhacks in https://github.com/nubjs/nub/pull/549
- compile: keep the bootstrap preload when the payload carries a resolve shim by @colinhacks in https://github.com/nubjs/nub/pull/1065
- registry: refuse an uncached full packument fetch under --offline by @colinhacks in https://github.com/nubjs/nub/pull/1059
- chore(make): add fix target for clippy autofix by @jdalton in https://github.com/nubjs/nub/pull/759
- pm: read the workspace root .npmrc from a workspace member by @colinhacks in https://github.com/nubjs/nub/pull/1051
- install: keep node_modules when an install fails after a virtual-store mode change by @colinhacks in https://github.com/nubjs/nub/pull/1035
- ci: parallelize documentation scans by @jdalton in https://github.com/nubjs/nub/pull/1002
- ci(node-matrix): retry setup-node once on the smoke legs by @pullfrog[bot] in https://github.com/nubjs/nub/pull/591
- pm: pnpm 12's --filter selector grammar and directory globs for every workspace command by @colinhacks in https://github.com/nubjs/nub/pull/1056
- pm: record the pinned pnpm in pnpm-lock.yaml's env document by @colinhacks in https://github.com/nubjs/nub/pull/1054
- docs: restructure and tighten every docs page by @colinhacks in https://github.com/nubjs/nub/pull/994
- ci: close trunk-red only when each reported workflow recovers; name failing legs in the Node matrix gate by @colinhacks in https://github.com/nubjs/nub/pull/588
- test: give the code cache generator a file as stdin by @colinhacks in https://github.com/nubjs/nub/pull/1071
- feat(runtime): backfill util debounce and throttle by @jdalton in https://github.com/nubjs/nub/pull/974
- rebuild: find a member's packages in the root store under per-project lockfiles by @colinhacks in https://github.com/nubjs/nub/pull/1058
- node discovery: never cache a version-manager shim's version by @colinhacks in https://github.com/nubjs/nub/pull/1049
- runtime: stop loading internal/file at startup; node-compat: reap each test's session by @colinhacks in https://github.com/nubjs/nub/pull/556
- perf(pm): cut eight-context index remapping time by 14.3% by @jdalton in https://github.com/nubjs/nub/pull/984
- node discovery: skip a PATH node that links back to nub by @colinhacks in https://github.com/nubjs/nub/pull/1063
- pm: a --filter that selects no project exits 0, as pnpm 12 does by @colinhacks in https://github.com/nubjs/nub/pull/1064
- pm: patch-commit writes one consolidated patch; nub patch resolves through the lockfile by @btea in https://github.com/nubjs/nub/pull/1010
- data loaders: keep NaN, -0, BigInt, dates and proto keys by @colinhacks in https://github.com/nubjs/nub/pull/1067
- pm: a repeated filtered install is up to date; recursiveInstall is read from pnpm-workspace.yaml by @colinhacks in https://github.com/nubjs/nub/pull/1055
- pm: write per-project lockfiles in their project's frame by @colinhacks in https://github.com/nubjs/nub/pull/1060
- compile: report a compiled program's uncaught error where it was thrown by @colinhacks in https://github.com/nubjs/nub/pull/1066
- test(registry): offline Verdaccio test registry + Socket safety checks by @jdalton in https://github.com/nubjs/nub/pull/765
- env: stand down when Varlock already resolved the schema, or is the program being launched by @colinhacks in https://github.com/nubjs/nub/pull/724
- fix(tsconfig): name the OS error when the tsconfig file cannot be read by @colinhacks in https://github.com/nubjs/nub/pull/899
- Align dependency build-script policy with pnpm 12 by @colinhacks in https://github.com/nubjs/nub/pull/1050
- node: spell --test and --watch bare so the children they start never inherit them by @colinhacks in https://github.com/nubjs/nub/pull/1075
- node discovery: bound any chain of nub processes by @colinhacks in https://github.com/nubjs/nub/pull/1073
- install: download a tarball again when its connection breaks off mid-body by @colinhacks in https://github.com/nubjs/nub/pull/1078
- compile: keep build-machine paths out of region comments by @colinhacks in https://github.com/nubjs/nub/pull/1077
- compile: bundle an unbundled package's dependencies from its real directory by @colinhacks in https://github.com/nubjs/nub/pull/1068
- feat(upgrade): add mise install channel by @afonsojramos in https://github.com/nubjs/nub/pull/571
- tooling: pin chrome-devtools-mcp and launch it on chrome-headless-shell by @colinhacks in https://github.com/nubjs/nub/pull/1085
- pm: write pnpm-lock.yaml's local paths and empty sections as pnpm does by @colinhacks in https://github.com/nubjs/nub/pull/1079
- pm: name each add-time refusal's own flag; keep a bin with the dependency under its own name by @colinhacks in https://github.com/nubjs/nub/pull/1074
- site: bump next to 16.4.5 for Google Fonts /l/font URLs by @colinhacks in https://github.com/nubjs/nub/pull/1084
- node: keep watch mode out of its children under every spelling and channel by @colinhacks in https://github.com/nubjs/nub/pull/1086
- pm: read member lockfiles before the drift check under per-project lockfiles by @colinhacks in https://github.com/nubjs/nub/pull/1062
- pm: flag an optional dep of a dev dep as dev and optional in package-lock.json by @colinhacks in https://github.com/nubjs/nub/pull/1088
- pm: eject a registry package whose dependency is overridden to a local copy by @colinhacks in https://github.com/nubjs/nub/pull/1080
- compile: ship a package as files when the bundle cannot reach its own install by @colinhacks in https://github.com/nubjs/nub/pull/1090
New Contributors
- @Arthur031221 made their first contribution in https://github.com/nubjs/nub/pull/1004
- @meoyawn made their first contribution in https://github.com/nubjs/nub/pull/953
- @jdx made their first contribution in https://github.com/nubjs/nub/pull/982
- @btea made their first contribution in https://github.com/nubjs/nub/pull/1010
Full Changelog: https://github.com/nubjs/nub/compare/v0.9.7...v0.10.0