diff --git a/.github/workflows/autofix-docs.yml b/.github/workflows/autofix-docs.yml index e7abf22cff..edc59497ef 100644 --- a/.github/workflows/autofix-docs.yml +++ b/.github/workflows/autofix-docs.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index 9dac29db6e..4af68cee5e 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 38d439240d..f3bc4aa393 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -29,7 +29,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index b2ed73180c..b2e4b57ea5 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: fetch-depth: 0 - run: corepack enable diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index 7eeb4ac0c9..4a81648ec0 100644 --- a/.github/workflows/check-links.yml +++ b/.github/workflows/check-links.yml @@ -25,10 +25,10 @@ jobs: restore-keys: cache-lychee- # check links with Lychee - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Lychee link checker - uses: lycheeverse/lychee-action@054a8e8c7a88ada133165c6633a49825a32174e2 # for v1.8.0 + uses: lycheeverse/lychee-action@25a231001d1723960a301b7d4c82884dc7ef857d # for v1.8.0 with: # arguments with file types to check args: >- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7bcd0507e8..337bde8734 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: timeout-minutes: 10 steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -72,7 +72,7 @@ jobs: - build steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -83,7 +83,7 @@ jobs: run: pnpm install - name: Initialize CodeQL - uses: github/codeql-action/init@ccf74c947955fd1cf117aef6a0e4e66191ef6f61 # v3.25.4 + uses: github/codeql-action/init@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6 with: languages: javascript queries: +security-and-quality @@ -95,7 +95,7 @@ jobs: path: packages - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@ccf74c947955fd1cf117aef6a0e4e66191ef6f61 # v3.25.4 + uses: github/codeql-action/analyze@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6 with: category: "/language:javascript" @@ -111,7 +111,7 @@ jobs: module: ["bundler", "node"] steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -142,7 +142,7 @@ jobs: timeout-minutes: 10 steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -166,7 +166,7 @@ jobs: needs: - build steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -218,7 +218,7 @@ jobs: timeout-minutes: 15 steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -246,9 +246,9 @@ jobs: TEST_CONTEXT: ${{ matrix.context }} TEST_V4: ${{ matrix.version == 'v4' }} TEST_PAYLOAD: ${{ matrix.payload }} - SKIP_BUNDLE_SIZE: ${{ github.event_name != 'push' || matrix.env == 'dev' || matrix.builder == 'webpack' || matrix.context == 'default' || runner.os == 'Windows' }} + 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@5ecb98a3c6b747ed38dc09f787459979aebb39be # v4.3.1 + - uses: codecov/codecov-action@125fc84a9a348dbcf27191600683ec096ec9021c # v4.4.1 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 }} @@ -270,7 +270,7 @@ jobs: timeout-minutes: 20 steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: fetch-depth: 0 - run: corepack enable @@ -309,7 +309,7 @@ jobs: timeout-minutes: 20 steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: fetch-depth: 0 - run: corepack enable diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 3ef8303b1e..62daccd7f9 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -17,6 +17,6 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout Repository' - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: 'Dependency Review' uses: actions/dependency-review-action@0c155c5e8556a497adf53f2c18edabf945ed8e70 # v4.3.2 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index dc5775d1b5..f2bdb9b4f1 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: diff --git a/.github/workflows/introspect.yml b/.github/workflows/introspect.yml index 2defd7357f..06f7994104 100644 --- a/.github/workflows/introspect.yml +++ b/.github/workflows/introspect.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 # From https://github.com/rhysd/actionlint/blob/main/docs/usage.md#use-actionlint-on-github-actions - name: Check workflow files run: | diff --git a/.github/workflows/nuxt2-edge.yml b/.github/workflows/nuxt2-edge.yml index 20263b55b5..0b12d5c635 100644 --- a/.github/workflows/nuxt2-edge.yml +++ b/.github/workflows/nuxt2-edge.yml @@ -21,7 +21,7 @@ jobs: permissions: id-token: write steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: ref: '2.x' fetch-depth: 0 # All history diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index 6558f9b7f8..7c0bbc3a86 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -22,14 +22,14 @@ jobs: steps: - name: Ensure action is by maintainer - uses: octokit/request-action@21d174fc38ff59af9cf4d7e07347d29df6dbaa99 # v2.3.0 + uses: octokit/request-action@872c5c97b3c85c23516a572f02b31401ef82415d # v2.3.1 id: check_role with: route: GET /repos/nuxt/nuxt/collaborators/${{ github.event.comment.user.login }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: ref: refs/pull/${{ github.event.issue.number }}/merge fetch-depth: 0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e59c7ae240..d975a363f3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 20 steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: fetch-depth: 0 - run: corepack enable diff --git a/.github/workflows/reproduire.yml b/.github/workflows/reproduire.yml index cc4aec0695..b001dd9fca 100644 --- a/.github/workflows/reproduire.yml +++ b/.github/workflows/reproduire.yml @@ -10,7 +10,7 @@ jobs: reproduire: runs-on: ubuntu-latest steps: - - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - uses: Hebilicious/reproduire@4b686ae9cbb72dad60f001d278b6e3b2ce40a9ac # v0.0.9-mp with: label: needs reproduction diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index e11e587645..1a0a7912d0 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -32,12 +32,12 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3 with: results_file: results.sarif results_format: sarif @@ -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@ccf74c947955fd1cf117aef6a0e4e66191ef6f61 # v3.25.4 + uses: github/codeql-action/upload-sarif@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6 if: github.repository == 'nuxt/nuxt' && success() with: sarif_file: results.sarif diff --git a/README.md b/README.md index c054f7cfff..9570d902ed 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ It provides a number of features that make it easy to build fast, SEO-friendly, - ❀️ [Contribute](#contribute) - 🏠 [Local Development](#local-development) - ⛰️ [Nuxt 2](#nuxt-2) +- πŸ›Ÿ [Professional Support](#professional-support) - πŸ”— [Follow us](#follow-us) - βš–οΈ [License](#license) @@ -104,6 +105,13 @@ Follow the docs to [Set Up Your Local Development Environment](https://nuxt.com/ You can find the code for Nuxt 2 on the [`2.x` branch](https://github.com/nuxt/nuxt/tree/2.x) and the documentation at [v2.nuxt.com](https://v2.nuxt.com). +If you expect to be using Nuxt 2 beyond the EOL (End of Life) date (June 30, 2024), and still need a maintained version that can satisfy security and browser compatibility requirements, make sure to check out [HeroDevs’ NES (Never-Ending Support) Nuxt 2](https://www.herodevs.com/support/nuxt-nes?utm_source=nuxt-github&utm_medium=nuxt-readme). + +## πŸ›Ÿ Professional Support + +- Technical audit & consulting: [Nuxt Experts](https://nuxt.com/enterprise/support) +- Custom development & more: [Nuxt Agencies Partners](https://nuxt.com/enterprise/agencies) + ## πŸ”— Follow us

diff --git a/docs/1.getting-started/11.testing.md b/docs/1.getting-started/11.testing.md index f9eea6d0cd..9ae5b6264c 100644 --- a/docs/1.getting-started/11.testing.md +++ b/docs/1.getting-started/11.testing.md @@ -10,6 +10,10 @@ If you are a module author, you can find more specific information in the [Modul Nuxt offers first-class support for end-to-end and unit testing of your Nuxt application via `@nuxt/test-utils`, a library of test utilities and configuration that currently powers the [tests we use on Nuxt itself](https://github.com/nuxt/nuxt/tree/main/test) and tests throughout the module ecosystem. +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=yGzwk9xi9gU" target="_blank"} +Watch a video from Alexander Lichter about getting started with the `@nuxt/test-utils`. +:: + ## Installation In order to allow you to manage your other testing dependencies, `@nuxt/test-utils` ships with various optional peer dependencies. For example: @@ -160,21 +164,32 @@ export default defineVitestConfig({ `mountSuspended` allows you to mount any Vue component within the Nuxt environment, allowing async setup and access to injections from your Nuxt plugins. For example: ```ts twoslash -import type { Component } from 'vue' import { it, expect } from 'vitest' -declare const SomeComponent: Component -declare const App: Component +import type { Component } from 'vue' +declare module '#components' { + export const SomeComponent: Component +} // ---cut--- // tests/components/SomeComponents.nuxt.spec.ts import { mountSuspended } from '@nuxt/test-utils/runtime' +import { SomeComponent } from '#components' it('can mount some component', async () => { const component = await mountSuspended(SomeComponent) expect(component.text()).toMatchInlineSnapshot( - 'This is an auto-imported component' + '"This is an auto-imported component"' ) }) +``` + +```ts twoslash +import { it, expect } from 'vitest' +// ---cut--- +// tests/components/SomeComponents.nuxt.spec.ts +import { mountSuspended } from '@nuxt/test-utils/runtime' +import App from '~/app.vue' + // tests/App.nuxt.spec.ts it('can also mount an app', async () => { const component = await mountSuspended(App, { route: '/test' }) @@ -199,13 +214,15 @@ The passed in component will be rendered inside a `

Examples: ```ts twoslash -import type { Component } from 'vue' import { it, expect } from 'vitest' -declare const SomeComponent: Component -declare const App: Component +import type { Component } from 'vue' +declare module '#components' { + export const SomeComponent: Component +} // ---cut--- // tests/components/SomeComponents.nuxt.spec.ts import { renderSuspended } from '@nuxt/test-utils/runtime' +import { SomeComponent } from '#components' import { screen } from '@testing-library/vue' it('can render some component', async () => { @@ -215,13 +232,11 @@ it('can render some component', async () => { ``` ```ts twoslash -import type { Component } from 'vue' import { it, expect } from 'vitest' -declare const SomeComponent: Component -declare const App: Component // ---cut--- // tests/App.nuxt.spec.ts import { renderSuspended } from '@nuxt/test-utils/runtime' +import App from '~/app.vue' it('can also render an app', async () => { const html = await renderSuspended(App, { route: '/test' }) @@ -358,7 +373,7 @@ registerEndpoint('/test/', { }) ``` -> **Note**: If your requests in a component go to external API, you can use `baseURL` and then make it empty using Nuxt Environment Config (`$test`) so all your requests will go to Nitro server. +> **Note**: If your requests in a component go to an external API, you can use `baseURL` and then make it empty using [Nuxt Environment Override Config](/docs/getting-started/configuration#environment-overrides) (`$test`) so all your requests will go to Nitro server. #### Conflict with End-To-End Testing diff --git a/docs/1.getting-started/12.upgrade.md b/docs/1.getting-started/12.upgrade.md index 626d63ab42..c8e6c66ef0 100644 --- a/docs/1.getting-started/12.upgrade.md +++ b/docs/1.getting-started/12.upgrade.md @@ -25,13 +25,17 @@ Nuxt 4 is planned to be released **on or before June 14** (though obviously this Until then, it is possible to test many of Nuxt 4's breaking changes on the nightly release channel. +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=r4wFKlcJK6c" target="_blank"} +Watch a video from Alexander Lichter showing how to opt in to Nuxt 4's breaking changes already. +:: + ### Opting in to Nuxt 4 First, opt in to the nightly release channel [following these steps](/docs/guide/going-further/nightly-release-channel#opting-in). Then you can set your `compatibilityVersion` to match Nuxt 4 behavior: -```ts +```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ future: { compatibilityVersion: 4, @@ -42,7 +46,9 @@ export default defineNuxtConfig({ // app: 'app' // }, // experimental: { + // sharedPrerenderData: false, // compileTemplate: true, + // resetAsyncDataToUndefined: true, // templateUtils: true, // relativeWatchPaths: true, // defaults: { @@ -100,6 +106,7 @@ app/ pages/ plugins/ utils/ + app.config.ts app.vue router.options.ts modules/ @@ -126,12 +133,12 @@ nuxt.config.ts ##### Migration Steps 1. Create a new directory called `app/`. -1. Move your `assets/`, `components/`, `composables/`, `layouts/`, `middleware/`, `pages/`, `plugins/` and `utils/` folders under it, as well as `app.vue`, `error.vue`. If you have an `app/router-options.ts` or `app/spa-loading-template.html`, these paths remain the same. +1. Move your `assets/`, `components/`, `composables/`, `layouts/`, `middleware/`, `pages/`, `plugins/` and `utils/` folders under it, as well as `app.vue`, `error.vue`, `app.config.ts`. If you have an `app/router-options.ts` or `app/spa-loading-template.html`, these paths remain the same. 1. Make sure your `nuxt.config.ts`, `modules/`, `public/` and `server/` folders remain outside the `app/` folder, in the root of your project. However, migration is _not required_. If you wish to keep your current folder structure, Nuxt should auto-detect it. (If it does not, please raise an issue.) You can also force a v3 folder structure with the following configuration: -```ts +```ts [nuxt.config.ts] export default defineNuxtConfig({ // This reverts the new srcDir default from `app` back to your root directory srcDir: '.', @@ -142,6 +149,104 @@ export default defineNuxtConfig({ }) ``` +#### Shared Prerender Data + +🚦 **Impact Level**: Medium + +##### What Changed + +We enabled a previously experimental feature to share data from `useAsyncData` and `useFetch` calls, across different pages. See [original PR](https://github.com/nuxt/nuxt/pull/24894). + +##### Reasons for Change + +This feature automatically shares payload _data_ between pages that are prerendered. This can result in a significant performance improvement when prerendering sites that use `useAsyncData` or `useFetch` and fetch the same data in different pages. + +For example, if your site requires a `useFetch` call for every page (for example, to get navigation data for a menu, or site settings from a CMS), this data would only be fetched once when prerendering the first page that uses it, and then cached for use when prerendering other pages. + +##### Migration Steps + +Make sure that any unique key of your data is always resolvable to the same data. For example, if you are using `useAsyncData` to fetch data related to a particular page, you should provide a key that uniquely matches that data. (`useFetch` should do this automatically for you.) + +```ts [app/pages/test/[slug\\].vue] +// This would be unsafe in a dynamic page (e.g. `[slug].vue`) because the route slug makes a difference +// to the data fetched, but Nuxt can't know that because it's not reflected in the key. +const route = useRoute() +const { data } = await useAsyncData(async () => { + return await $fetch(`/api/my-page/${route.params.slug}`) +}) +// Instead, you should use a key that uniquely identifies the data fetched. +const { data } = await useAsyncData(route.params.slug, async () => { + return await $fetch(`/api/my-page/${route.params.slug}`) +}) +``` + +Alternatively, you can disable this feature with: + +```ts twoslash [nuxt.config.ts] +export default defineNuxtConfig({ + experimental: { + sharedPrerenderData: false + } +}) +``` + +#### Default `data` and `error` values in `useAsyncData` and `useFetch` + +🚦 **Impact Level**: Minimal + +##### What Changed + +`data` and `error` objects returned from `useAsyncData` will now default to `undefined`. + +##### Reasons for Change + +Previously `data` was initialized to `null` but reset in `clearNuxtData` to `undefined`. `error` was initialized to `null`. This change is to bring greater consistency. + +##### Migration Steps + +If you encounter any issues you can revert back to the previous behavior with: + +```ts twoslash [nuxt.config.ts] +export default defineNuxtConfig({ + experimental: { + defaults: { + useAsyncData: { + value: 'null', + errorValue: 'null' + } + } + } +}) +``` + +Please report an issue if you are doing this, as we do not plan to keep this as configurable. + +#### Respect defaults when clearing `data` in `useAsyncData` and `useFetch` + +🚦 **Impact Level**: Minimal + +##### What Changed + +If you provide a custom `default` value for `useAsyncData`, this will now be used when calling `clear` or `clearNuxtData` and it will be reset to its default value rather than simply unset. + +##### Reasons for Change + +Often users set an appropriately empty value, such as an empty array, to avoid the need to check for `null`/`undefined` when iterating over it. This should be respected when resetting/clearing the data. + +##### Migration Steps + +If you encounter any issues you can revert back to the previous behavior, for now, with: + +```ts twoslash [nuxt.config.ts] +export default defineNuxtConfig({ + experimental: { + resetAsyncDataToUndefined: true, + } +}) +``` + +Please report an issue if you are doing so, as we do not plan to keep this as configurable. + #### Shallow Data Reactivity in `useAsyncData` and `useFetch` 🚦 **Impact Level**: Minimal @@ -166,7 +271,7 @@ In most cases, no migration steps are required, but if you rely on the reactivit + const { data } = useFetch('/api/test', { deep: true }) ``` 1. You can change the default behavior on a project-wide basis (not recommended): - ```ts + ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { defaults: { @@ -205,6 +310,34 @@ However, if you are a module author using the `builder:watch` hook and wishing t }) ``` +#### Directory index scanning + +🚦 **Impact Level**: Medium + +##### What Changed + +Child folders in your `middleware/` folder are also scanned for `index` files and these are now also registered as middleware in your project. + +##### Reasons for Change + +Nuxt scans a number of folders automatically, including `middleware/` and `plugins/`. + +Child folders in your `plugins/` folder are scanned for `index` files and we wanted to make this behavior consistent between scanned directories. + +##### Migration Steps + +Probably no migration is necessary but if you wish to revert to previous behavior you can add a hook to filter out these middleware: + +```ts +export default defineNuxtConfig({ + hooks: { + 'app:resolve'(app) { + app.middleware = app.middleware.filter(mw => !/\/index\.[^/]+$/.test(mw.path)) + } + } +}) +``` + #### Template Compilation Changes 🚦 **Impact Level**: Minimal @@ -265,6 +398,29 @@ const importSources = (sources: string | string[], { lazy = false } = {}) => { const importName = genSafeVariableName ``` +#### Removal of Experimental Features + +🚦 **Impact Level**: Minimal + +##### What Changed + +Four experimental features are no longer configurable in Nuxt 4: + +* `treeshakeClientOnly` will be `true` (default since v3.0) +* `configSchema` will be `true` (default since v3.3) +* `polyfillVueUseHead` will be `false` (default since v3.4) +* `respectNoSSRHeader` will be `false` (default since v3.4) + +##### Reasons for Change + +These options have been set to their current values for some time and we do not have a reason to believe that they need to remain configurable. + +##### Migration Steps + +* `polyfillVueUseHead` is implementable in user-land with [this plugin](https://github.com/nuxt/nuxt/blob/f209158352b09d1986aa320e29ff36353b91c358/packages/nuxt/src/head/runtime/plugins/vueuse-head-polyfill.ts#L10-L11) + +* `respectNoSSRHeader`is implementable in user-land with [server middleware](https://github.com/nuxt/nuxt/blob/c660b39447f0d5b8790c0826092638d321cd6821/packages/nuxt/src/core/runtime/nitro/no-ssr.ts#L8-L9) + ## Nuxt 2 vs Nuxt 3 In the table below, there is a quick comparison between 3 versions of Nuxt: @@ -272,7 +428,7 @@ In the table below, there is a quick comparison between 3 versions of Nuxt: Feature / Version | Nuxt 2 | Nuxt Bridge | Nuxt 3 -------------------------|-----------------|------------------|--------- Vue | 2 | 2 | 3 -Stability | 😊 Stable | 😌 Semi-stable | 😊 Stable +Stability | 😊 Stable | 😊 Stable | 😊 Stable Performance | 🏎 Fast | ✈️ Faster | πŸš€ Fastest Nitro Engine | ❌ | βœ… | βœ… ESM support | πŸŒ™ Partial | πŸ‘ Better | βœ… diff --git a/docs/1.getting-started/3.configuration.md b/docs/1.getting-started/3.configuration.md index c14e01f2f4..49a0822ecb 100644 --- a/docs/1.getting-started/3.configuration.md +++ b/docs/1.getting-started/3.configuration.md @@ -46,6 +46,10 @@ export default defineNuxtConfig({ }) ``` +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=DFZI2iVCrNc" target="_blank"} +Watch a video from Alexander Lichter about the env-aware `nuxt.config.ts`. +:: + ::note If you're authoring layers, you can also use the `$meta` key to provide metadata that you or the consumers of your layer might use. :: diff --git a/docs/1.getting-started/6.data-fetching.md b/docs/1.getting-started/6.data-fetching.md index 3a26848aef..32976b1f29 100644 --- a/docs/1.getting-started/6.data-fetching.md +++ b/docs/1.getting-started/6.data-fetching.md @@ -54,6 +54,10 @@ const { data: count } = await useFetch('/api/count') This composable is a wrapper around the [`useAsyncData`](/docs/api/composables/use-async-data) composable and `$fetch` utility. +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=njsGVmcWviY" target="_blank"} +Watch the video from Alexander Lichter to avoid using `useFetch` the wrong way! +:: + :read-more{to="/docs/api/composables/use-fetch"} :link-example{to="/docs/examples/features/data-fetching"} @@ -93,6 +97,10 @@ The `useAsyncData` composable is responsible for wrapping async logic and return It's developer experience sugar for the most common use case. :: +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=0X-aOpSGabA" target="_blank"} +Watch a video from Alexander Lichter to dig deeper into the difference between `useFetch` and `useAsyncData`. +:: + There are some cases when using the [`useFetch`](/docs/api/composables/use-fetch) composable is not appropriate, for example when a CMS or a third-party provide their own query layer. In this case, you can use [`useAsyncData`](/docs/api/composables/use-async-data) to wrap your calls and still keep the benefits provided by the composable. ```vue [pages/users.vue] diff --git a/docs/1.getting-started/7.state-management.md b/docs/1.getting-started/7.state-management.md index d9edf3f161..4957fb4a14 100644 --- a/docs/1.getting-started/7.state-management.md +++ b/docs/1.getting-started/7.state-management.md @@ -8,6 +8,10 @@ Nuxt provides the [`useState`](/docs/api/composables/use-state) composable to cr [`useState`](/docs/api/composables/use-state) is an SSR-friendly [`ref`](https://vuejs.org/api/reactivity-core.html#ref) replacement. Its value will be preserved after server-side rendering (during client-side hydration) and shared across all components using a unique key. +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=mv0WcBABcIk" target="_blank"} +Watch a video from Alexander Lichter about why and when to use `useState()`. +:: + ::important Because the data inside [`useState`](/docs/api/composables/use-state) will be serialized to JSON, it is important that it does not contain anything that cannot be serialized, such as classes, functions or symbols. :: @@ -208,6 +212,10 @@ const color = useColor() // Same as useState('color') ``` +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=dZSNW07sO-A" target="_blank"} +Watch a video from Daniel Roe on how to deal with global state and SSR in Nuxt. +:: + ## Using third-party libraries Nuxt **used to rely** on the Vuex library to provide global state management. If you are migrating from Nuxt 2, please head to [the migration guide](/docs/migration/configuration#vuex). diff --git a/docs/1.getting-started/8.server.md b/docs/1.getting-started/8.server.md index 49e0f21464..2a67cc3945 100644 --- a/docs/1.getting-started/8.server.md +++ b/docs/1.getting-started/8.server.md @@ -20,6 +20,10 @@ Using Nitro gives Nuxt superpowers: Nitro is internally using [h3](https://github.com/unjs/h3), a minimal H(TTP) framework built for high performance and portability. +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=DkvgJa-X31k" target="_blank"} +Watch a video from Alexander Lichter to understand the responsibilities of Nuxt and Nitro in your application. +:: + ## Server Endpoints & Middleware You can easily manage the server-only part of your Nuxt app, from API endpoints to middleware. diff --git a/docs/1.getting-started/9.layers.md b/docs/1.getting-started/9.layers.md index 0c25507946..7e05dadaf6 100644 --- a/docs/1.getting-started/9.layers.md +++ b/docs/1.getting-started/9.layers.md @@ -51,7 +51,7 @@ Read more about layers in the **Layer Author Guide**. Watch a video from Learn Vue about Nuxt Layers. :: -::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=fr5yo3aVkfA&t=271s" target="_blank"} +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=fr5yo3aVkfA" target="_blank"} Watch a video from Alexander Lichter about Nuxt Layers. :: diff --git a/docs/2.guide/0.index.md b/docs/2.guide/0.index.md index b45915e129..a93d01cdb6 100644 --- a/docs/2.guide/0.index.md +++ b/docs/2.guide/0.index.md @@ -16,4 +16,7 @@ surround: false ::card{icon="i-ph-star-duotone" title="Going Further" to="/docs/guide/going-further"} Master Nuxt with advanced concepts like experimental features, hooks, modules, and more. :: + ::card{icon="i-ph-book-open-duotone" title="Recipes" to="/docs/guide/recipes"} + Find solutions to common problems and learn how to implement them in your Nuxt project. + :: :: diff --git a/docs/2.guide/1.concepts/1.auto-imports.md b/docs/2.guide/1.concepts/1.auto-imports.md index 0fd8a3b63f..a7e24cd843 100644 --- a/docs/2.guide/1.concepts/1.auto-imports.md +++ b/docs/2.guide/1.concepts/1.auto-imports.md @@ -60,6 +60,10 @@ That means that (with very few exceptions) you cannot use them outside a Nuxt pl If you get an error message like `Nuxt instance is unavailable` then it probably means you are calling a Nuxt composable in the wrong place in the Vue or Nuxt lifecycle. +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=ofuKRZLtOdY" target="_blank"} +Watch a video from Alexander Lichter about handling async code in composables and fixing `Nuxt instance is unavailable` in your app. +:: + ::read-more{to="/docs/guide/going-further/experimental-features#asynccontext" icon="i-ph-star-duotone"} Checkout the `asyncContext` experimental feature to use Nuxt composables in async functions. :: @@ -168,3 +172,7 @@ export default defineNuxtConfig({ } }) ``` + +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=FT2LQJ2NvVI" target="_blank"} +Watch a video from Alexander Lichter on how to easily set up custom auto imports. +:: diff --git a/docs/2.guide/1.concepts/8.typescript.md b/docs/2.guide/1.concepts/8.typescript.md index 3c57bd7464..4e07f35631 100644 --- a/docs/2.guide/1.concepts/8.typescript.md +++ b/docs/2.guide/1.concepts/8.typescript.md @@ -65,6 +65,10 @@ This file contains the recommended basic TypeScript configuration for your proje [Read more about how to extend this configuration](/docs/guide/directory-structure/tsconfig). +::tip{icon="i-ph-video-duotone" to="https://youtu.be/umLI7SlPygY" target="_blank"} +Watch a video from Daniel Roe explaining built-in Nuxt aliases. +:: + ::note Nitro also [auto-generates types](/docs/guide/concepts/server-engine#typed-api-routes) for API routes. Plus, Nuxt also generates types for globally available components and [auto-imports from your composables](/docs/guide/directory-structure/composables), plus other core functionality. :: diff --git a/docs/2.guide/2.directory-structure/1.components.md b/docs/2.guide/2.directory-structure/1.components.md index 0685a3389c..0cdf5cb4b6 100644 --- a/docs/2.guide/2.directory-structure/1.components.md +++ b/docs/2.guide/2.directory-structure/1.components.md @@ -259,7 +259,7 @@ Watch Learn Vue video about Nuxt Server Components. :: ::tip{icon="i-ph-article-duotone" to="https://roe.dev/blog/nuxt-server-components" target="_blank"} -Read Daniel Roe's guide to Nuxt server components +Read Daniel Roe's guide to Nuxt Server Components. :: ### Standalone server components diff --git a/docs/2.guide/2.directory-structure/1.modules.md b/docs/2.guide/2.directory-structure/1.modules.md index 2664e5601e..ce9de8f9f2 100644 --- a/docs/2.guide/2.directory-structure/1.modules.md +++ b/docs/2.guide/2.directory-structure/1.modules.md @@ -46,7 +46,11 @@ export default defineEventHandler(() => { When starting Nuxt, the `hello` module will be registered and the `/api/hello` route will be available. -Local modules are registered in alphabetical order. You can change the order by adding a number to the front of each directory name: +Modules are executed in the following sequence: +- First, the modules defined in [`nuxt.config.ts`](/docs/api/nuxt-config#modules-1) are loaded. +- Then, modules found in the `modules/` directory are executed, and they load in alphabetical order. + +You can change the order of local module by adding a number to the front of each directory name: ```bash [Directory structure] modules/ diff --git a/docs/2.guide/2.directory-structure/1.plugins.md b/docs/2.guide/2.directory-structure/1.plugins.md index 3753847b5b..b525b1f4c4 100644 --- a/docs/2.guide/2.directory-structure/1.plugins.md +++ b/docs/2.guide/2.directory-structure/1.plugins.md @@ -76,6 +76,10 @@ export default defineNuxtPlugin({ }) ``` +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=2aXZyXB1QGQ" target="_blank"} +Watch a video from Alexander Lichter about the Object Syntax for Nuxt plugins. +:: + ::note If you are using the object-syntax, the properties may be statically analyzed in future to produce a more optimized build. So you should not define them at runtime. :br For example, setting `enforce: import.meta.server ? 'pre' : 'post'` would defeat any future optimization Nuxt is able to do for your plugins. diff --git a/docs/2.guide/2.directory-structure/2.env.md b/docs/2.guide/2.directory-structure/2.env.md index 660a8138d5..0a39c625a3 100644 --- a/docs/2.guide/2.directory-structure/2.env.md +++ b/docs/2.guide/2.directory-structure/2.env.md @@ -33,11 +33,25 @@ npx nuxi dev --dotenv .env.local When updating `.env` in development mode, the Nuxt instance is automatically restarted to apply new values to the `process.env`. -## Production Preview +## Production **After your server is built**, you are responsible for setting environment variables when you run the server. -Your `.env` file will not be read at this point. How you do this is different for every environment. +Your `.env` files will not be read at this point. How you do this is different for every environment. + +This design decision was made to ensure compatibility across various deployment environments, some of which may not have a traditional file system available, such as serverless platforms or edge networks like Cloudflare Workers. + +Since `.env` files are not used in production, you must explicitly set environment variables using the tools and methods provided by your hosting environment. Here are some common approaches: + +* You can pass the environment variables as arguments using the terminal: + + `$ DATABASE_HOST=mydatabaseconnectionstring node .output/server/index.mjs` + +* You can set environment variables in shell configuration files like `.bashrc` or `.profile`. + +* Many cloud service providers, such as Vercel, Netlify, and AWS, provide interfaces for setting environment variables via their dashboards, CLI tools or configuration files. + +## Production Preview For local production preview purpose, we recommend using [`nuxi 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.experimental-features.md b/docs/2.guide/3.going-further/1.experimental-features.md index fd6ad8d44a..691a2187c3 100644 --- a/docs/2.guide/3.going-further/1.experimental-features.md +++ b/docs/2.guide/3.going-further/1.experimental-features.md @@ -306,6 +306,10 @@ Out of the box, this will enable typed usage of [`navigateTo`](/docs/api/utils/n You can even get typed params within a page by using `const route = useRoute('route-name')`. +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=SXk-L19gTZk" target="_blank"} +Watch a video from Daniel Roe explaining type-safe routing in Nuxt. +:: + ## watcher Set an alternative watcher that will be used as the watching service for Nuxt. @@ -340,6 +344,10 @@ export default defineNuxtConfig({ }) ``` +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=1jUupYHVvrU" target="_blank"} +Watch a video from Alexander Lichter about the experimental `sharedPrerenderData` setting. +:: + It is particularly important when enabling this feature to make sure that any unique key of your data is always resolvable to the same data. For example, if you are using `useAsyncData` to fetch data related to a particular page, you should provide a key that uniquely matches that data. (`useFetch` @@ -378,6 +386,16 @@ This option allows exposing some route metadata defined in `definePageMeta` at b This only works with static or strings/arrays rather than variables or conditional assignment. See [original issue](https://github.com/nuxt/nuxt/issues/24770) for more information and context. + + ## cookieStore Enables CookieStore support to listen for cookie updates (if supported by the browser) and refresh `useCookie` ref values. diff --git a/docs/2.guide/3.going-further/10.runtime-config.md b/docs/2.guide/3.going-further/10.runtime-config.md index e84823704c..9a1832ad50 100644 --- a/docs/2.guide/3.going-further/10.runtime-config.md +++ b/docs/2.guide/3.going-further/10.runtime-config.md @@ -61,6 +61,10 @@ Setting the default of `runtimeConfig` values to *differently named environment It is advised to use environment variables that match the structure of your `runtimeConfig` object. :: +::tip{icon="i-ph-video-duotone" to="https://youtu.be/_FYV5WfiWvs" target="_blank"} +Watch a video from Alexander Lichter showcasing the top mistake developers make using runtimeConfig. +:: + #### Example ```sh [.env] diff --git a/docs/2.guide/3.going-further/4.kit.md b/docs/2.guide/3.going-further/4.kit.md index 0610737513..226762b899 100644 --- a/docs/2.guide/3.going-further/4.kit.md +++ b/docs/2.guide/3.going-further/4.kit.md @@ -15,6 +15,10 @@ Discover all Nuxt Kit utilities. You can install the latest Nuxt Kit by adding it to the `dependencies` section of your `package.json`. However, please consider always explicitly installing the `@nuxt/kit` package even if it is already installed by Nuxt. +::note +`@nuxt/kit` and `@nuxt/schema` are key dependencies for Nuxt. If you are installing it separately, make sure that the versions of `@nuxt/kit` and `@nuxt/schema` are equal to or greater than your `nuxt` version to avoid any unexpected behavior. +:: + ```json [package.json] { "dependencies": { diff --git a/docs/2.guide/4.recipes/3.custom-usefetch.md b/docs/2.guide/4.recipes/3.custom-usefetch.md new file mode 100644 index 0000000000..e27af2712f --- /dev/null +++ b/docs/2.guide/4.recipes/3.custom-usefetch.md @@ -0,0 +1,105 @@ +--- +navigation.title: 'Custom useFetch' +title: Custom useFetch in Nuxt +description: How to create a custom fetcher for calling your external API in Nuxt 3. +--- + +When working with Nuxt, you might be making the frontend and fetching an external API, and you might want to set some default options for fetching from your API. + +The [`$fetch`](/docs/api/utils/dollarfetch) utility function (used by the [`useFetch`](/docs/api/composables/use-fetch) composable) is intentionally not globally configurable. This is important so that fetching behavior throughout your application remains consistent, and other integrations (like modules) can rely on the behavior of core utilities like `$fetch`. + +However, Nuxt provides a way to create a custom fetcher for your API (or multiple fetchers if you have multiple APIs to call). + +## Custom `$fetch` + +Let's create a custom `$fetch` instance with a [Nuxt plugin](/docs/guide/directory-structure/plugins). + +::note +`$fetch` is a configured instance of [ofetch](https://github.com/unjs/ofetch) which supports adding the base URL of your Nuxt server as well as direct function calls during SSR (avoiding HTTP roundtrips). +:: + +Let's pretend here that: +- The main API is https://api.nuxt.com +- We are storing the JWT token in a session with [nuxt-auth-utils](https://github.com/atinux/nuxt-auth-utils) +- If the API responds with a `401` status code, we redirect the user to the `/login` page + +```ts [plugins/api.ts] +export default defineNuxtPlugin(() => { + const { session } = useUserSession() + + const api = $fetch.create({ + baseURL: 'https://api.nuxt.com', + onRequest({ request, options, error }) { + if (session.value?.token) { + const headers = options.headers ||= {} + if (Array.isArray(headers)) { + headers.push(['Authorization', `Bearer ${session.value?.token}`]) + } else if (headers instanceof Headers) { + headers.set('Authorization', `Bearer ${session.value?.token}`) + } else { + headers.Authorization = `Bearer ${session.value?.token}` + } + } + }, + async onResponseError({ response }) { + if (response.status === 401) { + await navigateTo('/login') + } + } + }) + + // Expose to useNuxtApp().$api + return { + provide: { + api + } + } +}) +``` + +With this Nuxt plugin, `$api` is exposed from `useNuxtApp()` to make API calls directly from the Vue components: + +```vue [app.vue] + +``` + +::callout +Wrapping with [`useAsyncData`](/docs/api/composables/use-async-data) **avoid double data fetching when doing server-side rendering** (server & client on hydration). +:: + +## Custom `useFetch` + +Now that `$api` has the logic we want, let's create a `useAPI` composable to replace the usage of `useAsyncData` + `$api`: + +```ts [composables/useAPI.ts] +import type { UseFetchOptions } from 'nuxt/app' + +export function useAPI( + url: string | (() => string), + options: Omit, 'default'> & { default: () => T | Ref }, +) { + return useFetch(url, { + ...options, + $fetch: useNuxtApp().$api + }) +} +``` + +Let's use the new composable and have a nice and clean component: + +```vue [app.vue] + +``` + +::callout{icon="i-simple-icons-youtube" color="red" to="https://www.youtube.com/watch?v=jXH8Tr-exhI"} +Watch a video about custom `$fetch` and Repository Pattern in Nuxt. +:: + +::note +We are currently discussing to find a cleaner way to let you create a custom fetcher, see https://github.com/nuxt/nuxt/issues/14736. +:: diff --git a/docs/3.api/1.components/4.nuxt-link.md b/docs/3.api/1.components/4.nuxt-link.md index 5e54807145..b623d1ce5a 100644 --- a/docs/3.api/1.components/4.nuxt-link.md +++ b/docs/3.api/1.components/4.nuxt-link.md @@ -25,6 +25,22 @@ In this example, we use `` component to link to another page of the ap ``` +### Passing Params to Dynamic Routes + +In this example, we pass the `id` param to link to the route `~/pages/posts/[id].vue`. + +```vue [pages/index.vue] + +``` + +::tip +Check out the Pages panel in Nuxt DevTools to see the route name and the params it might take. +:: + ### Handling 404s When using `` for `/public` directory files or when pointing to a different app on the same domain, you should use the `external` prop. diff --git a/docs/3.api/1.components/5.nuxt-loading-indicator.md b/docs/3.api/1.components/5.nuxt-loading-indicator.md index e22b582422..ad2f6936cb 100644 --- a/docs/3.api/1.components/5.nuxt-loading-indicator.md +++ b/docs/3.api/1.components/5.nuxt-loading-indicator.md @@ -30,6 +30,7 @@ You can pass custom HTML or components through the loading indicator's default s ## Props - `color`: The color of the loading bar. It can be set to `false` to turn off explicit color styling. +- `errorColor`: The color of the loading bar when `error` is set to `true`. - `height`: Height of the loading bar, in pixels (default `3`). - `duration`: Duration of the loading bar, in milliseconds (default `2000`). - `throttle`: Throttle the appearing and hiding, in milliseconds (default `200`). diff --git a/docs/3.api/2.composables/use-cookie.md b/docs/3.api/2.composables/use-cookie.md index 4319f7f05b..804d3e2784 100644 --- a/docs/3.api/2.composables/use-cookie.md +++ b/docs/3.api/2.composables/use-cookie.md @@ -59,13 +59,10 @@ Use these options to set the expiration of the cookie. The given number will be converted to an integer by rounding down. By default, no maximum age is set. `expires`: Specifies the `Date` object to be the value for the [`Expires` `Set-Cookie` attribute](https://tools.ietf.org/html/rfc6265#section-5.2.1). -By default, no expiration is set. Most clients will consider this a "non-persistent cookie" and -will delete it on a condition like exiting a web browser application. +By default, no expiration is set. Most clients will consider this a "non-persistent cookie" and will delete it on a condition like exiting a web browser application. ::note -The [cookie storage model specification](https://tools.ietf.org/html/rfc6265#section-5.3) states that if both `expires` and -`maxAge` is set, then `maxAge` takes precedence, but not all clients may obey this, -so if both are set, they should point to the same date and time! +The [cookie storage model specification](https://tools.ietf.org/html/rfc6265#section-5.3) states that if both `expires` and `maxAge` is set, then `maxAge` takes precedence, but not all clients may obey this, so if both are set, they should point to the same date and time! :: ::note @@ -74,22 +71,29 @@ If neither of `expires` and `maxAge` is set, the cookie will be session-only and ### `httpOnly` -Specifies the `boolean` value for the [`HttpOnly` `Set-Cookie` attribute](https://tools.ietf.org/html/rfc6265#section-5.2.6). When truthy, -the `HttpOnly` attribute is set; otherwise it is not. By default, the `HttpOnly` attribute is not set. +Specifies the `boolean` value for the [`HttpOnly` `Set-Cookie` attribute](https://tools.ietf.org/html/rfc6265#section-5.2.6). When truthy, the `HttpOnly` attribute is set; otherwise it is not. By default, the `HttpOnly` attribute is not set. ::warning -Be careful when setting this to `true`, as compliant clients will not allow client-side -JavaScript to see the cookie in `document.cookie`. +Be careful when setting this to `true`, as compliant clients will not allow client-side JavaScript to see the cookie in `document.cookie`. :: ### `secure` -Specifies the `boolean` value for the [`Secure` `Set-Cookie` attribute](https://tools.ietf.org/html/rfc6265#section-5.2.5). When truthy, -the `Secure` attribute is set; otherwise it is not. By default, the `Secure` attribute is not set. +Specifies the `boolean` value for the [`Secure` `Set-Cookie` attribute](https://tools.ietf.org/html/rfc6265#section-5.2.5). When truthy, the `Secure` attribute is set; otherwise it is not. By default, the `Secure` attribute is not set. ::warning -Be careful when setting this to `true`, as compliant clients will not send the cookie back to -the server in the future if the browser does not have an HTTPS connection. This can lead to hydration errors. +Be careful when setting this to `true`, as compliant clients will not send the cookie back to the server in the future if the browser does not have an HTTPS connection. This can lead to hydration errors. +:: + +### `partitioned` + +Specifies the `boolean` value for the [`Partitioned` `Set-Cookie`](https://datatracker.ietf.org/doc/html/draft-cutler-httpbis-partitioned-cookies#section-2.1) attribute. When truthy, the `Partitioned` attribute is set, otherwise it is not. By default, the `Partitioned` attribute is not set. + +::note +This is an attribute that has not yet been fully standardized, and may change in the future. +This also means many clients may ignore this attribute until they understand it. + +More information can be found in the [proposal](https://github.com/privacycg/CHIPS). :: ### `domain` @@ -114,23 +118,18 @@ More information about the different enforcement levels can be found in [the spe ### `encode` -Specifies a function that will be used to encode a cookie's value. Since the value of a cookie -has a limited character set (and must be a simple string), this function can be used to encode -a value into a string suited for a cookie's value. +Specifies a function that will be used to encode a cookie's value. Since the value of a cookie has a limited character set (and must be a simple string), this function can be used to encode a value into a string suited for a cookie's value. The default encoder is the `JSON.stringify` + `encodeURIComponent`. ### `decode` -Specifies a function that will be used to decode a cookie's value. Since the value of a cookie -has a limited character set (and must be a simple string), this function can be used to decode -a previously encoded cookie value into a JavaScript string or other object. +Specifies a function that will be used to decode a cookie's value. Since the value of a cookie has a limited character set (and must be a simple string), this function can be used to decode a previously encoded cookie value into a JavaScript string or other object. The default decoder is `decodeURIComponent` + [destr](https://github.com/unjs/destr). ::note -If an error is thrown from this function, the original, non-decoded cookie value will -be returned as the cookie's value. +If an error is thrown from this function, the original, non-decoded cookie value will be returned as the cookie's value. :: ### `default` diff --git a/docs/3.api/2.composables/use-fetch.md b/docs/3.api/2.composables/use-fetch.md index e0149ce11f..84a73d0fc9 100644 --- a/docs/3.api/2.composables/use-fetch.md +++ b/docs/3.api/2.composables/use-fetch.md @@ -66,6 +66,10 @@ const { data, pending, error, refresh } = await useFetch('/api/auth/login', { `useFetch` is a reserved function name transformed by the compiler, so you should not name your own function `useFetch`. :: +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=njsGVmcWviY" target="_blank"} +Watch the video from Alexander Lichter to avoid using `useFetch` the wrong way! +:: + :link-example{to="/docs/examples/advanced/use-custom-fetch-composable"} :read-more{to="/docs/getting-started/data-fetching"} @@ -83,6 +87,8 @@ const { data, pending, error, refresh } = await useFetch('/api/auth/login', { - `headers`: Request headers. - `baseURL`: Base URL for the request. - `timeout`: Milliseconds to automatically abort request + - `cache`: Handles cache control according to [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/fetch#cache) + - You can pass boolean to disable the cache or you can pass one of the following values: `default`, `no-store`, `reload`, `no-cache`, `force-cache`, and `only-if-cached`. ::note All fetch options can be given a `computed` or `ref` value. These will be watched and new requests made automatically with any new values if they are updated. diff --git a/docs/3.api/2.composables/use-loading-indicator.md b/docs/3.api/2.composables/use-loading-indicator.md index 2e4a5bb97b..357c260bbd 100644 --- a/docs/3.api/2.composables/use-loading-indicator.md +++ b/docs/3.api/2.composables/use-loading-indicator.md @@ -26,6 +26,11 @@ It hooks into [`page:loading:start`](/docs/api/advanced/hooks#app-hooks-runtime) - **type**: `Ref` - **description**: The loading state +### `error` + +- **type**: `Ref` +- **description**: The error state + ### `progress` - **type**: `Ref` @@ -39,7 +44,7 @@ Set `isLoading` to true and start to increase the `progress` value. ### `finish()` -Set the `progress` value to `100`, stop all timers and intervals then reset the loading state `500` ms later. `finish` accepts a `{ force: true }` option to skip the interval before the state is reset. +Set the `progress` value to `100`, stop all timers and intervals then reset the loading state `500` ms later. `finish` accepts a `{ force: true }` option to skip the interval before the state is reset, and `{ error: true }` to change the loading bar color and set the error property to true. ### `clear()` diff --git a/docs/3.api/2.composables/use-nuxt-app.md b/docs/3.api/2.composables/use-nuxt-app.md index 87c7be7418..860cb89c22 100644 --- a/docs/3.api/2.composables/use-nuxt-app.md +++ b/docs/3.api/2.composables/use-nuxt-app.md @@ -138,6 +138,10 @@ Nuxt exposes the following properties through `ssrContext`: Since [Nuxt v3.4](https://nuxt.com/blog/v3-4#payload-enhancements), it is possible to define your own reducer/reviver for types that are not supported by Nuxt. + ::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=8w6ffRBs8a4" target="_blank"} + Watch a video from Alexander Lichter about serializing payloads, especially with regards to classes. + :: + In the example below, we define a reducer (or a serializer) and a reviver (or deserializer) for the [Luxon](https://moment.github.io/luxon/#/) DateTime class, using a payload plugin. ```ts [plugins/date-time-payload.ts] diff --git a/docs/3.api/2.composables/use-state.md b/docs/3.api/2.composables/use-state.md index dccccae072..513bd407c6 100644 --- a/docs/3.api/2.composables/use-state.md +++ b/docs/3.api/2.composables/use-state.md @@ -25,6 +25,10 @@ Because the data inside `useState` will be serialized to JSON, it is important t `useState` is a reserved function name transformed by the compiler, so you should not name your own function `useState`. :: +::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=mv0WcBABcIk" target="_blank"} +Watch a video from Alexander Lichter about why and when to use `useState()`. +:: + ## Using `shallowRef` If you don't need your state to be deeply reactive, you can combine `useState` with [`shallowRef`](https://vuejs.org/api/reactivity-advanced.html#shallowref). This can improve performance when your state contains large objects and arrays. diff --git a/docs/5.community/4.contribution.md b/docs/5.community/4.contribution.md index 3702d845a8..a601e4a51d 100644 --- a/docs/5.community/4.contribution.md +++ b/docs/5.community/4.contribution.md @@ -32,6 +32,10 @@ We'll do our best to follow our [internal issue decision making flowchart](https ### Send a Pull Request +::Tip +On windows, you need to clone the repository with `git clone -c core.symlinks=true https://github.com/nuxt/nuxt.git` to make symlinks work. +:: + We always welcome pull requests! ❀️ #### Before You Start diff --git a/docs/5.community/6.roadmap.md b/docs/5.community/6.roadmap.md index 6337f81fe0..f485be077d 100644 --- a/docs/5.community/6.roadmap.md +++ b/docs/5.community/6.roadmap.md @@ -38,12 +38,12 @@ Translations | - | [nuxt/translations#4](https://github.com/nuxt/tra In addition to the Nuxt framework, there are modules that are vital for the ecosystem. Their status will be updated below. -Module | Status | Nuxt Support | Repository | Description ----------------|---------------------|--------------|------------|------------------- -Scripts | April 2024 | 3.x | `nuxt/scripts` to be announced | Easy 3rd party script management. [nuxt/nuxt#22016](https://github.com/nuxt/nuxt/discussions/22016) -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 | Nuxt 3 support is planned after session support -Hints | Planned | 3.x | `nuxt/hints` to be announced | Guidance and suggestions for enhancing development practices +Module | Status | Nuxt Support | Repository | Description +------------------------------------|---------------------|--------------|------------|------------------- +[Scripts](https://scripts.nuxt.com) | Public Preview | 3.x | [nuxt/scripts](https://github.com/nuxt/scripts) | Easy 3rd party script management. +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 | Nuxt 3 support is planned after session support. +Hints | Planned | 3.x | `nuxt/hints` to be announced | Guidance and suggestions for enhancing development practices. ## Release Cycle @@ -64,9 +64,9 @@ Each active version has its own nightly releases which are generated automatical Release | | Initial release | End Of Life | Docs ----------------------------------------|---------------------------------------------------------------------------------------------------|-----------------|--------------|------- **4.x** (scheduled) | | 2024 Q2 | |   -**3.x** (stable) | Nuxt latest 3.x version | 2022-11-16 | TBA | [nuxt.com](/docs) -**2.x** (maintenance) | Nuxt 2.x version | 2018-09-21 | 2024-06-30 | [v2.nuxt.com](https://v2.nuxt.com/docs) -**1.x** (unsupported) | Nuxt 1.x version | 2018-01-08 | 2019-09-21 |   +**3.x** (stable) | Nuxt latest 3.x version | 2022-11-16 | TBA | [nuxt.com](/docs) +**2.x** (maintenance) | Nuxt 2.x version | 2018-09-21 | 2024-06-30 | [v2.nuxt.com](https://v2.nuxt.com/docs) +**1.x** (unsupported) | Nuxt 1.x version | 2018-01-08 | 2019-09-21 |   ### Support Status diff --git a/docs/5.community/7.changelog.md b/docs/5.community/7.changelog.md index 3016d4f686..5daf444f3b 100644 --- a/docs/5.community/7.changelog.md +++ b/docs/5.community/7.changelog.md @@ -68,6 +68,16 @@ navigation.icon: i-ph-notification-duotone ::card --- icon: i-simple-icons-github + title: nuxt/scripts + to: https://github.com/nuxt/scripts/tags + target: _blank + ui.icon.base: text-black dark:text-white + --- + Nuxt Scripts releases. (Public Preview) + :: + ::card + --- + icon: i-simple-icons-github title: nuxt/ui to: https://github.com/nuxt/ui/releases target: _blank diff --git a/package.json b/package.json index 07b5a76a7f..6dd9b42cd8 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "lint:knip": "pnpx knip", "play": "nuxi dev playground", "play:build": "nuxi build playground", + "play:generate": "nuxi generate playground", "play:preview": "nuxi preview playground", "test": "pnpm test:fixtures && pnpm test:fixtures:dev && pnpm test:fixtures:webpack && pnpm test:unit && pnpm test:runtime && pnpm test:types && pnpm typecheck", "test:prepare": "jiti ./test/prepare.ts", @@ -40,20 +41,20 @@ "@nuxt/webpack-builder": "workspace:*", "magic-string": "^0.30.10", "nuxt": "workspace:*", - "rollup": "^4.17.2", - "vite": "5.2.11", + "rollup": "^4.18.0", + "vite": "5.2.12", "vue": "3.4.27" }, "devDependencies": { - "@eslint/js": "9.2.0", - "@nuxt/eslint-config": "0.3.12", + "@eslint/js": "9.3.0", + "@nuxt/eslint-config": "0.3.13", "@nuxt/kit": "workspace:*", - "@nuxt/test-utils": "3.12.1", + "@nuxt/test-utils": "3.13.1", "@nuxt/webpack-builder": "workspace:*", - "@testing-library/vue": "8.0.3", + "@testing-library/vue": "8.1.0", "@types/eslint__js": "8.42.3", "@types/fs-extra": "11.0.4", - "@types/node": "20.12.11", + "@types/node": "20.12.13", "@types/semver": "7.5.8", "@vitest/coverage-v8": "1.6.0", "@vue/test-utils": "2.4.6", @@ -61,25 +62,25 @@ "changelogen": "0.5.5", "consola": "3.2.3", "devalue": "5.0.0", - "eslint": "9.2.0", + "eslint": "9.3.0", "eslint-plugin-no-only-tests": "3.1.0", "eslint-plugin-perfectionist": "2.10.0", "eslint-typegen": "0.2.4", - "execa": "9.0.1", + "execa": "9.1.0", "fs-extra": "11.2.0", "globby": "14.0.1", "h3": "1.11.1", - "happy-dom": "14.10.1", + "happy-dom": "14.12.0", "jiti": "1.21.0", - "markdownlint-cli": "0.40.0", + "markdownlint-cli": "0.41.0", "nitropack": "2.9.6", "nuxi": "3.11.1", "nuxt": "workspace:*", "nuxt-content-twoslash": "0.0.10", "ofetch": "1.3.4", "pathe": "1.1.2", - "playwright-core": "1.44.0", - "rimraf": "5.0.5", + "playwright-core": "1.44.1", + "rimraf": "5.0.7", "semver": "7.6.2", "std-env": "3.7.0", "typescript": "5.4.5", @@ -88,9 +89,9 @@ "vitest-environment-nuxt": "1.0.0", "vue": "3.4.27", "vue-router": "4.3.2", - "vue-tsc": "2.0.16" + "vue-tsc": "2.0.19" }, - "packageManager": "pnpm@9.1.0", + "packageManager": "pnpm@9.1.3", "engines": { "node": "^16.10.0 || >=18.0.0" }, diff --git a/packages/kit/package.json b/packages/kit/package.json index 017b34cbce..62d145b029 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -44,7 +44,7 @@ "semver": "^7.6.2", "ufo": "^1.5.3", "unctx": "^2.3.1", - "unimport": "^3.7.1", + "unimport": "^3.7.2", "untyped": "^1.4.2" }, "devDependencies": { @@ -54,7 +54,7 @@ "lodash-es": "4.17.21", "nitropack": "2.9.6", "unbuild": "latest", - "vite": "5.2.11", + "vite": "5.2.12", "vitest": "1.6.0", "webpack": "5.91.0" }, diff --git a/packages/kit/src/compatibility.ts b/packages/kit/src/compatibility.ts index a03813dd48..b2720f05c9 100644 --- a/packages/kit/src/compatibility.ts +++ b/packages/kit/src/compatibility.ts @@ -4,7 +4,7 @@ import type { Nuxt, NuxtCompatibility, NuxtCompatibilityIssues } from '@nuxt/sch import { useNuxt } from './context' export function normalizeSemanticVersion (version: string) { - return version.replace(/-[0-9]+\.[0-9a-f]+/, '') // Remove edge prefix + return version.replace(/-\d+\.[0-9a-f]+/, '') // Remove edge prefix } const builderMap = { diff --git a/packages/kit/src/internal/template.ts b/packages/kit/src/internal/template.ts index 9349f704df..5b40a2a214 100644 --- a/packages/kit/src/internal/template.ts +++ b/packages/kit/src/internal/template.ts @@ -27,7 +27,7 @@ export async function compileTemplate (template: NuxtTemplate, ctx: any) { } /** @deprecated */ -const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"{(.+)}"(?=,?$)/gm, r => JSON.parse(r).replace(/^{(.*)}$/, '$1')) +const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"\{(.+)\}"(?=,?$)/gm, r => JSON.parse(r).replace(/^\{(.*)\}$/, '$1')) /** @deprecated */ const importSources = (sources: string | string[], { lazy = false } = {}) => { diff --git a/packages/kit/src/loader/config.ts b/packages/kit/src/loader/config.ts index 3c42525c4d..fab118bf6c 100644 --- a/packages/kit/src/loader/config.ts +++ b/packages/kit/src/loader/config.ts @@ -4,6 +4,8 @@ import type { ConfigLayer, ConfigLayerMeta, LoadConfigOptions } from 'c12' import { loadConfig } from 'c12' import type { NuxtConfig, NuxtOptions } from '@nuxt/schema' import { NuxtConfigSchema } from '@nuxt/schema' +import { globby } from 'globby' +import defu from 'defu' export interface LoadNuxtConfigOptions extends LoadConfigOptions {} @@ -11,12 +13,19 @@ const layerSchemaKeys = ['future', 'srcDir', 'rootDir', 'dir'] const layerSchema = Object.fromEntries(Object.entries(NuxtConfigSchema).filter(([key]) => layerSchemaKeys.includes(key))) export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise { + // Automatically detect and import layers from `~~/layers/` directory + opts.overrides = defu(opts.overrides, { + _extends: await globby('layers/*', { + onlyDirectories: true, + cwd: opts.cwd || process.cwd(), + }), + }); (globalThis as any).defineNuxtConfig = (c: any) => c const result = await loadConfig({ name: 'nuxt', configFile: 'nuxt.config', rcFile: '.nuxtrc', - extend: { extendKey: ['theme', 'extends'] }, + extend: { extendKey: ['theme', 'extends', '_extends'] }, dotenv: true, globalRc: true, ...opts, diff --git a/packages/kit/src/module/define.ts b/packages/kit/src/module/define.ts index 3327ca4ad5..ff2a56d2d4 100644 --- a/packages/kit/src/module/define.ts +++ b/packages/kit/src/module/define.ts @@ -73,8 +73,8 @@ export function defineNuxtModule (definition: Mo const key = `nuxt:module:${uniqueKey || (Math.round(Math.random() * 10000))}` const mark = performance.mark(key) const res = await module.setup?.call(null as any, _options, nuxt) ?? {} - const perf = performance.measure(key, mark?.name) // TODO: remove when Node 14 reaches EOL - const setupTime = perf ? Math.round((perf.duration * 100)) / 100 : 0 // TODO: remove when Node 14 reaches EOL + const perf = performance.measure(key, mark.name) + const setupTime = Math.round((perf.duration * 100)) / 100 // Measure setup time if (setupTime > 5000 && uniqueKey !== '@nuxt/telemetry') { diff --git a/packages/kit/src/plugin.ts b/packages/kit/src/plugin.ts index 5421c3027f..8905c11274 100644 --- a/packages/kit/src/plugin.ts +++ b/packages/kit/src/plugin.ts @@ -3,7 +3,6 @@ import type { NuxtPlugin, NuxtPluginTemplate } from '@nuxt/schema' import { useNuxt } from './context' import { addTemplate } from './template' import { resolveAlias } from './resolve' -import { logger } from './logger' /** * Normalize a nuxt plugin object @@ -20,12 +19,6 @@ export function normalizePlugin (plugin: NuxtPlugin | string): NuxtPlugin { throw new Error('Invalid plugin. src option is required: ' + JSON.stringify(plugin)) } - // TODO: only scan top-level files #18418 - const nonTopLevelPlugin = plugin.src.match(/\/plugins\/[^/]+\/index\.[^/]+$/i) - if (nonTopLevelPlugin && nonTopLevelPlugin.length > 0 && !useNuxt().options.plugins.find(i => (typeof i === 'string' ? i : i.src).endsWith(nonTopLevelPlugin[0]))) { - logger.warn(`[deprecation] You are using a plugin that is within a subfolder of your plugins directory without adding it to your config explicitly. You can move it to the top-level plugins directory, or include the file '~${nonTopLevelPlugin[0]}' in your plugins config (https://nuxt.com/docs/api/nuxt-config#plugins-1) to remove this warning.`) - } - // Normalize full path to plugin plugin.src = normalize(resolveAlias(plugin.src)) diff --git a/packages/kit/src/runtime-config.ts b/packages/kit/src/runtime-config.ts index a83358f11c..034c59fa14 100644 --- a/packages/kit/src/runtime-config.ts +++ b/packages/kit/src/runtime-config.ts @@ -36,12 +36,12 @@ export function updateRuntimeConfig (runtimeConfig: Record) { } /** - * @internal - * * https://github.com/unjs/nitro/blob/main/src/runtime/utils.env.ts. - * +* * These utils will be replaced by util exposed from nitropack. See https://github.com/unjs/nitro/pull/2404 * for more context and future plans.) + * + * @internal */ type EnvOptions = { @@ -94,7 +94,7 @@ function applyEnv ( return obj } -const envExpandRx = /{{(.*?)}}/g +const envExpandRx = /\{\{(.*?)\}\}/g function _expandFromEnv (value: string, env: Record = process.env) { return value.replace(envExpandRx, (match, key) => { diff --git a/packages/kit/src/template.ts b/packages/kit/src/template.ts index 9290e62fd5..4aa9ec147b 100644 --- a/packages/kit/src/template.ts +++ b/packages/kit/src/template.ts @@ -202,7 +202,7 @@ export async function _generateTypes (nuxt: Nuxt) { } else { const path = stats?.isFile() // remove extension - ? relativePath.replace(/(?<=\w)\.\w+$/g, '') + ? relativePath.replace(/\b\.\w+$/g, '') // non-existent file probably shouldn't be resolved : aliases[alias] @@ -230,7 +230,7 @@ export async function _generateTypes (nuxt: Nuxt) { tsConfig.compilerOptions!.paths[alias] = await Promise.all(paths.map(async (path: string) => { if (!isAbsolute(path)) { return path } const stats = await fsp.stat(path).catch(() => null /* file does not exist */) - return relativeWithDot(nuxt.options.buildDir, stats?.isFile() ? path.replace(/(?<=\w)\.\w+$/g, '') /* remove extension */ : path) + return relativeWithDot(nuxt.options.buildDir, stats?.isFile() ? path.replace(/\b\.\w+$/g, '') /* remove extension */ : path) })) } diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index ecb5f44470..403b6c4d87 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -60,14 +60,14 @@ }, "dependencies": { "@nuxt/devalue": "^2.0.2", - "@nuxt/devtools": "^1.2.0", + "@nuxt/devtools": "^1.3.2", "@nuxt/kit": "workspace:*", "@nuxt/schema": "workspace:*", "@nuxt/telemetry": "^2.5.4", "@nuxt/vite-builder": "workspace:*", - "@unhead/dom": "^1.9.10", - "@unhead/ssr": "^1.9.10", - "@unhead/vue": "^1.9.10", + "@unhead/dom": "^1.9.11", + "@unhead/ssr": "^1.9.11", + "@unhead/vue": "^1.9.11", "@vue/shared": "^3.4.27", "acorn": "8.11.3", "c12": "^1.10.0", @@ -76,7 +76,7 @@ "defu": "^6.1.4", "destr": "^2.0.3", "devalue": "^5.0.0", - "esbuild": "^0.21.1", + "esbuild": "^0.21.4", "escape-string-regexp": "^5.0.0", "estree-walker": "^3.0.3", "fs-extra": "^11.2.0", @@ -99,6 +99,7 @@ "pkg-types": "^1.1.1", "radix3": "^1.1.2", "scule": "^1.3.0", + "semver": "^7.6.2", "std-env": "^3.7.0", "strip-literal": "^2.1.0", "ufo": "^1.5.3", @@ -106,7 +107,7 @@ "uncrypto": "^0.1.3", "unctx": "^2.3.1", "unenv": "^1.9.0", - "unimport": "^3.7.1", + "unimport": "^3.7.2", "unplugin": "^1.10.1", "unplugin-vue-router": "^0.7.0", "unstorage": "^1.10.2", @@ -117,13 +118,13 @@ "vue-router": "^4.3.2" }, "devDependencies": { - "@nuxt/ui-templates": "1.3.3", + "@nuxt/ui-templates": "1.3.4", "@parcel/watcher": "2.4.1", "@types/estree": "1.0.5", "@types/fs-extra": "11.0.4", "@vitejs/plugin-vue": "5.0.4", "unbuild": "latest", - "vite": "5.2.11", + "vite": "5.2.12", "vitest": "1.6.0" }, "peerDependencies": { diff --git a/packages/nuxt/src/app/components/nuxt-loading-indicator.ts b/packages/nuxt/src/app/components/nuxt-loading-indicator.ts index 2b51dd6584..a0273a8428 100644 --- a/packages/nuxt/src/app/components/nuxt-loading-indicator.ts +++ b/packages/nuxt/src/app/components/nuxt-loading-indicator.ts @@ -20,20 +20,24 @@ export default defineComponent({ type: [String, Boolean], default: 'repeating-linear-gradient(to right,#00dc82 0%,#34cdfe 50%,#0047e1 100%)', }, + errorColor: { + type: String, + default: 'repeating-linear-gradient(to right,#f87171 0%,#ef4444 100%)', + }, estimatedProgress: { type: Function as unknown as () => (duration: number, elapsed: number) => number, required: false, }, }, setup (props, { slots, expose }) { - const { progress, isLoading, start, finish, clear } = useLoadingIndicator({ + const { progress, isLoading, error, start, finish, clear } = useLoadingIndicator({ duration: props.duration, throttle: props.throttle, estimatedProgress: props.estimatedProgress, }) expose({ - progress, isLoading, start, finish, clear, + progress, isLoading, error, start, finish, clear, }) return () => h('div', { @@ -47,7 +51,7 @@ export default defineComponent({ width: 'auto', height: `${props.height}px`, opacity: isLoading.value ? 1 : 0, - background: props.color || undefined, + background: error.value ? props.errorColor : props.color || undefined, backgroundSize: `${(100 / progress.value) * 100}% auto`, transform: `scaleX(${progress.value}%)`, transformOrigin: 'left', diff --git a/packages/nuxt/src/app/components/nuxt-root.vue b/packages/nuxt/src/app/components/nuxt-root.vue index 9c8c91516e..eefe5fee7f 100644 --- a/packages/nuxt/src/app/components/nuxt-root.vue +++ b/packages/nuxt/src/app/components/nuxt-root.vue @@ -1,7 +1,8 @@