diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a5708e8235..469ac12b19 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,7 +2,7 @@ // https://containers.dev/implementors/json_reference/ { "name": "nuxt-devcontainer", - "dockerFile": "Dockerfile", + "build": { "dockerfile": "Dockerfile" }, "features": {}, "customizations": { "vscode": { diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 513682b8ec..0000000000 --- a/.eslintrc +++ /dev/null @@ -1,168 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/eslintrc", - "ignorePatterns": [ - "dist", - "public", - "node_modules", - "packages/schema/schema" - ], - "globals": { - "NodeJS": true, - "$fetch": true - }, - "plugins": ["jsdoc", "import", "unicorn", "no-only-tests"], - "extends": [ - "plugin:jsdoc/recommended", - "@nuxt/eslint-config", - "plugin:import/typescript" - ], - "rules": { - "sort-imports": [ - "error", - { - "ignoreDeclarationSort": true - } - ], - "no-only-tests/no-only-tests": "error", - "unicorn/prefer-node-protocol": "error", - "no-console": "warn", - "vue/one-component-per-file": "off", - "vue/require-default-prop": "off", - - // Vue stylistic rules from `@antfu/eslint-config` - "vue/array-bracket-spacing": ["error", "never"], - "vue/arrow-spacing": ["error", { "after": true, "before": true }], - "vue/block-spacing": ["error", "always"], - "vue/block-tag-newline": [ - "error", - { - "multiline": "always", - "singleline": "always" - } - ], - "vue/brace-style": ["error", "stroustrup", { "allowSingleLine": true }], - "vue/comma-dangle": ["error", "always-multiline"], - "vue/comma-spacing": ["error", { "after": true, "before": false }], - "vue/comma-style": ["error", "last"], - "vue/html-comment-content-spacing": [ - "error", - "always", - { - "exceptions": ["-"] - } - ], - "vue/key-spacing": ["error", { "afterColon": true, "beforeColon": false }], - "vue/keyword-spacing": ["error", { "after": true, "before": true }], - "vue/object-curly-newline": "off", - "vue/object-curly-spacing": ["error", "always"], - "vue/object-property-newline": [ - "error", - { "allowMultiplePropertiesPerLine": true } - ], - "vue/operator-linebreak": ["error", "before"], - "vue/padding-line-between-blocks": ["error", "always"], - "vue/quote-props": ["error", "consistent-as-needed"], - "vue/space-in-parens": ["error", "never"], - "vue/template-curly-spacing": "error", - - "jsdoc/require-jsdoc": "off", - "jsdoc/require-param": "off", - "jsdoc/require-returns": "off", - "jsdoc/require-param-type": "off", - "import/order": [ - "error", - { - "pathGroups": [ - { - "pattern": "#vue-router", - "group": "external" - } - ] - } - ], - "import/no-restricted-paths": [ - "error", - { - "zones": [ - { - "from": "packages/nuxt/src/!(core)/**/*", - "target": "packages/nuxt/src/core", - "message": "core should not directly import from modules." - }, - { - "from": "packages/nuxt/src/!(app)/**/*", - "target": "packages/nuxt/src/app", - "message": "app should not directly import from modules." - }, - { - "from": "packages/nuxt/src/app/**/index.ts", - "target": "packages/nuxt/src", - "message": "should not import from barrel/index files" - }, - { - "from": "packages/nitro", - "target": "packages/!(nitro)/**/*", - "message": "nitro should not directly import other packages." - } - ] - } - ], - "@typescript-eslint/consistent-type-imports": [ - "error", - { - "disallowTypeAnnotations": false - } - ], - "@typescript-eslint/ban-ts-comment": [ - "error", - { - "ts-expect-error": "allow-with-description", - "ts-ignore": true - } - ], - "@typescript-eslint/prefer-ts-expect-error": "error", - "@typescript-eslint/no-unused-vars": [ - "error", - { - "argsIgnorePattern": "^_", - "varsIgnorePattern": "^_", - "ignoreRestSiblings": true - } - ], - "jsdoc/check-tag-names": [ - "error", - { - "definedTags": ["__NO_SIDE_EFFECTS__"] - } - ] - }, - "overrides": [ - { - "files": ["packages/schema/**"], - "rules": { - "jsdoc/valid-types": "off", - "jsdoc/check-tag-names": [ - "error", - { - "definedTags": ["experimental"] - } - ] - } - }, - { - "files": ["packages/nuxt/src/app/**", "test/**", "**/runtime/**"], - "rules": { - "no-console": "off" - } - } - ], - "settings": { - "jsdoc": { - "ignoreInternal": true, - "tagNamePreference": { - "warning": "warning", - "note": "note" - } - } - } -} diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 7b2d765c12..ff8f4c2377 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,10 +1,10 @@ blank_issues_enabled: true contact_links: - name: πŸ“š Nuxt 3 Documentation - url: https://nuxt.com/docs/ + url: https://nuxt.com/docs about: Check the documentation for usage of Nuxt 3 - name: πŸ“š Nuxt 2 Documentation - url: https://v2.nuxt.com/ + url: https://v2.nuxt.com about: Check the documentation for usage of Nuxt 2 - name: πŸ’¬ Discussions url: https://github.com/nuxt/nuxt/discussions diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f99be3b89d..2618070610 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,37 +1,19 @@ - - ### πŸ”— Linked issue - - -### ❓ Type of change - - - -- [ ] πŸ“– Documentation (updates to the documentation, readme or JSdoc annotations) -- [ ] 🐞 Bug fix (a non-breaking change that fixes an issue) -- [ ] πŸ‘Œ Enhancement (improving an existing functionality like performance) -- [ ] ✨ New feature (a non-breaking change that adds functionality) -- [ ] 🧹 Chore (updates to the build process or auxiliary tools and libraries) -- [ ] ⚠️ Breaking change (fix or feature that would cause existing functionality to change) + ### πŸ“š Description - - - + -### πŸ“ Checklist + - - +- Check that there isn't already a PR that solves the problem the same way. If you find a duplicate, please help us reviewing it. +- Read the contribution docs at https://nuxt.com/docs/community/contribution +- Ensure that PR title follows conventional commits (https://www.conventionalcommits.org) +- Update the corresponding documentation if needed. +- Include relevant tests that fail without this PR but pass with it. -- [ ] I have linked an issue or discussion. -- [ ] I have added tests (if possible). -- [ ] I have updated the documentation accordingly. +Thank you for contributing to Nuxt! +-----------------------------------------------------------------------> diff --git a/.github/workflows/autofix-docs.yml b/.github/workflows/autofix-docs.yml index 8091bf631b..d90a8e2f9e 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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - 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 09e2bfd25a..03d5a654f3 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -26,9 +26,6 @@ jobs: - name: Build (stub) run: pnpm dev:prepare - - name: Lint (code) - run: pnpm lint:fix - - name: Test (unit) run: pnpm test:unit -u @@ -52,4 +49,7 @@ jobs: if: ${{ !contains(github.head_ref, 'renovate') }} run: pnpm vitest run bundle -u + - name: Lint (code) + run: pnpm lint:fix + - uses: autofix-ci/action@ea32e3a12414e6d3183163c3424a7d7a8631ad84 diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 028a297f84..83645d7c3e 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -15,8 +15,6 @@ env: # 7 GiB by default on GitHub, setting to 6 GiB # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources NODE_OPTIONS: --max-old-space-size=6144 - # install playwright binary manually (because pnpm only runs install script once) - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" # Remove default permissions of GITHUB_TOKEN for security # https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs @@ -31,7 +29,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -48,7 +46,7 @@ jobs: run: pnpm build - name: Run benchmarks - uses: CodSpeedHQ/action@2e04019f4572c19684929a755da499f19a00b25b # v2.2.1 + uses: CodSpeedHQ/action@0b631f8998f2389eb5144632b6f9f8fabd33a86e # v2.4.1 with: run: pnpm vitest bench token: ${{ secrets.CODSPEED_TOKEN }} diff --git a/.github/workflows/changelogensets.yml b/.github/workflows/changelog.yml similarity index 87% rename from .github/workflows/changelogensets.yml rename to .github/workflows/changelog.yml index 92ad691096..492a0fb118 100644 --- a/.github/workflows/changelogensets.yml +++ b/.github/workflows/changelog.yml @@ -1,4 +1,4 @@ -name: Release +name: changelog on: push: @@ -15,12 +15,12 @@ concurrency: cancel-in-progress: ${{ github.event_name != 'push' }} jobs: - update-changelog: + update: if: github.repository_owner == 'nuxt' && !contains(github.event.head_commit.message, 'v3.') runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: fetch-depth: 0 - run: corepack enable diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml index 9b8ddaff9c..14a3db9075 100644 --- a/.github/workflows/check-links.yml +++ b/.github/workflows/check-links.yml @@ -18,17 +18,17 @@ jobs: steps: # Cache lychee results (e.g. to avoid hitting rate limits) - name: Restore lychee cache - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: path: .lycheecache key: cache-lychee-${{ github.sha }} restore-keys: cache-lychee- # check links with Lychee - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Lychee link checker - uses: lycheeverse/lychee-action@c053181aa0c3d17606addfe97a9075a32723548a # 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 f50805f2cc..1c4c56cfc2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,8 +20,6 @@ env: # 7 GiB by default on GitHub, setting to 6 GiB # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources NODE_OPTIONS: --max-old-space-size=6144 - # install playwright binary manually (because pnpm only runs install script once) - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1" # Remove default permissions of GITHUB_TOKEN for security # https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs @@ -37,7 +35,7 @@ jobs: timeout-minutes: 10 steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -57,7 +55,7 @@ jobs: run: pnpm build - name: Cache dist - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: retention-days: 3 name: dist @@ -74,7 +72,7 @@ jobs: - build steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -85,19 +83,19 @@ jobs: run: pnpm install - name: Initialize CodeQL - uses: github/codeql-action/init@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/init@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10 with: languages: javascript queries: +security-and-quality - name: Restore dist cache - uses: actions/download-artifact@87c55149d96e628cc2ef7e6fc2aab372015aec85 # v4.1.3 + uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 with: name: dist path: packages - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/analyze@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10 with: category: "/language:javascript" @@ -113,7 +111,7 @@ jobs: module: ["bundler", "node"] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -124,7 +122,7 @@ jobs: run: pnpm install - name: Restore dist cache - uses: actions/download-artifact@87c55149d96e628cc2ef7e6fc2aab372015aec85 # v4.1.3 + uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 with: name: dist path: packages @@ -144,7 +142,7 @@ jobs: timeout-minutes: 10 steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -168,7 +166,7 @@ jobs: needs: - build steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -200,8 +198,18 @@ jobs: builder: ["vite", "webpack"] context: ["async", "default"] manifest: ["manifest-on", "manifest-off"] + version: ["v4", "v3"] + payload: ["json", "js"] node: [18] exclude: + - builder: "webpack" + payload: "js" + - manifest: "manifest-off" + payload: "js" + - context: "default" + payload: "js" + - os: windows-latest + payload: "js" - env: "dev" builder: "webpack" - manifest: "manifest-off" @@ -210,7 +218,7 @@ jobs: timeout-minutes: 15 steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - run: corepack enable - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -220,34 +228,11 @@ jobs: - name: Install dependencies run: pnpm install - # Install playwright's binary under custom directory to cache - - name: (non-windows) Set Playwright path and Get playwright version - if: runner.os != 'Windows' - run: | - echo "PLAYWRIGHT_BROWSERS_PATH=$HOME/.cache/playwright-bin" >> $GITHUB_ENV - PLAYWRIGHT_VERSION="$(pnpm ls --depth 0 --json -w playwright-core | jq --raw-output '.[0].devDependencies["playwright-core"].version')" - echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_ENV - - - name: (windows) Set Playwright path and Get playwright version - if: runner.os == 'Windows' - run: | - echo "PLAYWRIGHT_BROWSERS_PATH=$HOME\.cache\playwright-bin" >> $env:GITHUB_ENV - $env:PLAYWRIGHT_VERSION="$(pnpm ls --depth 0 --json -w playwright-core | jq --raw-output '.[0].devDependencies["playwright-core"].version')" - echo "PLAYWRIGHT_VERSION=$env:PLAYWRIGHT_VERSION" >> $env:GITHUB_ENV - - - name: Cache Playwright's binary - uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0 - with: - key: ${{ runner.os }}-playwright-bin-v1-${{ env.PLAYWRIGHT_VERSION }} - path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }} - restore-keys: | - ${{ runner.os }}-playwright-bin-v1- - - name: Install Playwright run: pnpm playwright-core install chromium - name: Restore dist cache - uses: actions/download-artifact@87c55149d96e628cc2ef7e6fc2aab372015aec85 # v4.1.3 + uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 with: name: dist path: packages @@ -259,9 +244,11 @@ jobs: TEST_BUILDER: ${{ matrix.builder }} TEST_MANIFEST: ${{ matrix.manifest }} TEST_CONTEXT: ${{ matrix.context }} - SKIP_BUNDLE_SIZE: ${{ github.event_name != 'push' || matrix.env == 'dev' || matrix.builder == 'webpack' || matrix.context == 'default' || runner.os == 'Windows' }} + 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' || matrix.payload == 'js' || runner.os == 'Windows' }} - - uses: codecov/codecov-action@54bcd8715eee62d40e33596ef5e8f0f48dbbccab # v4.1.0 + - uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 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 }} @@ -283,7 +270,7 @@ jobs: timeout-minutes: 20 steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: fetch-depth: 0 - run: corepack enable @@ -296,7 +283,7 @@ jobs: run: pnpm install - name: Restore dist cache - uses: actions/download-artifact@87c55149d96e628cc2ef7e6fc2aab372015aec85 # v4.1.3 + uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 with: name: dist path: packages @@ -322,7 +309,7 @@ jobs: timeout-minutes: 20 steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: fetch-depth: 0 - run: corepack enable @@ -335,7 +322,7 @@ jobs: run: pnpm install - name: Restore dist cache - uses: actions/download-artifact@87c55149d96e628cc2ef7e6fc2aab372015aec85 # v4.1.3 + uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 with: name: dist path: packages diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 4537173ac3..7d1ac0d64a 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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: 'Dependency Review' - uses: actions/dependency-review-action@9129d7d40b8c12c1ed0f60400d00c92d437adcce # v4.1.3 + uses: actions/dependency-review-action@72eb03d02c7872a771aacd928f3123ac62ad6d3a # v4.3.3 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e71037f7e0..624cafa244 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - 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 5f25bfa3b6..6b7f4fd586 100644 --- a/.github/workflows/introspect.yml +++ b/.github/workflows/introspect.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 # 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 deleted file mode 100644 index 5e2df5cce6..0000000000 --- a/.github/workflows/nuxt2-edge.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: nuxt2-nightly - -on: - workflow_dispatch: - schedule: - - cron: '0 0 * * *' - -# https://github.com/vitejs/vite/blob/main/.github/workflows/ci.yml -env: - # 7 GiB by default on GitHub, setting to 6 GiB - # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources - NODE_OPTIONS: --max-old-space-size=6144 - -permissions: - contents: read - -jobs: - nightly: - if: github.repository_owner == 'nuxt' - runs-on: ubuntu-latest - permissions: - id-token: write - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - with: - ref: '2.x' - fetch-depth: 0 # All history - - name: fetch tags - run: git fetch --depth=1 origin "+refs/tags/*:refs/tags/*" - - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 - with: - node-version: 18 - registry-url: 'https://registry.npmjs.org' - - name: install - run: yarn --check-files --frozen-lockfile --non-interactive - - name: lint - run: yarn test:lint - - name: audit - run: yarn run audit - - name: build - run: yarn test:fixtures -i - - name: lint app - run: yarn lint:app - - name: test types - run: yarn test:types - - name: test dev - run: yarn test:dev - - name: test unit - run: yarn test:unit - - name: test e2e - run: yarn test:e2e - - name: bump version - run: yarn lerna version --yes --no-changelog --no-git-tag-version --no-push --force-publish "*" --loglevel verbose - - name: build - run: PACKAGE_SUFFIX=edge yarn build - - name: publish - run: ./scripts/workspace-run npm publish -q - env: - NODE_AUTH_TOKEN: ${{secrets.NODE_AUTH_TOKEN}} - NPM_CONFIG_PROVENANCE: true - diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index 778e5c454b..7a45a29223 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -1,4 +1,4 @@ -name: release +name: release-pr on: issue_comment: @@ -22,16 +22,16 @@ jobs: steps: - name: Ensure action is by maintainer - uses: octokit/request-action@89697eb6635e52c6e1e5559f15b5c91ba5100cb0 # v2.1.9 + 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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: - ref: refs/pull/${{ github.event.issue.number }}/merge + ref: ${{ github.event.issue.pull_request.head.sha }} fetch-depth: 0 - run: corepack enable diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..727d0669ea --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,40 @@ +name: release + +on: + push: + tags: + - "v*" + +# Remove default permissions of GITHUB_TOKEN for security +# https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs +permissions: {} + +jobs: + release: + if: github.repository == 'nuxt/nuxt' && startsWith(github.event.head_commit.message, 'v3.') + permissions: + id-token: write + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + fetch-depth: 0 + - run: corepack enable + - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + with: + node-version: 20 + registry-url: "https://registry.npmjs.org/" + cache: "pnpm" + + - name: Install dependencies + run: pnpm install + + - name: Build (stub) + run: pnpm dev:prepare + + - name: Release + run: ./scripts/release.sh + env: + NODE_AUTH_TOKEN: ${{secrets.RELEASE_NODE_AUTH_TOKEN}} + NPM_CONFIG_PROVENANCE: true diff --git a/.github/workflows/reproduire-close.yml b/.github/workflows/reproduire-close.yml index 6bf5ca88e6..cf72e9e0e0 100644 --- a/.github/workflows/reproduire-close.yml +++ b/.github/workflows/reproduire-close.yml @@ -10,6 +10,7 @@ permissions: jobs: stale: runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' || github.repository == 'nuxt/nuxt' steps: - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 with: diff --git a/.github/workflows/reproduire.yml b/.github/workflows/reproduire.yml index fd7895b49a..e38add83d7 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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - 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 4d7d7ae87e..fb0b7dc233 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -28,15 +28,16 @@ jobs: id-token: write contents: read actions: read + if: github.event_name == 'push' || github.repository == 'nuxt/nuxt' steps: - name: "Checkout code" - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 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 @@ -58,7 +59,8 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 + if: github.repository == 'nuxt/nuxt' && success() with: name: SARIF file path: results.sarif @@ -66,6 +68,7 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/upload-sarif@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10 + if: github.repository == 'nuxt/nuxt' && success() with: sarif_file: results.sarif diff --git a/.github/workflows/semantic-pull-requests.yml b/.github/workflows/semantic-pull-requests.yml index 4ee0a30d46..3bf40386bf 100644 --- a/.github/workflows/semantic-pull-requests.yml +++ b/.github/workflows/semantic-pull-requests.yml @@ -20,7 +20,7 @@ jobs: name: Semantic pull request steps: - name: Validate PR title - uses: amannn/action-semantic-pull-request@e9fabac35e210fea40ca5b14c0da95a099eff26f # v5.4.0 + uses: amannn/action-semantic-pull-request@cfb60706e18bc85e8aec535e3c577abe8f70378e # v5.5.2 with: scopes: | kit @@ -28,6 +28,7 @@ jobs: nuxt schema test-utils + ui-templates vite webpack deps diff --git a/.gitignore b/.gitignore index 35388c4fd2..815f0415e7 100644 --- a/.gitignore +++ b/.gitignore @@ -73,3 +73,5 @@ Temporary Items fixtures-temp .pnpm-store +eslint-typegen.d.ts +.eslintcache diff --git a/.npmrc b/.npmrc index e2ad808f8d..a1ecadcefc 100644 --- a/.npmrc +++ b/.npmrc @@ -1,3 +1,4 @@ -shamefully-hoist=true -strict-peer-dependencies=false +# TODO: consider resolving webpack loaders to absolute path +public-hoist-pattern[]=*-loader +public-hoist-pattern[]=webpack-* shell-emulator=true diff --git a/README.md b/README.md index 1289880ae0..9570d902ed 100644 --- a/README.md +++ b/README.md @@ -13,16 +13,33 @@ Nuxt is a free and open-source framework with an intuitive and extendable way to create type-safe, performant and production-grade full-stack web applications and websites with Vue.js. It provides a number of features that make it easy to build fast, SEO-friendly, and scalable web applications, including: -- Server-side rendering, Static Site Generation or Hybrid Rendering -- Automatic routing with code-splitting -- State management -- SEO Optimization -- Auto imports -- Extensible with [180+ modules](https://nuxt.com/modules) +- Server-side rendering, Static Site Generation, Hybrid Rendering and Edge-Side Rendering +- Automatic routing with code-splitting and pre-fetching +- Data fetching and state management +- SEO Optimization and Meta tags definition +- Auto imports of components, composables and utils +- TypeScript with zero configuration +- Go fullstack with our server/ directory +- Extensible with [200+ modules](https://nuxt.com/modules) - Deployment to a variety of [hosting platforms](https://nuxt.com/deploy) - ...[and much more](https://nuxt.com) πŸš€ -## Getting Started +### Table of Contents + +- πŸš€ [Getting Started](#getting-started) +- πŸ’» [ Vue Development](#vue-development) +- πŸ“– [Documentation](#documentation) +- 🧩 [Modules](#modules) +- ❀️ [Contribute](#contribute) +- 🏠 [Local Development](#local-development) +- ⛰️ [Nuxt 2](#nuxt-2) +- πŸ›Ÿ [Professional Support](#professional-support) +- πŸ”— [Follow us](#follow-us) +- βš–οΈ [License](#license) + +--- + +## πŸš€ Getting Started Use the following command to create a new starter project. This will create a starter project with all the necessary files and dependencies: @@ -30,9 +47,10 @@ Use the following command to create a new starter project. This will create a st npx nuxi@latest init ``` -Discover also [nuxt.new](https://nuxt.new): Open a Nuxt starter on CodeSandbox, StackBlitz or locally to get up and running in a few seconds. +> [!TIP] +> Discover also [nuxt.new](https://nuxt.new): Open a Nuxt starter on CodeSandbox, StackBlitz or locally to get up and running in a few seconds. -## Vue Development +## πŸ’» Vue Development Simple, intuitive and powerful, Nuxt lets you write Vue components in a way that makes sense. Every repetitive task is automated, so you can focus on writing your full-stack Vue application with confidence. @@ -54,7 +72,7 @@ useSeoMeta({ - ``` -## Documentation +## πŸ“– Documentation We highly recommend you take a look at the [Nuxt documentation](https://nuxt.com/docs) to level up. It’s a great resource for learning more about the framework. It covers everything from getting started to advanced topics. -## Modules +## 🧩 Modules Discover our [list of modules](https://nuxt.com/modules) to supercharge your Nuxt project, created by the Nuxt team and community. -## Contribute +## ❀️ Contribute We invite you to contribute and help improve Nuxt πŸ’š Here are a few ways you can get involved: - **Reporting Bugs:** If you come across any bugs or issues, please check out the [reporting bugs guide](https://nuxt.com/docs/community/reporting-bugs) to learn how to submit a bug report. -- **Suggestions:** Have ideas to enhance Nuxt? We'd love to hear them! Check out the [contribution guide](https://nuxt.com/docs/community/contribution#creating-an-issue) to share your suggestions. +- **Suggestions:** Have ideas to enhance Nuxt? We'd love to hear them! Check out the [contribution guide](https://nuxt.com/docs/community/contribution) to share your suggestions. - **Questions:** If you have questions or need assistance, the [getting help guide](https://nuxt.com/docs/community/getting-help) provides resources to help you out. -## Local Development +## 🏠 Local Development Follow the docs to [Set Up Your Local Development Environment](https://nuxt.com/docs/community/framework-contribution#setup) to contribute to the framework and documentation. -## Nuxt 2 +## ⛰️ Nuxt 2 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). -## Follow us +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

Discord  Twitter  GitHub

-## License +## βš–οΈ License [MIT](./LICENSE) - diff --git a/docs/1.getting-started/1.introduction.md b/docs/1.getting-started/1.introduction.md index 1337e7187b..47bfdb0bc4 100644 --- a/docs/1.getting-started/1.introduction.md +++ b/docs/1.getting-started/1.introduction.md @@ -10,6 +10,10 @@ We made everything so you can start writing `.vue` files from the beginning whil Nuxt has no vendor lock-in, allowing you to deploy your application [**everywhere, even on the edge**](/blog/nuxt-on-the-edge). +::tip +If you want to play around with Nuxt in your browser, you can [try it out in one of our online sandboxes](/docs/getting-started/installation#play-online). +:: + ## Automation and Conventions Nuxt uses conventions and an opinionated directory structure to automate repetitive tasks and allow developers to focus on pushing features. The configuration file can still customize and override its default behaviors. diff --git a/docs/1.getting-started/10.deployment.md b/docs/1.getting-started/10.deployment.md index f381d6ca62..91a4a71cc0 100644 --- a/docs/1.getting-started/10.deployment.md +++ b/docs/1.getting-started/10.deployment.md @@ -71,62 +71,7 @@ There are two ways to deploy a Nuxt application to any static hosting services: - Static site generation (SSG) with `ssr: true` pre-renders routes of your application at build time. (This is the default behavior when running `nuxi generate`.) It will also generate `/200.html` and `/404.html` single-page app fallback pages, which can render dynamic routes or 404 errors on the client (though you may need to configure this on your static host). - Alternatively, you can prerender your site with `ssr: false` (static single-page app). This will produce HTML pages with an empty `
` where your Vue app would normally be rendered. You will lose many SEO benefits of prerendering your site, so it is suggested instead to use [``](/docs/api/components/client-only) to wrap the portions of your site that cannot be server rendered (if any). -### Crawl-based Pre-rendering - -Use the [`nuxi generate` command](/docs/api/commands/generate) to build and pre-render your application using the [Nitro](/docs/guide/concepts/server-engine) crawler. This command is similar to `nuxt build` with the `nitro.static` option set to `true`, or running `nuxt build --prerender`. - -```bash [Terminal] -npx nuxi generate -``` - -That's it! You can now deploy the `.output/public` directory to any static hosting service or preview it locally with `npx serve .output/public`. - -Working of the Nitro crawler: - -1. Load the HTML of your application's root route (`/`), any non-dynamic pages in your `~/pages` directory, and any other routes in the `nitro.prerender.routes` array. -2. Save the HTML and `payload.json` to the `~/.output/public/` directory to be served statically. -3. Find all anchor tags (``) in the HTML to navigate to other routes. -4. Repeat steps 1-3 for each anchor tag found until there are no more anchor tags to crawl. - -This is important to understand since pages that are not linked to a discoverable page can't be pre-rendered automatically. - -::read-more{to="/docs/api/commands/generate#nuxi-generate"} -Read more about the `nuxi generate` command. -:: - -### Selective Pre-rendering - -You can manually specify routes that [Nitro](/docs/guide/concepts/server-engine) will fetch and pre-render during the build or ignore routes that you don't want to pre-render like `/dynamic` in the `nuxt.config` file: - -```ts twoslash [nuxt.config.ts] -export default defineNuxtConfig({ - nitro: { - prerender: { - routes: ['/user/1', '/user/2'], - ignore: ['/dynamic'] - } - } -}) -``` - -You can combine this with the `crawlLinks` option to pre-render a set of routes that the crawler can't discover like your `/sitemap.xml` or `/robots.txt`: - -```ts twoslash [nuxt.config.ts] -export default defineNuxtConfig({ - nitro: { - prerender: { - crawlLinks: true, - routes: ['/sitemap.xml', '/robots.txt'] - } - } -}) -``` - -Setting `nitro.prerender` to `true` is similar to `nitro.prerender.crawlLinks` to `true`. - -::read-more{to="https://nitro.unjs.io/config#prerender"} -Read more about pre-rendering in the Nitro documentation. -:: +:read-more{title="Nuxt prerendering" to="/docs/getting-started/prerendering"} ### Client-side Only Rendering diff --git a/docs/1.getting-started/11.testing.md b/docs/1.getting-started/11.testing.md index dba218d7dd..9ae5b6264c 100644 --- a/docs/1.getting-started/11.testing.md +++ b/docs/1.getting-started/11.testing.md @@ -10,13 +10,17 @@ 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: - you can choose between `happy-dom` and `jsdom` for a runtime Nuxt environment -- you can choose between `vitest` and `jest` for end-to-end test runners -- `playwright-core` is only required if you wish to use the built-in browser testing utilities +- you can choose between `vitest`, `cucumber`, `jest` and `playwright` for end-to-end test runners +- `playwright-core` is only required if you wish to use the built-in browser testing utilities (and are not using `@playwright/test` as your test runner) ::code-group ```bash [yarn] @@ -59,6 +63,11 @@ We currently ship an environment for unit testing code that needs a [Nuxt](https }) ``` +::tip +When importing `@nuxt/test-utils` in your vitest config, It is necessary to have `"type": "module"` specified in your `package.json` or rename your vitest config file appropriately. +> ie. `vitest.config.m{ts,js}`. +:: + ### Using a Nuxt Runtime Environment By default, `@nuxt/test-utils` will not change your default Vitest environment, so you can do fine-grained opt-in and run Nuxt tests together with other unit tests. @@ -155,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' }) @@ -194,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 () => { @@ -210,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' }) @@ -337,8 +357,8 @@ For example, to mock `/test/` endpoint, you can do: ```ts twoslash import { registerEndpoint } from '@nuxt/test-utils/runtime' -registerEndpoint("/test/", () => ({ - test: "test-field" +registerEndpoint('/test/', () => ({ + test: 'test-field' })) ``` @@ -347,13 +367,13 @@ By default, your request will be made using the `GET` method. You may use anothe ```ts twoslash import { registerEndpoint } from '@nuxt/test-utils/runtime' -registerEndpoint("/test/", { - method: "POST", - handler: () => ({ test: "test-field" }) +registerEndpoint('/test/', { + method: 'POST', + handler: () => ({ test: 'test-field' }) }) ``` -> **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 @@ -364,7 +384,7 @@ If you would like to use both the end-to-end and unit testing functionality of ` `app.nuxt.spec.ts` ```ts twoslash -import { mockNuxtImport } from "@nuxt/test-utils/runtime" +import { mockNuxtImport } from '@nuxt/test-utils/runtime' mockNuxtImport('useStorage', () => { return () => { @@ -386,9 +406,98 @@ await setup({ // ... ``` +### Using `@vue/test-utils` + +If you prefer to use `@vue/test-utils` on its own for unit testing in Nuxt, and you are only testing components which do not rely on Nuxt composables, auto-imports or context, you can follow these steps to set it up. + +1. Install the needed dependencies + + ::code-group + ```bash [yarn] + yarn add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue + ``` + ```bash [npm] + npm i --save-dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue + ``` + ```bash [pnpm] + pnpm add -D vitest @vue/test-utils happy-dom @vitejs/plugin-vue + ``` + ```bash [bun] + bun add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue + ``` + :: + +2. Create a `vitest.config.ts` with the following content: + + ```ts twoslash + import { defineConfig } from 'vitest/config' + import vue from '@vitejs/plugin-vue' + + export default defineConfig({ + plugins: [vue()], + test: { + environment: 'happy-dom', + }, + }); + ``` + +3. Add a new command for test in your `package.json` + + ```json + "scripts": { + "build": "nuxt build", + "dev": "nuxt dev", + ... + "test": "vitest" + }, + ``` + +4. Create a simple `` component `components/HelloWorld.vue` with the following content: + + ```vue + + ``` + +5. Create a simple unit test for this newly created component `~/components/HelloWorld.spec.ts` + + ```ts twoslash + import { describe, it, expect } from 'vitest' + import { mount } from '@vue/test-utils' + + import HelloWorld from './HelloWorld.vue' + + describe('HelloWorld', () => { + it('component renders Hello world properly', () => { + const wrapper = mount(HelloWorld) + expect(wrapper.text()).toContain('Hello world') + }) + }) + ``` + +6. Run vitest command + + ::code-group + ```bash [yarn] + yarn test + ``` + ```bash [npm] + npm run test + ``` + ```bash [pnpm] + pnpm run test + ``` + ```bash [bun] + bun run test + ``` + :: + +Congratulations, you're all set to start unit testing with `@vue/test-utils` in Nuxt! Happy testing! + ## End-To-End Testing -For end-to-end testing, we support [Vitest](https://github.com/vitest-dev/vitest) and [Jest](https://jestjs.io) as test runners. +For end-to-end testing, we support [Vitest](https://github.com/vitest-dev/vitest), [Jest](https://jestjs.io), [Cucumber](https://cucumber.io/) and [Playwright](https://playwright.dev/) as test runners. ### Setup @@ -454,7 +563,7 @@ Please use the options below for the `setup` method. - `type`: The type of browser to launch - either `chromium`, `firefox` or `webkit` - `launch`: `object` of options that will be passed to playwright when launching the browser. See [full API reference](https://playwright.dev/docs/api/class-browsertype#browser-type-launch). - `runner`: Specify the runner for the test suite. Currently, [Vitest](https://vitest.dev) is recommended. - - Type: `'vitest' | 'jest'` + - Type: `'vitest' | 'jest' | 'cucumber'` - Default: `'vitest'` ### APIs @@ -493,11 +602,11 @@ const pageUrl = url('/page') ### Testing in a Browser -We provide built-in support using Playwright within `@nuxt/test-utils`, but you can also use other test runners for end-to-end browser testing. +We provide built-in support using Playwright within `@nuxt/test-utils`, either programmatically or via the Playwright test runner. #### `createPage(url)` -You can create a configured Playwright browser instance, and (optionally) point it at a path from the running server. You can find out more about the API methods available from [in the Playwright documentation](https://playwright.dev/docs/api/class-page). +Within `vitest`, `jest` or `cucumber`, you can create a configured Playwright browser instance with `createPage`, and (optionally) point it at a path from the running server. You can find out more about the API methods available from [in the Playwright documentation](https://playwright.dev/docs/api/class-page). ```ts twoslash import { createPage } from '@nuxt/test-utils/e2e' @@ -505,3 +614,70 @@ import { createPage } from '@nuxt/test-utils/e2e' const page = await createPage('/page') // you can access all the Playwright APIs from the `page` variable ``` + +#### Testing with Playwright Test Runner + +We also provide first-class support for testing Nuxt within [the Playwright test runner](https://playwright.dev/docs/intro). + +::code-group +```bash [yarn] +yarn add --dev @playwright/test @nuxt/test-utils +``` +```bash [npm] +npm i --save-dev @playwright/test @nuxt/test-utils +``` +```bash [pnpm] +pnpm add -D @playwright/test @nuxt/test-utils +``` +```bash [bun] +bun add --dev @playwright/test @nuxt/test-utils +``` +:: + +You can provide global Nuxt configuration, with the same configuration details as the `setup()` function mentioned earlier in this section. + +```ts [playwright.config.ts] +import { fileURLToPath } from 'node:url' +import { defineConfig, devices } from '@playwright/test' +import type { ConfigOptions } from '@nuxt/test-utils/playwright' + +export default defineConfig({ + use: { + nuxt: { + rootDir: fileURLToPath(new URL('.', import.meta.url)) + } + }, + // ... +}) +``` + +::read-more{title="See full example config" to="https://github.com/nuxt/test-utils/blob/main/examples/app-playwright/playwright.config.ts" target="_blank"} +:: + +Your test file should then use `expect` and `test` directly from `@nuxt/test-utils/playwright`: + +```ts [tests/example.test.ts] +import { expect, test } from '@nuxt/test-utils/playwright' + +test('test', async ({ page, goto }) => { + await goto('/', { waitUntil: 'hydration' }) + await expect(page.getByRole('heading')).toHaveText('Welcome to Playwright!') +}) +``` + +You can alternatively configure your Nuxt server directly within your test file: + +```ts [tests/example.test.ts] +import { expect, test } from '@nuxt/test-utils/playwright' + +test.use({ + nuxt: { + rootDir: fileURLToPath(new URL('..', import.meta.url)) + } +}) + +test('test', async ({ page, goto }) => { + await goto('/', { waitUntil: 'hydration' }) + await expect(page.getByRole('heading')).toHaveText('Welcome to Playwright!') +}) +``` diff --git a/docs/1.getting-started/12.upgrade.md b/docs/1.getting-started/12.upgrade.md index 8d6a28477c..3017305c77 100644 --- a/docs/1.getting-started/12.upgrade.md +++ b/docs/1.getting-started/12.upgrade.md @@ -5,11 +5,11 @@ navigation.icon: i-ph-arrow-circle-up-duotone --- -## Upgrading Nuxt 3 +## Upgrading Nuxt ### Latest release -To upgrade Nuxt 3 to the [latest release](https://github.com/nuxt/nuxt/releases), use the `nuxi upgrade` command. +To upgrade Nuxt to the [latest release](https://github.com/nuxt/nuxt/releases), use the `nuxi upgrade` command. ```bash [Terminal] npx nuxi upgrade @@ -17,7 +17,451 @@ npx nuxi upgrade ### Nightly Release Channel -To use the latest Nuxt 3 build and test features before their release, read about the [nightly release channel](/docs/guide/going-further/nightly-release-channel) guide. +To use the latest Nuxt build and test features before their release, read about the [nightly release channel](/docs/guide/going-further/nightly-release-channel) guide. + +## Testing Nuxt 4 + +Nuxt 4 is planned to be released **on or before June 14** (though obviously this is dependent on having enough time after Nitro's major release to be properly tested in the community, so be aware that this is not an exact date). + +Until then, it is possible to test many of Nuxt 4's breaking changes from Nuxt version 3.12 or via 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 twoslash [nuxt.config.ts] +export default defineNuxtConfig({ + future: { + compatibilityVersion: 4, + }, + // To re-enable _all_ Nuxt v3 behavior, set the following options: + // srcDir: '.', + // dir: { + // app: 'app' + // }, + // experimental: { + // sharedPrerenderData: false, + // compileTemplate: true, + // resetAsyncDataToUndefined: true, + // templateUtils: true, + // relativeWatchPaths: true, + // defaults: { + // useAsyncData: { + // deep: true + // } + // } + // }, + // unhead: { + // renderSSRHeadOptions: { + // omitLineBreaks: false + // } + // } +}) +``` + +When you set your `compatibilityVersion` to `4`, defaults throughout your Nuxt configuration will change to opt in to Nuxt v4 behavior, but you can granularly re-enable Nuxt v3 behavior when testing, following the commented out lines above. Please file issues if so, so that we can address them in Nuxt or in the ecosystem. + +### Migrating to Nuxt 4 + +Breaking or significant changes will be noted here along with migration steps for backward/forward compatibility. + +::alert +This section is subject to change until the final release, so please check back here regularly if you are testing Nuxt 4 using `compatibilityVersion: 4`. +:: + +#### New Directory Structure + +🚦 **Impact Level**: Significant + +Nuxt now defaults to a new directory structure, with backwards compatibility (so if Nuxt detects you are using the old structure, such as with a top-level `pages/` directory, this new structure will not apply). + +πŸ‘‰ [See full RFC](https://github.com/nuxt/nuxt/issues/26444) + +##### What Changed + +* the new Nuxt default `srcDir` is `app/` by default, and most things are resolved from there. +* `serverDir` now defaults to `/server` rather than `/server` +* `modules` and `public` are resolved relative to `` by default +* a new `dir.app` is added, which is the directory we look for `router.options.ts` and `spa-loading-template.html` - this defaults to `/` + +
+ +An example v4 folder structure. + +```sh +.output/ +.nuxt/ +app/ + assets/ + components/ + composables/ + layouts/ + middleware/ + pages/ + plugins/ + utils/ + app.config.ts + app.vue + router.options.ts +modules/ +node_modules/ +public/ +server/ + api/ + middleware/ + plugins/ + routes/ + utils/ +nuxt.config.ts +``` + +
+ +πŸ‘‰ For more details, see the [PR implementing this change](https://github.com/nuxt/nuxt/pull/27029). + +##### Reasons for Change + +1. **Performance** - placing all your code in the root of your repo causes issues with `.git/` and `node_modules/` folders being scanned/included by FS watchers which can significantly delay startup on non-Mac OSes. +1. **IDE type-safety** - `server/` and the rest of your app are running in two entirely different contexts with different global imports available, and making sure `server/` isn't _inside_ the same folder as the rest of your app is a big first step to ensuring you get good auto-completes in your IDE. + +##### 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`, `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.) The one exception is that if you _already_ have a custom `srcDir`. In this case, you should be aware that your `modules/`, `public/` and `server/` folders will be resolved from your `rootDir` rather than from your custom `srcDir`. You can override this by configuring `dir.modules`, `dir.public` and `serverDir` if you need to. + +You can also force a v3 folder structure with the following configuration: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + // This reverts the new srcDir default from `app` back to your root directory + srcDir: '.', + // This specifies the directory prefix for `app/router.options.ts` and `app/spa-loading-template.html` + dir: { + app: 'app' + } +}) +``` + +#### 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. + +#### Removal of deprecated `boolean` values for `dedupe` option when calling `refresh` in `useAsyncData` and `useFetch` + +🚦 **Impact Level**: Minimal + +##### What Changed + +Previously it was possible to pass `dedupe: boolean` to `refresh`. These were aliases of `cancel` (`true`) and `defer` (`false`). + +```ts twoslash [app.vue] +const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt 3!' })) + +async function refreshData () { + await refresh({ dedupe: true }) +} +``` + +##### Reasons for Change + +These aliases were removed, for greater clarity. + +The issue came up when adding `dedupe` as an option to `useAsyncData`, and we removed the boolean values as they ended up being _opposites_. + +`refresh({ dedupe: false })` meant 'do not _cancel_ existing requests in favour of this new one'. But passing `dedupe: true` within the options of `useAsyncData` means 'do not make any new requests if there is an existing pending request.' (See [PR](https://github.com/nuxt/nuxt/pull/24564#pullrequestreview-1764584361).) + +##### Migration Steps + +The migration should be straightforward: + +```diff + const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt 3!' })) + + async function refreshData () { +- await refresh({ dedupe: true }) ++ await refresh({ dedupe: 'cancel' }) + +- await refresh({ dedupe: false }) ++ await refresh({ dedupe: 'defer' }) + } +``` + +#### 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 + +The `data` object returned from `useAsyncData`, `useFetch`, `useLazyAsyncData` and `useLazyFetch` is now a `shallowRef` rather than a `ref`. + +##### What Changed + +When new data is fetched, anything depending on `data` will still be reactive because the entire object is replaced. But if your code changes a property _within_ that data structure, this will not trigger any reactivity in your app. + +##### Reasons for Change + +This brings a **significant** performance improvement for deeply nested objects and arrays because Vue does not need to watch every single property/array for modification. In most cases, `data` should also be immutable. + +##### Migration Steps + +In most cases, no migration steps are required, but if you rely on the reactivity of the data object then you have two options: + +1. You can granularly opt in to deep reactivity on a per-composable basis: + ```diff + - const { data } = useFetch('/api/test') + + const { data } = useFetch('/api/test', { deep: true }) + ``` +1. You can change the default behavior on a project-wide basis (not recommended): + ```ts twoslash [nuxt.config.ts] + export default defineNuxtConfig({ + experimental: { + defaults: { + useAsyncData: { + deep: true + } + } + } + }) + ``` + +#### Absolute Watch Paths in `builder:watch` + +🚦 **Impact Level**: Minimal + +##### What Changed + +The Nuxt `builder:watch` hook now emits a path which is absolute rather than relative to your project `srcDir`. + +##### Reasons for Change + +This allows us to support watching paths which are outside your `srcDir`, and offers better support for layers and other more complex patterns. + +##### Migration Steps + +We have already proactively migrated the public Nuxt modules which we are aware use this hook. See [issue #25339](https://github.com/nuxt/nuxt/issues/25339). + +However, if you are a module author using the `builder:watch` hook and wishing to remain backwards/forwards compatible, you can use the following code to ensure that your code works the same in both Nuxt v3 and Nuxt v4: + + ```diff ++ import { relative, resolve } from 'node:fs' + // ... + nuxt.hook('builder:watch', async (event, path) => { ++ path = relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path)) + // ... + }) +``` + +#### 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 + +##### What Changed + +Previously, Nuxt used `lodash/template` to compile templates located on the file system using the `.ejs` file format/syntax. + +In addition, we provided some template utilities (`serialize`, `importName`, `importSources`) which could be used for code-generation within these templates, which are now being removed. + +##### Reasons for Change + +In Nuxt v3 we moved to a 'virtual' syntax with a `getContents()` function which is much more flexible and performant. + +In addition, `lodash/template` has had a succession of security issues. These do not really apply to Nuxt projects because it is being used at build-time, not runtime, and by trusted code. However, they still appear in security audits. Moreover, `lodash` is a hefty dependency and is unused by most projects. + +Finally, providing code serialization functions directly within Nuxt is not ideal. Instead, we maintain projects like [unjs/knitwork](http://github.com/unjs/knitwork) which can be dependencies of your project, and where security issues can be reported/resolved directly without requiring an upgrade of Nuxt itself. + +##### Migration Steps + +We have raised PRs to update modules using EJS syntax, but if you need to do this yourself, you have three backwards/forwards-compatible alternatives: + +* Moving your string interpolation logic directly into `getContents()`. +* Using a custom function to handle the replacement, such as in https://github.com/nuxt-modules/color-mode/pull/240. +* Continuing to use `lodash`, as a dependency of _your_ project rather than Nuxt: + +```diff ++ import { readFileSync } from 'node:fs' ++ import { template } from 'lodash-es' + // ... + addTemplate({ + fileName: 'appinsights-vue.js' + options: { /* some options */ }, +- src: resolver.resolve('./runtime/plugin.ejs'), ++ getContents({ options }) { ++ const contents = readFileSync(resolver.resolve('./runtime/plugin.ejs'), 'utf-8') ++ return template(contents)({ options }) ++ }, + }) +``` + +Finally, if you are using the template utilities (`serialize`, `importName`, `importSources`), you can replace them as follows with utilities from `knitwork`: + +```ts +import { genDynamicImport, genImport, genSafeVariableName } from 'knitwork' + +const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"{(.+)}"(?=,?$)/gm, r => JSON.parse(r).replace(/^{(.*)}$/, '$1')) + +const importSources = (sources: string | string[], { lazy = false } = {}) => { + return toArray(sources).map((src) => { + if (lazy) { + return `const ${genSafeVariableName(src)} = ${genDynamicImport(src, { comment: `webpackChunkName: ${JSON.stringify(src)}` })}` + } + return genImport(src, genSafeVariableName(src)) + }).join('\n') +} + +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 @@ -26,7 +470,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/2.installation.md b/docs/1.getting-started/2.installation.md index f7a4d317ac..1af58ad887 100644 --- a/docs/1.getting-started/2.installation.md +++ b/docs/1.getting-started/2.installation.md @@ -6,14 +6,14 @@ navigation.icon: i-ph-play-duotone ## Play Online -You can start playing with Nuxt 3 in your browser using our online sandboxes: +If you just want to play around with Nuxt in your browser without setting up a project, you can use one of our online sandboxes: ::card-group :card{title="Open on StackBlitz" icon="i-simple-icons-stackblitz" to="https://nuxt.new/s/v3" target="_blank"} :card{title="Open on CodeSandbox" icon="i-simple-icons-codesandbox" to="https://nuxt.new/c/v3" target="_blank"} :: -Start with one of our starters and themes directly by opening [nuxt.new](https://nuxt.new). +Or follow the steps below to set up a new Nuxt project on your computer. ## New Project @@ -22,7 +22,7 @@ Start with one of our starters and themes directly by opening [nuxt.new](https:/ #### 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 [Volar Extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar) +- **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) - **Terminal** - In order to run Nuxt commands ::note @@ -30,17 +30,6 @@ Start with one of our starters and themes directly by opening [nuxt.new](https:/ :summary[Additional notes for an optimal setup:] - **Node.js**: Make sure to use an even numbered version (18, 20, etc) - **Nuxtr**: Install the community-developed [Nuxtr extension](https://marketplace.visualstudio.com/items?itemName=Nuxtr.nuxtr-vscode) - - **Volar**: Either enable [**Take Over Mode**](https://vuejs.org/guide/typescript/overview.html#volar-takeover-mode) (recommended) or add the [TypeScript Vue Plugin](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) - - If you have enabled **Take Over Mode** or installed the **TypeScript Vue Plugin (Volar)**, you can disable generating the shim for `*.vue` files in your [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt-config) file: - - ```ts twoslash [nuxt.config.ts] - export default defineNuxtConfig({ - typescript: { - shim: false - } - }) - ``` :: :: @@ -62,6 +51,10 @@ bunx nuxi@latest init :: +::tip +Alternatively, you can find other starters or themes by opening [nuxt.new](https://nuxt.new) and following the instructions there. +:: + Open your project folder in Visual Studio Code: ```bash [Terminal] @@ -74,33 +67,6 @@ Or change directory into your new project from your terminal: cd ``` -Install the dependencies: - -::code-group - -```bash [yarn] -yarn install -``` - -```bash [npm] -npm install -``` - -```bash [pnpm] -pnpm install -``` - -```bash [bun] -bun install -``` - -:: - -::note -If you are using Yarn 2+ (Berry), add `nodeLinker: node-modules` to your `.yarnrc.yml` file. -[You can follow this issue status here](https://github.com/nuxt/nuxt/issues/22861) -:: - ## Development Server Now you'll be able to start your Nuxt app in development mode: diff --git a/docs/1.getting-started/3.configuration.md b/docs/1.getting-started/3.configuration.md index 1024c8e117..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. :: @@ -135,7 +139,7 @@ Non primitive JS types | ❌ No | βœ… Yes ## External Configuration Files -Nuxt uses [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt-config) file as the single source of trust for configurations and skips reading external configuration files. During the course of building your project, you may have a need to configure those. The following table highlights common configurations and, where applicable, how they can be configured with Nuxt. +Nuxt uses [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt-config) file as the single source of truth for configurations and skips reading external configuration files. During the course of building your project, you may have a need to configure those. The following table highlights common configurations and, where applicable, how they can be configured with Nuxt. Name | Config File | How To Configure ---------------------------------------------|---------------------------|------------------------- @@ -149,7 +153,7 @@ Here is a list of other common config files: Name | Config File | How To Configure ---------------------------------------------|-------------------------|-------------------------- [TypeScript](https://www.typescriptlang.org) | `tsconfig.json` | [More Info](/docs/guide/concepts/typescript#nuxttsconfigjson) -[ESLint](https://eslint.org) | `.eslintrc.js` | [More Info](https://eslint.org/docs/latest/use/configure/configuration-files) +[ESLint](https://eslint.org) | `eslint.config.js` | [More Info](https://eslint.org/docs/latest/use/configure/configuration-files) [Prettier](https://prettier.io) | `.prettierrc.json` | [More Info](https://prettier.io/docs/en/configuration.html) [Stylelint](https://stylelint.io) | `.stylelintrc.json` | [More Info](https://stylelint.io/user-guide/configure) [TailwindCSS](https://tailwindcss.com) | `tailwind.config.js` | [More Info](https://tailwindcss.nuxtjs.org/tailwind/config) diff --git a/docs/1.getting-started/3.views.md b/docs/1.getting-started/3.views.md index 72e85ce261..a3275b05c2 100644 --- a/docs/1.getting-started/3.views.md +++ b/docs/1.getting-started/3.views.md @@ -96,6 +96,16 @@ If you only have a single layout in your application, we recommend using [`app.v ::code-group +```vue [app.vue] + +``` + ```vue [layouts/default.vue] ``` +::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.error-handling.md b/docs/1.getting-started/8.error-handling.md index 7ede474f2f..c4b44a438d 100644 --- a/docs/1.getting-started/8.error-handling.md +++ b/docs/1.getting-started/8.error-handling.md @@ -7,15 +7,15 @@ navigation.icon: i-ph-bug-beetle-duotone Nuxt 3 is a full-stack framework, which means there are several sources of unpreventable user runtime errors that can happen in different contexts: - Errors during the Vue rendering lifecycle (SSR & CSR) -- Errors during Nitro server lifecycle ([`server/`](/docs/guide/directory-structure/server) directory) - Server and client startup errors (SSR + CSR) +- Errors during Nitro server lifecycle ([`server/`](/docs/guide/directory-structure/server) directory) - Errors downloading JS chunks ::tip **SSR** stands for **Server-Side Rendering** and **CSR** for **Client-Side Rendering**. :: -## Vue Rendering Lifecycle +## Vue Errors You can hook into Vue errors using [`onErrorCaptured`](https://vuejs.org/api/composition-api-lifecycle.html#onerrorcaptured). @@ -51,7 +51,7 @@ This includes: - mounting the app (on client-side), though you should handle this case with `onErrorCaptured` or with `vue:error` - processing the `app:mounted` hook -## Nitro Server Lifecycle +## Nitro Server Errors You cannot currently define a server-side handler for these errors, but can render an error page, see the [Render an Error Page](#error-page) section. @@ -125,6 +125,10 @@ When you are ready to remove the error page, you can call the [`clearError`](/do Make sure to check before using anything dependent on Nuxt plugins, such as `$route` or `useRouter`, as if a plugin threw an error, then it won't be re-run until you clear the error. :: +::note +Rendering an error page is an entirely separate page load, meaning any registered middleware will run again. You can use [`useError`](#useerror) in middleware to check if an error is being handled. +:: + ::note If you are running on Node 16 and you set any cookies when rendering your error page, they will [overwrite cookies previously set](https://github.com/nuxt/nuxt/pull/20585). We recommend using a newer version of Node as Node 16 reached end-of-life in September 2023. :: diff --git a/docs/1.getting-started/8.server.md b/docs/1.getting-started/8.server.md index 94e2da9e7c..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. @@ -83,9 +87,9 @@ export default defineNuxtConfig({ Learn about all available route rules are available to customize the rendering mode of your routes. :: -In addition, there are some route rules (for example, `ssr` and `experimentalNoScripts`) that are Nuxt specific to change the behavior when rendering your pages to HTML. +In addition, there are some route rules (for example, `ssr`, `appMiddleware`, and `experimentalNoScripts`) that are Nuxt specific to change the behavior when rendering your pages to HTML. -Some route rules (`redirect` and `prerender`) also affect client-side behavior. +Some route rules (`appMiddleware`, `redirect` and `prerender`) also affect client-side behavior. Nitro is used to build the app for server side rendering, as well as pre-rendering. diff --git a/docs/1.getting-started/9.layers.md b/docs/1.getting-started/9.layers.md index ed61136af1..7e05dadaf6 100644 --- a/docs/1.getting-started/9.layers.md +++ b/docs/1.getting-started/9.layers.md @@ -14,12 +14,13 @@ One of the core features of Nuxt 3 is the layers and extending support. You can - Create Nuxt module presets - Share standard setup across projects - Create Nuxt themes +- Enhance code organization by implementing a modular architecture and support Domain-Driven Design (DDD) pattern in large scale projects. ## Usage You can extend a layer by adding the [extends](/docs/api/nuxt-config#extends) property to the [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt-config) file. -```ts twoslash [nuxt.config.ts] +```ts [nuxt.config.ts] export default defineNuxtConfig({ extends: [ '../base', // Extend from a local layer @@ -29,12 +30,29 @@ export default defineNuxtConfig({ }) ``` +You can also pass an authentication token if you are extending from a private GitHub repository: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + extends: [ + // per layer configuration + ['github:my-themes/private-awesome', { auth: process.env.GITHUB_TOKEN }] + ] +}) +``` + +Nuxt uses [unjs/c12](https://c12.unjs.io) and [unjs/giget](https://giget.unjs.io) for extending remote layers. Check the documentation for more information and all available options. + ::read-more{to="/docs/guide/going-further/layers"} Read more about layers in the **Layer Author Guide**. :: ::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=lnFCM7c9f7I" target="_blank"} -Watch Learn Vue video about Nuxt Layers. +Watch a video from Learn Vue about Nuxt Layers. +:: + +::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. :: ## Examples diff --git a/docs/1.getting-started/9.prerendering.md b/docs/1.getting-started/9.prerendering.md new file mode 100644 index 0000000000..ee63d7b285 --- /dev/null +++ b/docs/1.getting-started/9.prerendering.md @@ -0,0 +1,177 @@ +--- +title: "Prerendering" +description: Nuxt allows pages to be statically rendered at build time to improve certain performance or SEO metrics +navigation.icon: i-ph-code-block-duotone +--- + +Nuxt allows for select pages from your application to be rendered at build time. Nuxt will serve the prebuilt pages when requested instead of generating them on the fly. + +:read-more{title="Nuxt rendering modes" to="/docs/guide/concepts/rendering"} + +## Crawl-based Pre-rendering + +Use the [`nuxi generate` command](/docs/api/commands/generate) to build and pre-render your application using the [Nitro](/docs/guide/concepts/server-engine) crawler. This command is similar to `nuxt build` with the `nitro.static` option set to `true`, or running `nuxt build --prerender`. + +This will build your site, stand up a nuxt instance, and, by default, prerender the root page `/` along with any of your site's pages it links to, any of your site's pages they link to, and so on. + +```bash [Terminal] +npx nuxi generate +``` + +You can now deploy the `.output/public` directory to any static hosting service or preview it locally with `npx serve .output/public`. + +Working of the Nitro crawler: + +1. Load the HTML of your application's root route (`/`), any non-dynamic pages in your `~/pages` directory, and any other routes in the `nitro.prerender.routes` array. +2. Save the HTML and `payload.json` to the `~/.output/public/` directory to be served statically. +3. Find all anchor tags (`
`) in the HTML to navigate to other routes. +4. Repeat steps 1-3 for each anchor tag found until there are no more anchor tags to crawl. + +This is important to understand since pages that are not linked to a discoverable page can't be pre-rendered automatically. + +::read-more{to="/docs/api/commands/generate#nuxi-generate"} +Read more about the `nuxi generate` command. +:: + +### Selective Pre-rendering + +You can manually specify routes that [Nitro](/docs/guide/concepts/server-engine) will fetch and pre-render during the build or ignore routes that you don't want to pre-render like `/dynamic` in the `nuxt.config` file: + +```ts twoslash [nuxt.config.ts] +export default defineNuxtConfig({ + nitro: { + prerender: { + routes: ["/user/1", "/user/2"], + ignore: ["/dynamic"], + }, + }, +}); +``` + +You can combine this with the `crawlLinks` option to pre-render a set of routes that the crawler can't discover like your `/sitemap.xml` or `/robots.txt`: + +```ts twoslash [nuxt.config.ts] +export default defineNuxtConfig({ + nitro: { + prerender: { + crawlLinks: true, + routes: ["/sitemap.xml", "/robots.txt"], + }, + }, +}); +``` + +Setting `nitro.prerender` to `true` is similar to `nitro.prerender.crawlLinks` to `true`. + +::read-more{to="https://nitro.unjs.io/config#prerender"} +Read more about pre-rendering in the Nitro documentation. +:: + +Lastly, you can manually configure this using routeRules. + +```ts twoslash [nuxt.config.ts] +export default defineNuxtConfig({ + routeRules: { + // Set prerender to true to configure it to be prerendered + "/rss.xml": { prerender: true }, + // Set it to false to configure it to be skipped for prerendering + "/this-DOES-NOT-get-prerendered": { prerender: false }, + // Everything under /blog gets prerendered as long as it + // is linked to from another page + "/blog/**": { prerender: true }, + }, +}); +``` + +::read-more{to="https://nitro.unjs.io/config/#routerules"} +Read more about Nitro's `routeRules` configuration. +:: + +As a shorthand, you can also configure this in a page file using [`defineRouteRules`](/docs/api/utils/define-route-rules). + +::read-more{to="/docs/guide/going-further/experimental-features#inlinerouterules" icon="i-ph-star-duotone"} +This feature is experimental and in order to use it you must enable the `experimental.inlineRouteRules` option in your `nuxt.config`. +:: + +```vue [pages/index.vue] + + + +``` + +This will be translated to: + +```ts [nuxt.config.ts] +export default defineNuxtConfig({ + routeRules: { + "/": { prerender: true }, + }, +}); +``` + +## Runtime prerender configuration + +### `prerenderRoutes` + +You can use this at runtime within a [Nuxt context](/docs/guide/going-further/nuxt-app#the-nuxt-context) to add more routes for Nitro to prerender. + +```vue [pages/index.vue] + + + +``` + +:read-more{title="prerenderRoutes" to="/docs/api/utils/prerender-routes"} + +### `prerender:routes` Nuxt hook + +This is called before prerendering for additional routes to be registered. + +```ts [nitro.config.ts] +export default defineNuxtConfig({ + hooks: { + async "prerender:routes"(ctx) { + const { pages } = await fetch("https://api.some-cms.com/pages").then( + (res) => res.json(), + ); + for (const page of pages) { + ctx.routes.add(`/${page.name}`); + } + }, + }, +}); +``` + +### `prerender:generate` Nitro hook + +This is called for each route during prerendering. You can use this for fine grained handling of each route that gets prerendered. + +```ts [nitro.config.ts] +export default defineNuxtConfig({ + nitro: { + hooks: { + "prerender:generate"(route) { + if (route.route?.includes("private")) { + route.skip = true; + } + }, + }, + }, +}); +``` 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 b26265d3c6..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. :: @@ -84,7 +88,7 @@ export const useMyComposable = () => { ```ts twoslash [composables/example.ts] export const useMyComposable = () => { // Because your composable is called in the right place in the lifecycle, - // useRuntimeConfig will also work + // useRuntimeConfig will work here const config = useRuntimeConfig() // ... @@ -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/2.vuejs-development.md b/docs/2.guide/1.concepts/2.vuejs-development.md index 83ef27cde3..2725ffea36 100644 --- a/docs/2.guide/1.concepts/2.vuejs-development.md +++ b/docs/2.guide/1.concepts/2.vuejs-development.md @@ -1,6 +1,6 @@ --- title: 'Vue.js Development' -description: "Nuxt uses Vue.js and adds features such as component auto-imports, file-based routing and composables for a SSR-friendly usage." +description: "Nuxt uses Vue.js and adds features such as component auto-imports, file-based routing and composables for an SSR-friendly usage." --- Nuxt integrates Vue 3, the new major release of Vue that enables new patterns for Nuxt users. diff --git a/docs/2.guide/1.concepts/3.rendering.md b/docs/2.guide/1.concepts/3.rendering.md index 3c4fa6ca56..8bfe220a30 100644 --- a/docs/2.guide/1.concepts/3.rendering.md +++ b/docs/2.guide/1.concepts/3.rendering.md @@ -69,6 +69,36 @@ If you do use `ssr: false`, you should also place an HTML file in `~/app/spa-loa :read-more{title="SPA Loading Template" to="/docs/api/configuration/nuxt-config#spaloadingtemplate"} :: +::tip{to="https://www.youtube.com/watch?v=7Lr0QTP1Ro8" icon="i-logos-youtube-icon" target="_blank"} +Watch a video from Alexander Lichter about **Building a plain SPA with Nuxt!?**. +:: + +### Deploying a Static Client-Rendered App + +If you deploy your app to [static hosting](/docs/getting-started/deployment#static-hosting) with the `nuxi generate` or `nuxi build --prerender` commands, then by default, Nuxt will render every page as a separate static HTML file. + +If you are using purely client-side rendering, then this might be unnecessary. You might only need a single `index.html` file, plus `200.html` and `404.html` fallbacks, which you can tell your static web host to serve up for all requests. + +In order to achieve this we can change how the routes are prerendered. Just add this to [your hooks](/docs/api/advanced/hooks#nuxt-hooks-build-time) in your `nuxt.config.ts`: + +```ts twoslash [nuxt.config.ts] +export default defineNuxtConfig({ + hooks: { + 'prerender:routes' ({ routes }) { + routes.clear() // Do not generate any routes (except the defaults) + } + }, +}) +``` + +This will produce three files: + +- `index.html` +- `200.html` +- `404.html` + +The `200.html` and `404.html` might be useful for the hosting provider you are using. + ## Hybrid Rendering Hybrid rendering allows different caching rules per route using **Route Rules** and decides how the server should respond to a new request on a given URL. @@ -109,10 +139,11 @@ The different properties you can use are the following: - `ssr: boolean`{lang=ts} - Disables server-side rendering for sections of your app and make them SPA-only with `ssr: false` - `cors: boolean`{lang=ts} - Automatically adds cors headers with `cors: true` - you can customize the output by overriding with `headers` - `headers: object`{lang=ts} - Add specific headers to sections of your site - for example, your assets -- `swr: number|boolean`{lang=ts} - Add cache headers to the server response and cache it on the server or reverse proxy for a configurable TTL (time to live). The `node-server` preset of Nitro is able to cache the full response. When the TTL expired, the cached response will be sent while the page will be regenerated in the background. If true is used, a `stale-while-revalidate` header is added without a MaxAge. -- `isr: number|boolean`{lang=ts} - The behavior is the same as `swr` except that we are able to add the response to the CDN cache on platforms that support this (currently Netlify or Vercel). If `true` is used, the content persists until the next deploy inside the CDN. -- `prerender:boolean`{lang=ts} - Prerenders routes at build time and includes them in your build as static assets +- `swr: number | boolean`{lang=ts} - Add cache headers to the server response and cache it on the server or reverse proxy for a configurable TTL (time to live). The `node-server` preset of Nitro is able to cache the full response. When the TTL expired, the cached response will be sent while the page will be regenerated in the background. If true is used, a `stale-while-revalidate` header is added without a MaxAge. +- `isr: number | boolean`{lang=ts} - The behavior is the same as `swr` except that we are able to add the response to the CDN cache on platforms that support this (currently Netlify or Vercel). If `true` is used, the content persists until the next deploy inside the CDN. +- `prerender: boolean`{lang=ts} - Prerenders routes at build time and includes them in your build as static assets - `experimentalNoScripts: boolean`{lang=ts} - Disables rendering of Nuxt scripts and JS resource hints for sections of your site. +- `appMiddleware: string | string[] | Record`{lang=ts} - Allows you to define middleware that should or should not run for page paths within the Vue app part of your application (that is, not your Nitro routes) Whenever possible, route rules will be automatically applied to the deployment platform's native rules for optimal performances (Netlify and Vercel are currently supported). diff --git a/docs/2.guide/1.concepts/8.typescript.md b/docs/2.guide/1.concepts/8.typescript.md index e2718dc55f..4e07f35631 100644 --- a/docs/2.guide/1.concepts/8.typescript.md +++ b/docs/2.guide/1.concepts/8.typescript.md @@ -9,22 +9,26 @@ By default, Nuxt doesn't check types when you run [`nuxi dev`](/docs/api/command To enable type-checking at build or development time, install `vue-tsc` and `typescript` as development dependency: +::alert{type="warning"} +You may experience issues with the latest `vue-tsc` and `vite-plugin-checker`, used internally when type checking. For now, you may need to stay on v1 of `vue-tsc`, and follow these upstream issues for updates: [fi3ework/vite-plugin-checker#306](https://github.com/fi3ework/vite-plugin-checker/issues/306) and [vuejs/language-tools#3969](https://github.com/vuejs/language-tools/issues/3969). +:: + ::code-group ```bash [yarn] - yarn add --dev vue-tsc typescript + yarn add --dev vue-tsc@^1 typescript ``` ```bash [npm] - npm install --save-dev vue-tsc typescript + npm install --save-dev vue-tsc@^1 typescript ``` ```bash [pnpm] - pnpm add -D vue-tsc typescript + pnpm add -D vue-tsc@^1 typescript ``` ```bash [bun] - bun add -D vue-tsc typescript + bun add -D vue-tsc@^1 typescript ``` :: @@ -61,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. :: @@ -72,18 +80,18 @@ Overwriting options such as `"compilerOptions.paths"` with your own configuratio In case you need to extend options provided by `./.nuxt/tsconfig.json` further, you can use the [`alias` property](/docs/api/nuxt-config#alias) within your `nuxt.config`. `nuxi` will pick them up and extend `./.nuxt/tsconfig.json` accordingly. :: -## Stricter Checks +## Strict Checks TypeScript comes with certain checks to give you more safety and analysis of your program. -Once you’ve converted your codebase to TypeScript and felt familiar with it, you can start enabling these checks for greater safety ([read more](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html#getting-stricter-checks)). +[Strict checks](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html#getting-stricter-checks) are enabled by default in Nuxt 3 to give you greater type safety. -In order to enable strict type checking, you have to update `nuxt.config`: +If you are currently converting your codebase to TypeScript, you may want to temporarily disable strict checks by setting `strict` to `false` in your `nuxt.config`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ typescript: { - strict: true + strict: false } }) ``` diff --git a/docs/2.guide/1.concepts/9.code-style.md b/docs/2.guide/1.concepts/9.code-style.md index 3b9e27bcbd..2a6a32afb3 100644 --- a/docs/2.guide/1.concepts/9.code-style.md +++ b/docs/2.guide/1.concepts/9.code-style.md @@ -5,76 +5,20 @@ description: "Nuxt supports ESLint out of the box" ## ESLint -The recommended approach for Nuxt is to enable ESLint support using [`@nuxt/eslint-config`](https://github.com/nuxt/eslint-config). +The recommended approach for Nuxt is to enable ESLint support using the [`@nuxt/eslint`](https://eslint.nuxt.com/packages/module) module, that will setup project-aware ESLint configuration for you. -At the moment, this configuration will not format your files; you can set up Prettier or another tool to do so. +:::callout{icon="i-ph-lightbulb-duotone"} +The module is designed for the [new ESLint flat config format](https://eslint.org/docs/latest/use/configure/configuration-files-new) with is the [default format since ESLint v9](https://eslint.org/blog/2024/04/eslint-v9.0.0-released/). -::alert{type=info} -We're currently working to refactor the Nuxt ESLint configuration. Subscribe to the [Nuxt ESLint roadmap](https://github.com/nuxt/eslint-config/issues/303) to follow updates. -:: +If you are using the legacy `.eslintrc` config, you will need to [configure manually with `@nuxt/eslint-config`](https://eslint.nuxt.com/packages/config#legacy-config-format). We highly recommend you to migrate over the flat config to be future-proof. +::: -### Install Dependencies +## Quick Setup -Install both ESLint and the Nuxt configuration as development dependencies. - -::code-group - -```bash [yarn] -yarn add --dev eslint @nuxt/eslint-config +```bash +npx nuxi module add eslint ``` -```bash [npm] -npm install --save-dev eslint @nuxt/eslint-config -``` +Start your Nuxt app, a `eslint.config.mjs` file will be generated under your project root. You can customize it as needed. -```bash [pnpm] -pnpm add -D eslint @nuxt/eslint-config -``` - -```bash [bun] -bun add -D eslint @nuxt/eslint-config -``` - -:: - -### Configuration - -Add `.eslintrc.cjs` to the root folder of your Nuxt app. - -```js -module.exports = { - root: true, - extends: ['@nuxt/eslint-config'], -} -``` - -### Modify package.json - -Add the below to lint commands to your `package.json` script section: - -```json - "scripts": { - ... - "lint": "eslint .", - "lint:fix": "eslint . --fix", - ... - }, -``` - -Run the `lint` command to check if the code style is correct or run `lint:fix` to automatically fix issues. - -### Configuring VS Code - -Install the [VS Code ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint). - -In VS Code press `ctrl+shift+p` (`cmd+shift+p` on Mac) to open the command prompt, find `Open Workspace Settings (JSON)`, add the below lines to the JSON and save: - -```json -{ - "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit" - } -} -``` - -You're good to go! On save, your files will be linted and auto-fixed. +You can learn more about the module and customizations in [Nuxt ESLint's documentation](https://eslint.nuxt.com/packages/module). diff --git a/docs/2.guide/2.directory-structure/1.components.md b/docs/2.guide/2.directory-structure/1.components.md index 655cb98bd9..0cdf5cb4b6 100644 --- a/docs/2.guide/2.directory-structure/1.components.md +++ b/docs/2.guide/2.directory-structure/1.components.md @@ -244,6 +244,10 @@ This feature only works with Nuxt auto-imports and `#components` imports. Explic `.client` components are rendered only after being mounted. To access the rendered template using `onMounted()`, add `await nextTick()` in the callback of the `onMounted()` hook. :: +::read-more{to="/docs/api/components/client-only"} +You can also achieve a similar result with the `` component. +:: + ## Server Components Server components allow server-rendering individual components within your client-side apps. It's possible to use server components within Nuxt, even if you are generating a static site. That makes it possible to build complex sites that mix dynamic components, server-rendered HTML and even static chunks of markup. @@ -255,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 @@ -295,6 +299,10 @@ Now you can register server-only components with the `.server` suffix and use th Server-only components use [``](/docs/api/components/nuxt-island) under the hood, meaning that `lazy` prop and `#fallback` slot are both passed down to it. +::alert{type=warning} +Server components (and islands) must have a single root element. (HTML comments are considered elements as well.) +:: + ::alert{type=warning} Most features for server-only components and island components, such as slots and client components, are only available for single file components. :: @@ -318,7 +326,7 @@ You can partially hydrate a component by setting a `nuxt-client` attribute on th ``` ::alert{type=info} -This only works within a server component.Β Slots for client components are not available yet. +This only works within a server component.Β Slots for client components are working only with `experimental.componentIsland.selectiveClient` set to `'deep'` and since they are rendered server-side, they are not interactive once client-side. :: #### Server Component Context @@ -357,89 +365,12 @@ In this case, the `.server` + `.client` components are two 'halves' of a compone ``` -## `` Component +## Built-In Nuxt Components -Nuxt provides the [``](/docs/api/components/client-only) component for purposely rendering a component only on client side. +There are a number of components that Nuxt provides, including `` and ``. You can read more about them in the API documentation. -```vue [pages/example.vue] - -``` - -Use a slot as fallback until `` is mounted on client side. - -```vue [pages/example.vue] - -``` - - - - -## `` Component - -Nuxt provides the `` component to render a component only during development. - -The content will not be included in production builds and tree-shaken. - -```vue [pages/example.vue] - -``` - -## `` Component - -Nuxt provides the `` component to render its content on the client if any of its children trigger an error in SSR. -You can specify a `fallbackTag` to make it render a specific tag if it fails to render on the server. - -```vue [pages/example.vue] - -``` ## Library Authors diff --git a/docs/2.guide/2.directory-structure/1.middleware.md b/docs/2.guide/2.directory-structure/1.middleware.md index 22c63eb24a..64e66ecf6d 100644 --- a/docs/2.guide/2.directory-structure/1.middleware.md +++ b/docs/2.guide/2.directory-structure/1.middleware.md @@ -125,15 +125,19 @@ However, if you want to avoid this behaviour you can do so: ```ts twoslash [middleware/example.ts] export default defineNuxtRouteMiddleware(to => { // skip middleware on server - if (process.server) return + if (import.meta.server) return // skip middleware on client side entirely - if (process.client) return + if (import.meta.client) return // or only skip middleware on initial client load const nuxtApp = useNuxtApp() - if (process.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) return + if (import.meta.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) return }) ``` +::note +Rendering an error page is an entirely separate page load, meaning any registered middleware will run again. You can use [`useError`](/docs/getting-started/error-handling#useerror) in middleware to check if an error is being handled. +:: + ## Adding Middleware Dynamically It is possible to add global or named route middleware manually using the [`addRouteMiddleware()`](/docs/api/utils/add-route-middleware) helper function, such as from within a plugin. diff --git a/docs/2.guide/2.directory-structure/1.modules.md b/docs/2.guide/2.directory-structure/1.modules.md index b98df654e6..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/ @@ -56,3 +60,7 @@ modules/ ``` :read-more{to="/docs/guide/going-further/modules"} + +::tip{icon="i-ph-video-duotone" to="https://vueschool.io/lessons/creating-your-first-module-from-scratch" target="_blank"} +Watch Vue School video about Nuxt private modules. +:: diff --git a/docs/2.guide/2.directory-structure/1.pages.md b/docs/2.guide/2.directory-structure/1.pages.md index 2332295f8a..ef83deb8da 100644 --- a/docs/2.guide/2.directory-structure/1.pages.md +++ b/docs/2.guide/2.directory-structure/1.pages.md @@ -6,7 +6,7 @@ navigation.icon: i-ph-folder-duotone --- ::note -To reduce your application's bundle size, this directory is **optional**, meaning that [`vue-router`](https://router.vuejs.org) won't be included if you only use [`app.vue`](/docs/guide/directory-structure/app). To force the pages system, set `pages: true` in `nuxt.config` or have a [`app/router.options.ts`](/docs/guide/directory-structure/pages#router-options). +To reduce your application's bundle size, this directory is **optional**, meaning that [`vue-router`](https://router.vuejs.org) won't be included if you only use [`app.vue`](/docs/guide/directory-structure/app). To force the pages system, set `pages: true` in `nuxt.config` or have a [`app/router.options.ts`](/docs/guide/going-further/custom-routing#using-approuteroptions). :: ## Usage @@ -57,7 +57,7 @@ If you are using [`app.vue`](/docs/guide/directory-structure/app), make sure to ``` -Pages **must have a single root element** to allow [route transitions](/docs/getting-started/transitions) between pages, HTML comments are considered elements as well. +Pages **must have a single root element** to allow [route transitions](/docs/getting-started/transitions) between pages. HTML comments are considered elements as well. This means that when the route is server-rendered, or statically generated, you will be able to see its contents correctly, but when you navigate towards that route during client-side navigation the transition between routes will fail and you'll see that the route will not be rendered. @@ -193,6 +193,14 @@ To display the `child.vue` component, you have to insert the `` compon ``` +```vue {}[pages/parent/child.vue] + +``` + ### Child Route Keys If you want more control over when the `` component is re-rendered (for example, for transitions), you can either pass a string or function via the `pageKey` prop, or you can define a `key` value via `definePageMeta`: @@ -208,7 +216,7 @@ If you want more control over when the `` component is re-rendered (fo Or alternatively: -```vue twoslash {}[pages/child.vue] +```vue twoslash {}[pages/parent/child.vue] ``` +## Client-Only Pages + +You can define a page as [client only](/docs/guide/directory-structure/components#client-components) by giving it a `.client.vue` suffix. None of the content of this page will be rendered on the server. + ## Server-Only Pages You can define a page as [server only](/docs/guide/directory-structure/components#server-components) by giving it a `.server.vue` suffix. While you will be able to navigate to the page using client-side navigation, controlled by `vue-router`, it will be rendered with a server component automatically, meaning the code required to render the page will not be in your client-side bundle. -::note -You will also need to enable `experimental.componentIslands` in order to make this possible. +::alert{type=warning} +Server-only pages must have a single root element. (HTML comments are considered elements as well.) :: ## Custom Routing diff --git a/docs/2.guide/2.directory-structure/1.plugins.md b/docs/2.guide/2.directory-structure/1.plugins.md index 995e6212d1..a30be3eac6 100644 --- a/docs/2.guide/2.directory-structure/1.plugins.md +++ b/docs/2.guide/2.directory-structure/1.plugins.md @@ -76,9 +76,14 @@ 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: process.server ? 'pre' : 'post'` would defeat any future optimization Nuxt is able to do for your plugins. +If you are using the object-syntax, the properties are statically analyzed 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. +Nuxt does statically pre-load any hook listeners when using object-syntax, allowing you to define hooks without needing to worry about order of plugin registration. :: ## Registration Order @@ -117,7 +122,7 @@ export default defineNuxtPlugin({ ### Plugins With Dependencies -If a plugin needs to await a parallel plugin before it runs, you can add the plugin's name to the `dependsOn` array. +If a plugin needs to wait for another plugin before it runs, you can add the plugin's name to the `dependsOn` array. ```ts twoslash [plugins/depending-on-my-plugin.ts] export default defineNuxtPlugin({ 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/2.directory-structure/3.app-config.md b/docs/2.guide/2.directory-structure/3.app-config.md index d8eb2ce4be..93c15381f8 100644 --- a/docs/2.guide/2.directory-structure/3.app-config.md +++ b/docs/2.guide/2.directory-structure/3.app-config.md @@ -99,7 +99,7 @@ Nuxt uses a custom merging strategy for the `AppConfig` within [the layers](/doc This strategy is implemented using a [Function Merger](https://github.com/unjs/defu#function-merger), which allows defining a custom merging strategy for every key in `app.config` that has an array as value. ::note -The Function Merger should only be used in the base `app.config` of your application. +The function merger can only be used in the extended layers and not the main `app.config` in project. :: Here's an example of how you can use: diff --git a/docs/2.guide/2.directory-structure/3.nuxt-config.md b/docs/2.guide/2.directory-structure/3.nuxt-config.md index d41971142e..1174095d2b 100644 --- a/docs/2.guide/2.directory-structure/3.nuxt-config.md +++ b/docs/2.guide/2.directory-structure/3.nuxt-config.md @@ -33,7 +33,7 @@ Discover all the available options in the **Nuxt configuration** documentation. To ensure your configuration is up to date, Nuxt will make a full restart when detecting changes in the main configuration file, the [`.env`](/docs/guide/directory-structure/env), [`.nuxtignore`](/docs/guide/directory-structure/nuxtignore) and `.nuxtrc` dotfiles. -The `.nuxtrc` file is a file that can be used to configure Nuxt with a fla syntax, it is based on [`unjs/rc9`](https://github.com/unjs/rc9). +The `.nuxtrc` file can be used to configure Nuxt with a flat syntax. It is based on [`unjs/rc9`](https://github.com/unjs/rc9). ``` [.nuxtrc] ssr=false 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..9105b2ebfa 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. +You can disable this feature if it causes issues in your project. + +```ts twoslash [nuxt.config.ts] +export default defineNuxtConfig({ + experimental: { + scanPageMeta: false + } +}) +``` + ## 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/1.features.md b/docs/2.guide/3.going-further/1.features.md index e43c6dda2d..02056f1e12 100644 --- a/docs/2.guide/3.going-further/1.features.md +++ b/docs/2.guide/3.going-further/1.features.md @@ -37,6 +37,47 @@ export default defineNuxtConfig({ There is also a `future` namespace for early opting-in to new features that will become default in a future (possibly major) version of the framework. +### compatibilityVersion + +::important +This configuration option is available in Nuxt v3.12+. +:: + +This enables early access to Nuxt features or flags. + +Setting `compatibilityVersion` to `4` changes defaults throughout your +Nuxt configuration to opt-in to Nuxt v4 behaviour, but you can granularly re-enable Nuxt v3 behaviour +when testing (see example). Please file issues if so, so that we can +address in Nuxt or in the ecosystem. + +```ts +export default defineNuxtConfig({ + future: { + compatibilityVersion: 4, + }, + // To re-enable _all_ Nuxt v3 behaviour, set the following options: + srcDir: '.', + dir: { + app: 'app' + }, + experimental: { + compileTemplate: true, + templateUtils: true, + relativeWatchPaths: true, + defaults: { + useAsyncData: { + deep: true + } + } + }, + unhead: { + renderSSRHeadOptions: { + omitLineBreaks: false + } + } +}) +``` + ### typescriptBundlerResolution This enables 'Bundler' module resolution mode for TypeScript, which is the recommended setting 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 f4ca19cd94..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] @@ -98,7 +102,7 @@ The behavior is different between the client-side and server-side: const config = useRuntimeConfig() console.log('Runtime config:', config) -if (process.server) { +if (import.meta.server) { console.log('API secret:', config.apiSecret) } @@ -164,3 +168,7 @@ declare module 'nuxt/schema' { // It is always important to ensure you import/export something when augmenting a type export {} ``` + +::note +`nuxt/schema` is provided as a convenience for end-users to access the version of the schema used by Nuxt in their project. Module authors should instead augment `@nuxt/schema`. +:: diff --git a/docs/2.guide/3.going-further/3.modules.md b/docs/2.guide/3.going-further/3.modules.md index f7eae0f03f..8c354e5e21 100644 --- a/docs/2.guide/3.going-further/3.modules.md +++ b/docs/2.guide/3.going-further/3.modules.md @@ -30,6 +30,10 @@ This will create a `my-module` project with all the boilerplate necessary to dev Learn how to perform basic tasks with the module starter. +::tip{icon="i-ph-video-duotone" to="https://vueschool.io/lessons/navigating-the-official-starter-template" target="_blank"} +Watch Vue School video about Nuxt module starter template. +:: + #### How to Develop While your module source code lives inside the `src` directory, in most cases, to develop a module, you need a Nuxt application. That's what the `playground` directory is about. It's a Nuxt application you can tinker with that is already configured to run with your module. @@ -72,7 +76,7 @@ Before publishing your module to npm, makes sure you have an [npmjs.com](https:/ While you can publish your module by bumping its version and using the `npm publish` command, the module starter comes with a release script that helps you make sure you publish a working version of your module to npm and more. -To use the release script, first, commit all your changes (we recommend you follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0) to also take advantage of automatic version bump and changelog update), then run the release script with `npm run release`. +To use the release script, first, commit all your changes (we recommend you follow [Conventional Commits](https://www.conventionalcommits.org) to also take advantage of automatic version bump and changelog update), then run the release script with `npm run release`. When running the release script, the following will happen: @@ -255,6 +259,10 @@ export default defineNuxtModule({ When you need to handle more complex configuration alterations, you should consider using [defu](https://github.com/unjs/defu). +::tip{icon="i-ph-video-duotone" to="https://vueschool.io/lessons/extending-and-altering-nuxt-configuration-and-options" target="_blank"} +Watch Vue School video about altering Nuxt configuration. +:: + #### Exposing Options to Runtime Because modules aren't part of the application runtime, their options aren't either. However, in many cases, you might need access to some of these module options within your runtime code. We recommend exposing the needed config using Nuxt's [`runtimeConfig`](/docs/api/nuxt-config#runtimeconfig). @@ -288,6 +296,10 @@ Be careful not to expose any sensitive module configuration on the public runtim :read-more{to="/docs/guide/going-further/runtime-config"} +::tip{icon="i-ph-video-duotone" to="https://vueschool.io/lessons/passing-and-exposing-module-options" target="_blank"} +Watch Vue School video about passing and exposing Nuxt module options. +:: + #### Injecting Plugins With `addPlugin` Plugins are a common way for a module to add runtime logic. You can use the `addPlugin` utility to register them from your module. @@ -511,6 +523,10 @@ export default defineNuxtModule({ :read-more{to="/docs/api/advanced/hooks"} +::tip{icon="i-ph-video-duotone" to="https://vueschool.io/lessons/nuxt-lifecycle-hooks" target="_blank"} +Watch Vue School video about using Nuxt lifecycle hooks in modules. +:: + ::note **Module cleanup** :br @@ -733,6 +749,10 @@ The module starter comes with a default set of tools and configurations (e.g. ES [Nuxt Module ecosystem](/modules) represents more than 15 million monthly NPM downloads and provides extended functionalities and integrations with all sort of tools. You can be part of this ecosystem! +::tip{icon="i-ph-video-duotone" to="https://vueschool.io/lessons/exploring-nuxt-modules-ecosystem-and-module-types" target="_blank"} +Watch Vue School video about Nuxt module types. +:: + ### Module Types **Official modules** are modules prefixed (scoped) with `@nuxt/` (e.g. [`@nuxt/content`](https://content.nuxtjs.org)). They are made and maintained actively by the Nuxt team. Like with the framework, contributions from the community are more than welcome to help make them better! 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/3.going-further/7.layers.md b/docs/2.guide/3.going-further/7.layers.md index 4533abfe49..3285135cfd 100644 --- a/docs/2.guide/3.going-further/7.layers.md +++ b/docs/2.guide/3.going-further/7.layers.md @@ -100,6 +100,10 @@ export default defineNuxtConfig({ If you want to extend a private remote source, you need to add the environment variable `GIGET_AUTH=` to provide a token. :: +::tip +If you want to extend a remote source from a self-hosted GitHub or GitLab instance, you need to supply its URL with the `GIGET_GITHUB_URL=` or `GIGET_GITLAB_URL=` environment variable - or directly configure it with [the `auth` option](https://github.com/unjs/c12#extending-config-layer-from-remote-sources) in your `nuxt.config`. +:: + ::note When using git remote sources, if a layer has npm dependencies and you wish to install them, you can do so by specifying `install: true` in your layer options. diff --git a/docs/2.guide/3.going-further/9.debugging.md b/docs/2.guide/3.going-further/9.debugging.md index 0771c6d959..59558ba67d 100644 --- a/docs/2.guide/3.going-further/9.debugging.md +++ b/docs/2.guide/3.going-further/9.debugging.md @@ -38,6 +38,10 @@ It is possible to debug your Nuxt app in your IDE while you are developing it. You may need to update the config below with a path to your web browser. For more information, visit the [VS Code documentation about debug configuration](https://go.microsoft.com/fwlink/?linkid=830387). +::important +If you use `pnpm`, you will need to have `nuxi` installed as a devDependency for the configuration below to work. +:: + ```json5 { // Use IntelliSense to learn about possible attributes. diff --git a/docs/2.guide/3.going-further/8.custom-routing.md b/docs/2.guide/4.recipes/1.custom-routing.md similarity index 97% rename from docs/2.guide/3.going-further/8.custom-routing.md rename to docs/2.guide/4.recipes/1.custom-routing.md index b8f39b048d..b3e408c2a3 100644 --- a/docs/2.guide/3.going-further/8.custom-routing.md +++ b/docs/2.guide/4.recipes/1.custom-routing.md @@ -105,7 +105,7 @@ export default defineNuxtConfig({ const resolver = createResolver(import.meta.url) // add a route files.push({ - path: resolver.resolve('./runtime/app/router-options') + path: resolver.resolve('./runtime/app/router-options'), optional: true }) } @@ -172,6 +172,6 @@ import { createMemoryHistory } from 'vue-router' export default { // https://router.vuejs.org/api/interfaces/routeroptions.html - history: base => process.client ? createMemoryHistory(base) : null /* default */ + history: base => import.meta.client ? createMemoryHistory(base) : null /* default */ } ``` diff --git a/docs/2.guide/4.recipes/2.vite-plugin.md b/docs/2.guide/4.recipes/2.vite-plugin.md new file mode 100644 index 0000000000..6a9b1e8a28 --- /dev/null +++ b/docs/2.guide/4.recipes/2.vite-plugin.md @@ -0,0 +1,65 @@ +--- +navigation.title: 'Vite Plugins' +title: Using Vite Plugins in Nuxt +description: Learn how to integrate Vite plugins into your Nuxt project. +--- + +While Nuxt modules offer extensive functionality, sometimes a specific Vite plugin might meet your needs more directly. + +First, we need to install the Vite plugin, for our example, we'll use `@rollup/plugin-yaml`: + +::code-group + + ```bash [npm] + npm install @rollup/plugin-yaml + ``` + + ```bash [yarn] + yarn add @rollup/plugin-yaml + ``` + + ```bash [pnpm] + pnpm add @rollup/plugin-yaml + ``` + + ```bash [bun] + bun add @rollup/plugin-yaml + ``` + +:: + +Next, we need to import and add it to our [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt-config) file: + +```ts [nuxt.config.ts] +import yaml from '@rollup/plugin-yaml' + +export default defineNuxtConfig({ + vite: { + plugins: [ + yaml() + ] + } +}) +``` + +Now we installed and configured our Vite plugin, we can start using YAML files directly in our project. + +For example, we can have a `config.yaml` that stores configuration data and import this data in our Nuxt components: + +::code-group + +```yaml [data/hello.yaml] +greeting: "Hello, Nuxt with Vite!" +``` + +```vue [components/Hello.vue] + + + +``` + +:: 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/2.guide/4.recipes/_dir.yml b/docs/2.guide/4.recipes/_dir.yml new file mode 100644 index 0000000000..b63c755e5f --- /dev/null +++ b/docs/2.guide/4.recipes/_dir.yml @@ -0,0 +1,3 @@ +title: Recipes +titleTemplate: '%s Β· Recipes' +icon: i-ph-cooking-pot-duotone diff --git a/docs/3.api/1.components/1.client-only.md b/docs/3.api/1.components/1.client-only.md index 481ab8ab9c..86e56d2547 100644 --- a/docs/3.api/1.components/1.client-only.md +++ b/docs/3.api/1.components/1.client-only.md @@ -8,7 +8,11 @@ links: size: xs --- -The `` component renders its slot only in client-side. To import a component only on the client, register the component in a client-side only plugin. +The `` component is used for purposely rendering a component only on client side. + +::note +The content of the default slot will be tree-shaken out of the server build. (This does mean that any CSS used by components within it may not be inlined when rendering the initial HTML.) +:: ## Props @@ -19,6 +23,7 @@ The `` component renders its slot only in client-side. To import a c