fix: TsconfigPathsPlugin circular references, unscoped pkg extends, and perf#575
Conversation
…nd perf improvements
- Fix infinite recursion when project references form a cycle (A→B→A)
by tracking visited reference paths
- Fix `extends: "unscoped-pkg"` failing to resolve to
node_modules/unscoped-pkg/tsconfig.json
- Use `stat` instead of `readFile` for existence checks in extends
resolution to avoid reading entire file contents unnecessarily
- Skip regex in `substituteConfigDir` when path has no `${configDir}`
🦋 Changeset detectedLatest commit: 510978b The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #575 +/- ##
==========================================
+ Coverage 96.54% 96.56% +0.01%
==========================================
Files 50 50
Lines 2927 2937 +10
Branches 922 928 +6
==========================================
+ Hits 2826 2836 +10
Misses 85 85
Partials 16 16
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Merging this PR will degrade performance by 94.56%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ⚡ | Memory | resolve-to-context: directory resolve (warm) |
96.6 KB | 1.8 KB | ×54 |
| ⚡ | Memory | tsconfig-paths: 5 path prefixes (warm) |
101.9 KB | 1.8 KB | ×58 |
| ❌ | Memory | deep-hierarchy: relative from 10-deep dir (warm) |
1 KB | 18.6 KB | -94.56% |
| ⚡ | Simulation | cache-predicate: mixed cached/uncached requests (warm) |
4 ms | 2 ms | ×2 |
Comparing fix/tsconfig-paths-plugin-bugs (510978b) with main (292aee5)
Summary
Fix multiple bugs and performance issues in
TsconfigPathsPlugin:references: "auto",_loadTsconfigReferencesrecurses infinitely. Fixed by tracking visited reference config paths in aSetand skipping already-visited ones.extends: "my-base-config"(unscoped package, no sub-path) failed to resolve tonode_modules/my-base-config/tsconfig.json. The existing fallback logic only handled scoped packages (@scope/name) and sub-path packages (pkg/tsconfig). Added an else branch for unscoped packages.readFileused for existence check:_loadTsconfigFromExtendscalledreadFilejust to check if a path exists, discarding the buffer. Changed tostatwhich avoids reading the entire file content unnecessarily.substituteConfigDirregex on every call: The regex replacement ran even when paths contained no${configDir}. Added a fastincludescheck to skip the regex when not needed.What kind of change does this PR introduce?
fix, perf
Did you add tests for your changes?
Yes. Added 3 new test cases:
test/tsconfig-paths.test.js: "should handle circular references without hanging or crashing"test/tsconfig-paths.test.js: "should resolve paths from a circular-referenced project"test/tsconfig-paths.test.js: "should resolve paths inherited from an unscoped npm package tsconfig"Test fixtures added:
test/fixtures/tsconfig-paths/references-circular/— A↔B circular reference projecttest/fixtures/tsconfig-paths/extends-unscoped-pkg/— unscoped npm package extendsDoes this PR introduce a breaking change?
No.
If relevant, what needs to be documented once your changes are merged or what have you already documented?
No documentation changes needed.
Use of AI
This PR was authored with the assistance of Claude Code (Claude Opus 4.6). The bugs were identified through code review, test cases were written first to reproduce, then fixes were applied and verified.