diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 87c74f0eb4..f76b0b4161 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:lts@sha256:de4c8be8232b7081d8846360d73ce6dbff33c6636f2259cd14d82c0de1ac3ff2
+FROM node:lts@sha256:5c76d05034644fa8ecc9c2aa84e0a83cd981d0ef13af5455b87b9adf5b216561
RUN apt-get update && \
apt-get install -fy libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdbus-1-3 libdrm2 libxkbcommon0 libatspi2.0-0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2 && \
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
index 99dd9217a6..ccbf0feaee 100644
--- a/.github/workflows/benchmark.yml
+++ b/.github/workflows/benchmark.yml
@@ -46,7 +46,7 @@ jobs:
run: pnpm build
- name: Run benchmarks
- uses: CodSpeedHQ/action@fa1dcde8d58f2ab0b407a6a24d6cc5a8c1444a8c # v3.1.0
+ uses: CodSpeedHQ/action@513a19673a831f139e8717bf45ead67e47f00044 # v3.2.0
with:
run: pnpm vitest bench
token: ${{ secrets.CODSPEED_TOKEN }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 85d020f5c3..5d211a26b1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -75,7 +75,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Initialize CodeQL
- uses: github/codeql-action/init@4f3212b61783c3c68e8309a0f18a699764811cda # v3.27.1
+ uses: github/codeql-action/init@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
with:
config: |
paths:
@@ -91,7 +91,7 @@ jobs:
queries: +security-and-quality
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@4f3212b61783c3c68e8309a0f18a699764811cda # v3.27.1
+ uses: github/codeql-action/analyze@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
with:
category: "/language:javascript-typescript"
@@ -248,7 +248,7 @@ jobs:
TEST_PAYLOAD: ${{ matrix.payload }}
SKIP_BUNDLE_SIZE: ${{ github.event_name != 'push' || matrix.env == 'dev' || matrix.builder == 'webpack' || matrix.context == 'default' || matrix.payload == 'js' || runner.os == 'Windows' }}
- - uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
+ - uses: codecov/codecov-action@015f24e6818733317a2da2edd6290ab26238649a # v5.0.7
if: github.event_name != 'push' && matrix.env == 'built' && matrix.builder == 'vite' && matrix.context == 'default' && matrix.os == 'ubuntu-latest' && matrix.manifest == 'manifest-on'
with:
token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml
index 7194dec4dd..b79e1f1eb8 100644
--- a/.github/workflows/dependency-review.yml
+++ b/.github/workflows/dependency-review.yml
@@ -19,4 +19,4 @@ jobs:
- name: 'Checkout Repository'
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: 'Dependency Review'
- uses: actions/dependency-review-action@4081bf99e2866ebe428fc0477b69eb4fcda7220a # v4.4.0
+ uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0
diff --git a/.github/workflows/docs-check-links.yml b/.github/workflows/docs-check-links.yml
index 9603f76e9b..5db033fd37 100644
--- a/.github/workflows/docs-check-links.yml
+++ b/.github/workflows/docs-check-links.yml
@@ -29,7 +29,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Lychee link checker
- uses: lycheeverse/lychee-action@f81112d0d2814ded911bd23e3beaa9dda9093915 # for v1.8.0
+ uses: lycheeverse/lychee-action@4aa18b6ccdac05029fab067313a6a04f941e6494 # for v1.8.0
with:
# arguments with file types to check
args: >-
diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml
deleted file mode 100644
index b7b46ad748..0000000000
--- a/.github/workflows/release-pr.yml
+++ /dev/null
@@ -1,84 +0,0 @@
-name: release-pr
-
-on:
- issue_comment:
- types: [created]
-
-env:
- # 7 GiB by default on GitHub, setting to 6 GiB
- NODE_OPTIONS: --max-old-space-size=6144
-
-permissions:
- contents: read
-
-jobs:
- release-pr:
- if: github.repository == 'nuxt/nuxt' && github.event.issue.pull_request && github.event.comment.body == '/trigger release'
- concurrency:
- group: release
- permissions:
- id-token: write
- pull-requests: write
- runs-on: ubuntu-latest
- timeout-minutes: 20
-
- steps:
- - name: Ensure action is by maintainer
- uses: octokit/request-action@dad4362715b7fb2ddedf9772c8670824af564f0d # v2.4.0
- id: check_role
- with:
- route: GET /repos/nuxt/nuxt/collaborators/${{ github.event.comment.user.login }}
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Get PR Info
- id: pr
- env:
- PR_NUMBER: ${{ github.event.issue.number }}
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GH_REPO: ${{ github.repository }}
- COMMENT_AT: ${{ github.event.comment.created_at }}
- run: |
- pr="$(gh api -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" /repos/"${GH_REPO}"/pulls/"${PR_NUMBER}")"
- head_sha="$(echo "$pr" | jq -r .head.sha)"
- updated_at="$(echo "$pr" | jq -r .updated_at)"
-
- if [[ $(date -d "$updated_at" +%s) -gt $(date -d "$COMMENT_AT" +%s) ]]; then
- exit 1
- fi
-
- echo "head_sha=$head_sha" >> "$GITHUB_OUTPUT"
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- with:
- ref: ${{ steps.pr.outputs.head_sha }}
- fetch-depth: 1
-
- - run: corepack enable
- - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
- with:
- node-version: 20
- cache: "pnpm"
-
- - name: Install dependencies
- run: pnpm install
-
- - name: Build
- run: pnpm build
-
- - name: Release Edge
- run: ./scripts/release-edge.sh pr-${{ github.event.issue.number }}
- env:
- NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
- NPM_CONFIG_PROVENANCE: true
-
- - name: Post comment
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- github.rest.issues.createComment({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- body: `:rocket: Release triggered! You can now install [nuxt@npm:nuxt-nightly@pr-${{ github.event.issue.number }}](https://www.npmjs.com/package/nuxt-nightly/v/pr-${{ github.event.issue.number }})`
- })
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 410556ccea..1d69fc4ee8 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -68,7 +68,7 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
- uses: github/codeql-action/upload-sarif@4f3212b61783c3c68e8309a0f18a699764811cda # v3.27.1
+ uses: github/codeql-action/upload-sarif@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5
if: github.repository == 'nuxt/nuxt' && success()
with:
sarif_file: results.sarif
diff --git a/docs/1.getting-started/2.installation.md b/docs/1.getting-started/2.installation.md
index 128a135f57..4abe89a93b 100644
--- a/docs/1.getting-started/2.installation.md
+++ b/docs/1.getting-started/2.installation.md
@@ -21,8 +21,8 @@ Or follow the steps below to set up a new Nuxt project on your computer.
#### Prerequisites
-- **Node.js** - [`v18.0.0`](https://nodejs.org/en) or newer
-- **Text editor** - We recommend [Visual Studio Code](https://code.visualstudio.com/) with the [official Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (previously known as Volar)
+- **Node.js** - [`18.x`](https://nodejs.org/en) or newer (but we recommend the [active LTS release](https://github.com/nodejs/release#release-schedule))
+- **Text editor** - There is no IDE requirement, but we recommend [Visual Studio Code](https://code.visualstudio.com/) with the [official Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (previously known as Volar) or [WebStorm](https://www.jetbrains.com/webstorm/), which, along with [other JetBrains IDEs](https://www.jetbrains.com/ides/), offers great Nuxt support right out-of-the-box.
- **Terminal** - In order to run Nuxt commands
::note
diff --git a/docs/1.getting-started/3.views.md b/docs/1.getting-started/3.views.md
index e169bdfa66..63c839841b 100644
--- a/docs/1.getting-started/3.views.md
+++ b/docs/1.getting-started/3.views.md
@@ -139,7 +139,7 @@ If you only have a single layout in your application, we recommend using [`app.v
If you want to create more layouts and learn how to use them in your pages, find more information in the [Layouts section](/docs/guide/directory-structure/layouts).
-## Advanced: Extending the HTML template
+## Advanced: Extending the HTML Template
::note
If you only need to modify the `
`, you can refer to the [SEO and meta section](/docs/getting-started/seo-meta).
diff --git a/docs/1.getting-started/5.transitions.md b/docs/1.getting-started/5.transitions.md
index dcef4b284e..17f064a0f5 100644
--- a/docs/1.getting-started/5.transitions.md
+++ b/docs/1.getting-started/5.transitions.md
@@ -8,7 +8,7 @@ navigation.icon: i-ph-exclude-square
Nuxt leverages Vue's [``](https://vuejs.org/guide/built-ins/transition.html#the-transition-component) component to apply transitions between pages and layouts.
::
-## Page transitions
+## Page Transitions
You can enable page transitions to apply an automatic transition for all your [pages](/docs/guide/directory-structure/pages).
@@ -113,7 +113,7 @@ Moving to the about page will add the 3d rotation effect:
-## Layout transitions
+## Layout Transitions
You can enable layout transitions to apply an automatic transition for all your [layouts](/docs/guide/directory-structure/layouts).
@@ -225,7 +225,7 @@ definePageMeta({
```
-## Global settings
+## Global Settings
You can customize these default transition names globally using `nuxt.config`.
@@ -468,6 +468,6 @@ export default defineNuxtRouteMiddleware(to => {
})
```
-### Known issues
+### Known Issues
- If you perform data fetching within your page setup functions, that you may wish to reconsider using this feature for the moment. (By design, View Transitions completely freeze DOM updates whilst they are taking place.) We're looking at restrict the View Transition to the final moments before `` resolves, but in the interim you may want to consider carefully whether to adopt this feature if this describes you.
diff --git a/docs/2.guide/2.directory-structure/3.package.md b/docs/2.guide/2.directory-structure/3.package.md
index c2e6e56607..6ca742906f 100644
--- a/docs/2.guide/2.directory-structure/3.package.md
+++ b/docs/2.guide/2.directory-structure/3.package.md
@@ -19,8 +19,7 @@ The minimal `package.json` of your Nuxt application should looks like:
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
- "devDependencies": {
- "@nuxt/devtools": "latest",
+ "dependencies": {
"nuxt": "latest",
"vue": "latest",
"vue-router": "latest"
diff --git a/docs/3.api/1.components/8.nuxt-island.md b/docs/3.api/1.components/8.nuxt-island.md
index cd715534f2..7bedd3db0b 100644
--- a/docs/3.api/1.components/8.nuxt-island.md
+++ b/docs/3.api/1.components/8.nuxt-island.md
@@ -41,6 +41,10 @@ Remote islands need `experimental.componentIslands` to be `'local+remote'` in yo
It is strongly discouraged to enable `dangerouslyLoadClientComponents` as you can't trust a remote server's javascript.
::
+::note
+By default, component islands are scanned from the `~/components/islands/` directory. So the `~/components/islands/MyIsland.vue` component could be rendered with ` `.
+::
+
## Slots
Slots can be passed to an island component if declared.
diff --git a/docs/3.api/2.composables/on-prehydrate.md b/docs/3.api/2.composables/on-prehydrate.md
index 1f034d6cec..484aa2c6cf 100644
--- a/docs/3.api/2.composables/on-prehydrate.md
+++ b/docs/3.api/2.composables/on-prehydrate.md
@@ -1,7 +1,6 @@
---
title: "onPrehydrate"
-description: "Use onPrehydrate to run a callback on the client immediately before
-Nuxt hydrates the page."
+description: "Use onPrehydrate to run a callback on the client immediately before Nuxt hydrates the page."
links:
- label: Source
icon: i-simple-icons-github
diff --git a/docs/3.api/2.composables/use-nuxt-data.md b/docs/3.api/2.composables/use-nuxt-data.md
index 4e55114748..a5d3c99016 100644
--- a/docs/3.api/2.composables/use-nuxt-data.md
+++ b/docs/3.api/2.composables/use-nuxt-data.md
@@ -54,7 +54,7 @@ const { data } = await useAsyncData('todos', () => $fetch('/api/todos'))
const newTodo = ref('')
const previousTodos = ref([])
-// Access to the cached value of useFetch in todos.vue
+// Access to the cached value of useAsyncData in todos.vue
const { data: todos } = useNuxtData('todos')
const { data } = await useFetch('/api/addTodo', {
diff --git a/docs/3.api/2.composables/use-seo-meta.md b/docs/3.api/2.composables/use-seo-meta.md
index e28debae32..f3b9e95e63 100644
--- a/docs/3.api/2.composables/use-seo-meta.md
+++ b/docs/3.api/2.composables/use-seo-meta.md
@@ -39,7 +39,7 @@ const title = ref('My title')
useSeoMeta({
title,
- description: () => `description: ${title.value}`
+ description: () => `This is a description for the ${title.value} page`
})
```
diff --git a/docs/5.community/6.roadmap.md b/docs/5.community/6.roadmap.md
index df987f8bfb..c659fd0e55 100644
--- a/docs/5.community/6.roadmap.md
+++ b/docs/5.community/6.roadmap.md
@@ -41,8 +41,8 @@ In addition to the Nuxt framework, there are modules that are vital for the ecos
Module | Status | Nuxt Support | Repository | Description
------------------------------------|---------------------|--------------|------------|-------------------
[Scripts](https://scripts.nuxt.com) | Public Beta | 3.x | [nuxt/scripts](https://github.com/nuxt/scripts) | Easy 3rd party script management.
+Auth Utils | Planned | 3.x | `nuxt/auth-utils` to be announced | The temporary repository [atinux/nuxt-auth-utils](https://github.com/atinux/nuxt-auth-utils) is available while awaiting its official integration into Nuxt via RFC.
A11y | Planned | 3.x | `nuxt/a11y` to be announced | Accessibility hinting and utilities [nuxt/nuxt#23255](https://github.com/nuxt/nuxt/issues/23255)
-Auth | Planned | 3.x | `nuxt/auth` to be announced | Support is planned after session support.
Hints | Planned | 3.x | `nuxt/hints` to be announced | Guidance and suggestions for enhancing development practices.
## Release Cycle
diff --git a/eslint.config.mjs b/eslint.config.mjs
index a6ad15a72e..c2e9cb9761 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -72,8 +72,9 @@ export default createConfigForNuxt({
'error',
{
argsIgnorePattern: '^_',
+ destructuredArrayIgnorePattern: '^_',
ignoreRestSiblings: true,
- varsIgnorePattern: '^_',
+ varsIgnorePattern: '',
},
],
'@typescript-eslint/triple-slash-reference': 'off',
@@ -188,7 +189,6 @@ export default createConfigForNuxt({
},
},
// Sort rule keys in eslint config
- // @ts-expect-error incorrect types 🤔
{
files: ['**/eslint.config.mjs'],
name: 'local/sort-eslint-config',
diff --git a/knip.json b/knip.json
index dd2512b5cd..5bb5831739 100644
--- a/knip.json
+++ b/knip.json
@@ -1,27 +1,60 @@
{
"$schema": "https://unpkg.com/knip@5/schema.json",
+ "ignoreBinaries": [
+ "prepack"
+ ],
+ "ignoreWorkspaces": ["test/fixtures/basic"],
+ "ignoreDependencies": [
+ "bing",
+ "uno.css"
+ ],
"workspaces": {
".": {
"entry": [
- "scripts/*",
- "test/*",
- "test/fixtures/*"
+ "scripts/*"
+ ]
+ },
+ "test/fixtures/*": {
+ "entry": [
+ "nuxt.config.{js,ts}",
+ "modules/*.ts",
+ "types.ts"
]
},
"packages/*": {
"entry": [
"src/index.ts",
- "src/runtime/**/*.ts"
+ "src/runtime/**/*.{js,ts,mjs}"
]
},
"packages/nuxt": {
"entry": [
"src/app/**/*.ts",
"src/app/*.ts",
- "src/*/runtime/**/*.ts",
+ "src/*/runtime/**/*.{ts,js}",
"src/core/templates.ts",
"src/index.ts"
]
+ },
+ "packages/rspack": {
+ "entry": [
+ "../webpack/src/index.ts",
+ "../webpack/src/runtime/**/*.{js,ts,mjs}",
+ "builder.mjs"
+ ]
+ },
+ "packages/ui-templates": {
+ "entry": [
+ "lib/*.ts",
+ "styles.ts"
+ ]
+ },
+ "packages/webpack": {
+ "entry": [
+ "src/index.ts",
+ "src/runtime/**/*.{js,ts,mjs}",
+ "builder.mjs"
+ ]
}
}
}
diff --git a/nuxt.config.ts b/nuxt.config.ts
index b6777a9d28..9763e21d43 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -17,5 +17,8 @@ export default defineNuxtConfig({
},
],
pages: process.env.DOCS_TYPECHECK === 'true',
- typescript: { shim: process.env.DOCS_TYPECHECK === 'true' },
+ typescript: {
+ shim: process.env.DOCS_TYPECHECK === 'true',
+ hoist: ['@vitejs/plugin-vue', 'vue-router'],
+ },
})
diff --git a/package.json b/package.json
index 853254b30f..0add508572 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,7 @@
"lint:fix": "eslint . --cache --fix",
"lint:docs": "markdownlint ./docs && case-police 'docs/**/*.md' *.md",
"lint:docs:fix": "markdownlint ./docs --fix && case-police 'docs/**/*.md' *.md --fix",
- "lint:knip": "pnpx knip",
+ "lint:knip": "knip",
"play": "nuxi dev playground",
"play:build": "nuxi build playground",
"play:generate": "nuxi generate playground",
@@ -37,88 +37,84 @@
"@nuxt/kit": "workspace:*",
"@nuxt/rspack-builder": "workspace:*",
"@nuxt/schema": "workspace:*",
- "@nuxt/ui-templates": "workspace:*",
"@nuxt/vite-builder": "workspace:*",
"@nuxt/webpack-builder": "workspace:*",
- "@types/node": "22.9.0",
- "@unhead/dom": "1.11.11",
- "@unhead/shared": "1.11.11",
- "@unhead/vue": "1.11.11",
- "@unhead/schema": "1.11.11",
- "@unhead/ssr": "1.11.11",
- "@vue/compiler-core": "3.5.12",
- "@vue/compiler-dom": "3.5.12",
- "@vue/shared": "3.5.12",
+ "@types/node": "22.10.0",
+ "@unhead/dom": "1.11.13",
+ "@unhead/schema": "1.11.13",
+ "@unhead/shared": "1.11.13",
+ "@unhead/ssr": "1.11.13",
+ "@unhead/vue": "1.11.13",
+ "@vue/compiler-core": "3.5.13",
+ "@vue/compiler-dom": "3.5.13",
+ "@vue/shared": "3.5.13",
"c12": "2.0.1",
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
"jiti": "2.4.0",
- "magic-string": "^0.30.12",
+ "magic-string": "^0.30.14",
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
"nuxt": "workspace:*",
"ohash": "1.1.4",
- "postcss": "8.4.47",
- "rollup": "4.24.4",
+ "postcss": "8.4.49",
+ "rollup": "4.27.4",
"send": ">=1.1.0",
"typescript": "5.6.3",
"ufo": "1.5.4",
"unbuild": "3.0.0-rc.11",
- "unhead": "1.11.11",
- "vite": "5.4.10",
- "vue": "3.5.12"
+ "unhead": "1.11.13",
+ "vite": "6.0.0",
+ "vue": "3.5.13"
},
"devDependencies": {
- "@eslint/js": "9.14.0",
- "@nuxt/eslint-config": "0.6.1",
+ "@nuxt/eslint-config": "0.7.2",
"@nuxt/kit": "workspace:*",
"@nuxt/rspack-builder": "workspace:*",
"@nuxt/test-utils": "3.14.4",
"@nuxt/webpack-builder": "workspace:*",
"@testing-library/vue": "8.1.0",
- "@types/eslint__js": "8.42.3",
- "@types/node": "22.9.0",
+ "@types/node": "22.10.0",
"@types/semver": "7.5.8",
- "@unhead/schema": "1.11.11",
- "@unhead/vue": "1.11.11",
- "@vitejs/plugin-vue": "5.1.4",
- "@vitest/coverage-v8": "2.1.4",
+ "@unhead/schema": "1.11.13",
+ "@unhead/vue": "1.11.13",
+ "@vitest/coverage-v8": "2.1.6",
"@vue/test-utils": "2.4.6",
"autoprefixer": "10.4.20",
- "case-police": "0.7.0",
+ "case-police": "0.7.2",
"changelogen": "0.5.7",
"consola": "3.2.3",
"cssnano": "7.0.6",
"destr": "2.0.3",
"devalue": "5.1.1",
- "eslint": "9.14.0",
+ "eslint": "9.15.0",
"eslint-plugin-no-only-tests": "3.3.0",
- "eslint-plugin-perfectionist": "3.9.1",
+ "eslint-plugin-perfectionist": "4.1.2",
"eslint-typegen": "0.3.2",
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
- "happy-dom": "15.11.0",
+ "happy-dom": "15.11.6",
"jiti": "2.4.0",
- "markdownlint-cli": "0.42.0",
+ "knip": "5.38.0",
+ "markdownlint-cli": "0.43.0",
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
"nuxi": "3.15.0",
"nuxt": "workspace:*",
"nuxt-content-twoslash": "0.1.1",
"ofetch": "1.4.1",
"pathe": "1.1.2",
- "playwright-core": "1.48.2",
+ "playwright-core": "1.49.0",
"rimraf": "6.0.1",
"semver": "7.6.3",
- "sherif": "1.0.1",
+ "sherif": "1.0.2",
"std-env": "3.8.0",
"tinyexec": "0.3.1",
"tinyglobby": "0.2.10",
"typescript": "5.6.3",
"ufo": "1.5.4",
- "vitest": "2.1.4",
+ "vitest": "2.1.6",
"vitest-environment-nuxt": "1.0.1",
- "vue": "3.5.12",
- "vue-router": "4.4.5",
+ "vue": "3.5.13",
"vue-tsc": "2.1.10"
},
- "packageManager": "pnpm@9.12.3",
+ "packageManager": "pnpm@9.14.2",
"engines": {
"node": "^16.10.0 || >=18.0.0"
},
diff --git a/packages/kit/package.json b/packages/kit/package.json
index 20b9e50981..c0532a8ff8 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -1,6 +1,6 @@
{
"name": "@nuxt/kit",
- "version": "3.12.2",
+ "version": "4.0.0-0",
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/nuxt.git",
@@ -33,28 +33,27 @@
"destr": "^2.0.3",
"errx": "^0.1.0",
"globby": "^14.0.2",
- "hash-sum": "^2.0.0",
"ignore": "^6.0.2",
"jiti": "^2.4.0",
"klona": "^2.0.6",
- "mlly": "^1.7.2",
+ "mlly": "^1.7.3",
+ "ohash": "^1.1.4",
"pathe": "^1.1.2",
"pkg-types": "^1.2.1",
"scule": "^1.3.0",
"semver": "^7.6.3",
"ufo": "^1.5.4",
"unctx": "^2.3.1",
- "unimport": "^3.13.1",
+ "unimport": "^3.13.3",
"untyped": "^1.5.1"
},
"devDependencies": {
- "@rspack/core": "1.1.0",
- "@types/hash-sum": "1.0.2",
+ "@rspack/core": "1.1.4",
"@types/semver": "7.5.8",
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
"unbuild": "3.0.0-rc.11",
- "vite": "5.4.10",
- "vitest": "2.1.4",
+ "vite": "6.0.0",
+ "vitest": "2.1.6",
"webpack": "5.96.1"
},
"engines": {
diff --git a/packages/kit/src/index.ts b/packages/kit/src/index.ts
index c9b94204b0..04056b2784 100644
--- a/packages/kit/src/index.ts
+++ b/packages/kit/src/index.ts
@@ -28,7 +28,7 @@ export type { AddPluginOptions } from './plugin'
export { createResolver, findPath, resolveAlias, resolveFiles, resolveNuxtModule, resolvePath } from './resolve'
export type { ResolvePathOptions, Resolver } from './resolve'
export { addServerHandler, addDevServerHandler, addServerPlugin, addPrerenderRoutes, useNitro, addServerImports, addServerImportsDir, addServerScanDir } from './nitro'
-export { addTemplate, addTypeTemplate, normalizeTemplate, updateTemplates, writeTypes } from './template'
+export { addTemplate, addServerTemplate, addTypeTemplate, normalizeTemplate, updateTemplates, writeTypes } from './template'
export { logger, useLogger } from './logger'
// Internal Utils
diff --git a/packages/kit/src/module/define.ts b/packages/kit/src/module/define.ts
index 33ac6d015a..b0f30d80fb 100644
--- a/packages/kit/src/module/define.ts
+++ b/packages/kit/src/module/define.ts
@@ -65,7 +65,7 @@ function _defineNuxtModule<
const optionsDefaults: TOptionsDefaults =
module.defaults instanceof Function
- ? module.defaults(nuxt)
+ ? await module.defaults(nuxt)
: module.defaults ?? {}
let options = defu(inlineOptions, nuxtConfigOptions, optionsDefaults)
diff --git a/packages/kit/src/module/install.ts b/packages/kit/src/module/install.ts
index 9399e0e346..aa841fa3b7 100644
--- a/packages/kit/src/module/install.ts
+++ b/packages/kit/src/module/install.ts
@@ -5,6 +5,7 @@ import { dirname, isAbsolute, join, resolve } from 'pathe'
import { defu } from 'defu'
import { createJiti } from 'jiti'
import { resolve as resolveModule } from 'mlly'
+import { isRelative } from 'ufo'
import { useNuxt } from '../context'
import { resolveAlias, resolvePath } from '../resolve'
import { logger } from '../logger'
@@ -78,14 +79,23 @@ export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, n
// Import if input is string
if (typeof nuxtModule === 'string') {
- const paths = [join(nuxtModule, 'nuxt'), join(nuxtModule, 'module'), nuxtModule, join(nuxt.options.rootDir, nuxtModule)]
+ const paths = new Set()
+ nuxtModule = resolveAlias(nuxtModule, nuxt.options.alias)
+
+ if (isRelative(nuxtModule)) {
+ nuxtModule = resolve(nuxt.options.rootDir, nuxtModule)
+ }
+
+ paths.add(join(nuxtModule, 'nuxt'))
+ paths.add(join(nuxtModule, 'module'))
+ paths.add(nuxtModule)
+
for (const path of paths) {
for (const parentURL of nuxt.options.modulesDir) {
try {
- const resolved = resolveAlias(path, nuxt.options.alias)
- const src = isAbsolute(resolved)
- ? pathToFileURL(await resolvePath(resolved, { cwd: parentURL, fallbackToOriginal: false, extensions: nuxt.options.extensions })).href
- : await resolveModule(resolved, { url: pathToFileURL(parentURL.replace(/\/node_modules\/?$/, '')), extensions: nuxt.options.extensions })
+ const src = isAbsolute(path)
+ ? pathToFileURL(await resolvePath(path, { cwd: parentURL, fallbackToOriginal: false, extensions: nuxt.options.extensions })).href
+ : await resolveModule(path, { url: pathToFileURL(parentURL.replace(/\/node_modules\/?$/, '')), extensions: nuxt.options.extensions })
nuxtModule = await jiti.import(src, { default: true }) as NuxtModule
diff --git a/packages/kit/src/template.ts b/packages/kit/src/template.ts
index 8b4ae046fd..dcccc878e8 100644
--- a/packages/kit/src/template.ts
+++ b/packages/kit/src/template.ts
@@ -1,6 +1,6 @@
import { existsSync, promises as fsp } from 'node:fs'
import { basename, isAbsolute, join, parse, relative, resolve } from 'pathe'
-import hash from 'hash-sum'
+import { hash } from 'ohash'
import type { Nuxt, NuxtServerTemplate, NuxtTemplate, NuxtTypeTemplate, ResolvedNuxtTemplate, TSReference } from '@nuxt/schema'
import { withTrailingSlash } from 'ufo'
import { defu } from 'defu'
@@ -22,9 +22,9 @@ export function addTemplate (_template: NuxtTemplate | string) {
// Normalize template
const template = normalizeTemplate(_template)
- // Remove any existing template with the same filename
+ // Remove any existing template with the same destination path
nuxt.options.build.templates = nuxt.options.build.templates
- .filter(p => normalizeTemplate(p).filename !== template.filename)
+ .filter(p => normalizeTemplate(p).dst !== template.dst)
// Add to templates array
nuxt.options.build.templates.push(template)
diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json
index dcd41cc12c..6994508032 100644
--- a/packages/nuxt/package.json
+++ b/packages/nuxt/package.json
@@ -60,16 +60,16 @@
},
"dependencies": {
"@nuxt/devalue": "^2.0.2",
- "@nuxt/devtools": "^1.6.0",
+ "@nuxt/devtools": "^1.6.1",
"@nuxt/kit": "workspace:*",
"@nuxt/schema": "workspace:*",
"@nuxt/telemetry": "^2.6.0",
"@nuxt/vite-builder": "workspace:*",
- "@unhead/dom": "^1.11.11",
- "@unhead/shared": "^1.11.11",
- "@unhead/ssr": "^1.11.11",
- "@unhead/vue": "^1.11.11",
- "@vue/shared": "^3.5.12",
+ "@unhead/dom": "^1.11.13",
+ "@unhead/shared": "^1.11.13",
+ "@unhead/ssr": "^1.11.13",
+ "@unhead/vue": "^1.11.13",
+ "@vue/shared": "^3.5.13",
"acorn": "8.14.0",
"c12": "^2.0.1",
"chokidar": "^4.0.1",
@@ -91,8 +91,8 @@
"jiti": "^2.4.0",
"klona": "^2.0.6",
"knitwork": "^1.1.0",
- "magic-string": "^0.30.12",
- "mlly": "^1.7.2",
+ "magic-string": "^0.30.14",
+ "mlly": "^1.7.3",
"nanotar": "^0.1.1",
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
"nuxi": "^3.15.0",
@@ -106,34 +106,33 @@
"scule": "^1.3.0",
"semver": "^7.6.3",
"std-env": "^3.8.0",
- "strip-literal": "^2.1.0",
+ "strip-literal": "^2.1.1",
"tinyglobby": "0.2.10",
"ufo": "^1.5.4",
"ultrahtml": "^1.5.3",
"uncrypto": "^0.1.3",
"unctx": "^2.3.1",
"unenv": "^1.10.0",
- "unhead": "^1.11.11",
- "unimport": "^3.13.1",
- "unplugin": "^1.15.0",
+ "unhead": "^1.11.13",
+ "unimport": "^3.13.3",
+ "unplugin": "^1.16.0",
"unplugin-vue-router": "^0.10.8",
"unstorage": "^1.13.1",
"untyped": "^1.5.1",
- "vue": "^3.5.12",
+ "vue": "^3.5.13",
"vue-bundle-renderer": "^2.1.1",
"vue-devtools-stub": "^0.1.0",
- "vue-router": "^4.4.5"
+ "vue-router": "^4.5.0"
},
"devDependencies": {
"@nuxt/scripts": "0.9.5",
- "@nuxt/ui-templates": "1.3.4",
"@parcel/watcher": "2.5.0",
"@types/estree": "1.0.6",
- "@vitejs/plugin-vue": "5.1.4",
- "@vue/compiler-sfc": "3.5.12",
+ "@vitejs/plugin-vue": "5.2.1",
+ "@vue/compiler-sfc": "3.5.13",
"unbuild": "3.0.0-rc.11",
- "vite": "5.4.10",
- "vitest": "2.1.4"
+ "vite": "6.0.0",
+ "vitest": "2.1.6"
},
"peerDependencies": {
"@parcel/watcher": "^2.1.0",
diff --git a/packages/nuxt/src/app/components/nuxt-layout.ts b/packages/nuxt/src/app/components/nuxt-layout.ts
index e7af6597fc..7472235a3e 100644
--- a/packages/nuxt/src/app/components/nuxt-layout.ts
+++ b/packages/nuxt/src/app/components/nuxt-layout.ts
@@ -16,7 +16,6 @@ import layouts from '#build/layouts'
// @ts-expect-error virtual file
import { appLayoutTransition as defaultLayoutTransition } from '#build/nuxt.config.mjs'
-// TODO: revert back to defineAsyncComponent when https://github.com/vuejs/core/issues/6638 is resolved
const LayoutLoader = defineComponent({
name: 'LayoutLoader',
inheritAttrs: false,
@@ -24,13 +23,10 @@ const LayoutLoader = defineComponent({
name: String,
layoutProps: Object,
},
- async setup (props, context) {
+ setup (props, context) {
// This is a deliberate hack - this component must always be called with an explicit key to ensure
// that setup reruns when the name changes.
-
- const LayoutComponent = await layouts[props.name]().then((r: any) => r.default || r)
-
- return () => h(LayoutComponent, props.layoutProps, context.slots)
+ return () => h(layouts[props.name], props.layoutProps, context.slots)
},
})
diff --git a/packages/nuxt/src/app/components/nuxt-link.ts b/packages/nuxt/src/app/components/nuxt-link.ts
index aa50e11e09..814df1658e 100644
--- a/packages/nuxt/src/app/components/nuxt-link.ts
+++ b/packages/nuxt/src/app/components/nuxt-link.ts
@@ -506,9 +506,9 @@ function useObserver (): { observe: ObserveFn } | undefined {
observer.observe(element)
return () => {
callbacks.delete(element)
- observer!.unobserve(element)
+ observer?.unobserve(element)
if (callbacks.size === 0) {
- observer!.disconnect()
+ observer?.disconnect()
observer = null
}
}
diff --git a/packages/nuxt/src/app/composables/asyncData.ts b/packages/nuxt/src/app/composables/asyncData.ts
index 2081b96307..29193a0616 100644
--- a/packages/nuxt/src/app/composables/asyncData.ts
+++ b/packages/nuxt/src/app/composables/asyncData.ts
@@ -525,7 +525,7 @@ function clearNuxtDataByKey (nuxtApp: NuxtApp, key: string): void {
}
if (nuxtApp._asyncData[key]) {
- nuxtApp._asyncData[key]!.data.value = nuxtApp._asyncData[key]!._default()
+ nuxtApp._asyncData[key]!.data.value = unref(nuxtApp._asyncData[key]!._default())
nuxtApp._asyncData[key]!.error.value = undefined
nuxtApp._asyncData[key]!.pending.value = false
nuxtApp._asyncData[key]!.status.value = 'idle'
diff --git a/packages/nuxt/src/components/plugins/component-names.ts b/packages/nuxt/src/components/plugins/component-names.ts
index af01adb5dc..47fec70203 100644
--- a/packages/nuxt/src/components/plugins/component-names.ts
+++ b/packages/nuxt/src/components/plugins/component-names.ts
@@ -1,6 +1,7 @@
import { createUnplugin } from 'unplugin'
import MagicString from 'magic-string'
import type { Component } from 'nuxt/schema'
+import type { Program } from 'acorn'
import { SX_RE, isVue } from '../../core/utils'
interface NameDevPluginOptions {
@@ -34,6 +35,16 @@ export const ComponentNamePlugin = (options: NameDevPluginOptions) => createUnpl
const s = new MagicString(code)
s.replace(NAME_RE, `__name: ${JSON.stringify(component.pascalName)}`)
+ // Without setup function, vue compiler does not generate __name
+ if (!s.hasChanged()) {
+ const ast = this.parse(code) as Program
+ const exportDefault = ast.body.find(node => node.type === 'ExportDefaultDeclaration')
+ if (exportDefault) {
+ const { start, end } = exportDefault.declaration
+ s.overwrite(start, end, `Object.assign(${code.slice(start, end)}, { __name: ${JSON.stringify(component.pascalName)} })`)
+ }
+ }
+
if (s.hasChanged()) {
return {
code: s.toString(),
diff --git a/packages/nuxt/src/core/builder.ts b/packages/nuxt/src/core/builder.ts
index 013c80fbe7..9b99319828 100644
--- a/packages/nuxt/src/core/builder.ts
+++ b/packages/nuxt/src/core/builder.ts
@@ -10,7 +10,6 @@ import { generateApp as _generateApp, createApp } from './app'
import { checkForExternalConfigurationFiles } from './external-config-files'
import { cleanupCaches, getVueHash } from './cache'
-const IS_RESTART_PATH_RE = /^(?:app\.|error\.|plugins\/|middleware\/|layouts\/)/i
export async function build (nuxt: Nuxt) {
const app = createApp(nuxt)
nuxt.apps.default = app
@@ -21,19 +20,24 @@ export async function build (nuxt: Nuxt) {
if (nuxt.options.dev) {
watch(nuxt)
nuxt.hook('builder:watch', async (event, relativePath) => {
- if (event === 'change') { return }
- const path = resolve(nuxt.options.srcDir, relativePath)
- const relativePaths = nuxt.options._layers.map(l => relative(l.config.srcDir || l.cwd, path))
- const restartPath = relativePaths.find(relativePath => IS_RESTART_PATH_RE.test(relativePath))
- if (restartPath) {
- if (restartPath.startsWith('app')) {
- app.mainComponent = undefined
+ // Unset mainComponent and errorComponent if app or error component is changed
+ if (event === 'add' || event === 'unlink') {
+ const path = resolve(nuxt.options.srcDir, relativePath)
+ for (const layer of nuxt.options._layers) {
+ const relativePath = relative(layer.config.srcDir || layer.cwd, path)
+ if (relativePath.match(/^app\./i)) {
+ app.mainComponent = undefined
+ break
+ }
+ if (relativePath.match(/^error\./i)) {
+ app.errorComponent = undefined
+ break
+ }
}
- if (restartPath.startsWith('error')) {
- app.errorComponent = undefined
- }
- await generateApp()
}
+
+ // Recompile app templates
+ await generateApp()
})
nuxt.hook('builder:generateApp', (options) => {
// Bypass debounce if we are selectively invalidating templates
diff --git a/packages/nuxt/src/core/cache.ts b/packages/nuxt/src/core/cache.ts
index 748d57bc79..3096ecf554 100644
--- a/packages/nuxt/src/core/cache.ts
+++ b/packages/nuxt/src/core/cache.ts
@@ -6,7 +6,7 @@ import { isIgnored } from '@nuxt/kit'
import type { Nuxt, NuxtConfig, NuxtConfigLayer } from '@nuxt/schema'
import { hash, murmurHash, objectHash } from 'ohash'
import { glob } from 'tinyglobby'
-import _consola, { consola } from 'consola'
+import { consola } from 'consola'
import { dirname, join, relative } from 'pathe'
import { createTar, parseTar } from 'nanotar'
import type { TarFileInput } from 'nanotar'
diff --git a/packages/nuxt/src/core/external-config-files.ts b/packages/nuxt/src/core/external-config-files.ts
index 6c6d5d0d21..1f0359cee8 100644
--- a/packages/nuxt/src/core/external-config-files.ts
+++ b/packages/nuxt/src/core/external-config-files.ts
@@ -1,6 +1,5 @@
import { findPath, logger } from '@nuxt/kit'
import { basename } from 'pathe'
-import { generateApp as _generateApp } from './app'
/**
* Check for those external configuration files that are not compatible with Nuxt,
diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts
index 670131cac3..e2402adf68 100644
--- a/packages/nuxt/src/core/nuxt.ts
+++ b/packages/nuxt/src/core/nuxt.ts
@@ -5,7 +5,6 @@ import { createDebugger, createHooks } from 'hookable'
import ignore from 'ignore'
import type { LoadNuxtOptions } from '@nuxt/kit'
import { addBuildPlugin, addComponent, addPlugin, addPluginTemplate, addRouteMiddleware, addServerPlugin, addVitePlugin, addWebpackPlugin, installModule, loadNuxtConfig, logger, nuxtCtx, resolveAlias, resolveFiles, resolveIgnorePatterns, resolvePath, tryResolveModule, useNitro } from '@nuxt/kit'
-import { resolvePath as _resolvePath } from 'mlly'
import type { Nuxt, NuxtHooks, NuxtModule, NuxtOptions } from 'nuxt/schema'
import type { PackageJson } from 'pkg-types'
import { readPackageJSON } from 'pkg-types'
@@ -41,6 +40,7 @@ import { initNitro } from './nitro'
import schemaModule from './schema'
import { RemovePluginMetadataPlugin } from './plugins/plugin-metadata'
import { AsyncContextInjectionPlugin } from './plugins/async-context'
+import { ComposableKeysPlugin } from './plugins/composable-keys'
import { resolveDeepImportsPlugin } from './plugins/resolve-deep-imports'
import { prehydrateTransformPlugin } from './plugins/prehydrate'
import { VirtualFSPlugin } from './plugins/virtual'
@@ -248,6 +248,13 @@ async function initNuxt (nuxt: Nuxt) {
// Add plugin normalization plugin
addBuildPlugin(RemovePluginMetadataPlugin(nuxt))
+ // Add keys for useFetch, useAsyncData, etc.
+ addBuildPlugin(ComposableKeysPlugin({
+ sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
+ rootDir: nuxt.options.rootDir,
+ composables: nuxt.options.optimization.keyedComposables,
+ }))
+
// shared folder import protection
const sharedDir = withTrailingSlash(resolve(nuxt.options.rootDir, nuxt.options.dir.shared))
const relativeSharedDir = withTrailingSlash(relative(nuxt.options.rootDir, resolve(nuxt.options.rootDir, nuxt.options.dir.shared)))
@@ -409,11 +416,15 @@ async function initNuxt (nuxt: Nuxt) {
}
// Add
- addComponent({
- name: 'NuxtWelcome',
- priority: 10, // built-in that we do not expect the user to override
- filePath: resolve(nuxt.options.appDir, 'components/welcome'),
- })
+ // TODO: revert when deep server component config is properly bundle-split: https://github.com/nuxt/nuxt/pull/29956
+ const islandsConfig = nuxt.options.experimental.componentIslands
+ if (nuxt.options.dev || !(typeof islandsConfig === 'object' && islandsConfig.selectiveClient === 'deep')) {
+ addComponent({
+ name: 'NuxtWelcome',
+ priority: 10, // built-in that we do not expect the user to override
+ filePath: resolve(nuxt.options.appDir, 'components/welcome'),
+ })
+ }
addComponent({
name: 'NuxtLayout',
diff --git a/packages/vite/src/plugins/composable-keys.ts b/packages/nuxt/src/core/plugins/composable-keys.ts
similarity index 98%
rename from packages/vite/src/plugins/composable-keys.ts
rename to packages/nuxt/src/core/plugins/composable-keys.ts
index 14233469aa..01e11159fc 100644
--- a/packages/vite/src/plugins/composable-keys.ts
+++ b/packages/nuxt/src/core/plugins/composable-keys.ts
@@ -9,7 +9,7 @@ import type { CallExpression, Pattern } from 'estree'
import { parseQuery, parseURL } from 'ufo'
import escapeRE from 'escape-string-regexp'
import { findStaticImports, parseStaticImport } from 'mlly'
-import { matchWithStringOrRegex } from '../utils'
+import { matchWithStringOrRegex } from '../utils/plugins'
interface ComposableKeysOptions {
sourcemap: boolean
@@ -22,7 +22,7 @@ const NUXT_LIB_RE = /node_modules\/(?:nuxt|nuxt3|nuxt-nightly)\//
const SUPPORTED_EXT_RE = /\.(?:m?[jt]sx?|vue)/
const SCRIPT_RE = /(?<=