feat(package-manager): NODE_EXTRAS ignore filter for runtime archives (#437 slice D2)#496
Conversation
…#437 slice D2) Construct the per-archive ignore filter at the install dispatcher. For unscoped `node` the filter matches upstream's [`NODE_EXTRAS_IGNORE_PATTERN`](https://github.com/pnpm/pnpm/blob/94240bc046/engine/runtime/node-resolver/src/index.ts), which strips bundled `npm` / `corepack` from the Node.js runtime archive during the CAS write — pnpm (and pacquet) install pnpm itself as the package manager, so the bundled tooling is dead weight and would shadow the user's pnpm via `node_modules/.bin/`. Wiring matches upstream's [`archiveFilters: { node: NODE_EXTRAS_IGNORE_PATTERN }`](https://github.com/pnpm/pnpm/blob/94240bc046/installing/client/src/index.ts): the per-package-name table is keyed by `pkg.name`, so `@foo/node` and other packages keep `None` and the full archive lands unfiltered. Pacquet uses a hand-coded matcher rather than the upstream regex so `pacquet-tarball` doesn't have to pull in a regex engine. The three branches mirror the regex alternation exactly: 1. `^(?:lib/)?node_modules/(?:npm|corepack)(?:/|$)` 2. `^bin/(?:npm|npx|corepack)$` 3. `^(?:npm|npx|corepack)(?:\.(?:cmd|ps1))?$` A `OnceLock` caches the `Arc<IgnoreEntryFilter>` so per-snapshot clones share one trait object. Unit tests pin every branch of the alternation plus the negative cases (e.g. `lib/node_modules/yarn/...` is *not* matched, `bin/npm.cmd` is *not* matched — the regex's `$` and arm-specific extension rules are deliberately asymmetric). Verified by temporarily collapsing the `bin/` branch to a no-op — the test fails as expected. Bin-link cmd-shims for the runtime executables and `@runtime:` substring handling in skip lists / reporter prefixes are Slice D3. End-to-end runtime install fixtures land in Slice F.
|
Caution Review failedPull request was closed or merged during review No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📜 Recent review details🧰 Additional context used📓 Path-based instructions (1)**/*.rs📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧠 Learnings (4)📚 Learning: 2026-05-07T23:19:08.272ZApplied to files:
📚 Learning: 2026-05-13T19:22:48.951ZApplied to files:
📚 Learning: 2026-05-13T20:09:22.171ZApplied to files:
📚 Learning: 2026-05-01T10:01:33.766ZApplied to files:
🔇 Additional comments (2)
📝 WalkthroughWalkthroughThe PR extends the binary package installation path to filter out bundled npm and corepack artifacts from unscoped ChangesNode Package Artifact Filtering
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #496 +/- ##
==========================================
+ Coverage 88.34% 88.40% +0.06%
==========================================
Files 121 121
Lines 12891 13001 +110
==========================================
+ Hits 11389 11494 +105
- Misses 1502 1507 +5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Micro-Benchmark ResultsLinux |
Integrated-Benchmark Report (Linux)Scenario: Frozen Lockfile
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 2.87317483936,
"stddev": 0.6016035091656307,
"median": 2.70658032596,
"user": 2.63084056,
"system": 3.7261554400000003,
"min": 2.56741450946,
"max": 4.56839424846,
"times": [
2.78863302746,
2.66744055246,
2.63700183646,
2.77917047446,
4.56839424846,
2.7457200994599997,
2.78011810146,
2.56741450946,
2.58066280546,
2.61719273846
]
},
{
"command": "pacquet@main",
"mean": 2.5866579661599998,
"stddev": 0.06014248180945031,
"median": 2.59593676846,
"user": 2.5478426599999997,
"system": 3.6456132400000003,
"min": 2.5069965444599998,
"max": 2.66565329746,
"times": [
2.66565329746,
2.62303287846,
2.5214656674600002,
2.50722847646,
2.59156893846,
2.66451993246,
2.62235604946,
2.5069965444599998,
2.60030459846,
2.56345327846
]
},
{
"command": "pnpm",
"mean": 6.11289905346,
"stddev": 0.06190679244910986,
"median": 6.0989098219599995,
"user": 9.08290036,
"system": 4.456002839999999,
"min": 6.03637327946,
"max": 6.24755415846,
"times": [
6.1237828284599995,
6.10106023246,
6.09149093646,
6.11318593746,
6.09675941146,
6.06312284646,
6.03637327946,
6.18528831046,
6.07037259346,
6.24755415846
]
}
]
}Scenario: Frozen Lockfile (Hot Cache)
BENCHMARK_REPORT.json{
"results": [
{
"command": "pacquet@HEAD",
"mean": 0.75621033478,
"stddev": 0.058012162561440414,
"median": 0.74076697588,
"user": 0.38468391999999996,
"system": 1.60158832,
"min": 0.71745643288,
"max": 0.91318885888,
"times": [
0.91318885888,
0.77251400188,
0.71745643288,
0.75885229588,
0.72364100688,
0.7339816528800001,
0.72608381688,
0.74755229888,
0.74828011788,
0.72055286488
]
},
{
"command": "pacquet@main",
"mean": 0.8066924887800001,
"stddev": 0.03286007094161814,
"median": 0.80584799988,
"user": 0.39535792000000003,
"system": 1.61888072,
"min": 0.76085144388,
"max": 0.8580945078800001,
"times": [
0.8580945078800001,
0.81753353588,
0.81619706788,
0.83290821488,
0.8455747678800001,
0.76557939588,
0.79549893188,
0.78860448888,
0.7860825328800001,
0.76085144388
]
},
{
"command": "pnpm",
"mean": 2.6364145615799996,
"stddev": 0.20871347816611038,
"median": 2.56461417738,
"user": 3.1644257199999997,
"system": 2.18683172,
"min": 2.50901679688,
"max": 3.20325764188,
"times": [
3.20325764188,
2.6569867558799998,
2.58139819588,
2.52640535388,
2.61962345188,
2.50901679688,
2.51273629688,
2.54783015888,
2.52171677588,
2.68517418788
]
}
]
} |
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
Summary
Slice D2 of #437 — wires the per-archive ignore filter at the install dispatcher. Matches upstream's
archiveFilters: { node: NODE_EXTRAS_IGNORE_PATTERN }per-package-name table.For unscoped
node, the filter strips bundlednpm/corepackfrom the Node.js runtime archive during the CAS write. pnpm (and pacquet) install pnpm itself as the package manager, so the bundled tooling is dead weight and would also shadow the user's pnpm vianode_modules/.bin/. Every other package getsNoneand the full archive lands in the CAS unfiltered.node_extras_filteris a hand-coded port of upstream'sNODE_EXTRAS_IGNORE_PATTERNregex (^(?:(?:lib/)?node_modules/(?:npm|corepack)(?:/|$)|bin/(?:npm|npx|corepack)$|(?:npm|npx|corepack)(?:\.(?:cmd|ps1))?$)). Hand-coded sopacquet-tarballdoesn't need a regex engine; the three branches mirror the alternation exactly.archive_filter_for(&PackageKey)is the per-package dispatcher. ReturnsSome(NODE_EXTRAS)only for unscopednode; everything else getsNone.OnceLock-cached so per-snapshotArc::clones share one trait object.What's not in this slice
BinaryResolution::bin),@runtime:substring handling in skip lists / reporter prefixes.--no-runtimeflag.Test plan
node_extras_filter_matches_upstream_regex_alternations— every positive case for each branch plus the negative cases the regex deliberately doesn't match (e.g.lib/node_modules/yarn/...,bin/npm.cmd,npm.bat,src/node_modules/npm/foo). Verified by temporarily collapsing thebin/branch to a no-op; the test fails as expected.archive_filter_for_only_returns_filter_for_unscoped_node— pins thepkg.name == "node"(unscoped) key match.@foo/node,react,bunall getNone.install_package_by_snapshottests pass.just ready,taplo format --check,just dylint,cargo doc -D warningsgreen.Written by an agent (Claude Code, claude-opus-4-7).
Summary by CodeRabbit
Bug Fixes
nodepackage.Tests