Skip to content

Conversation

danielroe
Copy link
Member

@danielroe danielroe commented Jun 18, 2025

🔗 Linked issue

resolves #31674

📚 Description

this is a draft PR to look at the effect of deferring modules registered with installModule until the end of the module install cycle. The kit utility is backwards compatible with previous versions of Nuxt (which won't set _modulesToDefer).

If installedModule is called more than once, it will merge all sets of options into resolved configuration - and, of course, respect the user's configuration.

(This will not solve the issue where a layer adds a module but another module in the user's project modifies the config or calls installModule itself.)

@danielroe danielroe requested a review from BobbieGoede June 18, 2025 16:26
@danielroe danielroe self-assigned this Jun 18, 2025
Copy link

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

Copy link

pkg-pr-new bot commented Jun 18, 2025

Open in StackBlitz

@nuxt/kit

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

nuxt

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

@nuxt/rspack-builder

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

@nuxt/schema

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

@nuxt/vite-builder

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

@nuxt/webpack-builder

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

commit: 1e3bf44

Copy link

codspeed-hq bot commented Jun 18, 2025

CodSpeed Performance Report

Merging #32431 will not alter performance

Comparing feat/deferred-modules (1e3bf44) with main (8eb92e6)

Summary

✅ 10 untouched benchmarks

@danielroe danielroe added this to the 4.0 milestone Jun 24, 2025
@danielroe danielroe marked this pull request as ready for review July 4, 2025 09:08
Copy link

coderabbitai bot commented Jul 4, 2025

Walkthrough

A mechanism for deferring module installation was introduced. The installModule function now accepts an options object with a defer property to control deferral. If deferral is enabled, modules are added to a _modulesToDefer map on the Nuxt instance instead of being installed immediately. The Nuxt initialisation function initNuxt was modified to install modules in two phases: immediate installation followed by deferred installation of collected modules. The Nuxt interface was extended to include the optional _modulesToDefer property. Tests and example modules were updated to demonstrate nested module registration and log the order of module setup.


📜 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 fa0da3a and 1e3bf44.

📒 Files selected for processing (1)
  • packages/kit/src/module/install.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/kit/src/module/install.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). (5)
  • GitHub Check: code
  • GitHub Check: codeql (actions)
  • GitHub Check: codeql (javascript-typescript)
  • GitHub Check: build
  • GitHub Check: lint-docs
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/deferred-modules

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

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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 (1)
packages/nuxt/src/core/nuxt.ts (1)

584-584: Use undefined assignment instead of delete operator.

The delete operator can impact performance by changing object shape. Consider using undefined assignment as suggested by static analysis.

-  delete nuxt._modulesToDefer
+  nuxt._modulesToDefer = undefined
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 69345c3 and fa0da3a.

📒 Files selected for processing (5)
  • packages/kit/src/module/install.ts (1 hunks)
  • packages/nuxt/src/core/nuxt.ts (1 hunks)
  • packages/schema/src/types/nuxt.ts (1 hunks)
  • test/basic.test.ts (1 hunks)
  • test/fixtures/basic/modules/example.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
`**/*.{ts,tsx}`: Follow standard TypeScript conventions and best practices

**/*.{ts,tsx}: Follow standard TypeScript conventions and best practices

📄 Source: CodeRabbit Inference Engine (.github/copilot-instructions.md)

List of files the instruction was applied to:

  • packages/schema/src/types/nuxt.ts
  • packages/kit/src/module/install.ts
  • test/fixtures/basic/modules/example.ts
  • test/basic.test.ts
  • packages/nuxt/src/core/nuxt.ts
`**/*.{ts,tsx,vue}`: Use clear, descriptive variable and function names Add comm...

**/*.{ts,tsx,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

📄 Source: CodeRabbit Inference Engine (.github/copilot-instructions.md)

List of files the instruction was applied to:

  • packages/schema/src/types/nuxt.ts
  • packages/kit/src/module/install.ts
  • test/fixtures/basic/modules/example.ts
  • test/basic.test.ts
  • packages/nuxt/src/core/nuxt.ts
`**/*.{test,spec}.{ts,tsx}`: Write unit tests for core functionality using vitest

**/*.{test,spec}.{ts,tsx}: Write unit tests for core functionality using vitest

📄 Source: CodeRabbit Inference Engine (.github/copilot-instructions.md)

List of files the instruction was applied to:

  • test/basic.test.ts
🧠 Learnings (5)
packages/schema/src/types/nuxt.ts (2)
Learnt from: GalacticHypernova
PR: nuxt/nuxt#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.
Learnt from: TheAlexLichter
PR: nuxt/nuxt#31812
File: packages/nuxt/src/components/plugins/islands-transform.ts:202-202
Timestamp: 2025-04-18T18:33:41.753Z
Learning: In Nuxt, using `rolldownVersion` (not `rollupVersion`) is intentional when detecting if rolldown-vite is being used, even though TypeScript may show an error because the property isn't in standard type definitions yet.
packages/kit/src/module/install.ts (1)
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:

```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```

instead of wrapping the import in a computed property or importing the component unconditionally.
test/fixtures/basic/modules/example.ts (5)
Learnt from: GalacticHypernova
PR: nuxt/nuxt#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.
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T13:48:28.134Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Write unit tests for core functionality using vitest
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T13:48:28.134Z
Learning: Applies to **/*.e2e.{ts,js} : Write end-to-end tests using Playwright and @nuxt/test-utils
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T13:48:28.134Z
Learning: Applies to **/*.vue : Use `<script setup lang="ts">` and the composition API when creating Vue components
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:

```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```

instead of wrapping the import in a computed property or importing the component unconditionally.
test/basic.test.ts (3)

undefined

<retrieved_learning>
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T13:48:28.134Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Write unit tests for core functionality using vitest
</retrieved_learning>

<retrieved_learning>
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T13:48:28.134Z
Learning: Applies to **/*.e2e.{ts,js} : Write end-to-end tests using Playwright and @nuxt/test-utils
</retrieved_learning>

<retrieved_learning>
Learnt from: huang-julien
PR: #29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In packages/nuxt/src/app/components/nuxt-root.vue, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:

const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null

instead of wrapping the import in a computed property or importing the component unconditionally.
</retrieved_learning>

packages/nuxt/src/core/nuxt.ts (2)
Learnt from: GalacticHypernova
PR: nuxt/nuxt#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.
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:

```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```

instead of wrapping the import in a computed property or importing the component unconditionally.
🧬 Code Graph Analysis (1)
test/fixtures/basic/modules/example.ts (1)
packages/kit/src/module/install.ts (1)
  • installModule (26-80)
🪛 Biome (1.9.4)
packages/nuxt/src/core/nuxt.ts

[error] 584-584: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

⏰ Context from checks skipped due to timeout of 90000ms (20)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, default, manifest-off, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, built, rspack, default, manifest-on, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, built, rspack, async, manifest-on, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, default, manifest-on, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, built, webpack, async, manifest-on, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-off, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, default, manifest-off, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-off, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-off, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-on, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, js, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, json, 20)
  • GitHub Check: release-pr
  • GitHub Check: test-benchmark
  • GitHub Check: test-size
  • GitHub Check: typecheck (windows-latest, bundler)
  • GitHub Check: typecheck (ubuntu-latest, bundler)
  • GitHub Check: code
  • GitHub Check: lint-docs
🔇 Additional comments (13)
packages/schema/src/types/nuxt.ts (1)

95-95: LGTM! Well-structured type definition for deferred module installation.

The type definition correctly supports the deferred module installation mechanism:

  • Optional property ensures backward compatibility
  • Map structure allows flexible module identification
  • Array value type supports multiple option sets per module as described in the PR objectives
  • Follows established private field naming conventions
test/basic.test.ts (1)

112-116: LGTM! Well-structured test for module registration functionality.

This test properly verifies the new deferred module installation feature by:

  • Using the appropriate test context to access the Nuxt instance
  • Reading from the virtual file system to check generated module logs
  • Asserting the correct sequence of module registration (parent → child → grandchild)
  • Following established vitest patterns with clear naming and structure

The test aligns well with the PR objectives and provides good coverage for the nested module registration functionality.

packages/nuxt/src/core/nuxt.ts (3)

576-576: LGTM!

Initialising the deferred modules map correctly sets up the two-phase installation mechanism.


578-578: Correct use of immediate installation flag.

Passing { defer: false } ensures modules are installed immediately in the first phase, maintaining expected behaviour.


580-583: Well-implemented deferred module installation logic.

The implementation correctly handles multiple registrations by reversing the options array and merging them with defu, ensuring first-registered options take precedence whilst preventing recursive deferral.

packages/kit/src/module/install.ts (3)

17-24: Well-documented interface for module installation options.

The interface clearly defines the deferral behaviour with appropriate documentation explaining the purpose and default behaviour.


29-29: Backward-compatible signature update.

Adding the options parameter with a default empty object maintains compatibility with existing code whilst enabling the new deferral feature.


30-35: Correct implementation of module deferral logic.

The implementation properly checks for deferral conditions, accumulates multiple registrations, and returns early to defer installation. The comment clearly indicates the deferral behaviour.

test/fixtures/basic/modules/example.ts (5)

1-1: Correct import for nested module installation.

Adding installModule import enables the demonstration of nested module registration.


8-8: Required changes for async module operations.

Making setup async and accepting the nuxt parameter enables the use of await for nested module installation.


13-14: Acceptable use of @ts-expect-error for testing.

Using @ts-expect-error is appropriate here as this is test code verifying that modules can extend nuxt.options with custom properties.


16-29: Excellent demonstration of nested module installation.

The implementation effectively showcases:

  • Inline module definition with configKey and defaults
  • Nested module installation using await
  • Option passing and logging at each level
  • The deferred installation mechanism in action

41-44: Smart approach for exposing test logs.

Using addTemplate to export the collected logs as a module enables effective testing of the module installation order and option merging behaviour.

@BobbieGoede
Copy link
Member

@danielroe
Here's a repo with an attempt at demonstrating some issues

  • the module installation order differs from layer priority order (maybe intended?)
  • nuxt-i18n has a langDir option which gets overwritten by each module installation, for layers we can retrieve it from _layers
  • one way we try to work around langDir being lost is by forcing users to pass absolute paths for locale files instead

Here is a repo with a variant of this setup using layers instead

  • shows the difference in merging order for the options passed to nuxt-i18n
  • in nuxt-i18n we use _layers to merge things manually, we ignore relative file paths from the module options (and assume absolute paths are provided by installModule)

The setups can get more complicated when locales have file arrays instead of a single file path, it's hard to demonstrate but you can imagine that the merging/overwrite order is important in these setups.

@BobbieGoede
Copy link
Member

But to be clear, the changes in this PR are probably good for the majority of use cases

@danielroe
Copy link
Member Author

/ecosystem-ci run

@nuxt-ecosystem-ci
Copy link

📝 Ran ecosystem CI on ``: Open

suite result latest scheduled
content failure success
ui failure success
starter failure success
pinia failure success
image failure success
nuxt-com failure success
fonts failure success
vite-pwa failure success
example-layers-monorepo failure success
bridge failure failure
devtools failure success
docus failure success
examples failure success
scripts failure success
histoire ⏹️ cancelled failure
og-image failure success
icon failure success
cli failure success
module-builder failure success
test-utils failure success
i18n-module failure success
sitemap failure failure
elk failure success
sanity-module failure success
tailwindcss failure success
storybook failure success

@danielroe danielroe marked this pull request as draft July 17, 2025 14:19
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.

order-dependent modules
2 participants