fix(externals): force-trace named traceDeps to fix pnpm nested deps#4391
fix(externals): force-trace named traceDeps to fix pnpm nested deps#4391pi0x wants to merge 4 commits into
Conversation
Pass the resolved named `traceDeps` (builtins + user, RegExp excluded) to nf3's `traceNodeModules` via the new `traceInclude` option. nft cannot statically detect dynamically-loaded packages (e.g. native bindings), and under pnpm such a nested dependency is not hoisted — it only resolves from the dependent package's real `.pnpm` location, which nf3 now handles. Requires nf3 with `traceInclude` support (unjs/nf3#50). Closes #4372
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthrough
ChangestraceInclude computation and forwarding
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@test/unit/trace-deps.test.ts`:
- Around line 43-50: The trace deps test is too loose because it only checks
that traceInclude entries are strings, which would still pass if the RegExp
selector were incorrectly stringified. Tighten the assertion in
trace-deps.test.ts around resolveTraceDeps to verify the exact traceInclude
membership and count for the ["my-pkg", /my-.*-pkg/] case, using the existing
defaults fixture and the resolveTraceDeps result to ensure only the expected
builtins plus "my-pkg" are present.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d67ddb6c-1c92-46e6-a3e8-a797f95e462a
📒 Files selected for processing (2)
src/build/plugins/externals.tstest/unit/trace-deps.test.ts
| it("returns named deps as traceInclude (builtins + user, RegExp excluded)", () => { | ||
| const result = resolveTraceDeps(["my-pkg", /my-.*-pkg/], defaults); | ||
| expect(result.traceInclude).toContain("sharp"); | ||
| expect(result.traceInclude).toContain("canvas"); | ||
| expect(result.traceInclude).toContain("my-pkg"); | ||
| // RegExp entries cannot be resolved by name and must be excluded | ||
| expect(result.traceInclude!.every((d) => typeof d === "string")).toBe(true); | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Assert the exact traceInclude membership.
This test doesn't really prove the RegExp selector is excluded. An implementation that accidentally stringifies the regex would still pass typeof d === "string". Please assert the final members/count instead.
Suggested test tightening
it("returns named deps as traceInclude (builtins + user, RegExp excluded)", () => {
const result = resolveTraceDeps(["my-pkg", /my-.*-pkg/], defaults);
- expect(result.traceInclude).toContain("sharp");
- expect(result.traceInclude).toContain("canvas");
- expect(result.traceInclude).toContain("my-pkg");
- // RegExp entries cannot be resolved by name and must be excluded
- expect(result.traceInclude!.every((d) => typeof d === "string")).toBe(true);
+ expect(result.traceInclude).toEqual(
+ expect.arrayContaining(["sharp", "canvas", "my-pkg"])
+ );
+ expect(result.traceInclude).toHaveLength(3);
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| it("returns named deps as traceInclude (builtins + user, RegExp excluded)", () => { | |
| const result = resolveTraceDeps(["my-pkg", /my-.*-pkg/], defaults); | |
| expect(result.traceInclude).toContain("sharp"); | |
| expect(result.traceInclude).toContain("canvas"); | |
| expect(result.traceInclude).toContain("my-pkg"); | |
| // RegExp entries cannot be resolved by name and must be excluded | |
| expect(result.traceInclude!.every((d) => typeof d === "string")).toBe(true); | |
| }); | |
| it("returns named deps as traceInclude (builtins + user, RegExp excluded)", () => { | |
| const result = resolveTraceDeps(["my-pkg", /my-.*-pkg/], defaults); | |
| expect(result.traceInclude).toEqual( | |
| expect.arrayContaining(["sharp", "canvas", "my-pkg"]) | |
| ); | |
| expect(result.traceInclude).toHaveLength(3); | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@test/unit/trace-deps.test.ts` around lines 43 - 50, The trace deps test is
too loose because it only checks that traceInclude entries are strings, which
would still pass if the RegExp selector were incorrectly stringified. Tighten
the assertion in trace-deps.test.ts around resolveTraceDeps to verify the exact
traceInclude membership and count for the ["my-pkg", /my-.*-pkg/] case, using
the existing defaults fixture and the resolveTraceDeps result to ensure only the
expected builtins plus "my-pkg" are present.
commit: |
Closes #4372
Problem
Under pnpm, transitive dependencies are not hoisted to the top-level
node_modules— a native, non-bundleable dependency (e.g.sharp/bcrypt) that is only an indirect dependency lives exclusively undernode_modules/.pnpm/.... nft can't statically detect such packages when they're loaded dynamically (native bindings), so they're dropped from the trace and the build fails / ships incomplete output. The known workaround was pnpm'spublicHoistPattern.Fix
resolveTraceDeps()now also returnstraceInclude— the resolved named deps (builtins + usertraceDeps, negations removed, RegExp entries excluded since they can't be resolved by name). ThebuildEndhandler forwards them to nf3'straceNodeModulesvia its newtraceIncludeoption.nf3 resolves each name from
rootDirand from the roots of traced packages that declare it as a dependency — so a pnpm nested dep resolves from its dependent's real.pnpmlocation and gets force-traced (native binaries included).Depends on
traceIncludeoption totraceNodeModules(resolved dependency-driven, so forwarding the full builtin allowlist stays cheap).Draft until that nf3 change is released and the
nf3dependency here is bumped to a version that includes it (otherwise CI typecheck fails against the published types).Tests
test/unit/trace-deps.test.ts— added coverage for thetraceIncludeoutput (builtins + user, RegExp excluded, negation handling). All unit tests pass.