diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1a96f715182..a202e82ec3c9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -240,7 +240,7 @@ jobs: run: pnpm test:prepare - name: Run benchmarks - uses: CodSpeedHQ/action@0010eb0ca6e89b80c88e8edaaa07cfe5f3e6664d # v3.5.0 + uses: CodSpeedHQ/action@c28fe9fbe7d57a3da1b7834ae3761c1d8217612d # v3.7.0 with: run: pnpm vitest bench token: ${{ secrets.CODSPEED_TOKEN }} diff --git a/debug/plugins/timings-unbuild.ts b/debug/plugins/timings-unbuild.ts index 10f9c309d9b7..1b3601a77d56 100644 --- a/debug/plugins/timings-unbuild.ts +++ b/debug/plugins/timings-unbuild.ts @@ -1,6 +1,7 @@ import type { Plugin } from 'rollup' import { parse } from 'acorn' -import { type Node, walk } from 'estree-walker' +import { walk } from 'estree-walker' +import type { Node } from 'estree-walker' import MagicString from 'magic-string' import tsBlankSpace from 'ts-blank-space' diff --git a/docs/1.getting-started/18.upgrade.md b/docs/1.getting-started/18.upgrade.md index b510b6cc2291..6880a65fb186 100644 --- a/docs/1.getting-started/18.upgrade.md +++ b/docs/1.getting-started/18.upgrade.md @@ -299,6 +299,64 @@ export default defineNuxtConfig({ }) ``` +### Corrected Module Loading Order in Layers + +🚦 **Impact Level**: Minimal + +#### What Changed + +The order in which modules are loaded when using [Nuxt layers](/docs/guide/going-further/layers) has been corrected. Previously, modules from the project root were loaded before modules from extended layers, which was the reverse of the expected behavior. + +Now modules are loaded in the correct order: + +1. **Layer modules first** (in extend order - deeper layers first) +2. **Project modules last** (highest priority) + +This affects both: +* Modules defined in the `modules` array in `nuxt.config.ts` +* Auto-discovered modules from the `modules/` directory + +#### Reasons for Change + +This change ensures that: +* Extended layers have lower priority than the consuming project +* Module execution order matches the intuitive layer inheritance pattern +* Module configuration and hooks work as expected in multi-layer setups + +#### Migration Steps + +**Most projects will not need changes**, as this corrects the loading order to match expected behavior. + +However, if your project was relying on the previous incorrect order, you may need to: + +1. **Review module dependencies**: Check if any modules depend on specific loading order +2. **Adjust module configuration**: If modules were configured to work around the incorrect order +3. **Test thoroughly**: Ensure all functionality works as expected with the corrected order + +Example of the new correct order: +```ts +// Layer: my-layer/nuxt.config.ts +export default defineNuxtConfig({ + modules: ['layer-module-1', 'layer-module-2'] +}) + +// Project: nuxt.config.ts +export default defineNuxtConfig({ + extends: ['./my-layer'], + modules: ['project-module-1', 'project-module-2'] +}) + +// Loading order (corrected): +// 1. layer-module-1 +// 2. layer-module-2 +// 3. project-module-1 (can override layer modules) +// 4. project-module-2 (can override layer modules) +``` + +If you encounter issues with module order dependencies due to needing to register a hook, consider using the [`modules:done` hook](/docs/guide/going-further/modules#custom-hooks) for modules that need to call a hook. This is run after all other modules have been loaded, which means it is safe to use. + +👉 See [PR #31507](https://github.com/nuxt/nuxt/pull/31507) and [issue #25719](https://github.com/nuxt/nuxt/issues/25719) for more details. + ### Deduplication of Route Metadata 🚦 **Impact Level**: Minimal @@ -777,7 +835,7 @@ This change should generally improve the expected behavior, but if you were expe query: { id }, immediate: false ) -+ watch(id, execute, { once: true }) ++ watch(id, () => execute(), { once: true }) ``` To opt out of this behavior: diff --git a/docs/2.guide/2.directory-structure/2.env.md b/docs/2.guide/2.directory-structure/2.env.md index f07b051821e0..3240ae03103c 100644 --- a/docs/2.guide/2.directory-structure/2.env.md +++ b/docs/2.guide/2.directory-structure/2.env.md @@ -55,6 +55,10 @@ Since `.env` files are not used in production, you must explicitly set environme * Many cloud service providers, such as Vercel, Netlify, and AWS, provide interfaces for setting environment variables via their dashboards, CLI tools or configuration files. +::important +`runtimeConfig` [won't pick up environment variables that don't start with `NUXT_` in production] (https://nuxt.com/docs/guide/going-further/runtime-config#environment-variables). +:: + ## Production Preview For local production preview purpose, we recommend using [`nuxt preview`](/docs/api/commands/preview) since using this command, the `.env` file will be loaded into `process.env` for convenience. Note that this command requires dependencies to be installed in the package directory. diff --git a/docs/2.guide/3.going-further/1.events.md b/docs/2.guide/3.going-further/1.events.md index 7eb88c986487..c86a0c2dd30a 100644 --- a/docs/2.guide/3.going-further/1.events.md +++ b/docs/2.guide/3.going-further/1.events.md @@ -58,25 +58,6 @@ await nuxtApp.callHook('app:user:registered', { You can inspect all events using the **Nuxt DevTools** Hooks panel. :: -## Augmenting Types - -You can augment the types of hooks provided by Nuxt. - -```ts -import { HookResult } from "@nuxt/schema"; - -declare module '#app' { - interface RuntimeNuxtHooks { - 'your-nuxt-runtime-hook': () => HookResult - } - interface NuxtHooks { - 'your-nuxt-hook': () => HookResult - } -} - -declare module 'nitropack' { - interface NitroRuntimeHooks { - 'your-nitro-hook': () => void; - } -} -``` +::read-more{to="/docs/guide/going-further/hooks"} +Learn more about Nuxt's built-in hooks and how to extend them +:: diff --git a/docs/2.guide/3.going-further/2.hooks.md b/docs/2.guide/3.going-further/2.hooks.md index 459066e71355..e7adb545e4c5 100644 --- a/docs/2.guide/3.going-further/2.hooks.md +++ b/docs/2.guide/3.going-further/2.hooks.md @@ -74,6 +74,25 @@ export default defineNitroPlugin((nitroApp) => { Learn more about available Nitro lifecycle hooks. :: -## Additional Hooks +## Adding Custom Hooks -Learn more about creating custom hooks in the [Events section](/docs/guide/going-further/events). +You can define your own custom hooks support by extending Nuxt's hook interfaces. + +```ts +import { HookResult } from "@nuxt/schema"; + +declare module '#app' { + interface RuntimeNuxtHooks { + 'your-nuxt-runtime-hook': () => HookResult + } + interface NuxtHooks { + 'your-nuxt-hook': () => HookResult + } +} + +declare module 'nitropack' { + interface NitroRuntimeHooks { + 'your-nitro-hook': () => void; + } +} +``` diff --git a/docs/2.guide/3.going-further/3.modules.md b/docs/2.guide/3.going-further/3.modules.md index 692b2acd118c..4916b5295b4f 100644 --- a/docs/2.guide/3.going-further/3.modules.md +++ b/docs/2.guide/3.going-further/3.modules.md @@ -559,6 +559,31 @@ export default defineNuxtModule({ ``` :: +##### Custom Hooks + +Modules can also define and call their own hooks, which is a powerful pattern for making your module extensible. + +If you expect other modules to be able to subscribe to your module's hooks, you should call them in the `modules:done` hook. This ensures that all other modules have had a chance to be set up and register their listeners to your hook during their own `setup` function. + +```ts +// my-module/module.ts +import { defineNuxtModule } from '@nuxt/kit' + +export interface ModuleHooks { + 'my-module:custom-hook': (payload: { foo: string }) => void +} + +export default defineNuxtModule({ + setup (options, nuxt) { + // Call your hook in `modules:done` + nuxt.hook('modules:done', async () => { + const payload = { foo: 'bar' } + await nuxt.callHook('my-module:custom-hook', payload) + }) + } +}) +``` + #### Adding Templates/Virtual Files If you need to add a virtual file that can be imported into the user's app, you can use the `addTemplate` utility. diff --git a/docs/3.api/2.composables/use-async-data.md b/docs/3.api/2.composables/use-async-data.md index c4f748c04b26..edb0f0a2ec50 100644 --- a/docs/3.api/2.composables/use-async-data.md +++ b/docs/3.api/2.composables/use-async-data.md @@ -159,7 +159,7 @@ const { data: users2 } = useAsyncData('users', () => $fetch('/api/users'), { imm By default, Nuxt waits until a `refresh` is finished before it can be executed again. ::note -If you have not fetched data on the server (for example, with `server: false`), then the data _will not_ be fetched until hydration completes. This means even if you await [`useAsyncData`](/docs/api/composables/use-async-data) on the client side, `data` will remain `null` within `