Skip to content

fix(js): reduce repeated work in TypeScript target inference - #37093

Draft
leosvelperez wants to merge 2 commits into
masterfrom
nxc-5015
Draft

leosvelperez wants to merge 2 commits into
masterfrom
nxc-5015

Conversation

@leosvelperez

Copy link
Copy Markdown
Member

Current Behavior

The @nx/js/typescript plugin 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 646e806da0 on 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.

Workload Cold master -> candidate Cold time change Warm master -> candidate Warm time change
Nx repo (this repo), main plugin configuration, 179 configs 404.6 -> 395.2 ms 2.9% lower 79.5 -> 64.1 ms 20.4% lower
Nx repo (this repo), all 372 configs with default options 515.0 -> 514.8 ms 0.7% higher, unresolved 107.3 -> 96.4 ms 10.2% lower
Synthetic: 1,500 projects with split configs, 3,000 configs 1,630.2 -> 1,404.9 ms 14.1% lower 1,170.0 -> 944.7 ms 19.3% lower
Synthetic: 1,300-project reference chain 1,193.4 -> 840.5 ms 29.4% lower 518.0 -> 513.9 ms 2.8% lower, unresolved
Synthetic: 750-project dense reference graph 732.5 -> 603.8 ms 17.7% lower 314.0 -> 308.2 ms 1.8% lower, unresolved
Synthetic: 696-project grouped reference graph 865.4 -> 677.3 ms 21.7% lower 346.3 -> 330.6 ms 4.8% lower
Synthetic: 3,000 isolated projects 1,516.7 -> 1,512.6 ms 0.3% lower, unresolved 1,139.0 -> 1,105.2 ms 2.8% lower

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.

Workload Cold heap growth, master -> candidate Change Warm heap growth, master -> candidate Change
Nx repo (this repo), main plugin configuration 11,974.3 -> 11,971.1 KiB 0.03% lower 862.3 -> 742.1 KiB 13.9% lower
Synthetic: 1,500 split-config projects 19,921.3 -> 19,376.5 KiB 2.7% lower 6,956.9 -> 6,439.3 KiB 7.4% lower
Synthetic: 696-project grouped reference graph 14,210.6 -> 13,743.6 KiB 3.3% lower 2,604.0 -> 2,361.7 KiB 9.3% lower
Synthetic: 3,000 isolated projects 19,477.3 -> 18,930.5 KiB 2.8% lower 7,308.7 -> 6,778.1 KiB 7.3% lower

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.

@leosvelperez leosvelperez self-assigned this Sep 17, 2026
@netlify

netlify Bot commented Sep 17, 2026

Copy link
Copy Markdown

Deploy Preview for nx-docs ready!

Name Link
🔨 Latest commit 225eaee
🔍 Latest deploy log https://app.netlify.com/projects/nx-docs/deploys/6aac16c819e6c30008951bae
😎 Deploy Preview https://deploy-preview-37093--nx-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Sep 17, 2026

Copy link
Copy Markdown

Deploy Preview for nx-dev ready!

Name Link
🔨 Latest commit 225eaee
🔍 Latest deploy log https://app.netlify.com/projects/nx-dev/deploys/6aac16c82da48800095a7e38
😎 Deploy Preview https://deploy-preview-37093--nx-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@nx-cloud

nx-cloud Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit 225eaee

Command Status Duration Result
nx affected --targets=lint,oxlint,test,build,e2... ✅ Succeeded 1h 5m 53s View ↗
nx run-many -t check-imports check-lock-files c... ✅ Succeeded 4s View ↗
nx-cloud record -- pnpm nx-cloud conformance:check ✅ Succeeded 1m 2s View ↗
nx build workspace-plugin ✅ Succeeded <1s View ↗
nx-cloud record -- nx sync:check ✅ Succeeded 19s View ↗
nx-cloud record -- nx format:check ✅ Succeeded <1s View ↗

☁️ Nx Cloud last updated this comment at 2026-09-17 17:45:48 UTC

@nx-cloud nx-cloud Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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.

Revert fix via Nx Cloud  

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>
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