Skip to content

perf: resolve dependency directories without Node's module resolver - #276

Merged
LadyBluenotes merged 1 commit into
mainfrom
perf-dep-resolver
Sep 12, 2026
Merged

perf: resolve dependency directories without Node's module resolver#276
LadyBluenotes merged 1 commit into
mainfrom
perf-dep-resolver

Conversation

@LadyBluenotes

@LadyBluenotes LadyBluenotes commented Sep 12, 2026

Copy link
Copy Markdown
Member

Summary

First of a three-PR perf stack (this → lazy scan path → bundled deps).

resolveDepDir called createRequire().resolve('<dep>/package.json') for every dependency edge in the scan. Node's resolver evaluates export maps, reads the nearest package.json, and realpaths every path segment per call. A CPU profile of intent list at this repo's root (pnpm monorepo, 438 package.json reads) put 624 of ~940 ms inside that call, with lstat and internalModuleStat as the top self-time entries.

This PR:

  • Replaces the resolver call with the plain node_modules ancestor walk the function already used as a fallback. It skips node_modules/node_modules ancestors like Node does and collapses a symlinked match (pnpm virtual store, workspace links) with one lstat plus one native realpath.
  • Resolves a package's real root once per package in skill discovery instead of once per skill file (readSkillEntry did three realpath calls per skill).
  • Adds a focused unit test for resolveDepDir covering nested, hoisted, scoped, virtual-store sibling, symlinked, and missing cases.

Measurements (Windows 11, warm cache, import + run of main(['list']))

Scenario main this PR
repo root (pnpm monorepo) ~770 ms ~555 ms
example/start-basic (npm, 7 pkgs / 24 skills) ~295 ms ~230 ms

intent list --json output on example/start-basic is byte-identical to main, and --debug stats (package.json read count) are unchanged at the repo root.

Summary by CodeRabbit

  • Performance

    • Improved dependency discovery speed, with intent list running approximately 30% faster in pnpm monorepos.
    • Preserved discovered packages and paths while reducing repeated dependency resolution work.
  • Bug Fixes

    • Improved resolution of nested, scoped, workspace-linked, and pnpm-managed dependencies.
    • Preserved useful path warnings and fallback reporting when package metadata cannot be read.

@coderabbitai

coderabbitai Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Warning

Review limit reached

Next included review available in 13 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 02590e89-7173-429b-b471-b567232b4680

📥 Commits

Reviewing files that changed from the base of the PR and between 53c5f79 and c8bda69.

📒 Files selected for processing (1)
  • packages/intent/src/discovery/scanner.ts

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 3d36a7d7-c5d7-4d6d-8b7c-a8f5150592ae

📥 Commits

Reviewing files that changed from the base of the PR and between 36a1bf1 and 53c5f79.

📒 Files selected for processing (4)
  • .changeset/fast-dep-resolution.md
  • packages/intent/src/discovery/scanner.ts
  • packages/intent/src/shared/utils.ts
  • packages/intent/tests/resolve-dep-dir.test.ts

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

The dependency resolver now walks node_modules directories directly and collapses symlinks once per package. Skill discovery resolves and reuses each package root across entries. Tests cover nested, hoisted, scoped, virtual-store, and symlinked dependencies.

Changes

Dependency Discovery Optimization

Layer / File(s) Summary
Direct dependency directory resolution
packages/intent/src/shared/utils.ts
resolveDepDir walks ancestor directories, validates package.json, skips nested node_modules ancestors, and resolves symlinked package directories.
Shared package-root discovery
packages/intent/src/discovery/scanner.ts
Hinted and bulk skill discovery resolve each package root once and reuse it while preserving invalid-entry fallbacks and warnings.
Resolver validation and release metadata
packages/intent/tests/resolve-dep-dir.test.ts, .changeset/fast-dep-resolution.md
Tests cover nested, hoisted, scoped, virtual-store, symlinked, invalid, and missing dependencies. The changeset declares a patch release.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Refactor

Merge Risk: ⚪ Minimal · up to 53c5f

Dependency discovery is optimized while preserving the tested package-resolution and skill-discovery behavior. No current merge-blocking risk was identified.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 3 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main performance change: replacing Node's module resolver for dependency directory resolution.
Description check ✅ Passed The description gives a detailed summary, motivation, measurements, implementation details, and test plan. It does not use the template's exact Changes, Checklist, and Release Impact headings, but it …
Full details: Docstring Coverage

Explanation

Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 3 files. (1 skipped: 1 unsupported.)

✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch perf-dep-resolver
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf-dep-resolver

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codspeed-hq

codspeed-hq Bot commented Sep 12, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 9 untouched benchmarks


Comparing perf-dep-resolver (c8bda69) with main (36a1bf1)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (9e73959) during the generation of this report, so 36a1bf1 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@nx-cloud

nx-cloud Bot commented Sep 12, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit c8bda69

Command Status Duration Result
nx affected --targets=test:eslint,test:sherif,t... ✅ Succeeded 48s View ↗
nx run-many --targets=build ✅ Succeeded 2s View ↗

☁️ Nx Cloud last updated this comment at 2026-09-12 01:51:51 UTC

@pkg-pr-new

pkg-pr-new Bot commented Sep 12, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@tanstack/intent@276

commit: c8bda69

`resolveDepDir` used `createRequire().resolve('<dep>/package.json')` for
every dependency edge in the scan. Node's resolver evaluates export maps,
reads the nearest package.json, and realpaths each path segment per call;
in a pnpm monorepo that was two thirds of `intent list` time.

Replace it with the plain `node_modules` ancestor walk the function
already used as a fallback (skipping `node_modules/node_modules` like
Node does), collapsing a symlinked match with one lstat and one native
realpath. Discovered packages and their roots are unchanged.

Also resolve a package's real root once per package in skill discovery
instead of once per skill file.
@LadyBluenotes
LadyBluenotes merged commit 74d1b26 into main Sep 12, 2026
9 checks passed
@LadyBluenotes
LadyBluenotes deleted the perf-dep-resolver branch September 12, 2026 01:48
@github-actions github-actions Bot mentioned this pull request Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant