Skip to content

feat(devkit): batch tmp installs in ensurePackage#35305

Open
AgentEnder wants to merge 3 commits intomasterfrom
chore/multi-ensure-pkg
Open

feat(devkit): batch tmp installs in ensurePackage#35305
AgentEnder wants to merge 3 commits intomasterfrom
chore/multi-ensure-pkg

Conversation

@AgentEnder
Copy link
Copy Markdown
Member

Current Behavior

ensurePackage only accepts a single package at a time and provisions a fresh temporary project per call. Generators that need multiple Nx plugins in one run (e.g. @nx/js:library with a bundler + linter + test runner, or @nx/react:application with a bundler + e2e runner) sequentially spin up N throwaway tmp projects — each one runs the package-manager preinstall, resolves its own lockfile, and fetches from the registry.

Expected Behavior

ensurePackage accepts a batch form:

ensurePackage({
  '@nx/eslint': nxVersion,
  '@nx/vite': nxVersion,
  '@nx/vitest': nxVersion,
});

It filters out packages already resolvable via require(pkg), then installs everything missing into a single tmp project via a new installPackagesToTmp internal. Subsequent single-package ensurePackage / require calls hit the cache warmed by the batch call. When running against an older nx core that predates installPackagesToTmp, the batch form degrades gracefully to sequential single-package installs.

Generators that sequentially install distinct plugins in one run now derive their plugin set up front and call ensurePackage once:

  • @nx/js:library
  • @nx/react:application, @nx/react:library, @nx/react add-e2e / add-vite setupVitest
  • @nx/remix:application
  • @nx/web:application
  • @nx/vue:library add-vite, @nx/vue:application add-e2e
  • @nx/expo:application add-e2e
  • @nx/next:cypress-component-configuration
  • @nx/nuxt:application add-vitest

Files whose ensurePackage calls are mutually exclusive (e.g. switch on bundler or e2e runner) were intentionally left alone — only one install happens at runtime regardless.

Related Issue(s)

N/A

Add an `ensurePackage(Record<string, string>)` overload that resolves
every missing package in a single tmp project instead of provisioning a
fresh one per call. Exposes `installPackagesToTmp` / `installPackagesToTmpAsync`
from `nx` core for feature-detection, with a graceful fallback to
sequential single-package installs on older cores.

Refactor generators that sequentially install distinct Nx plugins in the
same run (js/library, react/application, react/library, remix/application,
web/application, vue/library add-vite, vue/application add-e2e,
react/application add-e2e, react/application add-vite/setupVitest,
expo/application add-e2e, next/cypress-component-configuration,
nuxt/application add-vitest) to collect their plugin set up front and
ensure it once.
@AgentEnder AgentEnder requested a review from a team as a code owner April 15, 2026 17:55
@AgentEnder AgentEnder requested a review from MaxKless April 15, 2026 17:55
@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 15, 2026

Deploy Preview for nx-dev failed. Why did it fail? →

Name Link
🔨 Latest commit dfa7015
🔍 Latest deploy log https://app.netlify.com/projects/nx-dev/deploys/69e80fa60eccf700084cdde7

@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 15, 2026

Deploy Preview for nx-docs failed. Why did it fail? →

Name Link
🔨 Latest commit dfa7015
🔍 Latest deploy log https://app.netlify.com/projects/nx-docs/deploys/69e80fa6c997e80008a72fc1

@nx-cloud
Copy link
Copy Markdown
Contributor

nx-cloud Bot commented Apr 15, 2026

View your CI Pipeline Execution ↗ for commit dfa7015

Command Status Duration Result
nx affected --targets=lint,test,build,e2e,e2e-c... ❌ Failed 55m 54s View ↗
nx run-many -t check-imports check-lock-files c... ✅ Succeeded 3s View ↗
nx-cloud record -- pnpm nx-cloud conformance:check ✅ Succeeded 17s View ↗
nx build workspace-plugin ✅ Succeeded <1s View ↗
nx-cloud record -- nx sync:check ✅ Succeeded 21s View ↗
nx-cloud record -- nx format:check ✅ Succeeded 13s View ↗
nx affected -t e2e-macos-local --parallel=1 --b... ✅ Succeeded 42m 55s View ↗

☁️ Nx Cloud last updated this comment at 2026-04-22 01:10:19 UTC

nx-cloud[bot]

This comment was marked as outdated.

Co-authored-by: AgentEnder <AgentEnder@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@nx-cloud nx-cloud Bot left a comment

Choose a reason for hiding this comment

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

Nx Cloud is proposing a fix for your failed CI:

We added @nx/jest and @nx/vite to the ignoredDependencies list in packages/js/.eslintrc.json to fix the js:lint failure. The PR's typed require('@nx/vite') as typeof import('@nx/vite') and require('@nx/jest') as typeof import('@nx/jest') calls introduce static type-level references that the Nx project graph picks up as dependencies, causing @nx/dependency-checks to report them as missing from package.json — unlike the untyped require('@nx/rollup') calls which go undetected. These packages are optional runtime plugins installed separately by users, making ignoredDependencies the correct resolution.

Tip

We verified this fix by re-running js:lint.

Suggested Fix changes
diff --git a/packages/js/.eslintrc.json b/packages/js/.eslintrc.json
index e04b58643c..930d50bfc7 100644
--- a/packages/js/.eslintrc.json
+++ b/packages/js/.eslintrc.json
@@ -70,7 +70,10 @@
               "@swc/cli",
               "babel-plugin-const-enum",
               "babel-plugin-macros",
-              "babel-plugin-transform-typescript-metadata"
+              "babel-plugin-transform-typescript-metadata",
+              // Optional runtime plugins – installed by the user separately
+              "@nx/jest",
+              "@nx/vite"
             ]
           }
         ]
diff --git a/packages/js/src/generators/library/library.ts b/packages/js/src/generators/library/library.ts
index 287acefd66..ed44ce93d6 100644
--- a/packages/js/src/generators/library/library.ts
+++ b/packages/js/src/generators/library/library.ts
@@ -155,8 +155,8 @@ export async function libraryGeneratorInternal(
   }
 
   if (options.bundler === 'vite') {
-    // nx-ignore-next-line
     const { viteConfigurationGenerator, createOrEditViteConfig } =
+      // nx-ignore-next-line
       require('@nx/vite') as typeof import('@nx/vite');
     const viteTask = await viteConfigurationGenerator(tree, {
       project: options.name,
@@ -741,8 +741,8 @@ async function addJest(
   tree: Tree,
   options: NormalizedLibraryGeneratorOptions
 ): Promise<GeneratorCallback> {
-  // nx-ignore-next-line
   const { configurationGenerator } =
+    // nx-ignore-next-line
     require('@nx/jest') as typeof import('@nx/jest');
   return await configurationGenerator(tree, {
     ...options,

🔔 Heads up, your workspace has pending recommendations ↗ to auto-apply fixes for similar failures.

Apply fix via Nx Cloud  Reject fix via Nx Cloud


Or Apply changes locally with:

npx nx-cloud apply-locally z8mH-Asol

Apply fix locally with your editor ↗   View interactive diff ↗



🎓 Learn more about Self-Healing CI on nx.dev

@FrozenPandaz FrozenPandaz requested a review from jaysoo April 23, 2026 17:40
@FrozenPandaz FrozenPandaz added the priority: medium Medium Priority (not high, not low priority) label Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: medium Medium Priority (not high, not low priority)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants