fix(js): reduce repeated work in TypeScript target inference - #37093
leosvelperez wants to merge 2 commits into
Conversation
✅ Deploy Preview for nx-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for nx-dev ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
View your CI Pipeline Execution ↗ for commit 225eaee
☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
✅ The fix from Nx Cloud was applied
We fixed this by removing the unnecessary Promise.all([...]) wrapper around a single invokeCreateNodesOnMatchingFiles call in the new run test helper, which is what oxlint's unicorn/no-single-promise-in-promise-methods rule flagged. This resolves the js:oxlint failure while preserving the test's original behavior of returning an array of results for both the concurrent and non-concurrent branches.
Tip
✅ We verified this fix by re-running js:oxlint.
Suggested Fix changes
diff --git a/packages/js/src/plugins/typescript/plugin.spec.ts b/packages/js/src/plugins/typescript/plugin.spec.ts
index 70faa7f9..afd7da3d 100644
--- a/packages/js/src/plugins/typescript/plugin.spec.ts
+++ b/packages/js/src/plugins/typescript/plugin.spec.ts
@@ -175,13 +175,11 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
{ typecheck: { targetName: 'check-lib' } }
),
])
- : Promise.all([
- invokeCreateNodesOnMatchingFiles(
- ['apps/my-app/tsconfig.json', 'libs/my-lib/tsconfig.json'],
- context,
- {}
- ),
- ]);
+ : invokeCreateNodesOnMatchingFiles(
+ ['apps/my-app/tsconfig.json', 'libs/my-lib/tsconfig.json'],
+ context,
+ {}
+ ).then((result) => [result]);
const coldResults = await run();
expect(
🔔 Heads up, your workspace has pending recommendations ↗ to auto-apply fixes for similar failures.
View interactive diff ↗➡️ This fix was applied by Leosvel Pérez Espinosa
🎓 Learn more about Self-Healing CI on nx.dev
Co-authored-by: leosvelperez <leosvelperez@users.noreply.github.com>
Current Behavior
The
@nx/js/typescriptplugin repeats configuration and reference analysis when hashing configs and creating targets. Projects with multiple tsconfig files repeat directory scans, package metadata reads, and build validation. Warm invocations also rewrite unchanged caches.Expected Behavior
Reuse each config's analysis for hashing and target creation, and reuse project metadata for consecutive configs in the same project. Store reference expansions on the existing config contexts instead of a separate nested map. Release initialization-only source text and write caches only when their contents change.
Keep inferred targets, dependency inputs, cache invalidation, and error reporting unchanged. When references are missing from the initial discovery set, finish hashing before creating targets so filtered or concurrent plugin scopes retain complete dependency inputs. Cache build-validation results only after validation succeeds.
Performance
Measured against master at
646e806da0on an Apple M2 Max with 12 CPU cores and 96 GiB RAM, macOS 26.6.2, and Node.js 26.7.0. Seven interleaved repetitions per workload and cache mode used the same dependency tree. Other applications remained open; swap stayed unused and no thermal warning was reported.These are direct plugin invocation times, not whole-command times. Cold means empty Nx plugin caches, not an empty operating-system file cache. Warm means a second invocation in the same process. Times are medians; percentages are medians of paired changes, so they can differ from ratios of the displayed times. Changes marked unresolved had 95% paired bootstrap intervals crossing zero.
The all-configs row uses default options over the full config corpus; it is not this repo's configured plugin scope. Gains depend on reference structure and configs per project, not only project count. A separate 700-isolated-project control had 2.3% higher cold CPU time, an unresolved 1.9% cold wall-time increase, and 4.3% lower warm wall time.
Memory did not show a broad increase. The table measures post-GC heap growth during the invocation, before cleanup, including returned nodes and lazily loaded TypeScript state. It does not measure long-term daemon retention.
Across all measured workloads, peak RSS ranged from 22.8 MiB lower to 1.4 MiB higher; the largest increase was about 0.4% of a 322 MiB process. Peak RSS is noisier than the heap measurements. After cleanup, heap differences stayed within 47 KiB of master. The change reduces repeated work without retaining every config's full analysis or adding a transitive-reference cache.
Avoiding repeated filesystem reads and cache writes may provide larger savings on slower filesystems, including Windows, but those platforms were not benchmarked.
Related Issue(s)
Fixes NXC-5015.