Skip to content

Conversation

@deleteme
Copy link

🔗 Linked issue

📚 Description

This fixes two related problems:

  1. A longstanding issue with Nuxt v3 where CommonJS dependencies of Nuxt module plugins would not be optimized by Vite into ESM (GH issue). In development, people would be served a CommonJS file and get a TypeError. The workaround up until recently was to manually include these problematic packages in the vite.optimizeDeps.include config.
  2. A more recent issue around Nuxt v4.2 that caused the vite.optimizeDeps.include workaround to fail. A regression Commit was unsetting the include field.

Details

The module plugins would be added to VFS via plugins.client.mjs, not written to the filesystem, and so esbuild would not naturally discover the dependencies.

I'd like to use a prerelease of these changes in my app to verify that this really does fix the problems. The unit test I've added uses mocking and is incapable of verifying the fix. Is there a way for me to do this?

@deleteme deleteme requested a review from danielroe as a code owner December 10, 2025 21:58
@bolt-new-by-stackblitz
Copy link

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@coderabbitai
Copy link

coderabbitai bot commented Dec 10, 2025

Walkthrough

Adds write: true to the clientPluginTemplate so the generated client plugin is written to disk. Refactors the EnvironmentsPlugin to stop storing a captured Vite config and to detect SSR by environment.name === 'ssr', changing when optimizeDeps.include is cleared. Adds unit tests for EnvironmentsPlugin covering client vs SSR handling of optimizeDeps.include, the viteEnvironmentApi experimental flag, and multiple environments in a single plugin instance.

Possibly related PRs

  • nuxt/nuxt PR 33550 — Modifies the same EnvironmentsPlugin implementation and the environment-name-based SSR/optimizeDeps logic.
  • nuxt/nuxt PR 33586 — Changes packages/vite/src/plugins/environments.ts and adjusts plugin ordering/behaviour that interacts with optimizeDeps handling.
  • nuxt/nuxt PR 31969 — Relates to on-disk vs virtual resolution for client plugin templates; connected to making the client template writable to disk.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main fix: enabling Vite to optimize CommonJS dependencies of module plugins, which directly addresses the core technical problem in the changeset.
Description check ✅ Passed The description provides comprehensive context about the two related issues being fixed, explains the technical root cause, references the linked issue, and clearly relates to all changes in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f240c8 and a43d3df.

📒 Files selected for processing (3)
  • packages/nuxt/src/core/templates.ts (1 hunks)
  • packages/vite/src/plugins/environments.test.ts (1 hunks)
  • packages/vite/src/plugins/environments.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/vite/src/plugins/environments.ts
  • packages/vite/src/plugins/environments.test.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/core/templates.ts
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx,js,jsx,vue}: Use clear, descriptive variable and function names
Add comments only to explain complex logic or non-obvious implementations
Keep functions focused and manageable (generally under 50 lines), and extract complex logic into separate domain-specific files
Remove code that is not used or needed
Use error handling patterns consistently

Files:

  • packages/nuxt/src/core/templates.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: nuxt/nuxt PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-25T11:42:16.132Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js} : Write unit tests for core functionality using `vitest`
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
Repo: nuxt/nuxt PR: 26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.

Applied to files:

  • packages/nuxt/src/core/templates.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, webpack, default, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-off, json, lts/-1)
  • GitHub Check: test-fixtures (windows-latest, dev, vite-env-api, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite-env-api, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, js, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, json, lts/-1)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite-env-api, default, manifest-on, json, lts/-1)
  • GitHub Check: test-size
  • GitHub Check: test-benchmark
  • GitHub Check: release-pkg-pr-new
  • GitHub Check: typecheck (windows-latest, bundler)
  • GitHub Check: typecheck (ubuntu-latest, bundler)
  • GitHub Check: code
🔇 Additional comments (1)
packages/nuxt/src/core/templates.ts (1)

64-65: write: true on clientPluginTemplate is correct, with expected side-effect explained

Writing plugins.client.mjs to disk so that Vite/esbuild can discover module plugin dependencies aligns with the PR goal. The only in-file behavioural change is that buildTypeTemplate will stop emitting a declare module "#build/plugins.client.mjs" stub for this template, since it skips templates with file.write === true; that is consistent, as the module now exists physically and TypeScript can infer its shape from the generated JavaScript.

The added comment appropriately explains this non-obvious implementation detail in accordance with the coding guidelines.


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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86d67b2 and 366e538.

📒 Files selected for processing (3)
  • packages/nuxt/src/core/templates.ts (1 hunks)
  • packages/vite/src/plugins/environments.test.ts (1 hunks)
  • packages/vite/src/plugins/environments.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/vite/src/plugins/environments.test.ts
  • packages/nuxt/src/core/templates.ts
  • packages/vite/src/plugins/environments.ts
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx,js,jsx,vue}: Use clear, descriptive variable and function names
Add comments only to explain complex logic or non-obvious implementations
Keep functions focused and manageable (generally under 50 lines), and extract complex logic into separate domain-specific files
Remove code that is not used or needed
Use error handling patterns consistently

Files:

  • packages/vite/src/plugins/environments.test.ts
  • packages/nuxt/src/core/templates.ts
  • packages/vite/src/plugins/environments.ts
**/*.{test,spec}.{ts,tsx,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Write unit tests for core functionality using vitest

Files:

  • packages/vite/src/plugins/environments.test.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: nuxt/nuxt PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-25T11:42:16.132Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js} : Write unit tests for core functionality using `vitest`
📚 Learning: 2025-11-25T11:42:16.132Z
Learnt from: CR
Repo: nuxt/nuxt PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-25T11:42:16.132Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js} : Write unit tests for core functionality using `vitest`

Applied to files:

  • packages/vite/src/plugins/environments.test.ts
📚 Learning: 2025-11-25T11:42:16.132Z
Learnt from: CR
Repo: nuxt/nuxt PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-25T11:42:16.132Z
Learning: Applies to **/e2e/**/*.{ts,tsx,js} : Write end-to-end tests using Playwright and `nuxt/test-utils`

Applied to files:

  • packages/vite/src/plugins/environments.test.ts
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
Repo: nuxt/nuxt PR: 26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.

Applied to files:

  • packages/nuxt/src/core/templates.ts
  • packages/vite/src/plugins/environments.ts
🧬 Code graph analysis (1)
packages/vite/src/plugins/environments.test.ts (1)
packages/vite/src/plugins/environments.ts (1)
  • EnvironmentsPlugin (10-89)
🪛 GitHub Check: build
packages/vite/src/plugins/environments.test.ts

[failure] 102-102:
This expression is not callable.


[failure] 101-101:
This expression is not callable.


[failure] 83-83:
This expression is not callable.


[failure] 65-65:
This expression is not callable.


[failure] 51-51:
This expression is not callable.


[failure] 34-34:
This expression is not callable.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: code
🔇 Additional comments (5)
packages/nuxt/src/core/templates.ts (1)

64-65: LGTM! Enables dependency discovery for module plugins.

The addition of write: true allows Vite's esbuild to naturally discover CommonJS dependencies of module plugins during development, addressing the core issue where these dependencies were not being optimised. This is consistent with the existing pattern used for appConfigTemplate (line 431).

packages/vite/src/plugins/environments.ts (1)

34-34: LGTM! Simplified SSR detection logic.

The refactor to check name === 'ssr' directly (instead of relying on stored viteConfig.ssr) simplifies the logic and makes the condition more explicit. The behaviour correctly unsets optimizeDeps.include for SSR environments when the experimental Vite Environment API is disabled.

packages/vite/src/plugins/environments.test.ts (3)

1-19: LGTM! Well-structured test setup.

The mock configuration properly isolates the plugin from Nitro dependencies whilst preserving other exports from @nuxt/kit. The minimal mock approach reduces test brittleness.


21-109: Excellent test coverage!

The test suite comprehensively validates the optimizeDeps.include handling across all scenarios:

  • Client/SSR environment isolation
  • Experimental flag behaviour
  • Missing optimizeDeps initialisation
  • Multiple environment independence

The tests directly address the regression fix mentioned in the PR objectives.


111-127: LGTM! Well-designed test helper.

The createMockNuxt helper effectively reduces duplication and provides a minimal, focused mock with only the properties required by the plugin under test.

@deleteme deleteme force-pushed the fix-module-plugins-not-being-optimized branch from 366e538 to 0a51c91 Compare December 10, 2025 22:17
@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 10, 2025

Open in StackBlitz

@nuxt/kit

npm i https://pkg.pr.new/@nuxt/kit@33851

@nuxt/nitro-server

npm i https://pkg.pr.new/@nuxt/nitro-server@33851

nuxt

npm i https://pkg.pr.new/nuxt@33851

@nuxt/rspack-builder

npm i https://pkg.pr.new/@nuxt/rspack-builder@33851

@nuxt/schema

npm i https://pkg.pr.new/@nuxt/schema@33851

@nuxt/vite-builder

npm i https://pkg.pr.new/@nuxt/vite-builder@33851

@nuxt/webpack-builder

npm i https://pkg.pr.new/@nuxt/webpack-builder@33851

commit: a43d3df

@codspeed-hq
Copy link

codspeed-hq bot commented Dec 10, 2025

CodSpeed Performance Report

Merging #33851 will improve performances by 12.02%

Comparing deleteme:fix-module-plugins-not-being-optimized (a43d3df) with main (c1b3590)

Summary

⚡ 1 improvement
✅ 9 untouched

Benchmarks breakdown

Benchmark BASE HEAD Change
writeTypes in the basic-types fixture 90.5 ms 80.8 ms +12.02%

@deleteme deleteme force-pushed the fix-module-plugins-not-being-optimized branch from 4839965 to 1f240c8 Compare December 11, 2025 16:29
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/vite/src/plugins/environments.test.ts (2)

21-115: Test coverage of optimizeDeps.include behaviour across environments is thorough

The suite exercises the key paths in configEnvironment: preserving optimizeDeps.include on the client, unsetting it for SSR (including when optimizeDeps is initially missing), respecting the experimental.viteEnvironmentApi flag, and handling multiple environments independently on a single plugin instance. Using @ts-expect-error on the configEnvironment calls is a reasonable compromise here to avoid overfitting tests to the plugin’s TypeScript signature while still validating the runtime behaviour you care about.


117-133: createMockNuxt is minimal and targeted to plugin needs

The helper provides exactly the Nuxt fields the plugin relies on (experimental.viteEnvironmentApi, app.buildAssetsDir, buildDir, rootDir) and keeps the mock small. If you ever add tests around dev/prod‑specific behaviour, you might optionally extend this to include an explicit options.dev flag for clarity, but it’s not required for the current optimise‑deps scenarios.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4839965 and 1f240c8.

📒 Files selected for processing (3)
  • packages/nuxt/src/core/templates.ts (1 hunks)
  • packages/vite/src/plugins/environments.test.ts (1 hunks)
  • packages/vite/src/plugins/environments.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/vite/src/plugins/environments.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/core/templates.ts
  • packages/vite/src/plugins/environments.test.ts
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx,js,jsx,vue}: Use clear, descriptive variable and function names
Add comments only to explain complex logic or non-obvious implementations
Keep functions focused and manageable (generally under 50 lines), and extract complex logic into separate domain-specific files
Remove code that is not used or needed
Use error handling patterns consistently

Files:

  • packages/nuxt/src/core/templates.ts
  • packages/vite/src/plugins/environments.test.ts
**/*.{test,spec}.{ts,tsx,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Write unit tests for core functionality using vitest

Files:

  • packages/vite/src/plugins/environments.test.ts
🧠 Learnings (6)
📚 Learning: 2024-11-05T15:22:54.759Z
Learnt from: GalacticHypernova
Repo: nuxt/nuxt PR: 26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.

Applied to files:

  • packages/nuxt/src/core/templates.ts
📚 Learning: 2025-11-25T11:42:16.132Z
Learnt from: CR
Repo: nuxt/nuxt PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-25T11:42:16.132Z
Learning: Applies to **/*.{test,spec}.{ts,tsx,js} : Write unit tests for core functionality using `vitest`

Applied to files:

  • packages/vite/src/plugins/environments.test.ts
📚 Learning: 2025-11-25T11:42:16.132Z
Learnt from: CR
Repo: nuxt/nuxt PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-25T11:42:16.132Z
Learning: Applies to **/*.{ts,tsx,js,jsx,vue} : Add comments only to explain complex logic or non-obvious implementations

Applied to files:

  • packages/vite/src/plugins/environments.test.ts
📚 Learning: 2024-11-11T12:34:22.648Z
Learnt from: Tofandel
Repo: nuxt/nuxt PR: 0
File: :0-0
Timestamp: 2024-11-11T12:34:22.648Z
Learning: Ensure that AI-generated summaries accurately reflect the key changes in the PR, focusing on notable changes such as the removal of unused imports and variables starting with underscores.

Applied to files:

  • packages/vite/src/plugins/environments.test.ts
📚 Learning: 2025-11-25T11:42:16.132Z
Learnt from: CR
Repo: nuxt/nuxt PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-25T11:42:16.132Z
Learning: Applies to **/*.{ts,tsx,js,jsx,vue} : Remove code that is not used or needed

Applied to files:

  • packages/vite/src/plugins/environments.test.ts
📚 Learning: 2025-11-25T11:42:16.132Z
Learnt from: CR
Repo: nuxt/nuxt PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-25T11:42:16.132Z
Learning: Applies to **/e2e/**/*.{ts,tsx,js} : Write end-to-end tests using Playwright and `nuxt/test-utils`

Applied to files:

  • packages/vite/src/plugins/environments.test.ts
🧬 Code graph analysis (1)
packages/vite/src/plugins/environments.test.ts (1)
packages/vite/src/plugins/environments.ts (1)
  • EnvironmentsPlugin (10-89)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: code
🔇 Additional comments (2)
packages/nuxt/src/core/templates.ts (1)

61-66: Writing plugins.client.mjs to disk is an appropriate fix and integrates cleanly

Emitting the client plugin template (write: true) is a good way to let Vite/esbuild discover CommonJS dependencies during development, and it lines up with existing conventions (e.g. appConfigTemplate) that also write templates to disk. This change should not disrupt type generation: buildTypeTemplate will now correctly skip a stub for plugins.client.mjs because the file exists, and pluginsDeclaration.exists still won’t treat this template as a plugin source since its dst path doesn’t overlap plugin src paths.

packages/vite/src/plugins/environments.test.ts (1)

1-19: Mocking @nuxt/kit with a partial override of useNitro is clean and safe

Using vi.mock with vi.importActual<typeof kit> and overriding only useNitro gives the plugin a realistic Nitro shape while preserving the rest of the module behaviour, which is a solid pattern for isolating environment-dependent logic in tests.

@deleteme deleteme force-pushed the fix-module-plugins-not-being-optimized branch from 1f240c8 to a43d3df Compare December 11, 2025 19:47
},
configEnvironment (name, config) {
if (!nuxt.options.experimental.viteEnvironmentApi && viteConfig.ssr) {
if (!nuxt.options.experimental.viteEnvironmentApi && name === 'ssr') {
Copy link
Member

Choose a reason for hiding this comment

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

if the environment api isn't enabled, this will be true for both server + client builds. that's why we trigger on viteConfig.ssr instead.

Copy link
Author

Choose a reason for hiding this comment

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

The current implementation logic breaks optimizeDeps.include:

Copy link
Member

Choose a reason for hiding this comment

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

then we should not pass ssr in the client config (in client.ts)...

checking the name here will not work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants