mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-29 00:52:01 +00:00
Merge remote-tracking branch 'origin/main' into feat-add-auto-import-directives
This commit is contained in:
commit
93722c95e5
@ -1,4 +1,4 @@
|
|||||||
FROM node:lts@sha256:db5dd2f30cb82a8bdbd16acd4a8f3f2789f5b24f6ce43f98aa041be848c82e45
|
FROM node:lts@sha256:a5e0ed56f2c20b9689e0f7dd498cac7e08d2a3a283e92d9304e7b9b83e3c6ff3
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -fy libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdbus-1-3 libdrm2 libxkbcommon0 libatspi2.0-0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2 && \
|
apt-get install -fy libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdbus-1-3 libdrm2 libxkbcommon0 libatspi2.0-0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2 && \
|
||||||
|
4
.github/workflows/autofix-docs.yml
vendored
4
.github/workflows/autofix-docs.yml
vendored
@ -17,9 +17,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
4
.github/workflows/autofix.yml
vendored
4
.github/workflows/autofix.yml
vendored
@ -13,9 +13,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
6
.github/workflows/benchmark.yml
vendored
6
.github/workflows/benchmark.yml
vendored
@ -29,9 +29,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
@ -46,7 +46,7 @@ jobs:
|
|||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
- name: Run benchmarks
|
- name: Run benchmarks
|
||||||
uses: CodSpeedHQ/action@ab07afd34cbbb7a1306e8d14b7cc44e029eee37a # v3.0.0
|
uses: CodSpeedHQ/action@b587655f756aab640e742fec141261bc6f0a569d # v3.0.1
|
||||||
with:
|
with:
|
||||||
run: pnpm vitest bench
|
run: pnpm vitest bench
|
||||||
token: ${{ secrets.CODSPEED_TOKEN }}
|
token: ${{ secrets.CODSPEED_TOKEN }}
|
||||||
|
4
.github/workflows/changelog.yml
vendored
4
.github/workflows/changelog.yml
vendored
@ -22,11 +22,11 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
47
.github/workflows/ci.yml
vendored
47
.github/workflows/ci.yml
vendored
@ -37,9 +37,9 @@ jobs:
|
|||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
@ -57,7 +57,7 @@ jobs:
|
|||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
- name: Cache dist
|
- name: Cache dist
|
||||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
retention-days: 3
|
retention-days: 3
|
||||||
name: dist
|
name: dist
|
||||||
@ -72,10 +72,10 @@ jobs:
|
|||||||
security-events: write
|
security-events: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9
|
uses: github/codeql-action/init@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
|
||||||
with:
|
with:
|
||||||
config: |
|
config: |
|
||||||
paths:
|
paths:
|
||||||
@ -91,7 +91,7 @@ jobs:
|
|||||||
queries: +security-and-quality
|
queries: +security-and-quality
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9
|
uses: github/codeql-action/analyze@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
|
||||||
with:
|
with:
|
||||||
category: "/language:javascript-typescript"
|
category: "/language:javascript-typescript"
|
||||||
|
|
||||||
@ -107,9 +107,9 @@ jobs:
|
|||||||
module: ["bundler", "node"]
|
module: ["bundler", "node"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
@ -138,9 +138,9 @@ jobs:
|
|||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
@ -162,9 +162,9 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- build
|
- build
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
@ -191,7 +191,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest]
|
os: [ubuntu-latest, windows-latest]
|
||||||
env: ["dev", "built"]
|
env: ["dev", "built"]
|
||||||
builder: ["vite", "webpack"]
|
builder: ["vite", "rspack", "webpack"]
|
||||||
context: ["async", "default"]
|
context: ["async", "default"]
|
||||||
manifest: ["manifest-on", "manifest-off"]
|
manifest: ["manifest-on", "manifest-off"]
|
||||||
payload: ["json", "js"]
|
payload: ["json", "js"]
|
||||||
@ -199,12 +199,18 @@ jobs:
|
|||||||
exclude:
|
exclude:
|
||||||
- builder: "webpack"
|
- builder: "webpack"
|
||||||
payload: "js"
|
payload: "js"
|
||||||
|
- builder: "rspack"
|
||||||
|
payload: "js"
|
||||||
- manifest: "manifest-off"
|
- manifest: "manifest-off"
|
||||||
payload: "js"
|
payload: "js"
|
||||||
- context: "default"
|
- context: "default"
|
||||||
payload: "js"
|
payload: "js"
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
payload: "js"
|
payload: "js"
|
||||||
|
- env: "dev"
|
||||||
|
builder: "rspack"
|
||||||
|
- manifest: "manifest-off"
|
||||||
|
builder: "rspack"
|
||||||
- env: "dev"
|
- env: "dev"
|
||||||
builder: "webpack"
|
builder: "webpack"
|
||||||
- manifest: "manifest-off"
|
- manifest: "manifest-off"
|
||||||
@ -213,9 +219,9 @@ jobs:
|
|||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
@ -242,7 +248,7 @@ jobs:
|
|||||||
TEST_PAYLOAD: ${{ matrix.payload }}
|
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' }}
|
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@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0
|
- uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
|
||||||
if: github.event_name != 'push' && matrix.env == 'built' && matrix.builder == 'vite' && matrix.context == 'default' && matrix.os == 'ubuntu-latest' && matrix.manifest == 'manifest-on'
|
if: github.event_name != 'push' && matrix.env == 'built' && matrix.builder == 'vite' && matrix.context == 'default' && matrix.os == 'ubuntu-latest' && matrix.manifest == 'manifest-on'
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
@ -256,7 +262,6 @@ jobs:
|
|||||||
github.event_name == 'push' &&
|
github.event_name == 'push' &&
|
||||||
github.repository == 'nuxt/nuxt' &&
|
github.repository == 'nuxt/nuxt' &&
|
||||||
!contains(github.event.head_commit.message, '[skip-release]') &&
|
!contains(github.event.head_commit.message, '[skip-release]') &&
|
||||||
!startsWith(github.event.head_commit.message, 'chore') &&
|
|
||||||
!startsWith(github.event.head_commit.message, 'docs')
|
!startsWith(github.event.head_commit.message, 'docs')
|
||||||
needs:
|
needs:
|
||||||
- lint
|
- lint
|
||||||
@ -266,11 +271,11 @@ jobs:
|
|||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
@ -307,11 +312,11 @@ jobs:
|
|||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
4
.github/workflows/dependency-review.yml
vendored
4
.github/workflows/dependency-review.yml
vendored
@ -17,6 +17,6 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: 'Checkout Repository'
|
- name: 'Checkout Repository'
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: 'Dependency Review'
|
- name: 'Dependency Review'
|
||||||
uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4
|
uses: actions/dependency-review-action@a6993e2c61fd5dc440b409aa1d6904921c5e1894 # v4.3.5
|
||||||
|
6
.github/workflows/docs-check-links.yml
vendored
6
.github/workflows/docs-check-links.yml
vendored
@ -19,17 +19,17 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# Cache lychee results (e.g. to avoid hitting rate limits)
|
# Cache lychee results (e.g. to avoid hitting rate limits)
|
||||||
- name: Restore lychee cache
|
- name: Restore lychee cache
|
||||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2
|
||||||
with:
|
with:
|
||||||
path: .lycheecache
|
path: .lycheecache
|
||||||
key: cache-lychee-${{ github.sha }}
|
key: cache-lychee-${{ github.sha }}
|
||||||
restore-keys: cache-lychee-
|
restore-keys: cache-lychee-
|
||||||
|
|
||||||
# check links with Lychee
|
# check links with Lychee
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Lychee link checker
|
- name: Lychee link checker
|
||||||
uses: lycheeverse/lychee-action@5047c2a4052946424ce139fe111135f6d7c0fe0b # for v1.8.0
|
uses: lycheeverse/lychee-action@7cd0af4c74a61395d455af97419279d86aafaede # for v1.8.0
|
||||||
with:
|
with:
|
||||||
# arguments with file types to check
|
# arguments with file types to check
|
||||||
args: >-
|
args: >-
|
||||||
|
4
.github/workflows/docs.yml
vendored
4
.github/workflows/docs.yml
vendored
@ -21,9 +21,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
4
.github/workflows/lint-sherif.yml
vendored
4
.github/workflows/lint-sherif.yml
vendored
@ -23,9 +23,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
4
.github/workflows/lint-workflows.yml
vendored
4
.github/workflows/lint-workflows.yml
vendored
@ -23,9 +23,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
# From https://github.com/rhysd/actionlint/blob/main/docs/usage.md#use-actionlint-on-github-actions
|
# From https://github.com/rhysd/actionlint/blob/main/docs/usage.md#use-actionlint-on-github-actions
|
||||||
- name: Check workflow files
|
- name: Check workflow files
|
||||||
uses: docker://rhysd/actionlint:1.7.2@sha256:89d3f90f82781dee3c8724651129634b08cf2241bbd67fcd02a1c5198119fc5e
|
uses: docker://rhysd/actionlint:1.7.3@sha256:7617f05bd698cd2f1c3aedc05bc733ccec92cca0738f3e8722c32c5b42c70ae6
|
||||||
with:
|
with:
|
||||||
args: -color
|
args: -color
|
||||||
|
6
.github/workflows/release-pr.yml
vendored
6
.github/workflows/release-pr.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Ensure action is by maintainer
|
- name: Ensure action is by maintainer
|
||||||
uses: octokit/request-action@872c5c97b3c85c23516a572f02b31401ef82415d # v2.3.1
|
uses: octokit/request-action@dad4362715b7fb2ddedf9772c8670824af564f0d # v2.4.0
|
||||||
id: check_role
|
id: check_role
|
||||||
with:
|
with:
|
||||||
route: GET /repos/nuxt/nuxt/collaborators/${{ github.event.comment.user.login }}
|
route: GET /repos/nuxt/nuxt/collaborators/${{ github.event.comment.user.login }}
|
||||||
@ -48,13 +48,13 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "head_sha=$head_sha" >> "$GITHUB_OUTPUT"
|
echo "head_sha=$head_sha" >> "$GITHUB_OUTPUT"
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
ref: ${{ steps.pr.outputs.head_sha }}
|
ref: ${{ steps.pr.outputs.head_sha }}
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@ -19,11 +19,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
registry-url: "https://registry.npmjs.org/"
|
registry-url: "https://registry.npmjs.org/"
|
||||||
|
2
.github/workflows/reproduire.yml
vendored
2
.github/workflows/reproduire.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
|||||||
reproduire:
|
reproduire:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: Hebilicious/reproduire@4b686ae9cbb72dad60f001d278b6e3b2ce40a9ac # v0.0.9-mp
|
- uses: Hebilicious/reproduire@4b686ae9cbb72dad60f001d278b6e3b2ce40a9ac # v0.0.9-mp
|
||||||
with:
|
with:
|
||||||
label: needs reproduction
|
label: needs reproduction
|
||||||
|
6
.github/workflows/scorecards.yml
vendored
6
.github/workflows/scorecards.yml
vendored
@ -32,7 +32,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout code"
|
- name: "Checkout code"
|
||||||
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ jobs:
|
|||||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||||
# format to the repository Actions tab.
|
# format to the repository Actions tab.
|
||||||
- name: "Upload artifact"
|
- name: "Upload artifact"
|
||||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
if: github.repository == 'nuxt/nuxt' && success()
|
if: github.repository == 'nuxt/nuxt' && success()
|
||||||
with:
|
with:
|
||||||
name: SARIF file
|
name: SARIF file
|
||||||
@ -68,7 +68,7 @@ jobs:
|
|||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard.
|
# Upload the results to GitHub's code scanning dashboard.
|
||||||
- name: "Upload to code-scanning"
|
- name: "Upload to code-scanning"
|
||||||
uses: github/codeql-action/upload-sarif@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9
|
uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
|
||||||
if: github.repository == 'nuxt/nuxt' && success()
|
if: github.repository == 'nuxt/nuxt' && success()
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
2
.github/workflows/stackblitz-link.yml
vendored
2
.github/workflows/stackblitz-link.yml
vendored
@ -11,7 +11,7 @@ jobs:
|
|||||||
stackblitz:
|
stackblitz:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: huang-julien/reproduire-sur-stackblitz@9ceccbfbb0f2f9a9a8db2d1f0dd909cf5cfe67aa # v1.0.2
|
- uses: huang-julien/reproduire-sur-stackblitz@9ceccbfbb0f2f9a9a8db2d1f0dd909cf5cfe67aa # v1.0.2
|
||||||
with:
|
with:
|
||||||
reproduction-heading: '### Reproduction'
|
reproduction-heading: '### Reproduction'
|
||||||
|
@ -68,6 +68,10 @@ When importing `@nuxt/test-utils` in your vitest config, It is necessary to have
|
|||||||
> ie. `vitest.config.m{ts,js}`.
|
> ie. `vitest.config.m{ts,js}`.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
::tip
|
||||||
|
It is possible to set environment variables for testing by using the `.env.test` file.
|
||||||
|
::
|
||||||
|
|
||||||
### Using a Nuxt Runtime Environment
|
### 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.
|
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.
|
||||||
@ -285,7 +289,7 @@ import { mockNuxtImport } from '@nuxt/test-utils/runtime'
|
|||||||
|
|
||||||
const { useStorageMock } = vi.hoisted(() => {
|
const { useStorageMock } = vi.hoisted(() => {
|
||||||
return {
|
return {
|
||||||
useStorageMock: vi.fn().mockImplementation(() => {
|
useStorageMock: vi.fn(() => {
|
||||||
return { value: 'mocked storage'}
|
return { value: 'mocked storage'}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,7 @@ export default defineNuxtConfig({
|
|||||||
// app: 'app'
|
// app: 'app'
|
||||||
// },
|
// },
|
||||||
// experimental: {
|
// experimental: {
|
||||||
|
// scanPageMeta: 'after-resolve',
|
||||||
// sharedPrerenderData: false,
|
// sharedPrerenderData: false,
|
||||||
// compileTemplate: true,
|
// compileTemplate: true,
|
||||||
// resetAsyncDataToUndefined: true,
|
// resetAsyncDataToUndefined: true,
|
||||||
@ -178,6 +179,7 @@ nuxt.config.ts
|
|||||||
1. Create a new directory called `app/`.
|
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. 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`, `content/`, `layers/`, `modules/`, `public/` and `server/` folders remain outside the `app/` folder, in the root of your project.
|
1. Make sure your `nuxt.config.ts`, `content/`, `layers/`, `modules/`, `public/` and `server/` folders remain outside the `app/` folder, in the root of your project.
|
||||||
|
1. Remember to update any third-party configuration files to work with the new directory structure, such as your `tailwindcss` or `eslint` configuration (if required - `@nuxtjs/tailwindcss` should automatically configure `tailwindcss` correctly).
|
||||||
|
|
||||||
::tip
|
::tip
|
||||||
You can automate this migration by running `npx codemod@latest nuxt/4/file-structure`
|
You can automate this migration by running `npx codemod@latest nuxt/4/file-structure`
|
||||||
@ -235,6 +237,45 @@ export default defineNuxtConfig({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Scan Page Meta After Resolution
|
||||||
|
|
||||||
|
🚦 **Impact Level**: Minimal
|
||||||
|
|
||||||
|
##### What Changed
|
||||||
|
|
||||||
|
We now scan page metadata (defined in `definePageMeta`) _after_ calling the `pages:extend` hook rather than before.
|
||||||
|
|
||||||
|
##### Reasons for Change
|
||||||
|
|
||||||
|
This was to allow scanning metadata for pages that users wanted to add in `pages:extend`. We still offer an opportunity to change or override page metadata in a new `pages:resolved` hook.
|
||||||
|
|
||||||
|
##### Migration Steps
|
||||||
|
|
||||||
|
If you want to override page metadata, do that in `pages:resolved` rather than in `pages:extend`.
|
||||||
|
|
||||||
|
```diff
|
||||||
|
export default defineNuxtConfig({
|
||||||
|
hooks: {
|
||||||
|
- 'pages:extend'(pages) {
|
||||||
|
+ 'pages:resolved'(pages) {
|
||||||
|
const myPage = pages.find(page => page.path === '/')
|
||||||
|
myPage.meta ||= {}
|
||||||
|
myPage.meta.layout = 'overridden-layout'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can revert to the previous behaviour with:
|
||||||
|
|
||||||
|
```ts twoslash [nuxt.config.ts]
|
||||||
|
export default defineNuxtConfig({
|
||||||
|
experimental: {
|
||||||
|
scanPageMeta: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
#### Shared Prerender Data
|
#### Shared Prerender Data
|
||||||
|
|
||||||
🚦 **Impact Level**: Medium
|
🚦 **Impact Level**: Medium
|
||||||
|
@ -530,7 +530,7 @@ export default defineNuxtConfig({
|
|||||||
hooks: {
|
hooks: {
|
||||||
'build:manifest': (manifest) => {
|
'build:manifest': (manifest) => {
|
||||||
// find the app entry, css list
|
// find the app entry, css list
|
||||||
const css = manifest['node_modules/nuxt/dist/app/entry.js']?.css
|
const css = Object.values(manifest).find(options => options.isEntry)?.css
|
||||||
if (css) {
|
if (css) {
|
||||||
// start from the end of the array and go to the beginning
|
// start from the end of the array and go to the beginning
|
||||||
for (let i = css.length - 1; i >= 0; i--) {
|
for (let i = css.length - 1; i >= 0; i--) {
|
||||||
|
@ -15,7 +15,7 @@ This file system routing uses naming conventions to create dynamic and nested ro
|
|||||||
::code-group
|
::code-group
|
||||||
|
|
||||||
```bash [Directory Structure]
|
```bash [Directory Structure]
|
||||||
| pages/
|
-| pages/
|
||||||
---| about.vue
|
---| about.vue
|
||||||
---| index.vue
|
---| index.vue
|
||||||
---| posts/
|
---| posts/
|
||||||
|
@ -8,21 +8,17 @@ Nuxt comes with two composables and a built-in library to perform data-fetching
|
|||||||
|
|
||||||
In a nutshell:
|
In a nutshell:
|
||||||
|
|
||||||
- [`useFetch`](/docs/api/composables/use-fetch) is the most straightforward way to handle data fetching in a component setup function.
|
- [`$fetch`](/docs/api/utils/dollarfetch) is the simplest way to make a network request.
|
||||||
- [`$fetch`](/docs/api/utils/dollarfetch) is great to make network requests based on user interaction.
|
- [`useFetch`](/docs/api/composables/use-fetch) is wrapper around `$fetch` that fetches data only once in [universal rendering](/docs/guide/concepts/rendering#universal-rendering).
|
||||||
- [`useAsyncData`](/docs/api/composables/use-async-data), combined with `$fetch`, offers more fine-grained control.
|
- [`useAsyncData`](/docs/api/composables/use-async-data) is similar to `useFetch` but offers more fine-grained control.
|
||||||
|
|
||||||
Both `useFetch` and `useAsyncData` share a common set of options and patterns that we will detail in the last sections.
|
Both `useFetch` and `useAsyncData` share a common set of options and patterns that we will detail in the last sections.
|
||||||
|
|
||||||
Before that, it's imperative to know why these composables exist in the first place.
|
## The need for `useFetch` and `useAsyncData`
|
||||||
|
|
||||||
## Why use specific composables for data fetching?
|
Nuxt is a framework which can run isomorphic (or universal) code in both server and client environments. If the [`$fetch` function](/docs/api/utils/dollarfetch) is used to perform data fetching in the setup function of a Vue component, this may cause data to be fetched twice, once on the server (to render the HTML) and once again on the client (when the HTML is hydrated). This can cause hydration issues, increase the time to interactivity and cause unpredictable behavior.
|
||||||
|
|
||||||
Nuxt is a framework which can run isomorphic (or universal) code in both server and client environments. If the [`$fetch` function](/docs/api/utils/dollarfetch) is used to perform data fetching in the setup function of a Vue component, this may cause data to be fetched twice, once on the server (to render the HTML) and once again on the client (when the HTML is hydrated). This is why Nuxt offers specific data fetching composables so data is fetched only once.
|
The [`useFetch`](/docs/api/composables/use-fetch) and [`useAsyncData`](/docs/api/composables/use-async-data) composables solve this problem by ensuring that if an API call is made on the server, the data is forwarded to the client in the payload.
|
||||||
|
|
||||||
### Network calls duplication
|
|
||||||
|
|
||||||
The [`useFetch`](/docs/api/composables/use-fetch) and [`useAsyncData`](/docs/api/composables/use-async-data) composables ensure that once an API call is made on the server, the data is properly forwarded to the client in the payload.
|
|
||||||
|
|
||||||
The payload is a JavaScript object accessible through [`useNuxtApp().payload`](/docs/api/composables/use-nuxt-app#payload). It is used on the client to avoid refetching the same data when the code is executed in the browser [during hydration](/docs/guide/concepts/rendering#universal-rendering).
|
The payload is a JavaScript object accessible through [`useNuxtApp().payload`](/docs/api/composables/use-nuxt-app#payload). It is used on the client to avoid refetching the same data when the code is executed in the browser [during hydration](/docs/guide/concepts/rendering#universal-rendering).
|
||||||
|
|
||||||
@ -30,17 +26,71 @@ The payload is a JavaScript object accessible through [`useNuxtApp().payload`](/
|
|||||||
Use the [Nuxt DevTools](https://devtools.nuxt.com) to inspect this data in the **Payload tab**.
|
Use the [Nuxt DevTools](https://devtools.nuxt.com) to inspect this data in the **Payload tab**.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
```vue [app.vue]
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { data } = await useFetch('/api/data')
|
||||||
|
|
||||||
|
async function handleFormSubmit() {
|
||||||
|
const res = await $fetch('/api/submit', {
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
// My form data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="data == null">
|
||||||
|
No data
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<form @submit="handleFormSubmit">
|
||||||
|
<!-- form input tags -->
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
In the example above, `useFetch` would make sure that the request would occur in the server and is properly forwarded to the browser. `$fetch` has no such mechanism and is a better option to use when the request is solely made from the browser.
|
||||||
|
|
||||||
### Suspense
|
### Suspense
|
||||||
|
|
||||||
Nuxt uses Vue’s [`<Suspense>`](https://vuejs.org/guide/built-ins/suspense) component under the hood to prevent navigation before every async data is available to the view. The data fetching composables can help you leverage this feature and use what suits best on a per-calls basis.
|
Nuxt uses Vue’s [`<Suspense>`](https://vuejs.org/guide/built-ins/suspense) component under the hood to prevent navigation before every async data is available to the view. The data fetching composables can help you leverage this feature and use what suits best on a per-call basis.
|
||||||
|
|
||||||
::note
|
::note
|
||||||
You can add the [`<NuxtLoadingIndicator>`](/docs/api/components/nuxt-loading-indicator) to add a progress bar between page navigations.
|
You can add the [`<NuxtLoadingIndicator>`](/docs/api/components/nuxt-loading-indicator) to add a progress bar between page navigations.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
## `$fetch`
|
||||||
|
|
||||||
|
Nuxt includes the [ofetch](https://github.com/unjs/ofetch) library, and is auto-imported as the `$fetch` alias globally across your application.
|
||||||
|
|
||||||
|
```vue twoslash [pages/todos.vue]
|
||||||
|
<script setup lang="ts">
|
||||||
|
async function addTodo() {
|
||||||
|
const todo = await $fetch('/api/todos', {
|
||||||
|
method: 'POST',
|
||||||
|
body: {
|
||||||
|
// My todo data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
::warning
|
||||||
|
Beware that using only `$fetch` will not provide [network calls de-duplication and navigation prevention](#the-need-for-usefetch-and-useasyncdata). :br
|
||||||
|
It is recommended to use `$fetch` for client-side interactions (event based) or combined with [`useAsyncData`](#useasyncdata) when fetching the initial component data.
|
||||||
|
::
|
||||||
|
|
||||||
|
::read-more{to="/docs/api/utils/dollarfetch"}
|
||||||
|
Read more about `$fetch`.
|
||||||
|
::
|
||||||
|
|
||||||
## `useFetch`
|
## `useFetch`
|
||||||
|
|
||||||
The [`useFetch`](/docs/api/composables/use-fetch) composable is the most straightforward way to perform data fetching.
|
The [`useFetch`](/docs/api/composables/use-fetch) composable uses `$fetch` under-the-hood to make SSR-safe network calls in the setup function.
|
||||||
|
|
||||||
```vue twoslash [app.vue]
|
```vue twoslash [app.vue]
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -62,32 +112,6 @@ Watch the video from Alexander Lichter to avoid using `useFetch` the wrong way!
|
|||||||
|
|
||||||
:link-example{to="/docs/examples/features/data-fetching"}
|
:link-example{to="/docs/examples/features/data-fetching"}
|
||||||
|
|
||||||
## `$fetch`
|
|
||||||
|
|
||||||
Nuxt includes the [ofetch](https://github.com/unjs/ofetch) library, and is auto-imported as the `$fetch` alias globally across your application. It's what `useFetch` uses behind the scenes.
|
|
||||||
|
|
||||||
```vue twoslash [pages/todos.vue]
|
|
||||||
<script setup lang="ts">
|
|
||||||
async function addTodo() {
|
|
||||||
const todo = await $fetch('/api/todos', {
|
|
||||||
method: 'POST',
|
|
||||||
body: {
|
|
||||||
// My todo data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
::warning
|
|
||||||
Beware that using only `$fetch` will not provide [network calls de-duplication and navigation prevention](#why-use-specific-composables-for-data-fetching). :br
|
|
||||||
It is recommended to use `$fetch` for client-side interactions (event based) or combined with [`useAsyncData`](#useasyncdata) when fetching the initial component data.
|
|
||||||
::
|
|
||||||
|
|
||||||
::read-more{to="/docs/api/utils/dollarfetch"}
|
|
||||||
Read more about `$fetch`.
|
|
||||||
::
|
|
||||||
|
|
||||||
## `useAsyncData`
|
## `useAsyncData`
|
||||||
|
|
||||||
The `useAsyncData` composable is responsible for wrapping async logic and returning the result once it is resolved.
|
The `useAsyncData` composable is responsible for wrapping async logic and returning the result once it is resolved.
|
||||||
|
@ -11,18 +11,41 @@ By default, Nuxt uses **universal rendering** to provide better user experience,
|
|||||||
|
|
||||||
## Universal Rendering
|
## Universal Rendering
|
||||||
|
|
||||||
When the browser requests a URL with universal (server-side + client-side) rendering enabled, the server returns a fully rendered HTML page to the browser. Whether the page has been generated in advance and cached or is rendered on the fly, at some point, Nuxt has run the JavaScript (Vue.js) code in a server environment, producing an HTML document. Users immediately get the content of our application, contrary to client-side rendering. This step is similar to traditional **server-side rendering** performed by PHP or Ruby applications.
|
This step is similar to traditional **server-side rendering** performed by PHP or Ruby applications. When the browser requests a URL with universal rendering enabled, Nuxt runs the JavaScript (Vue.js) code in a server environment and returns a fully rendered HTML page to the browser. Nuxt may also return a fully rendered HTML page from a cache if the page was generated in advance. Users immediately get the entirety of the initial content of the application, contrary to client-side rendering.
|
||||||
|
|
||||||
To not lose the benefits of the client-side rendering method, such as dynamic interfaces and pages transitions, the Client (browser) loads the JavaScript code that runs on the Server in the background once the HTML document has been downloaded. The browser interprets it again (hence **Universal rendering**) and Vue.js takes control of the document and enables interactivity.
|
Once the HTML document has been downloaded, the browser interprets this and Vue.js takes control of the document. The same JavaScript code that once ran on the server runs on the client (browser) **again** in the background now enabling interactivity (hence **Universal rendering**) by binding its listeners to the HTML. This is called **Hydration**. When hydration is complete, the page can enjoy benefits such as dynamic interfaces and page transitions.
|
||||||
|
|
||||||
Making a static page interactive in the browser is called "Hydration".
|
|
||||||
|
|
||||||
Universal rendering allows a Nuxt application to provide quick page load times while preserving the benefits of client-side rendering. Furthermore, as the content is already present in the HTML document, crawlers can index it without overhead.
|
Universal rendering allows a Nuxt application to provide quick page load times while preserving the benefits of client-side rendering. Furthermore, as the content is already present in the HTML document, crawlers can index it without overhead.
|
||||||
|
|
||||||
![Users can access the static content when the HTML document is loaded. Hydration then allows page's interactivity](/assets/docs/concepts/rendering/ssr.svg)
|
![Users can access the static content when the HTML document is loaded. Hydration then allows page's interactivity](/assets/docs/concepts/rendering/ssr.svg)
|
||||||
|
|
||||||
|
**What's server-rendered and what's client-rendered?**
|
||||||
|
|
||||||
|
It is normal to ask which parts of a Vue file runs on the server and/or the client in universal rendering mode.
|
||||||
|
|
||||||
|
```vue [app.vue]
|
||||||
|
<script setup lang="ts">
|
||||||
|
const counter = ref(0); // executes in server and client environments
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
counter.value++; // executes only in a client environment
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<p>Count: {{ counter }}</p>
|
||||||
|
<button @click="handleClick">Increment</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
On the initial request, the `counter` ref is initialized in the server since it is rendered inside the `<p>` tag. The contents of `handleClick` is never executed here. During hydration in the browser, the `counter` ref is re-initialized. The `handleClick` finally binds itself to the button; Therefore it is reasonable to deduce that the body of `handleClick` will always run in a browser environment.
|
||||||
|
|
||||||
|
[Middlewares](/docs/guide/directory-structure/middleware) and [pages](/docs/guide/directory-structure/pages) run in the server and on the client during hydration. [Plugins](/docs/guide/directory-structure/plugins) can be rendered on the server or client or both. [Components](/docs/guide/directory-structure/components) can be forced to run on the client only as well. [Composables](/docs/guide/directory-structure/composables) and [utilities](/docs/guide/directory-structure/utils) are rendered based on the context of their usage.
|
||||||
|
|
||||||
**Benefits of server-side rendering:**
|
**Benefits of server-side rendering:**
|
||||||
- **Performance**: Users can get immediate access to the page's content because browsers can display static content much faster than JavaScript-generated content. At the same time, Nuxt preserves the interactivity of a web application when the hydration process happens.
|
- **Performance**: Users can get immediate access to the page's content because browsers can display static content much faster than JavaScript-generated content. At the same time, Nuxt preserves the interactivity of a web application during the hydration process.
|
||||||
- **Search Engine Optimization**: Universal rendering delivers the entire HTML content of the page to the browser as a classic server application. Web crawlers can directly index the page's content, which makes Universal rendering a great choice for any content that you want to index quickly.
|
- **Search Engine Optimization**: Universal rendering delivers the entire HTML content of the page to the browser as a classic server application. Web crawlers can directly index the page's content, which makes Universal rendering a great choice for any content that you want to index quickly.
|
||||||
|
|
||||||
**Downsides of server-side rendering:**
|
**Downsides of server-side rendering:**
|
||||||
|
@ -8,9 +8,9 @@ navigation.icon: i-ph-folder
|
|||||||
Nuxt automatically imports any components in this directory (along with components that are registered by any modules you may be using).
|
Nuxt automatically imports any components in this directory (along with components that are registered by any modules you may be using).
|
||||||
|
|
||||||
```bash [Directory Structure]
|
```bash [Directory Structure]
|
||||||
| components/
|
-| components/
|
||||||
--| AppHeader.vue
|
---| AppHeader.vue
|
||||||
--| AppFooter.vue
|
---| AppFooter.vue
|
||||||
```
|
```
|
||||||
|
|
||||||
```html [app.vue]
|
```html [app.vue]
|
||||||
@ -28,10 +28,10 @@ Nuxt automatically imports any components in this directory (along with componen
|
|||||||
If you have a component in nested directories such as:
|
If you have a component in nested directories such as:
|
||||||
|
|
||||||
```bash [Directory Structure]
|
```bash [Directory Structure]
|
||||||
| components/
|
-| components/
|
||||||
--| base/
|
---| base/
|
||||||
----| foo/
|
-----| foo/
|
||||||
------| Button.vue
|
-------| Button.vue
|
||||||
```
|
```
|
||||||
|
|
||||||
... then the component's name will be based on its own path directory and filename, with duplicate segments being removed. Therefore, the component's name will be:
|
... then the component's name will be based on its own path directory and filename, with duplicate segments being removed. Therefore, the component's name will be:
|
||||||
@ -285,8 +285,8 @@ export default defineNuxtConfig({
|
|||||||
Now you can register server-only components with the `.server` suffix and use them anywhere in your application automatically.
|
Now you can register server-only components with the `.server` suffix and use them anywhere in your application automatically.
|
||||||
|
|
||||||
```bash [Directory Structure]
|
```bash [Directory Structure]
|
||||||
| components/
|
-| components/
|
||||||
--| HighlightedMarkdown.server.vue
|
---| HighlightedMarkdown.server.vue
|
||||||
```
|
```
|
||||||
|
|
||||||
```vue [pages/example.vue]
|
```vue [pages/example.vue]
|
||||||
@ -359,9 +359,9 @@ Slots can be interactive and are wrapped within a `<div>` with `display: content
|
|||||||
In this case, the `.server` + `.client` components are two 'halves' of a component and can be used in advanced use cases for separate implementations of a component on server and client side.
|
In this case, the `.server` + `.client` components are two 'halves' of a component and can be used in advanced use cases for separate implementations of a component on server and client side.
|
||||||
|
|
||||||
```bash [Directory Structure]
|
```bash [Directory Structure]
|
||||||
| components/
|
-| components/
|
||||||
--| Comments.client.vue
|
---| Comments.client.vue
|
||||||
--| Comments.server.vue
|
---| Comments.server.vue
|
||||||
```
|
```
|
||||||
|
|
||||||
```vue [pages/example.vue]
|
```vue [pages/example.vue]
|
||||||
@ -389,15 +389,15 @@ You can use the `components:dirs` hook to extend the directory list without requ
|
|||||||
Imagine a directory structure like this:
|
Imagine a directory structure like this:
|
||||||
|
|
||||||
```bash [Directory Structure]
|
```bash [Directory Structure]
|
||||||
| node_modules/
|
-| node_modules/
|
||||||
---| awesome-ui/
|
---| awesome-ui/
|
||||||
------| components/
|
-----| components/
|
||||||
---------| Alert.vue
|
-------| Alert.vue
|
||||||
---------| Button.vue
|
-------| Button.vue
|
||||||
------| nuxt.js
|
-----| nuxt.js
|
||||||
| pages/
|
-| pages/
|
||||||
---| index.vue
|
---| index.vue
|
||||||
| nuxt.config.js
|
-| nuxt.config.js
|
||||||
```
|
```
|
||||||
|
|
||||||
Then in `awesome-ui/nuxt.js` you can use the `components:dirs` hook:
|
Then in `awesome-ui/nuxt.js` you can use the `components:dirs` hook:
|
||||||
|
@ -85,11 +85,11 @@ export const useHello = () => {
|
|||||||
Nuxt only scans files at the top level of the [`composables/` directory](/docs/guide/directory-structure/composables), e.g.:
|
Nuxt only scans files at the top level of the [`composables/` directory](/docs/guide/directory-structure/composables), e.g.:
|
||||||
|
|
||||||
```bash [Directory Structure]
|
```bash [Directory Structure]
|
||||||
| composables/
|
-| composables/
|
||||||
---| index.ts // scanned
|
---| index.ts // scanned
|
||||||
---| useFoo.ts // scanned
|
---| useFoo.ts // scanned
|
||||||
-----| nested/
|
---| nested/
|
||||||
-------| utils.ts // not scanned
|
-----| utils.ts // not scanned
|
||||||
```
|
```
|
||||||
|
|
||||||
Only `composables/index.ts` and `composables/useFoo.ts` would be searched for imports.
|
Only `composables/index.ts` and `composables/useFoo.ts` would be searched for imports.
|
||||||
|
@ -72,11 +72,11 @@ Middleware runs in the following order:
|
|||||||
|
|
||||||
For example, assuming you have the following middleware and component:
|
For example, assuming you have the following middleware and component:
|
||||||
|
|
||||||
```text [middleware/ directory]
|
```bash [middleware/ directory]
|
||||||
middleware/
|
-| middleware/
|
||||||
--| analytics.global.ts
|
---| analytics.global.ts
|
||||||
--| setup.global.ts
|
---| setup.global.ts
|
||||||
--| auth.ts
|
---| auth.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
```vue twoslash [pages/profile.vue]
|
```vue twoslash [pages/profile.vue]
|
||||||
@ -105,11 +105,11 @@ By default, global middleware is executed alphabetically based on the filename.
|
|||||||
|
|
||||||
However, there may be times you want to define a specific order. For example, in the last scenario, `setup.global.ts` may need to run before `analytics.global.ts`. In that case, we recommend prefixing global middleware with 'alphabetical' numbering.
|
However, there may be times you want to define a specific order. For example, in the last scenario, `setup.global.ts` may need to run before `analytics.global.ts`. In that case, we recommend prefixing global middleware with 'alphabetical' numbering.
|
||||||
|
|
||||||
```text [Directory structure]
|
```bash [Directory structure]
|
||||||
middleware/
|
-| middleware/
|
||||||
--| 01.setup.global.ts
|
---| 01.setup.global.ts
|
||||||
--| 02.analytics.global.ts
|
---| 02.analytics.global.ts
|
||||||
--| auth.ts
|
---| auth.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
::note
|
::note
|
||||||
|
@ -159,7 +159,7 @@ Example:
|
|||||||
```bash [Directory Structure]
|
```bash [Directory Structure]
|
||||||
-| pages/
|
-| pages/
|
||||||
---| parent/
|
---| parent/
|
||||||
------| child.vue
|
-----| child.vue
|
||||||
---| parent.vue
|
---| parent.vue
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -408,7 +408,7 @@ However, you can use [Nuxt Layers](/docs/getting-started/layers) to create group
|
|||||||
```bash [Directory Structure]
|
```bash [Directory Structure]
|
||||||
-| some-app/
|
-| some-app/
|
||||||
---| nuxt.config.ts
|
---| nuxt.config.ts
|
||||||
---| pages
|
---| pages/
|
||||||
-----| app-page.vue
|
-----| app-page.vue
|
||||||
-| nuxt.config.ts
|
-| nuxt.config.ts
|
||||||
```
|
```
|
||||||
|
@ -108,7 +108,7 @@ In case you're new to 'alphabetical' numbering, remember that filenames are sort
|
|||||||
|
|
||||||
### Parallel Plugins
|
### Parallel Plugins
|
||||||
|
|
||||||
By default, Nuxt loads plugins sequentially. You can define a plugin as `parallel` so Nuxt won't wait the end of the plugin's execution before loading the next plugin.
|
By default, Nuxt loads plugins sequentially. You can define a plugin as `parallel` so Nuxt won't wait until the end of the plugin's execution before loading the next plugin.
|
||||||
|
|
||||||
```ts twoslash [plugins/my-plugin.ts]
|
```ts twoslash [plugins/my-plugin.ts]
|
||||||
export default defineNuxtPlugin({
|
export default defineNuxtPlugin({
|
||||||
|
@ -19,6 +19,10 @@ export default defineAppConfig({
|
|||||||
Do not put any secret values inside `app.config` file. It is exposed to the user client bundle.
|
Do not put any secret values inside `app.config` file. It is exposed to the user client bundle.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
::note
|
||||||
|
When configuring a custom [`srcDir`](/docs/api/nuxt-config#srcdir), make sure to place the `app.config` file at the root of the new `srcDir` path.
|
||||||
|
::
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
To expose config and environment variables to the rest of your app, you will need to define configuration in `app.config` file.
|
To expose config and environment variables to the rest of your app, you will need to define configuration in `app.config` file.
|
||||||
@ -31,7 +35,7 @@ export default defineAppConfig({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
When adding `theme` to the `app.config`, Nuxt uses Vite or webpack to bundle the code. We can universally access `theme` both when server-rendering the page and in the browser using [`useAppConfig`](/docs/api/composables/use-app-config) composable.
|
We can now universally access `theme` both when server-rendering the page and in the browser using [`useAppConfig`](/docs/api/composables/use-app-config) composable.
|
||||||
|
|
||||||
```vue [pages/index.vue]
|
```vue [pages/index.vue]
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -41,7 +45,23 @@ console.log(appConfig.theme)
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
When configuring a custom [`srcDir`](/docs/api/nuxt-config#srcdir), make sure to place the `app.config` file at the root of the new `srcDir` path.
|
The [`updateAppConfig`](/docs/api/utils/update-app-config) utility can be used to update the `app.config` at runtime.
|
||||||
|
|
||||||
|
```vue [pages/index.vue]
|
||||||
|
<script setup>
|
||||||
|
const appConfig = useAppConfig() // { foo: 'bar' }
|
||||||
|
|
||||||
|
const newAppConfig = { foo: 'baz' }
|
||||||
|
|
||||||
|
updateAppConfig(newAppConfig)
|
||||||
|
|
||||||
|
console.log(appConfig) // { foo: 'baz' }
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
::read-more{to="/docs/api/utils/update-app-config"}
|
||||||
|
Read more about the `updateAppConfig` utility.
|
||||||
|
::
|
||||||
|
|
||||||
## Typing App Config
|
## Typing App Config
|
||||||
|
|
||||||
|
@ -334,6 +334,8 @@ 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.
|
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.
|
||||||
|
|
||||||
|
It is also possible to scan page metadata only after all routes have been registered in `pages:extend`. Then another hook, `pages:resolved` will be called. To enable this behavior, set `scanPageMeta: 'after-resolve'`.
|
||||||
|
|
||||||
You can disable this feature if it causes issues in your project.
|
You can disable this feature if it causes issues in your project.
|
||||||
|
|
||||||
```ts twoslash [nuxt.config.ts]
|
```ts twoslash [nuxt.config.ts]
|
||||||
|
@ -61,6 +61,7 @@ export default defineNuxtConfig({
|
|||||||
app: 'app'
|
app: 'app'
|
||||||
},
|
},
|
||||||
experimental: {
|
experimental: {
|
||||||
|
scanPageMeta: 'after-resolve',
|
||||||
sharedPrerenderData: false,
|
sharedPrerenderData: false,
|
||||||
compileTemplate: true,
|
compileTemplate: true,
|
||||||
resetAsyncDataToUndefined: true,
|
resetAsyncDataToUndefined: true,
|
||||||
|
@ -31,14 +31,8 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|||||||
baseURL: 'https://api.nuxt.com',
|
baseURL: 'https://api.nuxt.com',
|
||||||
onRequest({ request, options, error }) {
|
onRequest({ request, options, error }) {
|
||||||
if (session.value?.token) {
|
if (session.value?.token) {
|
||||||
const headers = options.headers ||= {}
|
// note that this relies on ofetch >= 1.4.0 - you may need to refresh your lockfile
|
||||||
if (Array.isArray(headers)) {
|
options.headers.set('Authorization', `Bearer ${session.value?.token}`)
|
||||||
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 }) {
|
async onResponseError({ response }) {
|
||||||
@ -96,6 +90,28 @@ const { data: modules } = await useAPI('/modules')
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you want to customize the type of any error returned, you can also do so:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import type { FetchError } from 'ofetch'
|
||||||
|
import type { UseFetchOptions } from 'nuxt/app'
|
||||||
|
|
||||||
|
interface CustomError {
|
||||||
|
message: string
|
||||||
|
statusCode: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAPI<T>(
|
||||||
|
url: string | (() => string),
|
||||||
|
options?: UseFetchOptions<T>,
|
||||||
|
) {
|
||||||
|
return useFetch<T, FetchError<CustomError>>(url, {
|
||||||
|
...options,
|
||||||
|
$fetch: useNuxtApp().$api
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
::note
|
::note
|
||||||
This example demonstrates how to use a custom `useFetch`, but the same structure is identical for a custom `useAsyncData`.
|
This example demonstrates how to use a custom `useFetch`, but the same structure is identical for a custom `useAsyncData`.
|
||||||
::
|
::
|
||||||
|
@ -50,8 +50,8 @@ You can also use [interceptors](https://github.com/unjs/ofetch#%EF%B8%8F-interce
|
|||||||
const { data, status, error, refresh, clear } = await useFetch('/api/auth/login', {
|
const { data, status, error, refresh, clear } = await useFetch('/api/auth/login', {
|
||||||
onRequest({ request, options }) {
|
onRequest({ request, options }) {
|
||||||
// Set the request headers
|
// Set the request headers
|
||||||
options.headers = options.headers || {}
|
// note that this relies on ofetch >= 1.4.0 - you may need to refresh your lockfile
|
||||||
options.headers.authorization = '...'
|
options.headers.set('Authorization', '...')
|
||||||
},
|
},
|
||||||
onRequestError({ request, options, error }) {
|
onRequestError({ request, options, error }) {
|
||||||
// Handle the request errors
|
// Handle the request errors
|
||||||
|
48
docs/3.api/2.composables/use-response-header.md
Normal file
48
docs/3.api/2.composables/use-response-header.md
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
title: "useResponseHeader"
|
||||||
|
description: "Use useResponseHeader to set a server response header."
|
||||||
|
links:
|
||||||
|
- label: Source
|
||||||
|
icon: i-simple-icons-github
|
||||||
|
to: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/ssr.ts
|
||||||
|
size: xs
|
||||||
|
---
|
||||||
|
|
||||||
|
::important
|
||||||
|
This composable is available in Nuxt v3.14+.
|
||||||
|
::
|
||||||
|
|
||||||
|
You can use the built-in [`useResponseHeader`](/docs/api/composables/use-response-header) composable to set any server response header within your pages, components, and plugins.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Set the a custom response header
|
||||||
|
const header = useResponseHeader('X-My-Header');
|
||||||
|
header.value = 'my-value';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
We can use `useResponseHeader` to easily set a response header on a per-page basis.
|
||||||
|
|
||||||
|
```vue [pages/test.vue]
|
||||||
|
<script setup>
|
||||||
|
// pages/test.vue
|
||||||
|
const header = useResponseHeader('X-My-Header');
|
||||||
|
header.value = 'my-value';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h1>Test page with custom header</h1>
|
||||||
|
<p>The response from the server for this "/test" page will have a custom "X-My-Header" header.</p>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
We can use `useResponseHeader` for example in Nuxt [middleware](/docs/guide/directory-structure/middleware) to set a response header for all pages.
|
||||||
|
|
||||||
|
```ts [middleware/my-header-middleware.ts]
|
||||||
|
export default defineNuxtRouteMiddleware((to, from) => {
|
||||||
|
const header = useResponseHeader('X-My-Always-Header');
|
||||||
|
header.value = `I'm Always here!`;
|
||||||
|
});
|
||||||
|
|
||||||
|
```
|
@ -30,6 +30,7 @@ interface PageMeta {
|
|||||||
redirect?: RouteRecordRedirectOption
|
redirect?: RouteRecordRedirectOption
|
||||||
name?: string
|
name?: string
|
||||||
path?: string
|
path?: string
|
||||||
|
props?: RouteRecordRaw['props']
|
||||||
alias?: string | string[]
|
alias?: string | string[]
|
||||||
pageTransition?: boolean | TransitionProps
|
pageTransition?: boolean | TransitionProps
|
||||||
layoutTransition?: boolean | TransitionProps
|
layoutTransition?: boolean | TransitionProps
|
||||||
@ -63,6 +64,12 @@ interface PageMeta {
|
|||||||
|
|
||||||
You may define a [custom regular expression](#using-a-custom-regular-expression) if you have a more complex pattern than can be expressed with the file name.
|
You may define a [custom regular expression](#using-a-custom-regular-expression) if you have a more complex pattern than can be expressed with the file name.
|
||||||
|
|
||||||
|
**`props`**
|
||||||
|
|
||||||
|
- **Type**: [`RouteRecordRaw['props']`](https://router.vuejs.org/guide/essentials/passing-props)
|
||||||
|
|
||||||
|
Allows accessing the route `params` as props passed to the page component.
|
||||||
|
|
||||||
**`alias`**
|
**`alias`**
|
||||||
|
|
||||||
- **Type**: `string | string[]`
|
- **Type**: `string | string[]`
|
||||||
|
@ -125,6 +125,19 @@ Make sure to always use `await` or `return` on result of `navigateTo` when calli
|
|||||||
|
|
||||||
`to` can be a plain string or a route object to redirect to. When passed as `undefined` or `null`, it will default to `'/'`.
|
`to` can be a plain string or a route object to redirect to. When passed as `undefined` or `null`, it will default to `'/'`.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Passing the URL directly will redirect to the '/blog' page
|
||||||
|
await navigateTo('/blog')
|
||||||
|
|
||||||
|
// Using the route object, will redirect to the route with the name 'blog'
|
||||||
|
await navigateTo({ name: 'blog' })
|
||||||
|
|
||||||
|
// Redirects to the 'product' route while passing a parameter (id = 1) using the route object.
|
||||||
|
await navigateTo({ name: 'product', params: { id: 1 } })
|
||||||
|
```
|
||||||
|
|
||||||
### `options` (optional)
|
### `options` (optional)
|
||||||
|
|
||||||
**Type**: `NavigateToOptions`
|
**Type**: `NavigateToOptions`
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
import { addPluginTemplate, addRouteMiddleware } from 'nuxt/kit'
|
import { addPluginTemplate, addRouteMiddleware } from 'nuxt/kit'
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
typescript: { shim: process.env.DOCS_TYPECHECK === 'true' },
|
|
||||||
pages: process.env.DOCS_TYPECHECK === 'true',
|
|
||||||
modules: [
|
modules: [
|
||||||
function () {
|
function () {
|
||||||
if (!process.env.DOCS_TYPECHECK) { return }
|
if (!process.env.DOCS_TYPECHECK) { return }
|
||||||
@ -18,4 +16,6 @@ export default defineNuxtConfig({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
pages: process.env.DOCS_TYPECHECK === 'true',
|
||||||
|
typescript: { shim: process.env.DOCS_TYPECHECK === 'true' },
|
||||||
})
|
})
|
||||||
|
68
package.json
68
package.json
@ -35,44 +35,46 @@
|
|||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@nuxt/kit": "workspace:*",
|
"@nuxt/kit": "workspace:*",
|
||||||
|
"@nuxt/rspack-builder": "workspace:*",
|
||||||
"@nuxt/schema": "workspace:*",
|
"@nuxt/schema": "workspace:*",
|
||||||
"@nuxt/ui-templates": "workspace:*",
|
"@nuxt/ui-templates": "workspace:*",
|
||||||
"@nuxt/vite-builder": "workspace:*",
|
"@nuxt/vite-builder": "workspace:*",
|
||||||
"@nuxt/webpack-builder": "workspace:*",
|
"@nuxt/webpack-builder": "workspace:*",
|
||||||
"@types/node": "20.16.10",
|
"@types/node": "20.17.0",
|
||||||
"@vue/compiler-core": "3.5.10",
|
"@vue/compiler-core": "3.5.12",
|
||||||
"@vue/compiler-dom": "3.5.10",
|
"@vue/compiler-dom": "3.5.12",
|
||||||
"@vue/shared": "3.5.10",
|
"@vue/shared": "3.5.12",
|
||||||
"c12": "2.0.0-beta.3",
|
"c12": "2.0.1",
|
||||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
"jiti": "2.0.0",
|
"jiti": "2.3.3",
|
||||||
"magic-string": "^0.30.11",
|
"magic-string": "^0.30.12",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||||
"nuxt": "workspace:*",
|
"nuxt": "workspace:*",
|
||||||
"ohash": "1.1.4",
|
"ohash": "1.1.4",
|
||||||
"postcss": "8.4.47",
|
"postcss": "8.4.47",
|
||||||
"rollup": "4.22.5",
|
"rollup": "4.24.0",
|
||||||
"send": ">=0.19.0",
|
"send": ">=1.1.0",
|
||||||
"typescript": "5.6.2",
|
"typescript": "5.6.3",
|
||||||
"ufo": "1.5.4",
|
"ufo": "1.5.4",
|
||||||
"unbuild": "3.0.0-rc.8",
|
"unbuild": "3.0.0-rc.11",
|
||||||
"vite": "5.4.8",
|
"vite": "5.4.10",
|
||||||
"vue": "3.5.10"
|
"vue": "3.5.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "9.11.1",
|
"@eslint/js": "9.13.0",
|
||||||
"@nuxt/eslint-config": "0.5.7",
|
"@nuxt/eslint-config": "0.6.0",
|
||||||
"@nuxt/kit": "workspace:*",
|
"@nuxt/kit": "workspace:*",
|
||||||
"@nuxt/test-utils": "3.14.2",
|
"@nuxt/rspack-builder": "workspace:*",
|
||||||
|
"@nuxt/test-utils": "3.14.4",
|
||||||
"@nuxt/webpack-builder": "workspace:*",
|
"@nuxt/webpack-builder": "workspace:*",
|
||||||
"@testing-library/vue": "8.1.0",
|
"@testing-library/vue": "8.1.0",
|
||||||
"@types/eslint__js": "8.42.3",
|
"@types/eslint__js": "8.42.3",
|
||||||
"@types/node": "20.16.10",
|
"@types/node": "20.17.0",
|
||||||
"@types/semver": "7.5.8",
|
"@types/semver": "7.5.8",
|
||||||
"@unhead/schema": "1.11.6",
|
"@unhead/schema": "1.11.10",
|
||||||
"@unhead/vue": "1.11.6",
|
"@unhead/vue": "1.11.10",
|
||||||
"@vitejs/plugin-vue": "5.1.4",
|
"@vitejs/plugin-vue": "5.1.4",
|
||||||
"@vitest/coverage-v8": "2.1.1",
|
"@vitest/coverage-v8": "2.1.3",
|
||||||
"@vue/test-utils": "2.4.6",
|
"@vue/test-utils": "2.4.6",
|
||||||
"autoprefixer": "10.4.20",
|
"autoprefixer": "10.4.20",
|
||||||
"case-police": "0.7.0",
|
"case-police": "0.7.0",
|
||||||
@ -81,36 +83,36 @@
|
|||||||
"cssnano": "7.0.6",
|
"cssnano": "7.0.6",
|
||||||
"destr": "2.0.3",
|
"destr": "2.0.3",
|
||||||
"devalue": "5.1.1",
|
"devalue": "5.1.1",
|
||||||
"eslint": "9.11.1",
|
"eslint": "9.13.0",
|
||||||
"eslint-plugin-no-only-tests": "3.3.0",
|
"eslint-plugin-no-only-tests": "3.3.0",
|
||||||
"eslint-plugin-perfectionist": "3.7.0",
|
"eslint-plugin-perfectionist": "3.9.1",
|
||||||
"eslint-typegen": "0.3.2",
|
"eslint-typegen": "0.3.2",
|
||||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
"happy-dom": "15.7.4",
|
"happy-dom": "15.7.4",
|
||||||
"jiti": "2.0.0",
|
"jiti": "2.3.3",
|
||||||
"markdownlint-cli": "0.42.0",
|
"markdownlint-cli": "0.42.0",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||||
"nuxi": "3.14.0",
|
"nuxi": "3.15.0",
|
||||||
"nuxt": "workspace:*",
|
"nuxt": "workspace:*",
|
||||||
"nuxt-content-twoslash": "0.1.1",
|
"nuxt-content-twoslash": "0.1.1",
|
||||||
"ofetch": "1.4.0",
|
"ofetch": "1.4.1",
|
||||||
"pathe": "1.1.2",
|
"pathe": "1.1.2",
|
||||||
"playwright-core": "1.47.2",
|
"playwright-core": "1.48.1",
|
||||||
"rimraf": "6.0.1",
|
"rimraf": "6.0.1",
|
||||||
"semver": "7.6.3",
|
"semver": "7.6.3",
|
||||||
"sherif": "1.0.0",
|
"sherif": "1.0.1",
|
||||||
"std-env": "3.7.0",
|
"std-env": "3.7.0",
|
||||||
"tinyexec": "0.3.0",
|
"tinyexec": "0.3.1",
|
||||||
"tinyglobby": "0.2.6",
|
"tinyglobby": "0.2.9",
|
||||||
"typescript": "5.6.2",
|
"typescript": "5.6.3",
|
||||||
"ufo": "1.5.4",
|
"ufo": "1.5.4",
|
||||||
"vitest": "2.1.1",
|
"vitest": "2.1.3",
|
||||||
"vitest-environment-nuxt": "1.0.1",
|
"vitest-environment-nuxt": "1.0.1",
|
||||||
"vue": "3.5.10",
|
"vue": "3.5.12",
|
||||||
"vue-router": "4.4.5",
|
"vue-router": "4.4.5",
|
||||||
"vue-tsc": "2.1.6"
|
"vue-tsc": "2.1.6"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.11.0",
|
"packageManager": "pnpm@9.12.2",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.10.0 || >=18.0.0"
|
"node": "^16.10.0 || >=18.0.0"
|
||||||
},
|
},
|
||||||
|
@ -6,6 +6,7 @@ export default defineBuildConfig({
|
|||||||
'src/index',
|
'src/index',
|
||||||
],
|
],
|
||||||
externals: [
|
externals: [
|
||||||
|
'@rspack/core',
|
||||||
'@nuxt/schema',
|
'@nuxt/schema',
|
||||||
'nitropack',
|
'nitropack',
|
||||||
'nitro',
|
'nitro',
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/schema": "workspace:*",
|
"@nuxt/schema": "workspace:*",
|
||||||
"c12": "^2.0.0-beta.3",
|
"c12": "^2.0.1",
|
||||||
"consola": "^3.2.3",
|
"consola": "^3.2.3",
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
"destr": "^2.0.3",
|
"destr": "^2.0.3",
|
||||||
@ -35,25 +35,26 @@
|
|||||||
"globby": "^14.0.2",
|
"globby": "^14.0.2",
|
||||||
"hash-sum": "^2.0.0",
|
"hash-sum": "^2.0.0",
|
||||||
"ignore": "^6.0.2",
|
"ignore": "^6.0.2",
|
||||||
"jiti": "^2.0.0",
|
"jiti": "^2.3.3",
|
||||||
"klona": "^2.0.6",
|
"klona": "^2.0.6",
|
||||||
"mlly": "^1.7.1",
|
"mlly": "^1.7.2",
|
||||||
"pathe": "^1.1.2",
|
"pathe": "^1.1.2",
|
||||||
"pkg-types": "^1.2.0",
|
"pkg-types": "^1.2.1",
|
||||||
"scule": "^1.3.0",
|
"scule": "^1.3.0",
|
||||||
"semver": "^7.6.3",
|
"semver": "^7.6.3",
|
||||||
"ufo": "^1.5.4",
|
"ufo": "^1.5.4",
|
||||||
"unctx": "^2.3.1",
|
"unctx": "^2.3.1",
|
||||||
"unimport": "^3.13.1",
|
"unimport": "^3.13.1",
|
||||||
"untyped": "^1.5.0"
|
"untyped": "^1.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@rspack/core": "1.0.14",
|
||||||
"@types/hash-sum": "1.0.2",
|
"@types/hash-sum": "1.0.2",
|
||||||
"@types/semver": "7.5.8",
|
"@types/semver": "7.5.8",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||||
"unbuild": "3.0.0-rc.8",
|
"unbuild": "3.0.0-rc.11",
|
||||||
"vite": "5.4.8",
|
"vite": "5.4.10",
|
||||||
"vitest": "2.1.1",
|
"vitest": "2.1.3",
|
||||||
"webpack": "5.95.0"
|
"webpack": "5.95.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import type { Configuration as WebpackConfig, WebpackPluginInstance } from 'webpack'
|
import type { Configuration as WebpackConfig, WebpackPluginInstance } from 'webpack'
|
||||||
|
import type { RspackPluginInstance } from '@rspack/core'
|
||||||
import type { UserConfig as ViteConfig, Plugin as VitePlugin } from 'vite'
|
import type { UserConfig as ViteConfig, Plugin as VitePlugin } from 'vite'
|
||||||
import { useNuxt } from './context'
|
import { useNuxt } from './context'
|
||||||
import { toArray } from './utils'
|
import { toArray } from './utils'
|
||||||
@ -36,16 +37,7 @@ export interface ExtendWebpackConfigOptions extends ExtendConfigOptions {}
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||||
export interface ExtendViteConfigOptions extends ExtendConfigOptions {}
|
export interface ExtendViteConfigOptions extends ExtendConfigOptions {}
|
||||||
|
|
||||||
/**
|
const extendWebpackCompatibleConfig = (builder: 'rspack' | 'webpack') => (fn: ((config: WebpackConfig) => void), options: ExtendWebpackConfigOptions = {}) => {
|
||||||
* Extend webpack config
|
|
||||||
*
|
|
||||||
* The fallback function might be called multiple times
|
|
||||||
* when applying to both client and server builds.
|
|
||||||
*/
|
|
||||||
export function extendWebpackConfig (
|
|
||||||
fn: ((config: WebpackConfig) => void),
|
|
||||||
options: ExtendWebpackConfigOptions = {},
|
|
||||||
) {
|
|
||||||
const nuxt = useNuxt()
|
const nuxt = useNuxt()
|
||||||
|
|
||||||
if (options.dev === false && nuxt.options.dev) {
|
if (options.dev === false && nuxt.options.dev) {
|
||||||
@ -55,7 +47,7 @@ export function extendWebpackConfig (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nuxt.hook('webpack:config', (configs: WebpackConfig[]) => {
|
nuxt.hook(`${builder}:config`, (configs) => {
|
||||||
if (options.server !== false) {
|
if (options.server !== false) {
|
||||||
const config = configs.find(i => i.name === 'server')
|
const config = configs.find(i => i.name === 'server')
|
||||||
if (config) {
|
if (config) {
|
||||||
@ -71,13 +63,25 @@ export function extendWebpackConfig (
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend webpack config
|
||||||
|
*
|
||||||
|
* The fallback function might be called multiple times
|
||||||
|
* when applying to both client and server builds.
|
||||||
|
*/
|
||||||
|
export const extendWebpackConfig = extendWebpackCompatibleConfig('webpack')
|
||||||
|
/**
|
||||||
|
* Extend rspack config
|
||||||
|
*
|
||||||
|
* The fallback function might be called multiple times
|
||||||
|
* when applying to both client and server builds.
|
||||||
|
*/
|
||||||
|
export const extendRspackConfig = extendWebpackCompatibleConfig('rspack')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend Vite config
|
* Extend Vite config
|
||||||
*/
|
*/
|
||||||
export function extendViteConfig (
|
export function extendViteConfig (fn: ((config: ViteConfig) => void), options: ExtendViteConfigOptions = {}) {
|
||||||
fn: ((config: ViteConfig) => void),
|
|
||||||
options: ExtendViteConfigOptions = {},
|
|
||||||
) {
|
|
||||||
const nuxt = useNuxt()
|
const nuxt = useNuxt()
|
||||||
|
|
||||||
if (options.dev === false && nuxt.options.dev) {
|
if (options.dev === false && nuxt.options.dev) {
|
||||||
@ -114,6 +118,18 @@ export function addWebpackPlugin (pluginOrGetter: WebpackPluginInstance | Webpac
|
|||||||
config.plugins[method](...toArray(plugin))
|
config.plugins[method](...toArray(plugin))
|
||||||
}, options)
|
}, options)
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Append rspack plugin to the config.
|
||||||
|
*/
|
||||||
|
export function addRspackPlugin (pluginOrGetter: RspackPluginInstance | RspackPluginInstance[] | (() => RspackPluginInstance | RspackPluginInstance[]), options?: ExtendWebpackConfigOptions) {
|
||||||
|
extendRspackConfig((config) => {
|
||||||
|
const method: 'push' | 'unshift' = options?.prepend ? 'unshift' : 'push'
|
||||||
|
const plugin = typeof pluginOrGetter === 'function' ? pluginOrGetter() : pluginOrGetter
|
||||||
|
|
||||||
|
config.plugins = config.plugins || []
|
||||||
|
config.plugins[method](...toArray(plugin))
|
||||||
|
}, options)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append Vite plugin to the config.
|
* Append Vite plugin to the config.
|
||||||
@ -131,6 +147,7 @@ export function addVitePlugin (pluginOrGetter: VitePlugin | VitePlugin[] | (() =
|
|||||||
interface AddBuildPluginFactory {
|
interface AddBuildPluginFactory {
|
||||||
vite?: () => VitePlugin | VitePlugin[]
|
vite?: () => VitePlugin | VitePlugin[]
|
||||||
webpack?: () => WebpackPluginInstance | WebpackPluginInstance[]
|
webpack?: () => WebpackPluginInstance | WebpackPluginInstance[]
|
||||||
|
rspack?: () => RspackPluginInstance | RspackPluginInstance[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addBuildPlugin (pluginFactory: AddBuildPluginFactory, options?: ExtendConfigOptions) {
|
export function addBuildPlugin (pluginFactory: AddBuildPluginFactory, options?: ExtendConfigOptions) {
|
||||||
@ -141,4 +158,8 @@ export function addBuildPlugin (pluginFactory: AddBuildPluginFactory, options?:
|
|||||||
if (pluginFactory.webpack) {
|
if (pluginFactory.webpack) {
|
||||||
addWebpackPlugin(pluginFactory.webpack, options)
|
addWebpackPlugin(pluginFactory.webpack, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pluginFactory.rspack) {
|
||||||
|
addRspackPlugin(pluginFactory.rspack, options)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,13 @@ import { readPackageJSON } from 'pkg-types'
|
|||||||
import type { Nuxt, NuxtCompatibility, NuxtCompatibilityIssues } from '@nuxt/schema'
|
import type { Nuxt, NuxtCompatibility, NuxtCompatibilityIssues } from '@nuxt/schema'
|
||||||
import { useNuxt } from './context'
|
import { useNuxt } from './context'
|
||||||
|
|
||||||
|
const SEMANTIC_VERSION_RE = /-\d+\.[0-9a-f]+/
|
||||||
export function normalizeSemanticVersion (version: string) {
|
export function normalizeSemanticVersion (version: string) {
|
||||||
return version.replace(/-\d+\.[0-9a-f]+/, '') // Remove edge prefix
|
return version.replace(SEMANTIC_VERSION_RE, '') // Remove edge prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
const builderMap = {
|
const builderMap = {
|
||||||
|
'@nuxt/rspack-builder': 'rspack',
|
||||||
'@nuxt/vite-builder': 'vite',
|
'@nuxt/vite-builder': 'vite',
|
||||||
'@nuxt/webpack-builder': 'webpack',
|
'@nuxt/webpack-builder': 'webpack',
|
||||||
}
|
}
|
||||||
@ -103,6 +105,7 @@ export function isNuxt3 (nuxt: Nuxt = useNuxt()) {
|
|||||||
return isNuxtMajorVersion(3, nuxt)
|
return isNuxtMajorVersion(3, nuxt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NUXT_VERSION_RE = /^v/g
|
||||||
/**
|
/**
|
||||||
* Get nuxt version
|
* Get nuxt version
|
||||||
*/
|
*/
|
||||||
@ -111,5 +114,5 @@ export function getNuxtVersion (nuxt: Nuxt | any = useNuxt() /* TODO: LegacyNuxt
|
|||||||
if (typeof rawVersion !== 'string') {
|
if (typeof rawVersion !== 'string') {
|
||||||
throw new TypeError('Cannot determine nuxt version! Is current instance passed?')
|
throw new TypeError('Cannot determine nuxt version! Is current instance passed?')
|
||||||
}
|
}
|
||||||
return rawVersion.replace(/^v/g, '')
|
return rawVersion.replace(NUXT_VERSION_RE, '')
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import type { Component, ComponentsDir } from '@nuxt/schema'
|
|||||||
import { useNuxt } from './context'
|
import { useNuxt } from './context'
|
||||||
import { assertNuxtCompatibility } from './compatibility'
|
import { assertNuxtCompatibility } from './compatibility'
|
||||||
import { logger } from './logger'
|
import { logger } from './logger'
|
||||||
|
import { MODE_RE } from './utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a directory to be scanned for components and imported only when used.
|
* Register a directory to be scanned for components and imported only when used.
|
||||||
@ -28,7 +29,7 @@ export async function addComponent (opts: AddComponentOptions) {
|
|||||||
nuxt.options.components = nuxt.options.components || []
|
nuxt.options.components = nuxt.options.components || []
|
||||||
|
|
||||||
if (!opts.mode) {
|
if (!opts.mode) {
|
||||||
const [, mode = 'all'] = opts.filePath.match(/\.(server|client)(\.\w+)*$/) || []
|
const [, mode = 'all'] = opts.filePath.match(MODE_RE) || []
|
||||||
opts.mode = mode as 'all' | 'client' | 'server'
|
opts.mode = mode as 'all' | 'client' | 'server'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ export type { LoadNuxtOptions } from './loader/nuxt'
|
|||||||
// Utils
|
// Utils
|
||||||
export { addImports, addImportsDir, addImportsSources } from './imports'
|
export { addImports, addImportsDir, addImportsSources } from './imports'
|
||||||
export { updateRuntimeConfig, useRuntimeConfig } from './runtime-config'
|
export { updateRuntimeConfig, useRuntimeConfig } from './runtime-config'
|
||||||
export { addBuildPlugin, addVitePlugin, addWebpackPlugin, extendViteConfig, extendWebpackConfig } from './build'
|
export { addBuildPlugin, addVitePlugin, addRspackPlugin, addWebpackPlugin, extendViteConfig, extendRspackConfig, extendWebpackConfig } from './build'
|
||||||
export type { ExtendConfigOptions, ExtendViteConfigOptions, ExtendWebpackConfigOptions } from './build'
|
export type { ExtendConfigOptions, ExtendViteConfigOptions, ExtendWebpackConfigOptions } from './build'
|
||||||
export { assertNuxtCompatibility, checkNuxtCompatibility, getNuxtVersion, hasNuxtCompatibility, isNuxtMajorVersion, normalizeSemanticVersion, isNuxt2, isNuxt3 } from './compatibility'
|
export { assertNuxtCompatibility, checkNuxtCompatibility, getNuxtVersion, hasNuxtCompatibility, isNuxtMajorVersion, normalizeSemanticVersion, isNuxt2, isNuxt3 } from './compatibility'
|
||||||
export { addComponent, addComponentsDir } from './components'
|
export { addComponent, addComponentsDir } from './components'
|
||||||
|
@ -5,10 +5,11 @@ import { useNuxt } from './context'
|
|||||||
import { logger } from './logger'
|
import { logger } from './logger'
|
||||||
import { addTemplate } from './template'
|
import { addTemplate } from './template'
|
||||||
|
|
||||||
|
const LAYOUT_RE = /["']/g
|
||||||
export function addLayout (template: NuxtTemplate | string, name?: string) {
|
export function addLayout (template: NuxtTemplate | string, name?: string) {
|
||||||
const nuxt = useNuxt()
|
const nuxt = useNuxt()
|
||||||
const { filename, src } = addTemplate(template)
|
const { filename, src } = addTemplate(template)
|
||||||
const layoutName = kebabCase(name || parse(filename).name).replace(/["']/g, '')
|
const layoutName = kebabCase(name || parse(filename).name).replace(LAYOUT_RE, '')
|
||||||
|
|
||||||
// Nuxt 3 adds layouts on app
|
// Nuxt 3 adds layouts on app
|
||||||
nuxt.hook('app:templates', (app) => {
|
nuxt.hook('app:templates', (app) => {
|
||||||
|
@ -72,10 +72,7 @@ export const normalizeModuleTranspilePath = (p: string) => {
|
|||||||
export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, nuxt: Nuxt = useNuxt()) {
|
export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, nuxt: Nuxt = useNuxt()) {
|
||||||
let buildTimeModuleMeta: ModuleMeta = {}
|
let buildTimeModuleMeta: ModuleMeta = {}
|
||||||
|
|
||||||
const jiti = createJiti(nuxt.options.rootDir, {
|
const jiti = createJiti(nuxt.options.rootDir, { alias: nuxt.options.alias })
|
||||||
interopDefault: true,
|
|
||||||
alias: nuxt.options.alias,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Import if input is string
|
// Import if input is string
|
||||||
if (typeof nuxtModule === 'string') {
|
if (typeof nuxtModule === 'string') {
|
||||||
@ -85,7 +82,7 @@ export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, n
|
|||||||
for (const path of paths) {
|
for (const path of paths) {
|
||||||
try {
|
try {
|
||||||
const src = jiti.esmResolve(path, { parentURL: parentURL.replace(/\/node_modules\/?$/, '') })
|
const src = jiti.esmResolve(path, { parentURL: parentURL.replace(/\/node_modules\/?$/, '') })
|
||||||
nuxtModule = await jiti.import(src) as NuxtModule
|
nuxtModule = await jiti.import(src, { default: true }) as NuxtModule
|
||||||
|
|
||||||
// nuxt-module-builder generates a module.json with metadata including the version
|
// nuxt-module-builder generates a module.json with metadata including the version
|
||||||
const moduleMetadataPath = join(dirname(src), 'module.json')
|
const moduleMetadataPath = join(dirname(src), 'module.json')
|
||||||
|
@ -4,13 +4,14 @@ import { normalize } from 'pathe'
|
|||||||
import { useNuxt } from './context'
|
import { useNuxt } from './context'
|
||||||
import { toArray } from './utils'
|
import { toArray } from './utils'
|
||||||
|
|
||||||
|
const HANDLER_METHOD_RE = /\.(get|head|patch|post|put|delete|connect|options|trace)(\.\w+)*$/
|
||||||
/**
|
/**
|
||||||
* normalize handler object
|
* normalize handler object
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function normalizeHandlerMethod (handler: NitroEventHandler) {
|
function normalizeHandlerMethod (handler: NitroEventHandler) {
|
||||||
// retrieve method from handler file name
|
// retrieve method from handler file name
|
||||||
const [, method = undefined] = handler.handler.match(/\.(get|head|patch|post|put|delete|connect|options|trace)(\.\w+)*$/) || []
|
const [, method = undefined] = handler.handler.match(HANDLER_METHOD_RE) || []
|
||||||
return {
|
return {
|
||||||
method: method as 'get' | 'head' | 'patch' | 'post' | 'put' | 'delete' | 'connect' | 'options' | 'trace' | undefined,
|
method: method as 'get' | 'head' | 'patch' | 'post' | 'put' | 'delete' | 'connect' | 'options' | 'trace' | undefined,
|
||||||
...handler,
|
...handler,
|
||||||
|
@ -3,6 +3,7 @@ import type { NuxtPlugin, NuxtPluginTemplate } from '@nuxt/schema'
|
|||||||
import { useNuxt } from './context'
|
import { useNuxt } from './context'
|
||||||
import { addTemplate } from './template'
|
import { addTemplate } from './template'
|
||||||
import { resolveAlias } from './resolve'
|
import { resolveAlias } from './resolve'
|
||||||
|
import { MODE_RE } from './utils'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize a nuxt plugin object
|
* Normalize a nuxt plugin object
|
||||||
@ -27,7 +28,7 @@ export function normalizePlugin (plugin: NuxtPlugin | string): NuxtPlugin {
|
|||||||
plugin.mode = 'server'
|
plugin.mode = 'server'
|
||||||
}
|
}
|
||||||
if (!plugin.mode) {
|
if (!plugin.mode) {
|
||||||
const [, mode = 'all'] = plugin.src.match(/\.(server|client)(\.\w+)*$/) || []
|
const [, mode = 'all'] = plugin.src.match(MODE_RE) || []
|
||||||
plugin.mode = mode as 'all' | 'client' | 'server'
|
plugin.mode = mode as 'all' | 'client' | 'server'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { existsSync, promises as fsp } from 'node:fs'
|
import { existsSync, promises as fsp } from 'node:fs'
|
||||||
import { basename, isAbsolute, join, parse, relative, resolve } from 'pathe'
|
import { basename, isAbsolute, join, parse, relative, resolve } from 'pathe'
|
||||||
import hash from 'hash-sum'
|
import hash from 'hash-sum'
|
||||||
import type { Nuxt, NuxtTemplate, NuxtTypeTemplate, ResolvedNuxtTemplate, TSReference } from '@nuxt/schema'
|
import type { Nuxt, NuxtServerTemplate, NuxtTemplate, NuxtTypeTemplate, ResolvedNuxtTemplate, TSReference } from '@nuxt/schema'
|
||||||
import { withTrailingSlash } from 'ufo'
|
import { withTrailingSlash } from 'ufo'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import type { TSConfig } from 'pkg-types'
|
import type { TSConfig } from 'pkg-types'
|
||||||
@ -32,6 +32,18 @@ export function addTemplate<T> (_template: NuxtTemplate<T> | string) {
|
|||||||
return template
|
return template
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a virtual file that can be used within the Nuxt Nitro server build.
|
||||||
|
*/
|
||||||
|
export function addServerTemplate (template: NuxtServerTemplate) {
|
||||||
|
const nuxt = useNuxt()
|
||||||
|
|
||||||
|
nuxt.options.nitro.virtual ||= {}
|
||||||
|
nuxt.options.nitro.virtual[template.filename] = template.getContents
|
||||||
|
|
||||||
|
return template
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders given types using lodash template during build into the project buildDir
|
* Renders given types using lodash template during build into the project buildDir
|
||||||
* and register them as types.
|
* and register them as types.
|
||||||
@ -111,6 +123,9 @@ export async function updateTemplates (options?: { filter?: (template: ResolvedN
|
|||||||
return await tryUseNuxt()?.hooks.callHook('builder:generateApp', options)
|
return await tryUseNuxt()?.hooks.callHook('builder:generateApp', options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const EXTENSION_RE = /\b\.\w+$/g
|
||||||
|
// Exclude bridge alias types to support Volar
|
||||||
|
const excludedAlias = [/^@vue\/.*$/, /^#internal\/nuxt/]
|
||||||
export async function _generateTypes (nuxt: Nuxt) {
|
export async function _generateTypes (nuxt: Nuxt) {
|
||||||
const rootDirWithSlash = withTrailingSlash(nuxt.options.rootDir)
|
const rootDirWithSlash = withTrailingSlash(nuxt.options.rootDir)
|
||||||
const relativeRootDir = relativeWithDot(nuxt.options.buildDir, nuxt.options.rootDir)
|
const relativeRootDir = relativeWithDot(nuxt.options.buildDir, nuxt.options.rootDir)
|
||||||
@ -211,13 +226,7 @@ export async function _generateTypes (nuxt: Nuxt) {
|
|||||||
exclude: [...exclude],
|
exclude: [...exclude],
|
||||||
} satisfies TSConfig)
|
} satisfies TSConfig)
|
||||||
|
|
||||||
const aliases: Record<string, string> = {
|
const aliases: Record<string, string> = nuxt.options.alias
|
||||||
...nuxt.options.alias,
|
|
||||||
'#build': nuxt.options.buildDir,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exclude bridge alias types to support Volar
|
|
||||||
const excludedAlias = [/^@vue\/.*$/]
|
|
||||||
|
|
||||||
const basePath = tsConfig.compilerOptions!.baseUrl
|
const basePath = tsConfig.compilerOptions!.baseUrl
|
||||||
? resolve(nuxt.options.buildDir, tsConfig.compilerOptions!.baseUrl)
|
? resolve(nuxt.options.buildDir, tsConfig.compilerOptions!.baseUrl)
|
||||||
@ -251,7 +260,7 @@ export async function _generateTypes (nuxt: Nuxt) {
|
|||||||
} else {
|
} else {
|
||||||
const path = stats?.isFile()
|
const path = stats?.isFile()
|
||||||
// remove extension
|
// remove extension
|
||||||
? relativePath.replace(/\b\.\w+$/g, '')
|
? relativePath.replace(EXTENSION_RE, '')
|
||||||
// non-existent file probably shouldn't be resolved
|
// non-existent file probably shouldn't be resolved
|
||||||
: aliases[alias]!
|
: aliases[alias]!
|
||||||
|
|
||||||
@ -280,7 +289,7 @@ export async function _generateTypes (nuxt: Nuxt) {
|
|||||||
tsConfig.compilerOptions!.paths[alias] = await Promise.all(paths.map(async (path: string) => {
|
tsConfig.compilerOptions!.paths[alias] = await Promise.all(paths.map(async (path: string) => {
|
||||||
if (!isAbsolute(path)) { return path }
|
if (!isAbsolute(path)) { return path }
|
||||||
const stats = await fsp.stat(path).catch(() => null /* file does not exist */)
|
const stats = await fsp.stat(path).catch(() => null /* file does not exist */)
|
||||||
return relativeWithDot(nuxt.options.buildDir, stats?.isFile() ? path.replace(/\b\.\w+$/g, '') /* remove extension */ : path)
|
return relativeWithDot(nuxt.options.buildDir, stats?.isFile() ? path.replace(EXTENSION_RE, '') /* remove extension */ : path)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,6 +344,7 @@ function renderAttr (key: string, value?: string) {
|
|||||||
return value ? `${key}="${value}"` : ''
|
return value ? `${key}="${value}"` : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RELATIVE_WITH_DOT_RE = /^([^.])/
|
||||||
function relativeWithDot (from: string, to: string) {
|
function relativeWithDot (from: string, to: string) {
|
||||||
return relative(from, to).replace(/^([^.])/, './$1') || '.'
|
return relative(from, to).replace(RELATIVE_WITH_DOT_RE, './$1') || '.'
|
||||||
}
|
}
|
||||||
|
@ -2,3 +2,5 @@
|
|||||||
export function toArray<T> (value: T | T[]): T[] {
|
export function toArray<T> (value: T | T[]): T[] {
|
||||||
return Array.isArray(value) ? value : [value]
|
return Array.isArray(value) ? value : [value]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const MODE_RE = /\.(server|client)(\.\w+)*$/
|
||||||
|
@ -34,9 +34,6 @@ describe('tsConfig generation', () => {
|
|||||||
const { tsConfig } = await _generateTypes(mockNuxt)
|
const { tsConfig } = await _generateTypes(mockNuxt)
|
||||||
expect(tsConfig.compilerOptions?.paths).toMatchInlineSnapshot(`
|
expect(tsConfig.compilerOptions?.paths).toMatchInlineSnapshot(`
|
||||||
{
|
{
|
||||||
"#build": [
|
|
||||||
".",
|
|
||||||
],
|
|
||||||
"some-custom-alias": [
|
"some-custom-alias": [
|
||||||
"../some-alias",
|
"../some-alias",
|
||||||
],
|
],
|
||||||
|
@ -60,19 +60,19 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/devalue": "^2.0.2",
|
"@nuxt/devalue": "^2.0.2",
|
||||||
"@nuxt/devtools": "^1.5.1",
|
"@nuxt/devtools": "^1.6.0",
|
||||||
"@nuxt/kit": "workspace:*",
|
"@nuxt/kit": "workspace:*",
|
||||||
"@nuxt/schema": "workspace:*",
|
"@nuxt/schema": "workspace:*",
|
||||||
"@nuxt/telemetry": "^2.6.0",
|
"@nuxt/telemetry": "^2.6.0",
|
||||||
"@nuxt/vite-builder": "workspace:*",
|
"@nuxt/vite-builder": "workspace:*",
|
||||||
"@unhead/dom": "^1.11.6",
|
"@unhead/dom": "^1.11.10",
|
||||||
"@unhead/shared": "^1.11.6",
|
"@unhead/shared": "^1.11.10",
|
||||||
"@unhead/ssr": "^1.11.6",
|
"@unhead/ssr": "^1.11.10",
|
||||||
"@unhead/vue": "^1.11.6",
|
"@unhead/vue": "^1.11.10",
|
||||||
"@vue/shared": "^3.5.10",
|
"@vue/shared": "^3.5.12",
|
||||||
"acorn": "8.12.1",
|
"acorn": "8.13.0",
|
||||||
"c12": "^2.0.0-beta.3",
|
"c12": "^2.0.1",
|
||||||
"chokidar": "^3.6.0",
|
"chokidar": "^4.0.1",
|
||||||
"compatx": "^0.1.8",
|
"compatx": "^0.1.8",
|
||||||
"consola": "^3.2.3",
|
"consola": "^3.2.3",
|
||||||
"cookie-es": "^1.2.2",
|
"cookie-es": "^1.2.2",
|
||||||
@ -87,53 +87,53 @@
|
|||||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
"hookable": "^5.5.3",
|
"hookable": "^5.5.3",
|
||||||
"ignore": "^6.0.2",
|
"ignore": "^6.0.2",
|
||||||
"impound": "^0.1.0",
|
"impound": "^0.2.0",
|
||||||
"jiti": "^2.0.0",
|
"jiti": "^2.3.3",
|
||||||
"klona": "^2.0.6",
|
"klona": "^2.0.6",
|
||||||
"knitwork": "^1.1.0",
|
"knitwork": "^1.1.0",
|
||||||
"magic-string": "^0.30.11",
|
"magic-string": "^0.30.12",
|
||||||
"mlly": "^1.7.1",
|
"mlly": "^1.7.2",
|
||||||
"nanotar": "^0.1.1",
|
"nanotar": "^0.1.1",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||||
"nuxi": "^3.14.0",
|
"nuxi": "^3.15.0",
|
||||||
"nypm": "^0.3.12",
|
"nypm": "^0.3.12",
|
||||||
"ofetch": "^1.4.0",
|
"ofetch": "^1.4.1",
|
||||||
"ohash": "^1.1.4",
|
"ohash": "^1.1.4",
|
||||||
"pathe": "^1.1.2",
|
"pathe": "^1.1.2",
|
||||||
"perfect-debounce": "^1.0.0",
|
"perfect-debounce": "^1.0.0",
|
||||||
"pkg-types": "^1.2.0",
|
"pkg-types": "^1.2.1",
|
||||||
"radix3": "^1.1.2",
|
"radix3": "^1.1.2",
|
||||||
"scule": "^1.3.0",
|
"scule": "^1.3.0",
|
||||||
"semver": "^7.6.3",
|
"semver": "^7.6.3",
|
||||||
"std-env": "^3.7.0",
|
"std-env": "^3.7.0",
|
||||||
"strip-literal": "^2.1.0",
|
"strip-literal": "^2.1.0",
|
||||||
"tinyglobby": "0.2.6",
|
"tinyglobby": "0.2.9",
|
||||||
"ufo": "^1.5.4",
|
"ufo": "^1.5.4",
|
||||||
"ultrahtml": "^1.5.3",
|
"ultrahtml": "^1.5.3",
|
||||||
"uncrypto": "^0.1.3",
|
"uncrypto": "^0.1.3",
|
||||||
"unctx": "^2.3.1",
|
"unctx": "^2.3.1",
|
||||||
"unenv": "^1.10.0",
|
"unenv": "^1.10.0",
|
||||||
"unhead": "^1.11.6",
|
"unhead": "^1.11.10",
|
||||||
"unimport": "^3.13.1",
|
"unimport": "^3.13.1",
|
||||||
"unplugin": "^1.14.1",
|
"unplugin": "^1.14.1",
|
||||||
"unplugin-vue-router": "^0.10.8",
|
"unplugin-vue-router": "^0.10.8",
|
||||||
"unstorage": "^1.12.0",
|
"unstorage": "^1.12.0",
|
||||||
"untyped": "^1.5.0",
|
"untyped": "^1.5.1",
|
||||||
"vue": "^3.5.10",
|
"vue": "^3.5.12",
|
||||||
"vue-bundle-renderer": "^2.1.1",
|
"vue-bundle-renderer": "^2.1.1",
|
||||||
"vue-devtools-stub": "^0.1.0",
|
"vue-devtools-stub": "^0.1.0",
|
||||||
"vue-router": "^4.4.5"
|
"vue-router": "^4.4.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/scripts": "0.9.4",
|
"@nuxt/scripts": "0.9.5",
|
||||||
"@nuxt/ui-templates": "1.3.4",
|
"@nuxt/ui-templates": "1.3.4",
|
||||||
"@parcel/watcher": "2.4.1",
|
"@parcel/watcher": "2.4.1",
|
||||||
"@types/estree": "1.0.6",
|
"@types/estree": "1.0.6",
|
||||||
"@vitejs/plugin-vue": "5.1.4",
|
"@vitejs/plugin-vue": "5.1.4",
|
||||||
"@vue/compiler-sfc": "3.5.10",
|
"@vue/compiler-sfc": "3.5.12",
|
||||||
"unbuild": "3.0.0-rc.8",
|
"unbuild": "3.0.0-rc.11",
|
||||||
"vite": "5.4.8",
|
"vite": "5.4.10",
|
||||||
"vitest": "2.1.1"
|
"vitest": "2.1.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@parcel/watcher": "^2.1.0",
|
"@parcel/watcher": "^2.1.0",
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import type { Component, PropType, VNode } from 'vue'
|
import type { Component, PropType, VNode } from 'vue'
|
||||||
import { Fragment, Teleport, computed, createStaticVNode, createVNode, defineComponent, getCurrentInstance, h, nextTick, onMounted, ref, toRaw, watch, withMemo } from 'vue'
|
import { Fragment, Teleport, computed, createStaticVNode, createVNode, defineComponent, getCurrentInstance, h, nextTick, onBeforeUnmount, onMounted, ref, toRaw, watch, withMemo } from 'vue'
|
||||||
import { debounce } from 'perfect-debounce'
|
import { debounce } from 'perfect-debounce'
|
||||||
import { hash } from 'ohash'
|
import { hash } from 'ohash'
|
||||||
import { appendResponseHeader } from 'h3'
|
import { appendResponseHeader } from 'h3'
|
||||||
import { injectHead } from '@unhead/vue'
|
import { type ActiveHeadEntry, type Head, injectHead } from '@unhead/vue'
|
||||||
import { randomUUID } from 'uncrypto'
|
import { randomUUID } from 'uncrypto'
|
||||||
import { joinURL, withQuery } from 'ufo'
|
import { joinURL, withQuery } from 'ufo'
|
||||||
import type { FetchResponse } from 'ofetch'
|
import type { FetchResponse } from 'ofetch'
|
||||||
@ -22,6 +22,7 @@ const SSR_UID_RE = /data-island-uid="([^"]*)"/
|
|||||||
const DATA_ISLAND_UID_RE = /data-island-uid(="")?(?!="[^"])/g
|
const DATA_ISLAND_UID_RE = /data-island-uid(="")?(?!="[^"])/g
|
||||||
const SLOTNAME_RE = /data-island-slot="([^"]*)"/g
|
const SLOTNAME_RE = /data-island-slot="([^"]*)"/g
|
||||||
const SLOT_FALLBACK_RE = / data-island-slot="([^"]*)"[^>]*>/g
|
const SLOT_FALLBACK_RE = / data-island-slot="([^"]*)"[^>]*>/g
|
||||||
|
const ISLAND_SCOPE_ID_RE = /^<[^> ]*/
|
||||||
|
|
||||||
let id = 1
|
let id = 1
|
||||||
const getId = import.meta.client ? () => (id++).toString() : randomUUID
|
const getId = import.meta.client ? () => (id++).toString() : randomUUID
|
||||||
@ -90,11 +91,13 @@ export default defineComponent({
|
|||||||
const instance = getCurrentInstance()!
|
const instance = getCurrentInstance()!
|
||||||
const event = useRequestEvent()
|
const event = useRequestEvent()
|
||||||
|
|
||||||
|
let activeHead: ActiveHeadEntry<Head>
|
||||||
|
|
||||||
// TODO: remove use of `$fetch.raw` when nitro 503 issues on windows dev server are resolved
|
// TODO: remove use of `$fetch.raw` when nitro 503 issues on windows dev server are resolved
|
||||||
const eventFetch = import.meta.server ? event!.fetch : import.meta.dev ? $fetch.raw : globalThis.fetch
|
const eventFetch = import.meta.server ? event!.fetch : import.meta.dev ? $fetch.raw : globalThis.fetch
|
||||||
const mounted = ref(false)
|
const mounted = ref(false)
|
||||||
onMounted(() => { mounted.value = true; teleportKey.value++ })
|
onMounted(() => { mounted.value = true; teleportKey.value++ })
|
||||||
|
onBeforeUnmount(() => { if (activeHead) { activeHead.dispose() } })
|
||||||
function setPayload (key: string, result: NuxtIslandResponse) {
|
function setPayload (key: string, result: NuxtIslandResponse) {
|
||||||
const toRevive: Partial<NuxtIslandResponse> = {}
|
const toRevive: Partial<NuxtIslandResponse> = {}
|
||||||
if (result.props) { toRevive.props = result.props }
|
if (result.props) { toRevive.props = result.props }
|
||||||
@ -140,7 +143,7 @@ export default defineComponent({
|
|||||||
let html = ssrHTML.value
|
let html = ssrHTML.value
|
||||||
|
|
||||||
if (props.scopeId) {
|
if (props.scopeId) {
|
||||||
html = html.replace(/^<[^> ]*/, full => full + ' ' + props.scopeId)
|
html = html.replace(ISLAND_SCOPE_ID_RE, full => full + ' ' + props.scopeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (import.meta.client && !canLoadClientComponent.value) {
|
if (import.meta.client && !canLoadClientComponent.value) {
|
||||||
@ -215,6 +218,14 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (res?.head) {
|
||||||
|
if (activeHead) {
|
||||||
|
activeHead.patch(res.head)
|
||||||
|
} else {
|
||||||
|
activeHead = head.push(res.head)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (import.meta.client) {
|
if (import.meta.client) {
|
||||||
// must await next tick for Teleport to work correctly with static node re-rendering
|
// must await next tick for Teleport to work correctly with static node re-rendering
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
@ -250,14 +261,6 @@ export default defineComponent({
|
|||||||
await loadComponents(props.source, payloads.components)
|
await loadComponents(props.source, payloads.components)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (import.meta.server || nuxtApp.isHydrating) {
|
|
||||||
// re-push head into active head instance
|
|
||||||
const responseHead = (nuxtApp.payload.data[`${props.name}_${hashId.value}`] as NuxtIslandResponse)?.head
|
|
||||||
if (responseHead) {
|
|
||||||
head.push(responseHead)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (_ctx: any, _cache: any) => {
|
return (_ctx: any, _cache: any) => {
|
||||||
if (!html.value || error.value) {
|
if (!html.value || error.value) {
|
||||||
return [slots.fallback?.({ error: error.value }) ?? createVNode('div')]
|
return [slots.fallback?.({ error: error.value }) ?? createVNode('div')]
|
||||||
|
@ -328,8 +328,9 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
|||||||
const path = typeof to.value === 'string'
|
const path = typeof to.value === 'string'
|
||||||
? to.value
|
? to.value
|
||||||
: isExternal.value ? resolveRouteObject(to.value) : router.resolve(to.value).fullPath
|
: isExternal.value ? resolveRouteObject(to.value) : router.resolve(to.value).fullPath
|
||||||
|
const normalizedPath = isExternal.value ? new URL(path, window.location.href).href : path
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
nuxtApp.hooks.callHook('link:prefetch', path).catch(() => {}),
|
nuxtApp.hooks.callHook('link:prefetch', normalizedPath).catch(() => {}),
|
||||||
!isExternal.value && !hasTarget.value && preloadRouteComponents(to.value as string, router).catch(() => {}),
|
!isExternal.value && !hasTarget.value && preloadRouteComponents(to.value as string, router).catch(() => {}),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@ -520,11 +521,12 @@ function useObserver (): { observe: ObserveFn } | undefined {
|
|||||||
return _observer
|
return _observer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const IS_2G_RE = /2g/
|
||||||
function isSlowConnection () {
|
function isSlowConnection () {
|
||||||
if (import.meta.server) { return }
|
if (import.meta.server) { return }
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/connection
|
// https://developer.mozilla.org/en-US/docs/Web/API/Navigator/connection
|
||||||
const cn = (navigator as any).connection as { saveData: boolean, effectiveType: string } | null
|
const cn = (navigator as any).connection as { saveData: boolean, effectiveType: string } | null
|
||||||
if (cn && (cn.saveData || /2g/.test(cn.effectiveType))) { return true }
|
if (cn && (cn.saveData || IS_2G_RE.test(cn.effectiveType))) { return true }
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,16 @@ export const _wrapIf = (component: Component, props: any, slots: any) => {
|
|||||||
return { default: () => props ? h(component, props, slots) : slots.default?.() }
|
return { default: () => props ? h(component, props, slots) : slots.default?.() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ROUTE_KEY_PARENTHESES_RE = /(:\w+)\([^)]+\)/g
|
||||||
|
const ROUTE_KEY_SYMBOLS_RE = /(:\w+)[?+*]/g
|
||||||
|
const ROUTE_KEY_NORMAL_RE = /:\w+/g
|
||||||
// TODO: consider refactoring into single utility
|
// TODO: consider refactoring into single utility
|
||||||
// See https://github.com/nuxt/nuxt/tree/main/packages/nuxt/src/pages/runtime/utils.ts#L8-L19
|
// See https://github.com/nuxt/nuxt/tree/main/packages/nuxt/src/pages/runtime/utils.ts#L8-L19
|
||||||
function generateRouteKey (route: RouteLocationNormalized) {
|
function generateRouteKey (route: RouteLocationNormalized) {
|
||||||
const source = route?.meta.key ?? route.path
|
const source = route?.meta.key ?? route.path
|
||||||
.replace(/(:\w+)\([^)]+\)/g, '$1')
|
.replace(ROUTE_KEY_PARENTHESES_RE, '$1')
|
||||||
.replace(/(:\w+)[?+*]/g, '$1')
|
.replace(ROUTE_KEY_SYMBOLS_RE, '$1')
|
||||||
.replace(/:\w+/g, r => route.params[r.slice(1)]?.toString() || '')
|
.replace(ROUTE_KEY_NORMAL_RE, r => route.params[r.slice(1)]?.toString() || '')
|
||||||
return typeof source === 'function' ? source(route) : source
|
return typeof source === 'function' ? source(route) : source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,6 @@ export const defineNuxtComponent: typeof defineComponent =
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.head) {
|
if (options.head) {
|
||||||
const nuxtApp = useNuxtApp()
|
|
||||||
useHead(typeof options.head === 'function' ? () => options.head(nuxtApp) : options.head)
|
useHead(typeof options.head === 'function' ? () => options.head(nuxtApp) : options.head)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ export { useFetch, useLazyFetch } from './fetch'
|
|||||||
export type { FetchResult, UseFetchOptions } from './fetch'
|
export type { FetchResult, UseFetchOptions } from './fetch'
|
||||||
export { useCookie, refreshCookie } from './cookie'
|
export { useCookie, refreshCookie } from './cookie'
|
||||||
export type { CookieOptions, CookieRef } from './cookie'
|
export type { CookieOptions, CookieRef } from './cookie'
|
||||||
export { onPrehydrate, prerenderRoutes, useRequestHeaders, useRequestEvent, useRequestFetch, setResponseStatus } from './ssr'
|
export { onPrehydrate, prerenderRoutes, useRequestHeaders, useRequestEvent, useRequestFetch, setResponseStatus, useResponseHeader } from './ssr'
|
||||||
export { onNuxtReady } from './ready'
|
export { onNuxtReady } from './ready'
|
||||||
export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter } from './router'
|
export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter } from './router'
|
||||||
export type { AddRouteMiddlewareOptions, RouteMiddleware } from './router'
|
export type { AddRouteMiddlewareOptions, RouteMiddleware } from './router'
|
||||||
|
@ -114,6 +114,7 @@ export interface NavigateToOptions {
|
|||||||
open?: OpenOptions
|
open?: OpenOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const URL_QUOTE_RE = /"/g
|
||||||
/** @since 3.0.0 */
|
/** @since 3.0.0 */
|
||||||
export const navigateTo = (to: RouteLocationRaw | undefined | null, options?: NavigateToOptions): Promise<void | NavigationFailure | false> | false | void | RouteLocationRaw => {
|
export const navigateTo = (to: RouteLocationRaw | undefined | null, options?: NavigateToOptions): Promise<void | NavigationFailure | false> | false | void | RouteLocationRaw => {
|
||||||
if (!to) {
|
if (!to) {
|
||||||
@ -166,7 +167,7 @@ export const navigateTo = (to: RouteLocationRaw | undefined | null, options?: Na
|
|||||||
const redirect = async function (response: any) {
|
const redirect = async function (response: any) {
|
||||||
// TODO: consider deprecating in favour of `app:rendered` and removing
|
// TODO: consider deprecating in favour of `app:rendered` and removing
|
||||||
await nuxtApp.callHook('app:redirected')
|
await nuxtApp.callHook('app:redirected')
|
||||||
const encodedLoc = location.replace(/"/g, '%22')
|
const encodedLoc = location.replace(URL_QUOTE_RE, '%22')
|
||||||
const encodedHeader = encodeURL(location, isExternalHost)
|
const encodedHeader = encodeURL(location, isExternalHost)
|
||||||
|
|
||||||
nuxtApp.ssrContext!._renderResponse = {
|
nuxtApp.ssrContext!._renderResponse = {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { H3Event } from 'h3'
|
import type { H3Event } from 'h3'
|
||||||
import { setResponseStatus as _setResponseStatus, appendHeader, getRequestHeader, getRequestHeaders } from 'h3'
|
import { setResponseStatus as _setResponseStatus, appendHeader, getRequestHeader, getRequestHeaders, getResponseHeader, removeResponseHeader, setResponseHeader } from 'h3'
|
||||||
import { getCurrentInstance } from 'vue'
|
import { computed, getCurrentInstance, ref } from 'vue'
|
||||||
import { useServerHead } from '@unhead/vue'
|
import { useServerHead } from '@unhead/vue'
|
||||||
|
|
||||||
import type { NuxtApp } from '../nuxt'
|
import type { NuxtApp } from '../nuxt'
|
||||||
@ -61,6 +61,34 @@ export function setResponseStatus (arg1: H3Event | number | undefined, arg2?: nu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 3.14.0 */
|
||||||
|
export function useResponseHeader (header: string) {
|
||||||
|
if (import.meta.client) {
|
||||||
|
if (import.meta.dev) {
|
||||||
|
return computed({
|
||||||
|
get: () => undefined,
|
||||||
|
set: () => console.warn('[nuxt] Setting response headers is not supported in the browser.'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
const event = useRequestEvent()!
|
||||||
|
|
||||||
|
return computed({
|
||||||
|
get () {
|
||||||
|
return getResponseHeader(event, header)
|
||||||
|
},
|
||||||
|
set (newValue) {
|
||||||
|
if (!newValue) {
|
||||||
|
return removeResponseHeader(event, header)
|
||||||
|
}
|
||||||
|
|
||||||
|
return setResponseHeader(event, header, newValue)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/** @since 3.8.0 */
|
/** @since 3.8.0 */
|
||||||
export function prerenderRoutes (path: string | string[]) {
|
export function prerenderRoutes (path: string | string[]) {
|
||||||
if (!import.meta.server || !import.meta.prerender) { return }
|
if (!import.meta.server || !import.meta.prerender) { return }
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
export { applyPlugin, applyPlugins, callWithNuxt, createNuxtApp, defineAppConfig, defineNuxtPlugin, definePayloadPlugin, isNuxtPlugin, registerPluginHooks, tryUseNuxtApp, useNuxtApp, useRuntimeConfig } from './nuxt'
|
export { applyPlugin, applyPlugins, callWithNuxt, createNuxtApp, defineAppConfig, defineNuxtPlugin, definePayloadPlugin, isNuxtPlugin, registerPluginHooks, tryUseNuxtApp, useNuxtApp, useRuntimeConfig } from './nuxt'
|
||||||
export type { CreateOptions, NuxtApp, NuxtPayload, NuxtPluginIndicator, NuxtSSRContext, ObjectPlugin, Plugin, PluginEnvContext, PluginMeta, ResolvedPluginMeta, RuntimeNuxtHooks } from './nuxt'
|
export type { CreateOptions, NuxtApp, NuxtPayload, NuxtPluginIndicator, NuxtSSRContext, ObjectPlugin, Plugin, PluginEnvContext, PluginMeta, ResolvedPluginMeta, RuntimeNuxtHooks } from './nuxt'
|
||||||
|
|
||||||
export { defineNuxtComponent, useAsyncData, useLazyAsyncData, useNuxtData, refreshNuxtData, clearNuxtData, useHydration, callOnce, useState, clearNuxtState, clearError, createError, isNuxtError, showError, useError, useFetch, useLazyFetch, useCookie, refreshCookie, onPrehydrate, prerenderRoutes, useRequestHeaders, useRequestEvent, useRequestFetch, setResponseStatus, onNuxtReady, abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter, preloadComponents, prefetchComponents, preloadRouteComponents, isPrerendered, loadPayload, preloadPayload, definePayloadReducer, definePayloadReviver, getAppManifest, getRouteRules, reloadNuxtApp, useRequestURL, usePreviewMode, useId, useRouteAnnouncer, useHead, useSeoMeta, useServerSeoMeta } from './composables/index'
|
export { defineNuxtComponent, useAsyncData, useLazyAsyncData, useNuxtData, refreshNuxtData, clearNuxtData, useHydration, callOnce, useState, clearNuxtState, clearError, createError, isNuxtError, showError, useError, useFetch, useLazyFetch, useCookie, refreshCookie, onPrehydrate, prerenderRoutes, useRequestHeaders, useRequestEvent, useRequestFetch, setResponseStatus, useResponseHeader, onNuxtReady, abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter, preloadComponents, prefetchComponents, preloadRouteComponents, isPrerendered, loadPayload, preloadPayload, definePayloadReducer, definePayloadReviver, getAppManifest, getRouteRules, reloadNuxtApp, useRequestURL, usePreviewMode, useId, useRouteAnnouncer, useHead, useSeoMeta, useServerSeoMeta } from './composables/index'
|
||||||
export type { AddRouteMiddlewareOptions, AsyncData, AsyncDataOptions, AsyncDataRequestStatus, CookieOptions, CookieRef, FetchResult, NuxtAppManifest, NuxtAppManifestMeta, NuxtError, ReloadNuxtAppOptions, RouteMiddleware, UseFetchOptions } from './composables/index'
|
export type { AddRouteMiddlewareOptions, AsyncData, AsyncDataOptions, AsyncDataRequestStatus, CookieOptions, CookieRef, FetchResult, NuxtAppManifest, NuxtAppManifestMeta, NuxtError, ReloadNuxtAppOptions, RouteMiddleware, UseFetchOptions } from './composables/index'
|
||||||
|
|
||||||
export { defineNuxtLink } from './components/index'
|
export { defineNuxtLink } from './components/index'
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { existsSync, statSync, writeFileSync } from 'node:fs'
|
import { existsSync, statSync, writeFileSync } from 'node:fs'
|
||||||
import { isAbsolute, join, normalize, relative, resolve } from 'pathe'
|
import { isAbsolute, join, normalize, relative, resolve } from 'pathe'
|
||||||
import { addBuildPlugin, addPluginTemplate, addTemplate, addTypeTemplate, addVitePlugin, defineNuxtModule, logger, resolveAlias, resolvePath, updateTemplates } from '@nuxt/kit'
|
import { addBuildPlugin, addPluginTemplate, addTemplate, addTypeTemplate, addVitePlugin, defineNuxtModule, findPath, logger, resolveAlias, resolvePath, updateTemplates } from '@nuxt/kit'
|
||||||
import type { Component, ComponentsDir, ComponentsOptions } from 'nuxt/schema'
|
import type { Component, ComponentsDir, ComponentsOptions } from 'nuxt/schema'
|
||||||
|
|
||||||
import { distDir } from '../dirs'
|
import { distDir } from '../dirs'
|
||||||
@ -16,11 +16,13 @@ import { ComponentNamePlugin } from './plugins/component-names'
|
|||||||
|
|
||||||
const isPureObjectOrString = (val: any) => (!Array.isArray(val) && typeof val === 'object') || typeof val === 'string'
|
const isPureObjectOrString = (val: any) => (!Array.isArray(val) && typeof val === 'object') || typeof val === 'string'
|
||||||
const isDirectory = (p: string) => { try { return statSync(p).isDirectory() } catch { return false } }
|
const isDirectory = (p: string) => { try { return statSync(p).isDirectory() } catch { return false } }
|
||||||
|
const SLASH_SEPARATOR_RE = /[\\/]/
|
||||||
function compareDirByPathLength ({ path: pathA }: { path: string }, { path: pathB }: { path: string }) {
|
function compareDirByPathLength ({ path: pathA }: { path: string }, { path: pathB }: { path: string }) {
|
||||||
return pathB.split(/[\\/]/).filter(Boolean).length - pathA.split(/[\\/]/).filter(Boolean).length
|
return pathB.split(SLASH_SEPARATOR_RE).filter(Boolean).length - pathA.split(SLASH_SEPARATOR_RE).filter(Boolean).length
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_COMPONENTS_DIRS_RE = /\/components(?:\/(?:global|islands))?$/
|
const DEFAULT_COMPONENTS_DIRS_RE = /\/components(?:\/(?:global|islands))?$/
|
||||||
|
const STARTER_DOT_RE = /^\./g
|
||||||
|
|
||||||
export type getComponentsT = (mode?: 'client' | 'server' | 'all') => Component[]
|
export type getComponentsT = (mode?: 'client' | 'server' | 'all') => Component[]
|
||||||
|
|
||||||
@ -32,7 +34,7 @@ export default defineNuxtModule<ComponentsOptions>({
|
|||||||
defaults: {
|
defaults: {
|
||||||
dirs: [],
|
dirs: [],
|
||||||
},
|
},
|
||||||
setup (componentOptions, nuxt) {
|
async setup (componentOptions, nuxt) {
|
||||||
let componentDirs: ComponentsDir[] = []
|
let componentDirs: ComponentsDir[] = []
|
||||||
const context = {
|
const context = {
|
||||||
components: [] as Component[],
|
components: [] as Component[],
|
||||||
@ -89,7 +91,7 @@ export default defineNuxtModule<ComponentsOptions>({
|
|||||||
const dirOptions: ComponentsDir = typeof dir === 'object' ? dir : { path: dir }
|
const dirOptions: ComponentsDir = typeof dir === 'object' ? dir : { path: dir }
|
||||||
const dirPath = resolveAlias(dirOptions.path)
|
const dirPath = resolveAlias(dirOptions.path)
|
||||||
const transpile = typeof dirOptions.transpile === 'boolean' ? dirOptions.transpile : 'auto'
|
const transpile = typeof dirOptions.transpile === 'boolean' ? dirOptions.transpile : 'auto'
|
||||||
const extensions = (dirOptions.extensions || nuxt.options.extensions).map(e => e.replace(/^\./g, ''))
|
const extensions = (dirOptions.extensions || nuxt.options.extensions).map(e => e.replace(STARTER_DOT_RE, ''))
|
||||||
|
|
||||||
const present = isDirectory(dirPath)
|
const present = isDirectory(dirPath)
|
||||||
if (!present && !DEFAULT_COMPONENTS_DIRS_RE.test(dirOptions.path)) {
|
if (!present && !DEFAULT_COMPONENTS_DIRS_RE.test(dirOptions.path)) {
|
||||||
@ -134,8 +136,9 @@ export default defineNuxtModule<ComponentsOptions>({
|
|||||||
addTemplate(componentsMetadataTemplate)
|
addTemplate(componentsMetadataTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
addBuildPlugin(TransformPlugin(nuxt, getComponents, 'server'), { server: true, client: false })
|
const serverComponentRuntime = await findPath(join(distDir, 'components/runtime/server-component')) ?? join(distDir, 'components/runtime/server-component')
|
||||||
addBuildPlugin(TransformPlugin(nuxt, getComponents, 'client'), { server: false, client: true })
|
addBuildPlugin(TransformPlugin(nuxt, { getComponents, serverComponentRuntime, mode: 'server' }), { server: true, client: false })
|
||||||
|
addBuildPlugin(TransformPlugin(nuxt, { getComponents, serverComponentRuntime, mode: 'client' }), { server: false, client: true })
|
||||||
|
|
||||||
// Do not prefetch global components chunks
|
// Do not prefetch global components chunks
|
||||||
nuxt.hook('build:manifest', (manifest) => {
|
nuxt.hook('build:manifest', (manifest) => {
|
||||||
@ -162,7 +165,7 @@ export default defineNuxtModule<ComponentsOptions>({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const serverPlaceholderPath = resolve(distDir, 'app/components/server-placeholder')
|
const serverPlaceholderPath = await findPath(join(distDir, 'app/components/server-placeholder')) ?? join(distDir, 'app/components/server-placeholder')
|
||||||
|
|
||||||
// Scan components and add to plugin
|
// Scan components and add to plugin
|
||||||
nuxt.hook('app:templates', async (app) => {
|
nuxt.hook('app:templates', async (app) => {
|
||||||
@ -222,6 +225,7 @@ export default defineNuxtModule<ComponentsOptions>({
|
|||||||
|
|
||||||
const sharedLoaderOptions = {
|
const sharedLoaderOptions = {
|
||||||
getComponents,
|
getComponents,
|
||||||
|
serverComponentRuntime,
|
||||||
transform: typeof nuxt.options.components === 'object' && !Array.isArray(nuxt.options.components) ? nuxt.options.components.transform : undefined,
|
transform: typeof nuxt.options.components === 'object' && !Array.isArray(nuxt.options.components) ? nuxt.options.components.transform : undefined,
|
||||||
experimentalComponentIslands: !!nuxt.options.experimental.componentIslands,
|
experimentalComponentIslands: !!nuxt.options.experimental.componentIslands,
|
||||||
}
|
}
|
||||||
@ -272,16 +276,18 @@ export default defineNuxtModule<ComponentsOptions>({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
nuxt.hook('webpack:config', (configs) => {
|
for (const key of ['rspack:config', 'webpack:config'] as const) {
|
||||||
configs.forEach((config) => {
|
nuxt.hook(key, (configs) => {
|
||||||
const mode = config.name === 'client' ? 'client' : 'server'
|
configs.forEach((config) => {
|
||||||
config.plugins = config.plugins || []
|
const mode = config.name === 'client' ? 'client' : 'server'
|
||||||
|
config.plugins = config.plugins || []
|
||||||
|
|
||||||
if (mode !== 'server') {
|
if (mode !== 'server') {
|
||||||
writeFileSync(join(nuxt.options.buildDir, 'components-chunk.mjs'), 'export const paths = {}')
|
writeFileSync(join(nuxt.options.buildDir, 'components-chunk.mjs'), 'export const paths = {}')
|
||||||
}
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -12,6 +12,7 @@ interface LoaderOptions {
|
|||||||
}
|
}
|
||||||
const CLIENT_FALLBACK_RE = /<(?:NuxtClientFallback|nuxt-client-fallback)(?: [^>]*)?>/
|
const CLIENT_FALLBACK_RE = /<(?:NuxtClientFallback|nuxt-client-fallback)(?: [^>]*)?>/
|
||||||
const CLIENT_FALLBACK_GLOBAL_RE = /<(NuxtClientFallback|nuxt-client-fallback)( [^>]*)?>/g
|
const CLIENT_FALLBACK_GLOBAL_RE = /<(NuxtClientFallback|nuxt-client-fallback)( [^>]*)?>/g
|
||||||
|
const UID_RE = / :?uid=/
|
||||||
export const ClientFallbackAutoIdPlugin = (options: LoaderOptions) => createUnplugin(() => {
|
export const ClientFallbackAutoIdPlugin = (options: LoaderOptions) => createUnplugin(() => {
|
||||||
const exclude = options.transform?.exclude || []
|
const exclude = options.transform?.exclude || []
|
||||||
const include = options.transform?.include || []
|
const include = options.transform?.include || []
|
||||||
@ -37,7 +38,7 @@ export const ClientFallbackAutoIdPlugin = (options: LoaderOptions) => createUnpl
|
|||||||
|
|
||||||
s.replace(CLIENT_FALLBACK_GLOBAL_RE, (full, name, attrs) => {
|
s.replace(CLIENT_FALLBACK_GLOBAL_RE, (full, name, attrs) => {
|
||||||
count++
|
count++
|
||||||
if (/ :?uid=/.test(attrs)) { return full }
|
if (UID_RE.test(attrs)) { return full }
|
||||||
return `<${name} :uid="'${hash(relativeID)}' + JSON.stringify($props) + '${count}'" ${attrs ?? ''}>`
|
return `<${name} :uid="'${hash(relativeID)}' + JSON.stringify($props) + '${count}'" ${attrs ?? ''}>`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { createUnplugin } from 'unplugin'
|
import { createUnplugin } from 'unplugin'
|
||||||
import MagicString from 'magic-string'
|
import MagicString from 'magic-string'
|
||||||
import type { Component } from 'nuxt/schema'
|
import type { Component } from 'nuxt/schema'
|
||||||
import { isVue } from '../../core/utils'
|
import { SX_RE, isVue } from '../../core/utils'
|
||||||
|
|
||||||
interface NameDevPluginOptions {
|
interface NameDevPluginOptions {
|
||||||
sourcemap: boolean
|
sourcemap: boolean
|
||||||
getComponents: () => Component[]
|
getComponents: () => Component[]
|
||||||
}
|
}
|
||||||
|
const FILENAME_RE = /([^/\\]+)\.\w+$/
|
||||||
/**
|
/**
|
||||||
* Set the default name of components to their PascalCase name
|
* Set the default name of components to their PascalCase name
|
||||||
*/
|
*/
|
||||||
@ -15,10 +16,10 @@ export const ComponentNamePlugin = (options: NameDevPluginOptions) => createUnpl
|
|||||||
name: 'nuxt:component-name-plugin',
|
name: 'nuxt:component-name-plugin',
|
||||||
enforce: 'post',
|
enforce: 'post',
|
||||||
transformInclude (id) {
|
transformInclude (id) {
|
||||||
return isVue(id) || !!id.match(/\.[tj]sx$/)
|
return isVue(id) || !!id.match(SX_RE)
|
||||||
},
|
},
|
||||||
transform (code, id) {
|
transform (code, id) {
|
||||||
const filename = id.match(/([^/\\]+)\.\w+$/)?.[1]
|
const filename = id.match(FILENAME_RE)?.[1]
|
||||||
if (!filename) {
|
if (!filename) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ const TEMPLATE_RE = /<template>([\s\S]*)<\/template>/
|
|||||||
const NUXTCLIENT_ATTR_RE = /\s:?nuxt-client(="[^"]*")?/g
|
const NUXTCLIENT_ATTR_RE = /\s:?nuxt-client(="[^"]*")?/g
|
||||||
const IMPORT_CODE = '\nimport { mergeProps as __mergeProps } from \'vue\'' + '\nimport { vforToArray as __vforToArray } from \'#app/components/utils\'' + '\nimport NuxtTeleportIslandComponent from \'#app/components/nuxt-teleport-island-component\'' + '\nimport NuxtTeleportSsrSlot from \'#app/components/nuxt-teleport-island-slot\''
|
const IMPORT_CODE = '\nimport { mergeProps as __mergeProps } from \'vue\'' + '\nimport { vforToArray as __vforToArray } from \'#app/components/utils\'' + '\nimport NuxtTeleportIslandComponent from \'#app/components/nuxt-teleport-island-component\'' + '\nimport NuxtTeleportSsrSlot from \'#app/components/nuxt-teleport-island-slot\''
|
||||||
const EXTRACTED_ATTRS_RE = /v-(?:if|else-if|else)(="[^"]*")?/g
|
const EXTRACTED_ATTRS_RE = /v-(?:if|else-if|else)(="[^"]*")?/g
|
||||||
|
const KEY_RE = /:?key="[^"]"/g
|
||||||
|
|
||||||
function wrapWithVForDiv (code: string, vfor: string): string {
|
function wrapWithVForDiv (code: string, vfor: string): string {
|
||||||
return `<div v-for="${vfor}" style="display: contents;">${code}</div>`
|
return `<div v-for="${vfor}" style="display: contents;">${code}</div>`
|
||||||
@ -90,7 +91,7 @@ export const IslandsTransformPlugin = (options: ServerOnlyComponentTransformPlug
|
|||||||
if (children.length) {
|
if (children.length) {
|
||||||
// pass slot fallback to NuxtTeleportSsrSlot fallback
|
// pass slot fallback to NuxtTeleportSsrSlot fallback
|
||||||
const attrString = attributeToString(attributes)
|
const attrString = attributeToString(attributes)
|
||||||
const slice = code.slice(startingIndex + loc[0].end, startingIndex + loc[1].start).replaceAll(/:?key="[^"]"/g, '')
|
const slice = code.slice(startingIndex + loc[0].end, startingIndex + loc[1].start).replaceAll(KEY_RE, '')
|
||||||
s.overwrite(startingIndex + loc[0].start, startingIndex + loc[1].end, `<slot${attrString.replaceAll(EXTRACTED_ATTRS_RE, '')}/><template #fallback>${attributes['v-for'] ? wrapWithVForDiv(slice, attributes['v-for']) : slice}</template>`)
|
s.overwrite(startingIndex + loc[0].start, startingIndex + loc[1].end, `<slot${attrString.replaceAll(EXTRACTED_ATTRS_RE, '')}/><template #fallback>${attributes['v-for'] ? wrapWithVForDiv(slice, attributes['v-for']) : slice}</template>`)
|
||||||
} else {
|
} else {
|
||||||
s.overwrite(startingIndex + loc[0].start, startingIndex + loc[0].end, code.slice(startingIndex + loc[0].start, startingIndex + loc[0].end).replaceAll(EXTRACTED_ATTRS_RE, ''))
|
s.overwrite(startingIndex + loc[0].start, startingIndex + loc[0].end, code.slice(startingIndex + loc[0].start, startingIndex + loc[0].end).replaceAll(EXTRACTED_ATTRS_RE, ''))
|
||||||
|
@ -2,25 +2,26 @@ import { createUnplugin } from 'unplugin'
|
|||||||
import { genDynamicImport, genImport } from 'knitwork'
|
import { genDynamicImport, genImport } from 'knitwork'
|
||||||
import MagicString from 'magic-string'
|
import MagicString from 'magic-string'
|
||||||
import { pascalCase } from 'scule'
|
import { pascalCase } from 'scule'
|
||||||
import { resolve } from 'pathe'
|
import { relative } from 'pathe'
|
||||||
import type { Component, ComponentsOptions } from 'nuxt/schema'
|
import type { Component, ComponentsOptions } from 'nuxt/schema'
|
||||||
|
|
||||||
import { logger, tryUseNuxt } from '@nuxt/kit'
|
import { logger, tryUseNuxt } from '@nuxt/kit'
|
||||||
import { distDir } from '../../dirs'
|
import { QUOTE_RE, SX_RE, isVue } from '../../core/utils'
|
||||||
import { isVue } from '../../core/utils'
|
|
||||||
|
|
||||||
interface LoaderOptions {
|
interface LoaderOptions {
|
||||||
getComponents (): Component[]
|
getComponents (): Component[]
|
||||||
mode: 'server' | 'client'
|
mode: 'server' | 'client'
|
||||||
|
serverComponentRuntime: string
|
||||||
sourcemap?: boolean
|
sourcemap?: boolean
|
||||||
transform?: ComponentsOptions['transform']
|
transform?: ComponentsOptions['transform']
|
||||||
experimentalComponentIslands?: boolean
|
experimentalComponentIslands?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const REPLACE_COMPONENT_TO_DIRECT_IMPORT_RE = /(?<=[ (])_?resolveComponent\(\s*["'](lazy-|Lazy(?=[A-Z]))?([^'"]*)["'][^)]*\)/g
|
||||||
export const LoaderPlugin = (options: LoaderOptions) => createUnplugin(() => {
|
export const LoaderPlugin = (options: LoaderOptions) => createUnplugin(() => {
|
||||||
const exclude = options.transform?.exclude || []
|
const exclude = options.transform?.exclude || []
|
||||||
const include = options.transform?.include || []
|
const include = options.transform?.include || []
|
||||||
const serverComponentRuntime = resolve(distDir, 'components/runtime/server-component')
|
const nuxt = tryUseNuxt()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'nuxt:components-loader',
|
name: 'nuxt:components-loader',
|
||||||
@ -32,9 +33,9 @@ export const LoaderPlugin = (options: LoaderOptions) => createUnplugin(() => {
|
|||||||
if (include.some(pattern => pattern.test(id))) {
|
if (include.some(pattern => pattern.test(id))) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return isVue(id, { type: ['template', 'script'] }) || !!id.match(/\.[tj]sx$/)
|
return isVue(id, { type: ['template', 'script'] }) || !!id.match(SX_RE)
|
||||||
},
|
},
|
||||||
transform (code) {
|
transform (code, id) {
|
||||||
const components = options.getComponents()
|
const components = options.getComponents()
|
||||||
|
|
||||||
let num = 0
|
let num = 0
|
||||||
@ -43,13 +44,17 @@ export const LoaderPlugin = (options: LoaderOptions) => createUnplugin(() => {
|
|||||||
const s = new MagicString(code)
|
const s = new MagicString(code)
|
||||||
|
|
||||||
// replace `_resolveComponent("...")` to direct import
|
// replace `_resolveComponent("...")` to direct import
|
||||||
s.replace(/(?<=[ (])_?resolveComponent\(\s*["'](lazy-|Lazy(?=[A-Z]))?([^'"]*)["'][^)]*\)/g, (full: string, lazy: string, name: string) => {
|
s.replace(REPLACE_COMPONENT_TO_DIRECT_IMPORT_RE, (full: string, lazy: string, name: string) => {
|
||||||
const component = findComponent(components, name, options.mode)
|
const component = findComponent(components, name, options.mode)
|
||||||
if (component) {
|
if (component) {
|
||||||
// @ts-expect-error TODO: refactor to nuxi
|
// TODO: refactor to nuxi
|
||||||
if (component._internal_install && tryUseNuxt()?.options.test === false) {
|
const internalInstall = ((component as any)._internal_install) as string
|
||||||
// @ts-expect-error TODO: refactor to nuxi
|
if (internalInstall && nuxt?.options.test === false) {
|
||||||
import('../../core/features').then(({ installNuxtModule }) => installNuxtModule(component._internal_install))
|
if (!nuxt.options.dev) {
|
||||||
|
const relativePath = relative(nuxt.options.rootDir, id)
|
||||||
|
throw new Error(`[nuxt] \`~/${relativePath}\` is using \`${component.pascalName}\` which requires \`${internalInstall}\``)
|
||||||
|
}
|
||||||
|
import('../../core/features').then(({ installNuxtModule }) => installNuxtModule(internalInstall))
|
||||||
}
|
}
|
||||||
let identifier = map.get(component) || `__nuxt_component_${num++}`
|
let identifier = map.get(component) || `__nuxt_component_${num++}`
|
||||||
map.set(component, identifier)
|
map.set(component, identifier)
|
||||||
@ -57,7 +62,7 @@ export const LoaderPlugin = (options: LoaderOptions) => createUnplugin(() => {
|
|||||||
const isServerOnly = !component._raw && component.mode === 'server' &&
|
const isServerOnly = !component._raw && component.mode === 'server' &&
|
||||||
!components.some(c => c.pascalName === component.pascalName && c.mode === 'client')
|
!components.some(c => c.pascalName === component.pascalName && c.mode === 'client')
|
||||||
if (isServerOnly) {
|
if (isServerOnly) {
|
||||||
imports.add(genImport(serverComponentRuntime, [{ name: 'createServerComponent' }]))
|
imports.add(genImport(options.serverComponentRuntime, [{ name: 'createServerComponent' }]))
|
||||||
imports.add(`const ${identifier} = createServerComponent(${JSON.stringify(component.pascalName)})`)
|
imports.add(`const ${identifier} = createServerComponent(${JSON.stringify(component.pascalName)})`)
|
||||||
if (!options.experimentalComponentIslands) {
|
if (!options.experimentalComponentIslands) {
|
||||||
logger.warn(`Standalone server components (\`${name}\`) are not yet supported without enabling \`experimental.componentIslands\`.`)
|
logger.warn(`Standalone server components (\`${name}\`) are not yet supported without enabling \`experimental.componentIslands\`.`)
|
||||||
@ -107,7 +112,7 @@ export const LoaderPlugin = (options: LoaderOptions) => createUnplugin(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function findComponent (components: Component[], name: string, mode: LoaderOptions['mode']) {
|
function findComponent (components: Component[], name: string, mode: LoaderOptions['mode']) {
|
||||||
const id = pascalCase(name).replace(/["']/g, '')
|
const id = pascalCase(name).replace(QUOTE_RE, '')
|
||||||
// Prefer exact match
|
// Prefer exact match
|
||||||
const component = components.find(component => id === component.pascalName && ['all', mode, undefined].includes(component.mode))
|
const component = components.find(component => id === component.pascalName && ['all', mode, undefined].includes(component.mode))
|
||||||
if (component) { return component }
|
if (component) { return component }
|
||||||
|
@ -5,15 +5,19 @@ import { createUnimport } from 'unimport'
|
|||||||
import { createUnplugin } from 'unplugin'
|
import { createUnplugin } from 'unplugin'
|
||||||
import { parseURL } from 'ufo'
|
import { parseURL } from 'ufo'
|
||||||
import { parseQuery } from 'vue-router'
|
import { parseQuery } from 'vue-router'
|
||||||
import { normalize, resolve } from 'pathe'
|
import { normalize } from 'pathe'
|
||||||
import { genImport } from 'knitwork'
|
import { genImport } from 'knitwork'
|
||||||
import { distDir } from '../../dirs'
|
|
||||||
import type { getComponentsT } from '../module'
|
import type { getComponentsT } from '../module'
|
||||||
|
|
||||||
const COMPONENT_QUERY_RE = /[?&]nuxt_component=/
|
const COMPONENT_QUERY_RE = /[?&]nuxt_component=/
|
||||||
|
|
||||||
export function TransformPlugin (nuxt: Nuxt, getComponents: getComponentsT, mode: 'client' | 'server' | 'all') {
|
interface TransformPluginOptions {
|
||||||
const serverComponentRuntime = resolve(distDir, 'components/runtime/server-component')
|
getComponents: getComponentsT
|
||||||
|
mode: 'client' | 'server' | 'all'
|
||||||
|
serverComponentRuntime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TransformPlugin (nuxt: Nuxt, options: TransformPluginOptions) {
|
||||||
const componentUnimport = createUnimport({
|
const componentUnimport = createUnimport({
|
||||||
imports: [
|
imports: [
|
||||||
{
|
{
|
||||||
@ -26,7 +30,7 @@ export function TransformPlugin (nuxt: Nuxt, getComponents: getComponentsT, mode
|
|||||||
})
|
})
|
||||||
|
|
||||||
function getComponentsImports (): Import[] {
|
function getComponentsImports (): Import[] {
|
||||||
const components = getComponents(mode)
|
const components = options.getComponents(options.mode)
|
||||||
return components.flatMap((c): Import[] => {
|
return components.flatMap((c): Import[] => {
|
||||||
const withMode = (mode: string | undefined) => mode
|
const withMode = (mode: string | undefined) => mode
|
||||||
? `${c.filePath}${c.filePath.includes('?') ? '&' : '?'}nuxt_component=${mode}&nuxt_component_name=${c.pascalName}&nuxt_component_export=${c.export || 'default'}`
|
? `${c.filePath}${c.filePath.includes('?') ? '&' : '?'}nuxt_component=${mode}&nuxt_component_name=${c.pascalName}&nuxt_component_export=${c.export || 'default'}`
|
||||||
@ -95,7 +99,7 @@ export function TransformPlugin (nuxt: Nuxt, getComponents: getComponentsT, mode
|
|||||||
const name = query.nuxt_component_name
|
const name = query.nuxt_component_name
|
||||||
return {
|
return {
|
||||||
code: [
|
code: [
|
||||||
`import { createServerComponent } from ${JSON.stringify(serverComponentRuntime)}`,
|
`import { createServerComponent } from ${JSON.stringify(options.serverComponentRuntime)}`,
|
||||||
`${exportWording} createServerComponent(${JSON.stringify(name)})`,
|
`${exportWording} createServerComponent(${JSON.stringify(name)})`,
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
map: null,
|
map: null,
|
||||||
|
@ -33,8 +33,9 @@ export const TreeShakeTemplatePlugin = (options: TreeShakeTemplatePluginOptions)
|
|||||||
const components = options.getComponents()
|
const components = options.getComponents()
|
||||||
|
|
||||||
if (!regexpMap.has(components)) {
|
if (!regexpMap.has(components)) {
|
||||||
|
const serverPlaceholderPath = resolve(distDir, 'app/components/server-placeholder')
|
||||||
const clientOnlyComponents = components
|
const clientOnlyComponents = components
|
||||||
.filter(c => c.mode === 'client' && !components.some(other => other.mode !== 'client' && other.pascalName === c.pascalName && other.filePath !== resolve(distDir, 'app/components/server-placeholder')))
|
.filter(c => c.mode === 'client' && !components.some(other => other.mode !== 'client' && other.pascalName === c.pascalName && !other.filePath.startsWith(serverPlaceholderPath)))
|
||||||
.flatMap(c => [c.pascalName, c.kebabName.replaceAll('-', '_')])
|
.flatMap(c => [c.pascalName, c.kebabName.replaceAll('-', '_')])
|
||||||
.concat(['ClientOnly', 'client_only'])
|
.concat(['ClientOnly', 'client_only'])
|
||||||
|
|
||||||
|
@ -6,8 +6,12 @@ import { isIgnored, logger, useNuxt } from '@nuxt/kit'
|
|||||||
import { withTrailingSlash } from 'ufo'
|
import { withTrailingSlash } from 'ufo'
|
||||||
import type { Component, ComponentsDir } from 'nuxt/schema'
|
import type { Component, ComponentsDir } from 'nuxt/schema'
|
||||||
|
|
||||||
import { resolveComponentNameSegments } from '../core/utils'
|
import { QUOTE_RE, resolveComponentNameSegments } from '../core/utils'
|
||||||
|
|
||||||
|
const ISLAND_RE = /\.island(?:\.global)?$/
|
||||||
|
const GLOBAL_RE = /\.global(?:\.island)?$/
|
||||||
|
const COMPONENT_MODE_RE = /(?<=\.)(client|server)(\.global|\.island)*$/
|
||||||
|
const MODE_REPLACEMENT_RE = /(\.(client|server))?(\.global|\.island)*$/
|
||||||
/**
|
/**
|
||||||
* Scan the components inside different components folders
|
* Scan the components inside different components folders
|
||||||
* and return a unique list of components
|
* and return a unique list of components
|
||||||
@ -83,17 +87,17 @@ export async function scanComponents (dirs: ComponentsDir[], srcDir: string): Pr
|
|||||||
*/
|
*/
|
||||||
let fileName = basename(filePath, extname(filePath))
|
let fileName = basename(filePath, extname(filePath))
|
||||||
|
|
||||||
const island = /\.island(?:\.global)?$/.test(fileName) || dir.island
|
const island = ISLAND_RE.test(fileName) || dir.island
|
||||||
const global = /\.global(?:\.island)?$/.test(fileName) || dir.global
|
const global = GLOBAL_RE.test(fileName) || dir.global
|
||||||
const mode = island ? 'server' : (fileName.match(/(?<=\.)(client|server)(\.global|\.island)*$/)?.[1] || 'all') as 'client' | 'server' | 'all'
|
const mode = island ? 'server' : (fileName.match(COMPONENT_MODE_RE)?.[1] || 'all') as 'client' | 'server' | 'all'
|
||||||
fileName = fileName.replace(/(\.(client|server))?(\.global|\.island)*$/, '')
|
fileName = fileName.replace(MODE_REPLACEMENT_RE, '')
|
||||||
|
|
||||||
if (fileName.toLowerCase() === 'index') {
|
if (fileName.toLowerCase() === 'index') {
|
||||||
fileName = dir.pathPrefix === false ? basename(dirname(filePath)) : '' /* inherits from path */
|
fileName = dir.pathPrefix === false ? basename(dirname(filePath)) : '' /* inherits from path */
|
||||||
}
|
}
|
||||||
|
|
||||||
const suffix = (mode !== 'all' ? `-${mode}` : '')
|
const suffix = (mode !== 'all' ? `-${mode}` : '')
|
||||||
const componentNameSegments = resolveComponentNameSegments(fileName.replace(/["']/g, ''), prefixParts)
|
const componentNameSegments = resolveComponentNameSegments(fileName.replace(QUOTE_RE, ''), prefixParts)
|
||||||
const pascalName = pascalCase(componentNameSegments)
|
const pascalName = pascalCase(componentNameSegments)
|
||||||
|
|
||||||
if (LAZY_COMPONENT_NAME_REGEX.test(pascalName)) {
|
if (LAZY_COMPONENT_NAME_REGEX.test(pascalName)) {
|
||||||
|
@ -102,14 +102,15 @@ export const componentsIslandsTemplate: NuxtTemplate = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NON_VUE_RE = /\b\.(?!vue)\w+$/g
|
||||||
export const componentsTypeTemplate = {
|
export const componentsTypeTemplate = {
|
||||||
filename: 'components.d.ts' as const,
|
filename: 'components.d.ts' as const,
|
||||||
getContents: ({ app, nuxt }) => {
|
getContents: ({ app, nuxt }) => {
|
||||||
const buildDir = nuxt.options.buildDir
|
const buildDir = nuxt.options.buildDir
|
||||||
const componentTypes = app.components.filter(c => !c.island).map((c) => {
|
const componentTypes = app.components.filter(c => !c.island).map((c) => {
|
||||||
const type = `typeof ${genDynamicImport(isAbsolute(c.filePath)
|
const type = `typeof ${genDynamicImport(isAbsolute(c.filePath)
|
||||||
? relative(buildDir, c.filePath).replace(/\b\.(?!vue)\w+$/g, '')
|
? relative(buildDir, c.filePath).replace(NON_VUE_RE, '')
|
||||||
: c.filePath.replace(/\b\.(?!vue)\w+$/g, ''), { wrapper: false })}['${c.export}']`
|
: c.filePath.replace(NON_VUE_RE, ''), { wrapper: false })}['${c.export}']`
|
||||||
return [
|
return [
|
||||||
c.pascalName,
|
c.pascalName,
|
||||||
c.island || c.mode === 'server' ? `IslandComponent<${type}>` : type,
|
c.island || c.mode === 'server' ? `IslandComponent<${type}>` : type,
|
||||||
|
@ -57,7 +57,7 @@ export async function generateApp (nuxt: Nuxt, app: NuxtApp, options: { filter?:
|
|||||||
|
|
||||||
const writes: Array<() => void> = []
|
const writes: Array<() => void> = []
|
||||||
const changedTemplates: Array<ResolvedNuxtTemplate<any>> = []
|
const changedTemplates: Array<ResolvedNuxtTemplate<any>> = []
|
||||||
|
const FORWARD_SLASH_RE = /\//g
|
||||||
async function processTemplate (template: ResolvedNuxtTemplate) {
|
async function processTemplate (template: ResolvedNuxtTemplate) {
|
||||||
const fullPath = template.dst || resolve(nuxt.options.buildDir, template.filename!)
|
const fullPath = template.dst || resolve(nuxt.options.buildDir, template.filename!)
|
||||||
const start = performance.now()
|
const start = performance.now()
|
||||||
@ -72,12 +72,12 @@ export async function generateApp (nuxt: Nuxt, app: NuxtApp, options: { filter?:
|
|||||||
if (template.modified) {
|
if (template.modified) {
|
||||||
nuxt.vfs[fullPath] = contents
|
nuxt.vfs[fullPath] = contents
|
||||||
|
|
||||||
const aliasPath = '#build/' + template.filename!.replace(/\.\w+$/, '')
|
const aliasPath = '#build/' + template.filename
|
||||||
nuxt.vfs[aliasPath] = contents
|
nuxt.vfs[aliasPath] = contents
|
||||||
|
|
||||||
// In case a non-normalized absolute path is called for on Windows
|
// In case a non-normalized absolute path is called for on Windows
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
nuxt.vfs[fullPath.replace(/\//g, '\\')] = contents
|
nuxt.vfs[fullPath.replace(FORWARD_SLASH_RE, '\\')] = contents
|
||||||
}
|
}
|
||||||
|
|
||||||
changedTemplates.push(template)
|
changedTemplates.push(template)
|
||||||
|
@ -10,6 +10,7 @@ import { generateApp as _generateApp, createApp } from './app'
|
|||||||
import { checkForExternalConfigurationFiles } from './external-config-files'
|
import { checkForExternalConfigurationFiles } from './external-config-files'
|
||||||
import { cleanupCaches, getVueHash } from './cache'
|
import { cleanupCaches, getVueHash } from './cache'
|
||||||
|
|
||||||
|
const IS_RESTART_PATH_RE = /^(?:app\.|error\.|plugins\/|middleware\/|layouts\/)/i
|
||||||
export async function build (nuxt: Nuxt) {
|
export async function build (nuxt: Nuxt) {
|
||||||
const app = createApp(nuxt)
|
const app = createApp(nuxt)
|
||||||
nuxt.apps.default = app
|
nuxt.apps.default = app
|
||||||
@ -23,7 +24,7 @@ export async function build (nuxt: Nuxt) {
|
|||||||
if (event === 'change') { return }
|
if (event === 'change') { return }
|
||||||
const path = resolve(nuxt.options.srcDir, relativePath)
|
const path = resolve(nuxt.options.srcDir, relativePath)
|
||||||
const relativePaths = nuxt.options._layers.map(l => relative(l.config.srcDir || l.cwd, path))
|
const relativePaths = nuxt.options._layers.map(l => relative(l.config.srcDir || l.cwd, path))
|
||||||
const restartPath = relativePaths.find(relativePath => /^(?:app\.|error\.|plugins\/|middleware\/|layouts\/)/i.test(relativePath))
|
const restartPath = relativePaths.find(relativePath => IS_RESTART_PATH_RE.test(relativePath))
|
||||||
if (restartPath) {
|
if (restartPath) {
|
||||||
if (restartPath.startsWith('app')) {
|
if (restartPath.startsWith('app')) {
|
||||||
app.mainComponent = undefined
|
app.mainComponent = undefined
|
||||||
|
@ -18,6 +18,7 @@ import { distDir } from '../dirs'
|
|||||||
import { toArray } from '../utils'
|
import { toArray } from '../utils'
|
||||||
import { template as defaultSpaLoadingTemplate } from '../../../ui-templates/dist/templates/spa-loading-icon'
|
import { template as defaultSpaLoadingTemplate } from '../../../ui-templates/dist/templates/spa-loading-icon'
|
||||||
import { nuxtImportProtections } from './plugins/import-protection'
|
import { nuxtImportProtections } from './plugins/import-protection'
|
||||||
|
import { EXTENSION_RE } from './utils'
|
||||||
|
|
||||||
const logLevelMapReverse = {
|
const logLevelMapReverse = {
|
||||||
silent: 0,
|
silent: 0,
|
||||||
@ -25,12 +26,14 @@ const logLevelMapReverse = {
|
|||||||
verbose: 3,
|
verbose: 3,
|
||||||
} satisfies Record<NuxtOptions['logLevel'], NitroConfig['logLevel']>
|
} satisfies Record<NuxtOptions['logLevel'], NitroConfig['logLevel']>
|
||||||
|
|
||||||
|
const NODE_MODULES_RE = /(?<=\/)node_modules\/(.+)$/
|
||||||
|
const PNPM_NODE_MODULES_RE = /\.pnpm\/.+\/node_modules\/(.+)$/
|
||||||
export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
||||||
// Resolve config
|
// Resolve config
|
||||||
const excludePaths = nuxt.options._layers
|
const excludePaths = nuxt.options._layers
|
||||||
.flatMap(l => [
|
.flatMap(l => [
|
||||||
l.cwd.match(/(?<=\/)node_modules\/(.+)$/)?.[1],
|
l.cwd.match(NODE_MODULES_RE)?.[1],
|
||||||
l.cwd.match(/\.pnpm\/.+\/node_modules\/(.+)$/)?.[1],
|
l.cwd.match(PNPM_NODE_MODULES_RE)?.[1],
|
||||||
])
|
])
|
||||||
.filter((dir): dir is string => Boolean(dir))
|
.filter((dir): dir is string => Boolean(dir))
|
||||||
.map(dir => escapeRE(dir))
|
.map(dir => escapeRE(dir))
|
||||||
@ -101,8 +104,8 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
devHandlers: [],
|
devHandlers: [],
|
||||||
baseURL: nuxt.options.app.baseURL,
|
baseURL: nuxt.options.app.baseURL,
|
||||||
virtual: {
|
virtual: {
|
||||||
'#internal/nuxt.config.mjs': () => nuxt.vfs['#build/nuxt.config'],
|
'#internal/nuxt.config.mjs': () => nuxt.vfs['#build/nuxt.config.mjs'],
|
||||||
'#internal/nuxt/app-config': () => nuxt.vfs['#build/app.config']?.replace(/\/\*\* client \*\*\/[\s\S]*\/\*\* client-end \*\*\//, ''),
|
'#internal/nuxt/app-config': () => nuxt.vfs['#build/app.config.mjs']?.replace(/\/\*\* client \*\*\/[\s\S]*\/\*\* client-end \*\*\//, ''),
|
||||||
'#spa-template': async () => `export const template = ${JSON.stringify(await spaLoadingTemplate(nuxt))}`,
|
'#spa-template': async () => `export const template = ${JSON.stringify(await spaLoadingTemplate(nuxt))}`,
|
||||||
},
|
},
|
||||||
routeRules: {
|
routeRules: {
|
||||||
@ -189,11 +192,11 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
},
|
},
|
||||||
'@vue/devtools-api': 'vue-devtools-stub',
|
'@vue/devtools-api': 'vue-devtools-stub',
|
||||||
|
|
||||||
// Paths
|
|
||||||
'#internal/nuxt/paths': resolve(distDir, 'core/runtime/nitro/paths'),
|
|
||||||
|
|
||||||
// Nuxt aliases
|
// Nuxt aliases
|
||||||
...nuxt.options.alias,
|
...nuxt.options.alias,
|
||||||
|
|
||||||
|
// Paths
|
||||||
|
'#internal/nuxt/paths': resolve(distDir, 'core/runtime/nitro/paths'),
|
||||||
},
|
},
|
||||||
replace: {
|
replace: {
|
||||||
'process.env.NUXT_NO_SSR': nuxt.options.ssr === false,
|
'process.env.NUXT_NO_SSR': nuxt.options.ssr === false,
|
||||||
@ -339,19 +342,20 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add fallback server for `ssr: false`
|
// Add fallback server for `ssr: false`
|
||||||
|
const FORWARD_SLASH_RE = /\//g
|
||||||
if (!nuxt.options.ssr) {
|
if (!nuxt.options.ssr) {
|
||||||
nitroConfig.virtual!['#build/dist/server/server.mjs'] = 'export default () => {}'
|
nitroConfig.virtual!['#build/dist/server/server.mjs'] = 'export default () => {}'
|
||||||
// In case a non-normalized absolute path is called for on Windows
|
// In case a non-normalized absolute path is called for on Windows
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
nitroConfig.virtual!['#build/dist/server/server.mjs'.replace(/\//g, '\\')] = 'export default () => {}'
|
nitroConfig.virtual!['#build/dist/server/server.mjs'.replace(FORWARD_SLASH_RE, '\\')] = 'export default () => {}'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nuxt.options.builder === '@nuxt/webpack-builder' || nuxt.options.dev) {
|
if (nuxt.options.dev || nuxt.options.builder === '@nuxt/webpack-builder' || nuxt.options.builder === '@nuxt/rspack-builder') {
|
||||||
nitroConfig.virtual!['#build/dist/server/styles.mjs'] = 'export default {}'
|
nitroConfig.virtual!['#build/dist/server/styles.mjs'] = 'export default {}'
|
||||||
// In case a non-normalized absolute path is called for on Windows
|
// In case a non-normalized absolute path is called for on Windows
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
nitroConfig.virtual!['#build/dist/server/styles.mjs'.replace(/\//g, '\\')] = 'export default {}'
|
nitroConfig.virtual!['#build/dist/server/styles.mjs'.replace(FORWARD_SLASH_RE, '\\')] = 'export default {}'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,7 +393,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
tsConfig.compilerOptions.paths[alias] = [absolutePath]
|
tsConfig.compilerOptions.paths[alias] = [absolutePath]
|
||||||
tsConfig.compilerOptions.paths[`${alias}/*`] = [`${absolutePath}/*`]
|
tsConfig.compilerOptions.paths[`${alias}/*`] = [`${absolutePath}/*`]
|
||||||
} else {
|
} else {
|
||||||
tsConfig.compilerOptions.paths[alias] = [absolutePath.replace(/\b\.\w+$/g, '')] /* remove extension */
|
tsConfig.compilerOptions.paths[alias] = [absolutePath.replace(EXTENSION_RE, '')] /* remove extension */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,18 +452,20 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
nuxt.hook('webpack:config', (configuration) => {
|
for (const hook of ['webpack:config', 'rspack:config'] as const) {
|
||||||
const clientConfig = configuration.find(config => config.name === 'client')
|
nuxt.hook(hook, (configuration) => {
|
||||||
if (!clientConfig!.resolve) { clientConfig!.resolve!.alias = {} }
|
const clientConfig = configuration.find(config => config.name === 'client')
|
||||||
if (Array.isArray(clientConfig!.resolve!.alias)) {
|
if (!clientConfig!.resolve) { clientConfig!.resolve!.alias = {} }
|
||||||
clientConfig!.resolve!.alias.push({
|
if (Array.isArray(clientConfig!.resolve!.alias)) {
|
||||||
name: 'vue',
|
clientConfig!.resolve!.alias.push({
|
||||||
alias: 'vue/dist/vue.esm-bundler',
|
name: 'vue',
|
||||||
})
|
alias: 'vue/dist/vue.esm-bundler',
|
||||||
} else {
|
})
|
||||||
clientConfig!.resolve!.alias!.vue = 'vue/dist/vue.esm-bundler'
|
} else {
|
||||||
}
|
clientConfig!.resolve!.alias!.vue = 'vue/dist/vue.esm-bundler'
|
||||||
})
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup handlers
|
// Setup handlers
|
||||||
@ -545,13 +551,15 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
|
|
||||||
// nuxt dev
|
// nuxt dev
|
||||||
if (nuxt.options.dev) {
|
if (nuxt.options.dev) {
|
||||||
nuxt.hook('webpack:compile', ({ name, compiler }) => {
|
for (const builder of ['webpack', 'rspack'] as const) {
|
||||||
if (name === 'server') {
|
nuxt.hook(`${builder}:compile`, ({ name, compiler }) => {
|
||||||
const memfs = compiler.outputFileSystem as typeof import('node:fs')
|
if (name === 'server') {
|
||||||
nitro.options.virtual['#build/dist/server/server.mjs'] = () => memfs.readFileSync(join(nuxt.options.buildDir, 'dist/server/server.mjs'), 'utf-8')
|
const memfs = compiler.outputFileSystem as typeof import('node:fs')
|
||||||
}
|
nitro.options.virtual['#build/dist/server/server.mjs'] = () => memfs.readFileSync(join(nuxt.options.buildDir, 'dist/server/server.mjs'), 'utf-8')
|
||||||
})
|
}
|
||||||
nuxt.hook('webpack:compiled', () => { nuxt.server.reload() })
|
})
|
||||||
|
nuxt.hook(`${builder}:compiled`, () => { nuxt.server.reload() })
|
||||||
|
}
|
||||||
nuxt.hook('vite:compiled', () => { nuxt.server.reload() })
|
nuxt.hook('vite:compiled', () => { nuxt.server.reload() })
|
||||||
|
|
||||||
nuxt.hook('server:devHandler', (h) => { devMiddlewareHandler.set(h) })
|
nuxt.hook('server:devHandler', (h) => { devMiddlewareHandler.set(h) })
|
||||||
@ -562,8 +570,9 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RELATIVE_RE = /^([^.])/
|
||||||
function relativeWithDot (from: string, to: string) {
|
function relativeWithDot (from: string, to: string) {
|
||||||
return relative(from, to).replace(/^([^.])/, './$1') || '.'
|
return relative(from, to).replace(RELATIVE_RE, './$1') || '.'
|
||||||
}
|
}
|
||||||
|
|
||||||
async function spaLoadingTemplatePath (nuxt: Nuxt) {
|
async function spaLoadingTemplatePath (nuxt: Nuxt) {
|
||||||
|
@ -44,6 +44,7 @@ import { RemovePluginMetadataPlugin } from './plugins/plugin-metadata'
|
|||||||
import { AsyncContextInjectionPlugin } from './plugins/async-context'
|
import { AsyncContextInjectionPlugin } from './plugins/async-context'
|
||||||
import { resolveDeepImportsPlugin } from './plugins/resolve-deep-imports'
|
import { resolveDeepImportsPlugin } from './plugins/resolve-deep-imports'
|
||||||
import { prehydrateTransformPlugin } from './plugins/prehydrate'
|
import { prehydrateTransformPlugin } from './plugins/prehydrate'
|
||||||
|
import { VirtualFSPlugin } from './plugins/virtual'
|
||||||
|
|
||||||
export function createNuxt (options: NuxtOptions): Nuxt {
|
export function createNuxt (options: NuxtOptions): Nuxt {
|
||||||
const hooks = createHooks<NuxtHooks>()
|
const hooks = createHooks<NuxtHooks>()
|
||||||
@ -177,9 +178,10 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
|
|
||||||
const coreTypePackages = nuxt.options.typescript.hoist || []
|
const coreTypePackages = nuxt.options.typescript.hoist || []
|
||||||
const packageJSON = await readPackageJSON(nuxt.options.rootDir).catch(() => ({}) as PackageJson)
|
const packageJSON = await readPackageJSON(nuxt.options.rootDir).catch(() => ({}) as PackageJson)
|
||||||
|
const NESTED_PKG_RE = /^[^@]+\//
|
||||||
nuxt._dependencies = new Set([...Object.keys(packageJSON.dependencies || {}), ...Object.keys(packageJSON.devDependencies || {})])
|
nuxt._dependencies = new Set([...Object.keys(packageJSON.dependencies || {}), ...Object.keys(packageJSON.devDependencies || {})])
|
||||||
const paths = Object.fromEntries(await Promise.all(coreTypePackages.map(async (pkg) => {
|
const paths = Object.fromEntries(await Promise.all(coreTypePackages.map(async (pkg) => {
|
||||||
const [_pkg = pkg, _subpath] = /^[^@]+\//.test(pkg) ? pkg.split('/') : [pkg]
|
const [_pkg = pkg, _subpath] = NESTED_PKG_RE.test(pkg) ? pkg.split('/') : [pkg]
|
||||||
const subpath = _subpath ? '/' + _subpath : ''
|
const subpath = _subpath ? '/' + _subpath : ''
|
||||||
|
|
||||||
// ignore packages that exist in `package.json` as these can be resolved by TypeScript
|
// ignore packages that exist in `package.json` as these can be resolved by TypeScript
|
||||||
@ -240,6 +242,10 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Support Nuxt VFS
|
||||||
|
addBuildPlugin(VirtualFSPlugin(nuxt, { mode: 'server' }), { client: false })
|
||||||
|
addBuildPlugin(VirtualFSPlugin(nuxt, { mode: 'client', alias: { 'nitro/runtime': join(nuxt.options.buildDir, 'nitro.client.mjs') } }), { server: false })
|
||||||
|
|
||||||
// Add plugin normalization plugin
|
// Add plugin normalization plugin
|
||||||
addBuildPlugin(RemovePluginMetadataPlugin(nuxt))
|
addBuildPlugin(RemovePluginMetadataPlugin(nuxt))
|
||||||
|
|
||||||
@ -492,9 +498,11 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
|
|
||||||
const envMap = {
|
const envMap = {
|
||||||
// defaults from `builder` based on package name
|
// defaults from `builder` based on package name
|
||||||
|
'@nuxt/rspack-builder': '@rspack/core/module',
|
||||||
'@nuxt/vite-builder': 'vite/client',
|
'@nuxt/vite-builder': 'vite/client',
|
||||||
'@nuxt/webpack-builder': 'webpack/module',
|
'@nuxt/webpack-builder': 'webpack/module',
|
||||||
// simpler overrides from `typescript.builder` for better DX
|
// simpler overrides from `typescript.builder` for better DX
|
||||||
|
'rspack': '@rspack/core/module',
|
||||||
'vite': 'vite/client',
|
'vite': 'vite/client',
|
||||||
'webpack': 'webpack/module',
|
'webpack': 'webpack/module',
|
||||||
// default 'merged' builder environment for module authors
|
// default 'merged' builder environment for module authors
|
||||||
|
@ -16,7 +16,7 @@ export function resolveDeepImportsPlugin (nuxt: Nuxt): Plugin {
|
|||||||
conditions = config.mode === 'test' ? [...config.resolve.conditions, 'import', 'require'] : config.resolve.conditions
|
conditions = config.mode === 'test' ? [...config.resolve.conditions, 'import', 'require'] : config.resolve.conditions
|
||||||
},
|
},
|
||||||
async resolveId (id, importer) {
|
async resolveId (id, importer) {
|
||||||
if (!importer || isAbsolute(id) || (!isAbsolute(importer) && !importer.startsWith('virtual:')) || exclude.some(e => id.startsWith(e))) {
|
if (!importer || isAbsolute(id) || (!isAbsolute(importer) && !importer.startsWith('virtual:') && !importer.startsWith('\0virtual:')) || exclude.some(e => id.startsWith(e))) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
68
packages/nuxt/src/core/plugins/virtual.ts
Normal file
68
packages/nuxt/src/core/plugins/virtual.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { resolveAlias } from '@nuxt/kit'
|
||||||
|
import type { Nuxt } from '@nuxt/schema'
|
||||||
|
import { dirname, isAbsolute, resolve } from 'pathe'
|
||||||
|
import { createUnplugin } from 'unplugin'
|
||||||
|
|
||||||
|
const PREFIX = '\0virtual:nuxt:'
|
||||||
|
|
||||||
|
interface VirtualFSPluginOptions {
|
||||||
|
mode: 'client' | 'server'
|
||||||
|
alias?: Record<string, string>
|
||||||
|
}
|
||||||
|
|
||||||
|
const RELATIVE_ID_RE = /^\.{1,2}[\\/]/
|
||||||
|
export const VirtualFSPlugin = (nuxt: Nuxt, options: VirtualFSPluginOptions) => createUnplugin(() => {
|
||||||
|
const extensions = ['', ...nuxt.options.extensions]
|
||||||
|
const alias = { ...nuxt.options.alias, ...options.alias }
|
||||||
|
|
||||||
|
const resolveWithExt = (id: string) => {
|
||||||
|
for (const suffix of ['', '.' + options.mode]) {
|
||||||
|
for (const ext of extensions) {
|
||||||
|
const rId = id + suffix + ext
|
||||||
|
if (rId in nuxt.vfs) {
|
||||||
|
return rId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: 'nuxt:virtual',
|
||||||
|
resolveId (id, importer) {
|
||||||
|
id = resolveAlias(id, alias)
|
||||||
|
|
||||||
|
if (process.platform === 'win32' && isAbsolute(id)) {
|
||||||
|
// Add back C: prefix on Windows
|
||||||
|
id = resolve(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolvedId = resolveWithExt(id)
|
||||||
|
if (resolvedId) {
|
||||||
|
return PREFIX + resolvedId
|
||||||
|
}
|
||||||
|
|
||||||
|
if (importer && RELATIVE_ID_RE.test(id)) {
|
||||||
|
const path = resolve(dirname(withoutPrefix(importer)), id)
|
||||||
|
const resolved = resolveWithExt(path)
|
||||||
|
if (resolved) {
|
||||||
|
return PREFIX + resolved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
loadInclude (id) {
|
||||||
|
return id.startsWith(PREFIX) && withoutPrefix(id) in nuxt.vfs
|
||||||
|
},
|
||||||
|
|
||||||
|
load (id) {
|
||||||
|
return {
|
||||||
|
code: nuxt.vfs[withoutPrefix(id)] || '',
|
||||||
|
map: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function withoutPrefix (id: string) {
|
||||||
|
return id.startsWith(PREFIX) ? id.slice(PREFIX.length) : id
|
||||||
|
}
|
@ -23,7 +23,6 @@ export default defineNuxtModule({
|
|||||||
|
|
||||||
// Initialize untyped/jiti loader
|
// Initialize untyped/jiti loader
|
||||||
const _resolveSchema = createJiti(fileURLToPath(import.meta.url), {
|
const _resolveSchema = createJiti(fileURLToPath(import.meta.url), {
|
||||||
interopDefault: true,
|
|
||||||
cache: false,
|
cache: false,
|
||||||
transformOptions: {
|
transformOptions: {
|
||||||
babel: {
|
babel: {
|
||||||
@ -97,7 +96,7 @@ export default defineNuxtModule({
|
|||||||
let loadedConfig: SchemaDefinition
|
let loadedConfig: SchemaDefinition
|
||||||
try {
|
try {
|
||||||
// TODO: fix type for second argument of `import`
|
// TODO: fix type for second argument of `import`
|
||||||
loadedConfig = await _resolveSchema.import(filePath, {}) as SchemaDefinition
|
loadedConfig = await _resolveSchema.import(filePath, { default: true }) as SchemaDefinition
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
'Unable to load schema from',
|
'Unable to load schema from',
|
||||||
|
@ -11,6 +11,7 @@ import type { NuxtTemplate } from 'nuxt/schema'
|
|||||||
import type { Nitro } from 'nitro/types'
|
import type { Nitro } from 'nitro/types'
|
||||||
|
|
||||||
import { annotatePlugins, checkForCircularDependencies } from './app'
|
import { annotatePlugins, checkForCircularDependencies } from './app'
|
||||||
|
import { EXTENSION_RE } from './utils'
|
||||||
|
|
||||||
export const vueShim: NuxtTemplate = {
|
export const vueShim: NuxtTemplate = {
|
||||||
filename: 'types/vue-shim.d.ts',
|
filename: 'types/vue-shim.d.ts',
|
||||||
@ -57,8 +58,9 @@ export const cssTemplate: NuxtTemplate = {
|
|||||||
getContents: ctx => ctx.nuxt.options.css.map(i => genImport(i)).join('\n'),
|
getContents: ctx => ctx.nuxt.options.css.map(i => genImport(i)).join('\n'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PLUGIN_TEMPLATE_RE = /_(45|46|47)/g
|
||||||
export const clientPluginTemplate: NuxtTemplate = {
|
export const clientPluginTemplate: NuxtTemplate = {
|
||||||
filename: 'plugins/client.mjs',
|
filename: 'plugins.client.mjs',
|
||||||
async getContents (ctx) {
|
async getContents (ctx) {
|
||||||
const clientPlugins = await annotatePlugins(ctx.nuxt, ctx.app.plugins.filter(p => !p.mode || p.mode !== 'server'))
|
const clientPlugins = await annotatePlugins(ctx.nuxt, ctx.app.plugins.filter(p => !p.mode || p.mode !== 'server'))
|
||||||
checkForCircularDependencies(clientPlugins)
|
checkForCircularDependencies(clientPlugins)
|
||||||
@ -66,7 +68,7 @@ export const clientPluginTemplate: NuxtTemplate = {
|
|||||||
const imports: string[] = []
|
const imports: string[] = []
|
||||||
for (const plugin of clientPlugins) {
|
for (const plugin of clientPlugins) {
|
||||||
const path = relative(ctx.nuxt.options.rootDir, plugin.src)
|
const path = relative(ctx.nuxt.options.rootDir, plugin.src)
|
||||||
const variable = genSafeVariableName(filename(plugin.src)).replace(/_(45|46|47)/g, '_') + '_' + hash(path)
|
const variable = genSafeVariableName(filename(plugin.src)).replace(PLUGIN_TEMPLATE_RE, '_') + '_' + hash(path)
|
||||||
exports.push(variable)
|
exports.push(variable)
|
||||||
imports.push(genImport(plugin.src, variable))
|
imports.push(genImport(plugin.src, variable))
|
||||||
}
|
}
|
||||||
@ -78,7 +80,7 @@ export const clientPluginTemplate: NuxtTemplate = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const serverPluginTemplate: NuxtTemplate = {
|
export const serverPluginTemplate: NuxtTemplate = {
|
||||||
filename: 'plugins/server.mjs',
|
filename: 'plugins.server.mjs',
|
||||||
async getContents (ctx) {
|
async getContents (ctx) {
|
||||||
const serverPlugins = await annotatePlugins(ctx.nuxt, ctx.app.plugins.filter(p => !p.mode || p.mode !== 'client'))
|
const serverPlugins = await annotatePlugins(ctx.nuxt, ctx.app.plugins.filter(p => !p.mode || p.mode !== 'client'))
|
||||||
checkForCircularDependencies(serverPlugins)
|
checkForCircularDependencies(serverPlugins)
|
||||||
@ -86,7 +88,7 @@ export const serverPluginTemplate: NuxtTemplate = {
|
|||||||
const imports: string[] = []
|
const imports: string[] = []
|
||||||
for (const plugin of serverPlugins) {
|
for (const plugin of serverPlugins) {
|
||||||
const path = relative(ctx.nuxt.options.rootDir, plugin.src)
|
const path = relative(ctx.nuxt.options.rootDir, plugin.src)
|
||||||
const variable = genSafeVariableName(filename(path)).replace(/_(45|46|47)/g, '_') + '_' + hash(path)
|
const variable = genSafeVariableName(filename(path)).replace(PLUGIN_TEMPLATE_RE, '_') + '_' + hash(path)
|
||||||
exports.push(variable)
|
exports.push(variable)
|
||||||
imports.push(genImport(plugin.src, variable))
|
imports.push(genImport(plugin.src, variable))
|
||||||
}
|
}
|
||||||
@ -98,7 +100,9 @@ export const serverPluginTemplate: NuxtTemplate = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const TS_RE = /\.[cm]?tsx?$/
|
const TS_RE = /\.[cm]?tsx?$/
|
||||||
|
const JS_LETTER_RE = /\.(?<letter>[cm])?jsx?$/
|
||||||
|
const JS_RE = /\.[cm]jsx?$/
|
||||||
|
const JS_CAPTURE_RE = /\.[cm](jsx?)$/
|
||||||
export const pluginsDeclaration: NuxtTemplate = {
|
export const pluginsDeclaration: NuxtTemplate = {
|
||||||
filename: 'types/plugins.d.ts',
|
filename: 'types/plugins.d.ts',
|
||||||
getContents: async ({ nuxt, app }) => {
|
getContents: async ({ nuxt, app }) => {
|
||||||
@ -120,18 +124,18 @@ export const pluginsDeclaration: NuxtTemplate = {
|
|||||||
const pluginPath = resolve(typesDir, plugin.src)
|
const pluginPath = resolve(typesDir, plugin.src)
|
||||||
const relativePath = relative(typesDir, pluginPath)
|
const relativePath = relative(typesDir, pluginPath)
|
||||||
|
|
||||||
const correspondingDeclaration = pluginPath.replace(/\.(?<letter>[cm])?jsx?$/, '.d.$<letter>ts')
|
const correspondingDeclaration = pluginPath.replace(JS_LETTER_RE, '.d.$<letter>ts')
|
||||||
// if `.d.ts` file exists alongside a `.js` plugin, or if `.d.mts` file exists alongside a `.mjs` plugin, we can use the entire path
|
// if `.d.ts` file exists alongside a `.js` plugin, or if `.d.mts` file exists alongside a `.mjs` plugin, we can use the entire path
|
||||||
if (correspondingDeclaration !== pluginPath && exists(correspondingDeclaration)) {
|
if (correspondingDeclaration !== pluginPath && exists(correspondingDeclaration)) {
|
||||||
tsImports.push(relativePath)
|
tsImports.push(relativePath)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const incorrectDeclaration = pluginPath.replace(/\.[cm]jsx?$/, '.d.ts')
|
const incorrectDeclaration = pluginPath.replace(JS_RE, '.d.ts')
|
||||||
// if `.d.ts` file exists, but plugin is `.mjs`, add `.js` extension to the import
|
// if `.d.ts` file exists, but plugin is `.mjs`, add `.js` extension to the import
|
||||||
// to hotfix issue until ecosystem updates to `@nuxt/module-builder@>=0.8.0`
|
// to hotfix issue until ecosystem updates to `@nuxt/module-builder@>=0.8.0`
|
||||||
if (incorrectDeclaration !== pluginPath && exists(incorrectDeclaration)) {
|
if (incorrectDeclaration !== pluginPath && exists(incorrectDeclaration)) {
|
||||||
tsImports.push(relativePath.replace(/\.[cm](jsx?)$/, '.$1'))
|
tsImports.push(relativePath.replace(JS_CAPTURE_RE, '.$1'))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,11 +178,13 @@ export { }
|
|||||||
}
|
}
|
||||||
|
|
||||||
const adHocModules = ['router', 'pages', 'imports', 'meta', 'components', 'nuxt-config-schema']
|
const adHocModules = ['router', 'pages', 'imports', 'meta', 'components', 'nuxt-config-schema']
|
||||||
|
const IMPORT_NAME_RE = /\.\w+$/
|
||||||
|
const GIT_RE = /^git\+/
|
||||||
export const schemaTemplate: NuxtTemplate = {
|
export const schemaTemplate: NuxtTemplate = {
|
||||||
filename: 'types/schema.d.ts',
|
filename: 'types/schema.d.ts',
|
||||||
getContents: async ({ nuxt }) => {
|
getContents: async ({ nuxt }) => {
|
||||||
const relativeRoot = relative(resolve(nuxt.options.buildDir, 'types'), nuxt.options.rootDir)
|
const relativeRoot = relative(resolve(nuxt.options.buildDir, 'types'), nuxt.options.rootDir)
|
||||||
const getImportName = (name: string) => (name[0] === '.' ? './' + join(relativeRoot, name) : name).replace(/\.\w+$/, '')
|
const getImportName = (name: string) => (name[0] === '.' ? './' + join(relativeRoot, name) : name).replace(IMPORT_NAME_RE, '')
|
||||||
|
|
||||||
const modules = nuxt.options._installedModules
|
const modules = nuxt.options._installedModules
|
||||||
.filter(m => m.meta && m.meta.configKey && m.meta.name && !adHocModules.includes(m.meta.name))
|
.filter(m => m.meta && m.meta.configKey && m.meta.name && !adHocModules.includes(m.meta.name))
|
||||||
@ -210,7 +216,7 @@ export const schemaTemplate: NuxtTemplate = {
|
|||||||
}
|
}
|
||||||
if (link) {
|
if (link) {
|
||||||
if (link.startsWith('git+')) {
|
if (link.startsWith('git+')) {
|
||||||
link = link.replace(/^git\+/, '')
|
link = link.replace(GIT_RE, '')
|
||||||
}
|
}
|
||||||
if (!link.startsWith('http')) {
|
if (!link.startsWith('http')) {
|
||||||
link = 'https://github.com/' + link
|
link = 'https://github.com/' + link
|
||||||
@ -377,7 +383,7 @@ export const appConfigDeclarationTemplate: NuxtTemplate = {
|
|||||||
filename: 'types/app.config.d.ts',
|
filename: 'types/app.config.d.ts',
|
||||||
getContents ({ app, nuxt }) {
|
getContents ({ app, nuxt }) {
|
||||||
const typesDir = join(nuxt.options.buildDir, 'types')
|
const typesDir = join(nuxt.options.buildDir, 'types')
|
||||||
const configPaths = app.configs.map(path => relative(typesDir, path).replace(/\b\.\w+$/g, ''))
|
const configPaths = app.configs.map(path => relative(typesDir, path).replace(EXTENSION_RE, ''))
|
||||||
|
|
||||||
return `
|
return `
|
||||||
import type { CustomAppConfig } from 'nuxt/schema'
|
import type { CustomAppConfig } from 'nuxt/schema'
|
||||||
|
@ -14,3 +14,7 @@ export function uniqueBy<T, K extends keyof T> (arr: T[], key: K) {
|
|||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const QUOTE_RE = /["']/g
|
||||||
|
export const EXTENSION_RE = /\b\.\w+$/g
|
||||||
|
export const SX_RE = /\.[tj]sx$/
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { basename, dirname, extname, normalize } from 'pathe'
|
import { basename, dirname, extname, normalize } from 'pathe'
|
||||||
import { kebabCase, splitByCase } from 'scule'
|
import { kebabCase, splitByCase } from 'scule'
|
||||||
import { withTrailingSlash } from 'ufo'
|
import { withTrailingSlash } from 'ufo'
|
||||||
|
import { QUOTE_RE } from '.'
|
||||||
|
|
||||||
export function getNameFromPath (path: string, relativeTo?: string) {
|
export function getNameFromPath (path: string, relativeTo?: string) {
|
||||||
const relativePath = relativeTo
|
const relativePath = relativeTo
|
||||||
@ -9,7 +10,7 @@ export function getNameFromPath (path: string, relativeTo?: string) {
|
|||||||
const prefixParts = splitByCase(dirname(relativePath))
|
const prefixParts = splitByCase(dirname(relativePath))
|
||||||
const fileName = basename(relativePath, extname(relativePath))
|
const fileName = basename(relativePath, extname(relativePath))
|
||||||
const segments = resolveComponentNameSegments(fileName.toLowerCase() === 'index' ? '' : fileName, prefixParts).filter(Boolean)
|
const segments = resolveComponentNameSegments(fileName.toLowerCase() === 'index' ? '' : fileName, prefixParts).filter(Boolean)
|
||||||
return kebabCase(segments).replace(/["']/g, '')
|
return kebabCase(segments).replace(QUOTE_RE, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hasSuffix (path: string, suffix: string) {
|
export function hasSuffix (path: string, suffix: string) {
|
||||||
|
@ -76,8 +76,8 @@ export default import.meta.server ? [CapoPlugin({ track: true })] : [];`
|
|||||||
|
|
||||||
// template is only exposed in nuxt context, expose in nitro context as well
|
// template is only exposed in nuxt context, expose in nitro context as well
|
||||||
nuxt.hooks.hook('nitro:config', (config) => {
|
nuxt.hooks.hook('nitro:config', (config) => {
|
||||||
config.virtual!['#internal/unhead-plugins.mjs'] = () => nuxt.vfs['#build/unhead-plugins']
|
config.virtual!['#internal/unhead-plugins.mjs'] = () => nuxt.vfs['#build/unhead-plugins.mjs']
|
||||||
config.virtual!['#internal/unhead.config.mjs'] = () => nuxt.vfs['#build/unhead.config']
|
config.virtual!['#internal/unhead.config.mjs'] = () => nuxt.vfs['#build/unhead.config.mjs']
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add library-specific plugin
|
// Add library-specific plugin
|
||||||
|
@ -66,7 +66,7 @@ const granularAppPresets: InlinePreset[] = [
|
|||||||
from: '#app/composables/cookie',
|
from: '#app/composables/cookie',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
imports: ['onPrehydrate', 'prerenderRoutes', 'useRequestHeader', 'useRequestHeaders', 'useRequestEvent', 'useRequestFetch', 'setResponseStatus'],
|
imports: ['onPrehydrate', 'prerenderRoutes', 'useRequestHeader', 'useRequestHeaders', 'useResponseHeader', 'useRequestEvent', 'useRequestFetch', 'setResponseStatus'],
|
||||||
from: '#app/composables/ssr',
|
from: '#app/composables/ssr',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -52,7 +52,7 @@ export default defineNuxtModule({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add default options at beginning
|
// Add default options at beginning
|
||||||
context.files.unshift({ path: resolve(runtimeDir, 'router.options'), optional: true })
|
context.files.unshift({ path: await findPath(resolve(runtimeDir, 'router.options')) || resolve(runtimeDir, 'router.options'), optional: true })
|
||||||
|
|
||||||
await nuxt.callHook('pages:routerOptions', context)
|
await nuxt.callHook('pages:routerOptions', context)
|
||||||
return context.files
|
return context.files
|
||||||
@ -170,10 +170,15 @@ export default defineNuxtModule({
|
|||||||
if (nuxt.apps.default) {
|
if (nuxt.apps.default) {
|
||||||
nuxt.apps.default.pages = pages
|
nuxt.apps.default.pages = pages
|
||||||
}
|
}
|
||||||
|
const addedPagePaths = new Set<string>()
|
||||||
function addPage (parent: EditableTreeNode, page: NuxtPage) {
|
function addPage (parent: EditableTreeNode, page: NuxtPage) {
|
||||||
|
// Avoid duplicate keys in the generated RouteNamedMap type
|
||||||
|
const absolutePagePath = joinURL(parent.path, page.path)
|
||||||
|
|
||||||
// @ts-expect-error TODO: either fix types upstream or figure out another
|
// @ts-expect-error TODO: either fix types upstream or figure out another
|
||||||
// way to add a route without a file, which must be possible
|
// way to add a route without a file, which must be possible
|
||||||
const route = parent.insert(page.path, page.file)
|
const route = addedPagePaths.has(absolutePagePath) ? parent : parent.insert(page.path, page.file)
|
||||||
|
addedPagePaths.add(absolutePagePath)
|
||||||
if (page.meta) {
|
if (page.meta) {
|
||||||
route.addToMeta(page.meta)
|
route.addToMeta(page.meta)
|
||||||
}
|
}
|
||||||
@ -414,8 +419,18 @@ export default defineNuxtModule({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const componentStubPath = await resolvePath(resolve(runtimeDir, 'component-stub'))
|
||||||
|
if (nuxt.options.test && nuxt.options.dev) {
|
||||||
|
// add component testing route so 404 won't be triggered
|
||||||
|
nuxt.hook('pages:extend', (routes) => {
|
||||||
|
routes.push({
|
||||||
|
_sync: true,
|
||||||
|
path: '/__nuxt_component_test__/:pathMatch(.*)',
|
||||||
|
file: componentStubPath,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
if (nuxt.options.experimental.appManifest) {
|
if (nuxt.options.experimental.appManifest) {
|
||||||
const componentStubPath = await resolvePath(resolve(runtimeDir, 'component-stub'))
|
|
||||||
// Add all redirect paths as valid routes to router; we will handle these in a client-side middleware
|
// Add all redirect paths as valid routes to router; we will handle these in a client-side middleware
|
||||||
// when the app manifest is enabled.
|
// when the app manifest is enabled.
|
||||||
nuxt.hook('pages:extend', (routes) => {
|
nuxt.hook('pages:extend', (routes) => {
|
||||||
@ -477,12 +492,19 @@ export default defineNuxtModule({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const serverComponentRuntime = await findPath(join(distDir, 'components/runtime/server-component')) ?? join(distDir, 'components/runtime/server-component')
|
||||||
|
const clientComponentRuntime = await findPath(join(distDir, 'components/runtime/client-component')) ?? join(distDir, 'components/runtime/client-component')
|
||||||
|
|
||||||
// Add routes template
|
// Add routes template
|
||||||
addTemplate({
|
addTemplate({
|
||||||
filename: 'routes.mjs',
|
filename: 'routes.mjs',
|
||||||
getContents ({ app }) {
|
getContents ({ app }) {
|
||||||
if (!app.pages) { return 'export default []' }
|
if (!app.pages) { return 'export default []' }
|
||||||
const { routes, imports } = normalizeRoutes(app.pages, new Set(), nuxt.options.experimental.scanPageMeta)
|
const { routes, imports } = normalizeRoutes(app.pages, new Set(), {
|
||||||
|
serverComponentRuntime,
|
||||||
|
clientComponentRuntime,
|
||||||
|
overrideMeta: !!nuxt.options.experimental.scanPageMeta,
|
||||||
|
})
|
||||||
return [...imports, `export default ${routes}`].join('\n')
|
return [...imports, `export default ${routes}`].join('\n')
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -176,8 +176,10 @@ export const PageMetaPlugin = (options: PageMetaPluginOptions) => createUnplugin
|
|||||||
|
|
||||||
// https://github.com/vuejs/vue-loader/pull/1911
|
// https://github.com/vuejs/vue-loader/pull/1911
|
||||||
// https://github.com/vitejs/vite/issues/8473
|
// https://github.com/vitejs/vite/issues/8473
|
||||||
|
const QUERY_START_RE = /^\?/
|
||||||
|
const MACRO_RE = /¯o=true/
|
||||||
function rewriteQuery (id: string) {
|
function rewriteQuery (id: string) {
|
||||||
return id.replace(/\?.+$/, r => '?macro=true&' + r.replace(/^\?/, '').replace(/¯o=true/, ''))
|
return id.replace(/\?.+$/, r => '?macro=true&' + r.replace(QUERY_START_RE, '').replace(MACRO_RE, ''))
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseMacroQuery (id: string) {
|
function parseMacroQuery (id: string) {
|
||||||
@ -189,6 +191,7 @@ function parseMacroQuery (id: string) {
|
|||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QUOTED_SPECIFIER_RE = /(["']).*\1/
|
||||||
function getQuotedSpecifier (id: string) {
|
function getQuotedSpecifier (id: string) {
|
||||||
return id.match(/(["']).*\1/)?.[0]
|
return id.match(QUOTED_SPECIFIER_RE)?.[0]
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { KeepAliveProps, TransitionProps, UnwrapRef } from 'vue'
|
import type { KeepAliveProps, TransitionProps, UnwrapRef } from 'vue'
|
||||||
import { getCurrentInstance } from 'vue'
|
import { getCurrentInstance } from 'vue'
|
||||||
import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteRecordRedirectOption } from 'vue-router'
|
import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteRecordRaw, RouteRecordRedirectOption } from 'vue-router'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import type { NitroRouteConfig } from 'nitro/types'
|
import type { NitroRouteConfig } from 'nitro/types'
|
||||||
import { useNuxtApp } from '#app/nuxt'
|
import { useNuxtApp } from '#app/nuxt'
|
||||||
@ -37,6 +37,11 @@ export interface PageMeta {
|
|||||||
name?: string
|
name?: string
|
||||||
/** You may define a path matcher, if you have a more complex pattern than can be expressed with the file name. */
|
/** You may define a path matcher, if you have a more complex pattern than can be expressed with the file name. */
|
||||||
path?: string
|
path?: string
|
||||||
|
/**
|
||||||
|
* Allows accessing the route `params` as props passed to the page component.
|
||||||
|
* @see https://router.vuejs.org/guide/essentials/passing-props
|
||||||
|
*/
|
||||||
|
props?: RouteRecordRaw['props']
|
||||||
/** Set to `false` to avoid scrolling to top on page navigations */
|
/** Set to `false` to avoid scrolling to top on page navigations */
|
||||||
scrollToTop?: boolean | ((to: RouteLocationNormalizedLoaded, from: RouteLocationNormalizedLoaded) => boolean)
|
scrollToTop?: boolean | ((to: RouteLocationNormalizedLoaded, from: RouteLocationNormalizedLoaded) => boolean)
|
||||||
}
|
}
|
||||||
|
@ -148,16 +148,8 @@ const plugin: Plugin<{ router: Router }> = defineNuxtPlugin({
|
|||||||
if (import.meta.server && failure?.type === 4 /* ErrorTypes.NAVIGATION_ABORTED */) {
|
if (import.meta.server && failure?.type === 4 /* ErrorTypes.NAVIGATION_ABORTED */) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (to.matched.length === 0) {
|
|
||||||
await nuxtApp.runWithContext(() => showError(createError({
|
if (import.meta.server && to.redirectedFrom && to.fullPath !== initialURL) {
|
||||||
statusCode: 404,
|
|
||||||
fatal: false,
|
|
||||||
statusMessage: `Page not found: ${to.fullPath}`,
|
|
||||||
data: {
|
|
||||||
path: to.fullPath,
|
|
||||||
},
|
|
||||||
})))
|
|
||||||
} else if (import.meta.server && to.redirectedFrom && to.fullPath !== initialURL) {
|
|
||||||
await nuxtApp.runWithContext(() => navigateTo(to.fullPath || '/'))
|
await nuxtApp.runWithContext(() => navigateTo(to.fullPath || '/'))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -252,6 +244,19 @@ const plugin: Plugin<{ router: Router }> = defineNuxtPlugin({
|
|||||||
await nuxtApp.callHook('page:loading:end')
|
await nuxtApp.callHook('page:loading:end')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
router.afterEach(async (to, _from) => {
|
||||||
|
if (to.matched.length === 0) {
|
||||||
|
await nuxtApp.runWithContext(() => showError(createError({
|
||||||
|
statusCode: 404,
|
||||||
|
fatal: false,
|
||||||
|
statusMessage: `Page not found: ${to.fullPath}`,
|
||||||
|
data: {
|
||||||
|
path: to.fullPath,
|
||||||
|
},
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
nuxtApp.hooks.hookOnce('app:created', async () => {
|
nuxtApp.hooks.hookOnce('app:created', async () => {
|
||||||
try {
|
try {
|
||||||
// #4920, #4982
|
// #4920, #4982
|
||||||
|
@ -5,11 +5,14 @@ type InstanceOf<T> = T extends new (...args: any[]) => infer R ? R : never
|
|||||||
type RouterViewSlot = Exclude<InstanceOf<typeof RouterView>['$slots']['default'], undefined>
|
type RouterViewSlot = Exclude<InstanceOf<typeof RouterView>['$slots']['default'], undefined>
|
||||||
export type RouterViewSlotProps = Parameters<RouterViewSlot>[0]
|
export type RouterViewSlotProps = Parameters<RouterViewSlot>[0]
|
||||||
|
|
||||||
|
const ROUTE_KEY_PARENTHESES_RE = /(:\w+)\([^)]+\)/g
|
||||||
|
const ROUTE_KEY_SYMBOLS_RE = /(:\w+)[?+*]/g
|
||||||
|
const ROUTE_KEY_NORMAL_RE = /:\w+/g
|
||||||
const interpolatePath = (route: RouteLocationNormalizedLoaded, match: RouteLocationMatched) => {
|
const interpolatePath = (route: RouteLocationNormalizedLoaded, match: RouteLocationMatched) => {
|
||||||
return match.path
|
return match.path
|
||||||
.replace(/(:\w+)\([^)]+\)/g, '$1')
|
.replace(ROUTE_KEY_PARENTHESES_RE, '$1')
|
||||||
.replace(/(:\w+)[?+*]/g, '$1')
|
.replace(ROUTE_KEY_SYMBOLS_RE, '$1')
|
||||||
.replace(/:\w+/g, r => route.params[r.slice(1)]?.toString() || '')
|
.replace(ROUTE_KEY_NORMAL_RE, r => route.params[r.slice(1)]?.toString() || '')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateRouteKey = (routeProps: RouterViewSlotProps, override?: string | ((route: RouteLocationNormalizedLoaded) => string)) => {
|
export const generateRouteKey = (routeProps: RouterViewSlotProps, override?: string | ((route: RouteLocationNormalizedLoaded) => string)) => {
|
||||||
|
@ -15,7 +15,6 @@ import type { NuxtPage } from 'nuxt/schema'
|
|||||||
|
|
||||||
import { getLoader, uniqueBy } from '../core/utils'
|
import { getLoader, uniqueBy } from '../core/utils'
|
||||||
import { toArray } from '../utils'
|
import { toArray } from '../utils'
|
||||||
import { distDir } from '../dirs'
|
|
||||||
|
|
||||||
enum SegmentParserState {
|
enum SegmentParserState {
|
||||||
initial,
|
initial,
|
||||||
@ -65,18 +64,25 @@ export async function resolvePagesRoutes (): Promise<NuxtPage[]> {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const pages = uniqueBy(allRoutes, 'path')
|
const pages = uniqueBy(allRoutes, 'path')
|
||||||
|
|
||||||
const shouldAugment = nuxt.options.experimental.scanPageMeta || nuxt.options.experimental.typedPages
|
const shouldAugment = nuxt.options.experimental.scanPageMeta || nuxt.options.experimental.typedPages
|
||||||
|
|
||||||
if (shouldAugment) {
|
if (shouldAugment === false) {
|
||||||
|
await nuxt.callHook('pages:extend', pages)
|
||||||
|
return pages
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldAugment === 'after-resolve') {
|
||||||
|
await nuxt.callHook('pages:extend', pages)
|
||||||
|
await augmentPages(pages, nuxt.vfs)
|
||||||
|
} else {
|
||||||
const augmentedPages = await augmentPages(pages, nuxt.vfs)
|
const augmentedPages = await augmentPages(pages, nuxt.vfs)
|
||||||
await nuxt.callHook('pages:extend', pages)
|
await nuxt.callHook('pages:extend', pages)
|
||||||
await augmentPages(pages, nuxt.vfs, augmentedPages)
|
await augmentPages(pages, nuxt.vfs, augmentedPages)
|
||||||
augmentedPages.clear()
|
augmentedPages.clear()
|
||||||
} else {
|
|
||||||
await nuxt.callHook('pages:extend', pages)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await nuxt.callHook('pages:resolved', pages)
|
||||||
|
|
||||||
return pages
|
return pages
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,6 +90,7 @@ type GenerateRoutesFromFilesOptions = {
|
|||||||
shouldUseServerComponents?: boolean
|
shouldUseServerComponents?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const INDEX_PAGE_RE = /\/index$/
|
||||||
export function generateRoutesFromFiles (files: ScannedFile[], options: GenerateRoutesFromFilesOptions = {}): NuxtPage[] {
|
export function generateRoutesFromFiles (files: ScannedFile[], options: GenerateRoutesFromFilesOptions = {}): NuxtPage[] {
|
||||||
const routes: NuxtPage[] = []
|
const routes: NuxtPage[] = []
|
||||||
|
|
||||||
@ -129,7 +136,7 @@ export function generateRoutesFromFiles (files: ScannedFile[], options: Generate
|
|||||||
route.name += (route.name && '/') + segmentName
|
route.name += (route.name && '/') + segmentName
|
||||||
|
|
||||||
// ex: parent.vue + parent/child.vue
|
// ex: parent.vue + parent/child.vue
|
||||||
const path = withLeadingSlash(joinURL(route.path, getRoutePath(tokens).replace(/\/index$/, '/')))
|
const path = withLeadingSlash(joinURL(route.path, getRoutePath(tokens).replace(INDEX_PAGE_RE, '/')))
|
||||||
const child = parent.find(parentRoute => parentRoute.name === route.name && parentRoute.path === path)
|
const child = parent.find(parentRoute => parentRoute.name === route.name && parentRoute.path === path)
|
||||||
|
|
||||||
if (child && child.children) {
|
if (child && child.children) {
|
||||||
@ -184,7 +191,7 @@ export function extractScriptContent (html: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const PAGE_META_RE = /definePageMeta\([\s\S]*?\)/
|
const PAGE_META_RE = /definePageMeta\([\s\S]*?\)/
|
||||||
const extractionKeys = ['name', 'path', 'alias', 'redirect'] as const
|
const extractionKeys = ['name', 'path', 'props', 'alias', 'redirect'] as const
|
||||||
const DYNAMIC_META_KEY = '__nuxt_dynamic_meta_key' as const
|
const DYNAMIC_META_KEY = '__nuxt_dynamic_meta_key' as const
|
||||||
|
|
||||||
const pageContentsCache: Record<string, string> = {}
|
const pageContentsCache: Record<string, string> = {}
|
||||||
@ -266,7 +273,7 @@ export async function getRouteMeta (contents: string, absolutePath: string): Pro
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (property.value.type !== 'Literal' || typeof property.value.value !== 'string') {
|
if (property.value.type !== 'Literal' || (typeof property.value.value !== 'string' && typeof property.value.value !== 'boolean')) {
|
||||||
console.debug(`[nuxt] Skipping extraction of \`${key}\` metadata as it is not a string literal or array of string literals (reading \`${absolutePath}\`).`)
|
console.debug(`[nuxt] Skipping extraction of \`${key}\` metadata as it is not a string literal or array of string literals (reading \`${absolutePath}\`).`)
|
||||||
dynamicProperties.add(key)
|
dynamicProperties.add(key)
|
||||||
continue
|
continue
|
||||||
@ -301,6 +308,7 @@ export async function getRouteMeta (contents: string, absolutePath: string): Pro
|
|||||||
return extractedMeta
|
return extractedMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const COLON_RE = /:/g
|
||||||
function getRoutePath (tokens: SegmentToken[]): string {
|
function getRoutePath (tokens: SegmentToken[]): string {
|
||||||
return tokens.reduce((path, token) => {
|
return tokens.reduce((path, token) => {
|
||||||
return (
|
return (
|
||||||
@ -313,7 +321,7 @@ function getRoutePath (tokens: SegmentToken[]): string {
|
|||||||
? `:${token.value}(.*)*`
|
? `:${token.value}(.*)*`
|
||||||
: token.type === SegmentTokenType.group
|
: token.type === SegmentTokenType.group
|
||||||
? ''
|
? ''
|
||||||
: encodePath(token.value).replace(/:/g, '\\:'))
|
: encodePath(token.value).replace(COLON_RE, '\\:'))
|
||||||
)
|
)
|
||||||
}, '/')
|
}, '/')
|
||||||
}
|
}
|
||||||
@ -433,13 +441,14 @@ function findRouteByName (name: string, routes: NuxtPage[]): NuxtPage | undefine
|
|||||||
return findRouteByName(name, routes)
|
return findRouteByName(name, routes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NESTED_PAGE_RE = /\//g
|
||||||
function prepareRoutes (routes: NuxtPage[], parent?: NuxtPage, names = new Set<string>()) {
|
function prepareRoutes (routes: NuxtPage[], parent?: NuxtPage, names = new Set<string>()) {
|
||||||
for (const route of routes) {
|
for (const route of routes) {
|
||||||
// Remove -index
|
// Remove -index
|
||||||
if (route.name) {
|
if (route.name) {
|
||||||
route.name = route.name
|
route.name = route.name
|
||||||
.replace(/\/index$/, '')
|
.replace(INDEX_PAGE_RE, '')
|
||||||
.replace(/\//g, '-')
|
.replace(NESTED_PAGE_RE, '-')
|
||||||
|
|
||||||
if (names.has(route.name)) {
|
if (names.has(route.name)) {
|
||||||
const existingRoute = findRouteByName(route.name, routes)
|
const existingRoute = findRouteByName(route.name, routes)
|
||||||
@ -476,7 +485,12 @@ function serializeRouteValue (value: any, skipSerialisation = false) {
|
|||||||
|
|
||||||
type NormalizedRoute = Partial<Record<Exclude<keyof NuxtPage, 'file'>, string>> & { component?: string }
|
type NormalizedRoute = Partial<Record<Exclude<keyof NuxtPage, 'file'>, string>> & { component?: string }
|
||||||
type NormalizedRouteKeys = (keyof NormalizedRoute)[]
|
type NormalizedRouteKeys = (keyof NormalizedRoute)[]
|
||||||
export function normalizeRoutes (routes: NuxtPage[], metaImports: Set<string> = new Set(), overrideMeta = false): { imports: Set<string>, routes: string } {
|
interface NormalizeRoutesOptions {
|
||||||
|
overrideMeta?: boolean
|
||||||
|
serverComponentRuntime: string
|
||||||
|
clientComponentRuntime: string
|
||||||
|
}
|
||||||
|
export function normalizeRoutes (routes: NuxtPage[], metaImports: Set<string> = new Set(), options: NormalizeRoutesOptions): { imports: Set<string>, routes: string } {
|
||||||
return {
|
return {
|
||||||
imports: metaImports,
|
imports: metaImports,
|
||||||
routes: genArrayFromRaw(routes.map((page) => {
|
routes: genArrayFromRaw(routes.map((page) => {
|
||||||
@ -506,7 +520,7 @@ export function normalizeRoutes (routes: NuxtPage[], metaImports: Set<string> =
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (page.children?.length) {
|
if (page.children?.length) {
|
||||||
route.children = normalizeRoutes(page.children, metaImports, overrideMeta).routes
|
route.children = normalizeRoutes(page.children, metaImports, options).routes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Without a file, we can't use `definePageMeta` to extract route-level meta from the file
|
// Without a file, we can't use `definePageMeta` to extract route-level meta from the file
|
||||||
@ -528,6 +542,7 @@ export function normalizeRoutes (routes: NuxtPage[], metaImports: Set<string> =
|
|||||||
const metaRoute: NormalizedRoute = {
|
const metaRoute: NormalizedRoute = {
|
||||||
name: `${metaImportName}?.name ?? ${route.name}`,
|
name: `${metaImportName}?.name ?? ${route.name}`,
|
||||||
path: `${metaImportName}?.path ?? ${route.path}`,
|
path: `${metaImportName}?.path ?? ${route.path}`,
|
||||||
|
props: `${metaImportName}?.props ?? false`,
|
||||||
meta: `${metaImportName} || {}`,
|
meta: `${metaImportName} || {}`,
|
||||||
alias: `${metaImportName}?.alias || []`,
|
alias: `${metaImportName}?.alias || []`,
|
||||||
redirect: `${metaImportName}?.redirect`,
|
redirect: `${metaImportName}?.redirect`,
|
||||||
@ -542,14 +557,14 @@ export function normalizeRoutes (routes: NuxtPage[], metaImports: Set<string> =
|
|||||||
metaImports.add(`
|
metaImports.add(`
|
||||||
let _createIslandPage
|
let _createIslandPage
|
||||||
async function createIslandPage (name) {
|
async function createIslandPage (name) {
|
||||||
_createIslandPage ||= await import(${JSON.stringify(resolve(distDir, 'components/runtime/server-component'))}).then(r => r.createIslandPage)
|
_createIslandPage ||= await import(${JSON.stringify(options?.serverComponentRuntime)}).then(r => r.createIslandPage)
|
||||||
return _createIslandPage(name)
|
return _createIslandPage(name)
|
||||||
};`)
|
};`)
|
||||||
} else if (page.mode === 'client') {
|
} else if (page.mode === 'client') {
|
||||||
metaImports.add(`
|
metaImports.add(`
|
||||||
let _createClientPage
|
let _createClientPage
|
||||||
async function createClientPage(loader) {
|
async function createClientPage(loader) {
|
||||||
_createClientPage ||= await import(${JSON.stringify(resolve(distDir, 'components/runtime/client-component'))}).then(r => r.createClientPage)
|
_createClientPage ||= await import(${JSON.stringify(options?.clientComponentRuntime)}).then(r => r.createClientPage)
|
||||||
return _createClientPage(loader);
|
return _createClientPage(loader);
|
||||||
}`)
|
}`)
|
||||||
}
|
}
|
||||||
@ -562,7 +577,7 @@ async function createClientPage(loader) {
|
|||||||
metaRoute.meta = `{ ...(${metaImportName} || {}), ...${route.meta} }`
|
metaRoute.meta = `{ ...(${metaImportName} || {}), ...${route.meta} }`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overrideMeta) {
|
if (options?.overrideMeta) {
|
||||||
// skip and retain fallback if marked dynamic
|
// skip and retain fallback if marked dynamic
|
||||||
// set to extracted value or fallback if none extracted
|
// set to extracted value or fallback if none extracted
|
||||||
for (const key of ['name', 'path'] satisfies NormalizedRouteKeys) {
|
for (const key of ['name', 'path'] satisfies NormalizedRouteKeys) {
|
||||||
@ -571,7 +586,7 @@ async function createClientPage(loader) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set to extracted value or delete if none extracted
|
// set to extracted value or delete if none extracted
|
||||||
for (const key of ['meta', 'alias', 'redirect'] satisfies NormalizedRouteKeys) {
|
for (const key of ['meta', 'alias', 'redirect', 'props'] satisfies NormalizedRouteKeys) {
|
||||||
if (markedDynamic.has(key)) { continue }
|
if (markedDynamic.has(key)) { continue }
|
||||||
|
|
||||||
if (route[key] == null) {
|
if (route[key] == null) {
|
||||||
@ -596,6 +611,7 @@ async function createClientPage(loader) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PATH_TO_NITRO_GLOB_RE = /\/[^:/]*:\w.*$/
|
||||||
export function pathToNitroGlob (path: string) {
|
export function pathToNitroGlob (path: string) {
|
||||||
if (!path) {
|
if (!path) {
|
||||||
return null
|
return null
|
||||||
@ -605,7 +621,7 @@ export function pathToNitroGlob (path: string) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return path.replace(/\/[^:/]*:\w.*$/, '/**')
|
return path.replace(PATH_TO_NITRO_GLOB_RE, '/**')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveRoutePaths (page: NuxtPage, parent = '/'): string[] {
|
export function resolveRoutePaths (page: NuxtPage, parent = '/'): string[] {
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
"meta": "{ ...(mockMeta || {}), ...{"someMetaData":true} }",
|
"meta": "{ ...(mockMeta || {}), ...{"someMetaData":true} }",
|
||||||
"name": "mockMeta?.name ?? "pushed-route"",
|
"name": "mockMeta?.name ?? "pushed-route"",
|
||||||
"path": "mockMeta?.path ?? "/"",
|
"path": "mockMeta?.path ?? "/"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -24,6 +25,18 @@
|
|||||||
"meta": "{ ...(mockMeta || {}), ...{"test":1} }",
|
"meta": "{ ...(mockMeta || {}), ...{"test":1} }",
|
||||||
"name": "mockMeta?.name ?? "page-with-meta"",
|
"name": "mockMeta?.name ?? "page-with-meta"",
|
||||||
"path": "mockMeta?.path ?? "/page-with-meta"",
|
"path": "mockMeta?.path ?? "/page-with-meta"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
|
"redirect": "mockMeta?.redirect",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"route.meta props generate by file": [
|
||||||
|
{
|
||||||
|
"alias": "mockMeta?.alias || []",
|
||||||
|
"component": "() => import("pages/page-with-props.vue")",
|
||||||
|
"meta": "mockMeta || {}",
|
||||||
|
"name": "mockMeta?.name ?? "page-with-props"",
|
||||||
|
"path": "mockMeta?.path ?? "/page-with-props"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -34,6 +47,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "test:name"",
|
"name": "mockMeta?.name ?? "test:name"",
|
||||||
"path": "mockMeta?.path ?? "/test\\:name"",
|
"path": "mockMeta?.path ?? "/test\\:name"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -50,6 +64,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "param-index"",
|
"name": "mockMeta?.name ?? "param-index"",
|
||||||
"path": "mockMeta?.path ?? """,
|
"path": "mockMeta?.path ?? """,
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -58,6 +73,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "param-index-sibling"",
|
"name": "mockMeta?.name ?? "param-index-sibling"",
|
||||||
"path": "mockMeta?.path ?? "sibling"",
|
"path": "mockMeta?.path ?? "sibling"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -65,6 +81,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? undefined",
|
"name": "mockMeta?.name ?? undefined",
|
||||||
"path": "mockMeta?.path ?? """,
|
"path": "mockMeta?.path ?? """,
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -73,6 +90,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "param-sibling"",
|
"name": "mockMeta?.name ?? "param-sibling"",
|
||||||
"path": "mockMeta?.path ?? "sibling"",
|
"path": "mockMeta?.path ?? "sibling"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -80,6 +98,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? undefined",
|
"name": "mockMeta?.name ?? undefined",
|
||||||
"path": "mockMeta?.path ?? "/param"",
|
"path": "mockMeta?.path ?? "/param"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -91,6 +110,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "wrapper-expose-other"",
|
"name": "mockMeta?.name ?? "wrapper-expose-other"",
|
||||||
"path": "mockMeta?.path ?? """,
|
"path": "mockMeta?.path ?? """,
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -99,6 +119,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "wrapper-expose-other-sibling"",
|
"name": "mockMeta?.name ?? "wrapper-expose-other-sibling"",
|
||||||
"path": "mockMeta?.path ?? "sibling"",
|
"path": "mockMeta?.path ?? "sibling"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -106,6 +127,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? undefined",
|
"name": "mockMeta?.name ?? undefined",
|
||||||
"path": "mockMeta?.path ?? "/wrapper-expose/other"",
|
"path": "mockMeta?.path ?? "/wrapper-expose/other"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -116,6 +138,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "home"",
|
"name": "mockMeta?.name ?? "home"",
|
||||||
"path": "mockMeta?.path ?? "/"",
|
"path": "mockMeta?.path ?? "/"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": ""/"",
|
"redirect": ""/"",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -126,6 +149,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "slug"",
|
"name": "mockMeta?.name ?? "slug"",
|
||||||
"path": "mockMeta?.path ?? "/:slug(.*)*"",
|
"path": "mockMeta?.path ?? "/:slug(.*)*"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -134,6 +158,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "index"",
|
"name": "mockMeta?.name ?? "index"",
|
||||||
"path": "mockMeta?.path ?? "/"",
|
"path": "mockMeta?.path ?? "/"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -144,6 +169,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "index"",
|
"name": "mockMeta?.name ?? "index"",
|
||||||
"path": "mockMeta?.path ?? "/"",
|
"path": "mockMeta?.path ?? "/"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -152,6 +178,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "slug"",
|
"name": "mockMeta?.name ?? "slug"",
|
||||||
"path": "mockMeta?.path ?? "/:slug()"",
|
"path": "mockMeta?.path ?? "/:slug()"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -163,6 +190,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "foo"",
|
"name": "mockMeta?.name ?? "foo"",
|
||||||
"path": "mockMeta?.path ?? """,
|
"path": "mockMeta?.path ?? """,
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -170,6 +198,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? undefined",
|
"name": "mockMeta?.name ?? undefined",
|
||||||
"path": "mockMeta?.path ?? "/:foo?"",
|
"path": "mockMeta?.path ?? "/:foo?"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -178,6 +207,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "optional-opt"",
|
"name": "mockMeta?.name ?? "optional-opt"",
|
||||||
"path": "mockMeta?.path ?? "/optional/:opt?"",
|
"path": "mockMeta?.path ?? "/optional/:opt?"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -186,6 +216,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "optional-prefix-opt"",
|
"name": "mockMeta?.name ?? "optional-prefix-opt"",
|
||||||
"path": "mockMeta?.path ?? "/optional/prefix-:opt?"",
|
"path": "mockMeta?.path ?? "/optional/prefix-:opt?"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -194,6 +225,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "optional-opt-postfix"",
|
"name": "mockMeta?.name ?? "optional-opt-postfix"",
|
||||||
"path": "mockMeta?.path ?? "/optional/:opt?-postfix"",
|
"path": "mockMeta?.path ?? "/optional/:opt?-postfix"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -202,6 +234,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "optional-prefix-opt-postfix"",
|
"name": "mockMeta?.name ?? "optional-prefix-opt-postfix"",
|
||||||
"path": "mockMeta?.path ?? "/optional/prefix-:opt?-postfix"",
|
"path": "mockMeta?.path ?? "/optional/prefix-:opt?-postfix"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -210,6 +243,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "bar"",
|
"name": "mockMeta?.name ?? "bar"",
|
||||||
"path": "mockMeta?.path ?? "/:bar()"",
|
"path": "mockMeta?.path ?? "/:bar()"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -218,6 +252,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "nonopt-slug"",
|
"name": "mockMeta?.name ?? "nonopt-slug"",
|
||||||
"path": "mockMeta?.path ?? "/nonopt/:slug()"",
|
"path": "mockMeta?.path ?? "/nonopt/:slug()"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -226,6 +261,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "opt-slug"",
|
"name": "mockMeta?.name ?? "opt-slug"",
|
||||||
"path": "mockMeta?.path ?? "/opt/:slug?"",
|
"path": "mockMeta?.path ?? "/opt/:slug?"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -234,6 +270,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "sub-route-slug"",
|
"name": "mockMeta?.name ?? "sub-route-slug"",
|
||||||
"path": "mockMeta?.path ?? "/:sub?/route-:slug()"",
|
"path": "mockMeta?.path ?? "/:sub?/route-:slug()"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -244,6 +281,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "stories"",
|
"name": "mockMeta?.name ?? "stories"",
|
||||||
"path": "mockMeta?.path ?? "/:stories(.*)*"",
|
"path": "mockMeta?.path ?? "/:stories(.*)*"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -252,6 +290,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "stories-id"",
|
"name": "mockMeta?.name ?? "stories-id"",
|
||||||
"path": "mockMeta?.path ?? "/stories/:id()"",
|
"path": "mockMeta?.path ?? "/stories/:id()"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -262,6 +301,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "stories-id"",
|
"name": "mockMeta?.name ?? "stories-id"",
|
||||||
"path": "mockMeta?.path ?? "/stories/:id()"",
|
"path": "mockMeta?.path ?? "/stories/:id()"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -270,6 +310,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "stories"",
|
"name": "mockMeta?.name ?? "stories"",
|
||||||
"path": "mockMeta?.path ?? "/:stories(.*)*"",
|
"path": "mockMeta?.path ?? "/:stories(.*)*"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -280,6 +321,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "kebab-case"",
|
"name": "mockMeta?.name ?? "kebab-case"",
|
||||||
"path": "mockMeta?.path ?? "/kebab-case"",
|
"path": "mockMeta?.path ?? "/kebab-case"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -290,6 +332,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "snake_case"",
|
"name": "mockMeta?.name ?? "snake_case"",
|
||||||
"path": "mockMeta?.path ?? "/snake_case"",
|
"path": "mockMeta?.path ?? "/snake_case"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -300,6 +343,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "index"",
|
"name": "mockMeta?.name ?? "index"",
|
||||||
"path": "mockMeta?.path ?? "/"",
|
"path": "mockMeta?.path ?? "/"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -308,6 +352,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "parent"",
|
"name": "mockMeta?.name ?? "parent"",
|
||||||
"path": "mockMeta?.path ?? "/parent"",
|
"path": "mockMeta?.path ?? "/parent"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -316,6 +361,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "parent-child"",
|
"name": "mockMeta?.name ?? "parent-child"",
|
||||||
"path": "mockMeta?.path ?? "/parent/child"",
|
"path": "mockMeta?.path ?? "/parent/child"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -329,6 +375,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "parent-child"",
|
"name": "mockMeta?.name ?? "parent-child"",
|
||||||
"path": "mockMeta?.path ?? "child"",
|
"path": "mockMeta?.path ?? "child"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -336,6 +383,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "parent"",
|
"name": "mockMeta?.name ?? "parent"",
|
||||||
"path": "mockMeta?.path ?? "/parent"",
|
"path": "mockMeta?.path ?? "/parent"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -346,6 +394,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "index"",
|
"name": "mockMeta?.name ?? "index"",
|
||||||
"path": "mockMeta?.path ?? "/"",
|
"path": "mockMeta?.path ?? "/"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -357,6 +406,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "about"",
|
"name": "mockMeta?.name ?? "about"",
|
||||||
"path": "mockMeta?.path ?? """,
|
"path": "mockMeta?.path ?? """,
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -364,6 +414,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? undefined",
|
"name": "mockMeta?.name ?? undefined",
|
||||||
"path": "mockMeta?.path ?? "/about"",
|
"path": "mockMeta?.path ?? "/about"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -377,6 +428,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "index-index-all"",
|
"name": "mockMeta?.name ?? "index-index-all"",
|
||||||
"path": "mockMeta?.path ?? "all"",
|
"path": "mockMeta?.path ?? "all"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -384,6 +436,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "index"",
|
"name": "mockMeta?.name ?? "index"",
|
||||||
"path": "mockMeta?.path ?? "/"",
|
"path": "mockMeta?.path ?? "/"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -394,6 +447,7 @@
|
|||||||
"meta": "{ ...(mockMeta || {}), ...{"test":1} }",
|
"meta": "{ ...(mockMeta || {}), ...{"test":1} }",
|
||||||
"name": "mockMeta?.name ?? "page-with-meta"",
|
"name": "mockMeta?.name ?? "page-with-meta"",
|
||||||
"path": "mockMeta?.path ?? "/page-with-meta"",
|
"path": "mockMeta?.path ?? "/page-with-meta"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -404,6 +458,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "parent-child"",
|
"name": "mockMeta?.name ?? "parent-child"",
|
||||||
"path": "mockMeta?.path ?? "/parent/:child()"",
|
"path": "mockMeta?.path ?? "/parent/:child()"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -412,6 +467,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "parent-child"",
|
"name": "mockMeta?.name ?? "parent-child"",
|
||||||
"path": "mockMeta?.path ?? "/parent-:child()"",
|
"path": "mockMeta?.path ?? "/parent-:child()"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -422,6 +478,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "foo"",
|
"name": "mockMeta?.name ?? "foo"",
|
||||||
"path": "mockMeta?.path ?? "/:foo?"",
|
"path": "mockMeta?.path ?? "/:foo?"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -430,6 +487,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "foo"",
|
"name": "mockMeta?.name ?? "foo"",
|
||||||
"path": "mockMeta?.path ?? "/:foo()"",
|
"path": "mockMeta?.path ?? "/:foo()"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -440,6 +498,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "a1_1a"",
|
"name": "mockMeta?.name ?? "a1_1a"",
|
||||||
"path": "mockMeta?.path ?? "/:a1_1a()"",
|
"path": "mockMeta?.path ?? "/:a1_1a()"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -448,6 +507,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "b2.2b"",
|
"name": "mockMeta?.name ?? "b2.2b"",
|
||||||
"path": "mockMeta?.path ?? "/:b2.2b()"",
|
"path": "mockMeta?.path ?? "/:b2.2b()"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -456,6 +516,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "b2_2b"",
|
"name": "mockMeta?.name ?? "b2_2b"",
|
||||||
"path": "mockMeta?.path ?? "/:b2()_:2b()"",
|
"path": "mockMeta?.path ?? "/:b2()_:2b()"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -464,6 +525,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "c33c"",
|
"name": "mockMeta?.name ?? "c33c"",
|
||||||
"path": "mockMeta?.path ?? "/:c33c?"",
|
"path": "mockMeta?.path ?? "/:c33c?"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -472,6 +534,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "d44d"",
|
"name": "mockMeta?.name ?? "d44d"",
|
||||||
"path": "mockMeta?.path ?? "/:d44d?"",
|
"path": "mockMeta?.path ?? "/:d44d?"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -482,6 +545,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "home"",
|
"name": "mockMeta?.name ?? "home"",
|
||||||
"path": "mockMeta?.path ?? "/"",
|
"path": "mockMeta?.path ?? "/"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -492,6 +556,7 @@
|
|||||||
"meta": "mockMeta || {}",
|
"meta": "mockMeta || {}",
|
||||||
"name": "mockMeta?.name ?? "index"",
|
"name": "mockMeta?.name ?? "index"",
|
||||||
"path": "mockMeta?.path ?? "/"",
|
"path": "mockMeta?.path ?? "/"",
|
||||||
|
"props": "mockMeta?.props ?? false",
|
||||||
"redirect": "mockMeta?.redirect",
|
"redirect": "mockMeta?.redirect",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -24,6 +24,13 @@
|
|||||||
"path": ""/page-with-meta"",
|
"path": ""/page-with-meta"",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
"route.meta props generate by file": [
|
||||||
|
{
|
||||||
|
"component": "() => import("pages/page-with-props.vue")",
|
||||||
|
"name": ""page-with-props"",
|
||||||
|
"path": ""/page-with-props"",
|
||||||
|
},
|
||||||
|
],
|
||||||
"should allow pages with `:` in their path": [
|
"should allow pages with `:` in their path": [
|
||||||
{
|
{
|
||||||
"component": "() => import("pages/test:name.vue")",
|
"component": "() => import("pages/test:name.vue")",
|
||||||
|
@ -92,7 +92,11 @@ function createTransformer (components: Component[], mode: 'client' | 'server' |
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as Nuxt
|
} as Nuxt
|
||||||
const plugin = TransformPlugin(stubNuxt, () => components, mode).vite()
|
const plugin = TransformPlugin(stubNuxt, {
|
||||||
|
mode,
|
||||||
|
getComponents: () => components,
|
||||||
|
serverComponentRuntime: '<repo>/nuxt/src/components/runtime/server-component',
|
||||||
|
}).vite()
|
||||||
|
|
||||||
return async (code: string, id: string) => {
|
return async (code: string, id: string) => {
|
||||||
const result = await (plugin as any).transform!(code, id)
|
const result = await (plugin as any).transform!(code, id)
|
||||||
|
@ -168,7 +168,11 @@ describe('normalizeRoutes', () => {
|
|||||||
page.meta.layout = 'test'
|
page.meta.layout = 'test'
|
||||||
page.meta.foo = 'bar'
|
page.meta.foo = 'bar'
|
||||||
|
|
||||||
const { routes, imports } = normalizeRoutes([page], new Set(), true)
|
const { routes, imports } = normalizeRoutes([page], new Set(), {
|
||||||
|
clientComponentRuntime: '<client-component-runtime>',
|
||||||
|
serverComponentRuntime: '<server-component-runtime>',
|
||||||
|
overrideMeta: true,
|
||||||
|
})
|
||||||
expect({ routes, imports }).toMatchInlineSnapshot(`
|
expect({ routes, imports }).toMatchInlineSnapshot(`
|
||||||
{
|
{
|
||||||
"imports": Set {
|
"imports": Set {
|
||||||
@ -193,7 +197,11 @@ describe('normalizeRoutes', () => {
|
|||||||
page.meta.layout = 'test'
|
page.meta.layout = 'test'
|
||||||
page.meta.foo = 'bar'
|
page.meta.foo = 'bar'
|
||||||
|
|
||||||
const { routes, imports } = normalizeRoutes([page], new Set())
|
const { routes, imports } = normalizeRoutes([page], new Set(), {
|
||||||
|
clientComponentRuntime: '<client-component-runtime>',
|
||||||
|
serverComponentRuntime: '<server-component-runtime>',
|
||||||
|
overrideMeta: false,
|
||||||
|
})
|
||||||
expect({ routes, imports }).toMatchInlineSnapshot(`
|
expect({ routes, imports }).toMatchInlineSnapshot(`
|
||||||
{
|
{
|
||||||
"imports": Set {
|
"imports": Set {
|
||||||
@ -203,6 +211,7 @@ describe('normalizeRoutes', () => {
|
|||||||
{
|
{
|
||||||
name: indexN6pT4Un8hYMeta?.name ?? undefined,
|
name: indexN6pT4Un8hYMeta?.name ?? undefined,
|
||||||
path: indexN6pT4Un8hYMeta?.path ?? "/",
|
path: indexN6pT4Un8hYMeta?.path ?? "/",
|
||||||
|
props: indexN6pT4Un8hYMeta?.props ?? false,
|
||||||
meta: { ...(indexN6pT4Un8hYMeta || {}), ...{"layout":"test","foo":"bar"} },
|
meta: { ...(indexN6pT4Un8hYMeta || {}), ...{"layout":"test","foo":"bar"} },
|
||||||
alias: indexN6pT4Un8hYMeta?.alias || [],
|
alias: indexN6pT4Un8hYMeta?.alias || [],
|
||||||
redirect: indexN6pT4Un8hYMeta?.redirect,
|
redirect: indexN6pT4Un8hYMeta?.redirect,
|
||||||
|
@ -601,6 +601,30 @@ describe('pages:generateRoutesFromFiles', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: 'route.meta props generate by file',
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
path: `${pagesDir}/page-with-props.vue`,
|
||||||
|
template: `
|
||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
props: true
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
output: [
|
||||||
|
{
|
||||||
|
name: 'page-with-props',
|
||||||
|
path: '/page-with-props',
|
||||||
|
file: `${pagesDir}/page-with-props.vue`,
|
||||||
|
children: [],
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: 'should handle route groups',
|
description: 'should handle route groups',
|
||||||
files: [
|
files: [
|
||||||
@ -667,8 +691,18 @@ describe('pages:generateRoutesFromFiles', () => {
|
|||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
expect(result).toEqual(test.output)
|
expect(result).toEqual(test.output)
|
||||||
normalizedResults[test.description] = normalizeRoutes(result, new Set()).routes
|
|
||||||
normalizedOverrideMetaResults[test.description] = normalizeRoutes(result, new Set(), true).routes
|
normalizedResults[test.description] = normalizeRoutes(result, new Set(), {
|
||||||
|
clientComponentRuntime: '<client-component-runtime>',
|
||||||
|
serverComponentRuntime: '<server-component-runtime>',
|
||||||
|
overrideMeta: false,
|
||||||
|
}).routes
|
||||||
|
|
||||||
|
normalizedOverrideMetaResults[test.description] = normalizeRoutes(result, new Set(), {
|
||||||
|
clientComponentRuntime: '<client-component-runtime>',
|
||||||
|
serverComponentRuntime: '<server-component-runtime>',
|
||||||
|
overrideMeta: true,
|
||||||
|
}).routes
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
18
packages/rspack/build.config.ts
Normal file
18
packages/rspack/build.config.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { defineBuildConfig } from 'unbuild'
|
||||||
|
import config from '../webpack/build.config'
|
||||||
|
|
||||||
|
export default defineBuildConfig({
|
||||||
|
...config[0],
|
||||||
|
externals: [
|
||||||
|
'@rspack/core',
|
||||||
|
'#builder',
|
||||||
|
'@nuxt/schema',
|
||||||
|
],
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
input: '../webpack/src/index',
|
||||||
|
name: 'index',
|
||||||
|
declaration: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
5
packages/rspack/builder.mjs
Normal file
5
packages/rspack/builder.mjs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import webpack from '@rspack/core'
|
||||||
|
|
||||||
|
export const builder = 'rspack'
|
||||||
|
export { webpack }
|
||||||
|
export const MiniCssExtractPlugin = webpack.CssExtractRspackPlugin
|
94
packages/rspack/package.json
Normal file
94
packages/rspack/package.json
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
{
|
||||||
|
"name": "@nuxt/rspack-builder",
|
||||||
|
"version": "3.12.2",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/nuxt/nuxt.git",
|
||||||
|
"directory": "packages/rspack"
|
||||||
|
},
|
||||||
|
"description": "rspack bundler for Nuxt",
|
||||||
|
"homepage": "https://nuxt.com",
|
||||||
|
"license": "MIT",
|
||||||
|
"type": "module",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"imports": {
|
||||||
|
"#builder": "./builder.mjs"
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"import": "./dist/index.mjs"
|
||||||
|
},
|
||||||
|
"./dist/*": "./dist/*"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"builder.mjs"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"prepack": "unbuild"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nuxt/friendly-errors-webpack-plugin": "^2.6.0",
|
||||||
|
"@nuxt/kit": "workspace:*",
|
||||||
|
"@rspack/core": "^1.0.14",
|
||||||
|
"autoprefixer": "^10.4.20",
|
||||||
|
"css-loader": "^7.1.2",
|
||||||
|
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||||
|
"cssnano": "^7.0.6",
|
||||||
|
"defu": "^6.1.4",
|
||||||
|
"esbuild-loader": "^4.2.2",
|
||||||
|
"escape-string-regexp": "^5.0.0",
|
||||||
|
"estree-walker": "^3.0.3",
|
||||||
|
"file-loader": "^6.2.0",
|
||||||
|
"fork-ts-checker-webpack-plugin": "^9.0.2",
|
||||||
|
"globby": "^14.0.2",
|
||||||
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
|
"hash-sum": "^2.0.0",
|
||||||
|
"jiti": "^2.3.3",
|
||||||
|
"knitwork": "^1.1.0",
|
||||||
|
"lodash-es": "4.17.21",
|
||||||
|
"magic-string": "^0.30.12",
|
||||||
|
"memfs": "^4.14.0",
|
||||||
|
"mlly": "^1.7.2",
|
||||||
|
"ohash": "^1.1.4",
|
||||||
|
"pathe": "^1.1.2",
|
||||||
|
"pify": "^6.1.0",
|
||||||
|
"postcss": "^8.4.47",
|
||||||
|
"postcss-import": "^16.1.0",
|
||||||
|
"postcss-import-resolver": "^2.0.0",
|
||||||
|
"postcss-loader": "^8.1.1",
|
||||||
|
"postcss-url": "^10.1.3",
|
||||||
|
"pug-plain-loader": "^1.1.0",
|
||||||
|
"std-env": "^3.7.0",
|
||||||
|
"time-fix-plugin": "^2.0.7",
|
||||||
|
"ufo": "^1.5.4",
|
||||||
|
"unenv": "^1.10.0",
|
||||||
|
"unplugin": "^1.14.1",
|
||||||
|
"url-loader": "^4.1.1",
|
||||||
|
"vue-bundle-renderer": "^2.1.1",
|
||||||
|
"vue-loader": "^17.4.2",
|
||||||
|
"webpack-bundle-analyzer": "^4.10.2",
|
||||||
|
"webpack-dev-middleware": "^7.4.2",
|
||||||
|
"webpack-hot-middleware": "^2.26.1",
|
||||||
|
"webpack-virtual-modules": "^0.6.2",
|
||||||
|
"webpackbar": "^6.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nuxt/schema": "workspace:*",
|
||||||
|
"@types/hash-sum": "1.0.2",
|
||||||
|
"@types/lodash-es": "4.17.12",
|
||||||
|
"@types/pify": "5.0.4",
|
||||||
|
"@types/webpack-bundle-analyzer": "4.7.0",
|
||||||
|
"@types/webpack-hot-middleware": "2.25.9",
|
||||||
|
"rollup": "4.24.0",
|
||||||
|
"unbuild": "3.0.0-rc.11",
|
||||||
|
"vue": "3.5.12"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.3.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.18.0 || >=16.10.0"
|
||||||
|
}
|
||||||
|
}
|
@ -39,23 +39,22 @@
|
|||||||
"@types/file-loader": "5.0.4",
|
"@types/file-loader": "5.0.4",
|
||||||
"@types/pug": "2.0.10",
|
"@types/pug": "2.0.10",
|
||||||
"@types/sass-loader": "8.0.9",
|
"@types/sass-loader": "8.0.9",
|
||||||
"@unhead/schema": "1.11.6",
|
"@unhead/schema": "1.11.10",
|
||||||
"@vitejs/plugin-vue": "5.1.4",
|
"@vitejs/plugin-vue": "5.1.4",
|
||||||
"@vitejs/plugin-vue-jsx": "4.0.1",
|
"@vitejs/plugin-vue-jsx": "4.0.1",
|
||||||
"@vue/compiler-core": "3.5.10",
|
"@vue/compiler-core": "3.5.12",
|
||||||
"@vue/compiler-sfc": "3.5.10",
|
"@vue/compiler-sfc": "3.5.12",
|
||||||
"@vue/language-core": "2.1.6",
|
"@vue/language-core": "2.1.6",
|
||||||
"c12": "2.0.0-beta.3",
|
|
||||||
"esbuild-loader": "4.2.2",
|
"esbuild-loader": "4.2.2",
|
||||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
"ignore": "6.0.2",
|
"ignore": "6.0.2",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||||
"ofetch": "1.4.0",
|
"ofetch": "1.4.1",
|
||||||
"unbuild": "3.0.0-rc.8",
|
"unbuild": "3.0.0-rc.11",
|
||||||
"unctx": "2.3.1",
|
"unctx": "2.3.1",
|
||||||
"unenv": "1.10.0",
|
"unenv": "1.10.0",
|
||||||
"vite": "5.4.8",
|
"vite": "5.4.10",
|
||||||
"vue": "3.5.10",
|
"vue": "3.5.12",
|
||||||
"vue-bundle-renderer": "2.1.1",
|
"vue-bundle-renderer": "2.1.1",
|
||||||
"vue-loader": "17.4.2",
|
"vue-loader": "17.4.2",
|
||||||
"vue-router": "4.4.5",
|
"vue-router": "4.4.5",
|
||||||
@ -63,18 +62,19 @@
|
|||||||
"webpack-dev-middleware": "7.4.2"
|
"webpack-dev-middleware": "7.4.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"c12": "^2.0.1",
|
||||||
"compatx": "^0.1.8",
|
"compatx": "^0.1.8",
|
||||||
"consola": "^3.2.3",
|
"consola": "^3.2.3",
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
"hookable": "^5.5.3",
|
"hookable": "^5.5.3",
|
||||||
"pathe": "^1.1.2",
|
"pathe": "^1.1.2",
|
||||||
"pkg-types": "^1.2.0",
|
"pkg-types": "^1.2.1",
|
||||||
"scule": "^1.3.0",
|
"scule": "^1.3.0",
|
||||||
"std-env": "^3.7.0",
|
"std-env": "^3.7.0",
|
||||||
"ufo": "^1.5.4",
|
"ufo": "^1.5.4",
|
||||||
"uncrypto": "^0.1.3",
|
"uncrypto": "^0.1.3",
|
||||||
"unimport": "^3.13.1",
|
"unimport": "^3.13.1",
|
||||||
"untyped": "^1.5.0"
|
"untyped": "^1.5.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.10.0"
|
"node": "^14.18.0 || >=16.10.0"
|
||||||
|
@ -7,14 +7,15 @@ import { consola } from 'consola'
|
|||||||
export default defineUntypedSchema({
|
export default defineUntypedSchema({
|
||||||
/**
|
/**
|
||||||
* The builder to use for bundling the Vue part of your application.
|
* The builder to use for bundling the Vue part of your application.
|
||||||
* @type {'vite' | 'webpack' | { bundle: (nuxt: typeof import('../src/types/nuxt').Nuxt) => Promise<void> }}
|
* @type {'vite' | 'webpack' | 'rspack' | { bundle: (nuxt: typeof import('../src/types/nuxt').Nuxt) => Promise<void> }}
|
||||||
*/
|
*/
|
||||||
builder: {
|
builder: {
|
||||||
$resolve: async (val: 'vite' | 'webpack' | { bundle: (nuxt: unknown) => Promise<void> } | undefined = 'vite', get) => {
|
$resolve: async (val: 'vite' | 'webpack' | 'rspack' | { bundle: (nuxt: unknown) => Promise<void> } | undefined = 'vite', get) => {
|
||||||
if (typeof val === 'object') {
|
if (typeof val === 'object') {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
const map: Record<string, string> = {
|
const map: Record<string, string> = {
|
||||||
|
rspack: '@nuxt/rspack-builder',
|
||||||
vite: '@nuxt/vite-builder',
|
vite: '@nuxt/vite-builder',
|
||||||
webpack: '@nuxt/webpack-builder',
|
webpack: '@nuxt/webpack-builder',
|
||||||
}
|
}
|
||||||
|
@ -424,7 +424,7 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
alias: {
|
alias: {
|
||||||
$resolve: async (val: Record<string, string>, get): Promise<Record<string, string>> => {
|
$resolve: async (val: Record<string, string>, get): Promise<Record<string, string>> => {
|
||||||
const [srcDir, rootDir, assetsDir, publicDir] = await Promise.all([get('srcDir'), get('rootDir'), get('dir.assets'), get('dir.public')]) as [string, string, string, string]
|
const [srcDir, rootDir, assetsDir, publicDir, buildDir] = await Promise.all([get('srcDir'), get('rootDir'), get('dir.assets'), get('dir.public'), get('buildDir')]) as [string, string, string, string, string]
|
||||||
return {
|
return {
|
||||||
'~': srcDir,
|
'~': srcDir,
|
||||||
'@': srcDir,
|
'@': srcDir,
|
||||||
@ -432,6 +432,8 @@ export default defineUntypedSchema({
|
|||||||
'@@': rootDir,
|
'@@': rootDir,
|
||||||
[basename(assetsDir)]: resolve(srcDir, assetsDir),
|
[basename(assetsDir)]: resolve(srcDir, assetsDir),
|
||||||
[basename(publicDir)]: resolve(srcDir, publicDir),
|
[basename(publicDir)]: resolve(srcDir, publicDir),
|
||||||
|
'#build': buildDir,
|
||||||
|
'#internal/nuxt/paths': resolve(buildDir, 'paths.mjs'),
|
||||||
...val,
|
...val,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -297,8 +297,13 @@ export default defineUntypedSchema({
|
|||||||
* This only works with static or strings/arrays rather than variables or conditional assignment.
|
* This only works with static or strings/arrays rather than variables or conditional assignment.
|
||||||
*
|
*
|
||||||
* @see [Nuxt Issues #24770](https://github.com/nuxt/nuxt/issues/24770)
|
* @see [Nuxt Issues #24770](https://github.com/nuxt/nuxt/issues/24770)
|
||||||
|
* @type {boolean | 'after-resolve'}
|
||||||
*/
|
*/
|
||||||
scanPageMeta: true,
|
scanPageMeta: {
|
||||||
|
async $resolve (val, get) {
|
||||||
|
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion === 4 ? 'after-resolve' : true)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically share payload _data_ between pages that are prerendered. This can result in a significant
|
* Automatically share payload _data_ between pages that are prerendered. This can result in a significant
|
||||||
|
@ -20,7 +20,7 @@ export default defineUntypedSchema({
|
|||||||
* builder environment types (with `false`) to handle this fully yourself, or opt for a 'shared' option.
|
* builder environment types (with `false`) to handle this fully yourself, or opt for a 'shared' option.
|
||||||
*
|
*
|
||||||
* The 'shared' option is advised for module authors, who will want to support multiple possible builders.
|
* The 'shared' option is advised for module authors, who will want to support multiple possible builders.
|
||||||
* @type {'vite' | 'webpack' | 'shared' | false | undefined}
|
* @type {'vite' | 'webpack' | 'rspack' | 'shared' | false | undefined}
|
||||||
*/
|
*/
|
||||||
builder: {
|
builder: {
|
||||||
$resolve: val => val ?? null,
|
$resolve: val => val ?? null,
|
||||||
|
@ -6,7 +6,7 @@ export type { GenerateAppOptions, HookResult, ImportPresetWithDeprecation, NuxtA
|
|||||||
export type { ImportsOptions } from './types/imports'
|
export type { ImportsOptions } from './types/imports'
|
||||||
export type { AppHeadMetaObject, MetaObject, MetaObjectRaw, HeadAugmentations } from './types/head'
|
export type { AppHeadMetaObject, MetaObject, MetaObjectRaw, HeadAugmentations } from './types/head'
|
||||||
export type { ModuleDefinition, ModuleMeta, ModuleOptions, ModuleSetupInstallResult, ModuleSetupReturn, NuxtModule, ResolvedModuleOptions } from './types/module'
|
export type { ModuleDefinition, ModuleMeta, ModuleOptions, ModuleSetupInstallResult, ModuleSetupReturn, NuxtModule, ResolvedModuleOptions } from './types/module'
|
||||||
export type { Nuxt, NuxtApp, NuxtPlugin, NuxtPluginTemplate, NuxtTemplate, NuxtTypeTemplate, ResolvedNuxtTemplate } from './types/nuxt'
|
export type { Nuxt, NuxtApp, NuxtPlugin, NuxtPluginTemplate, NuxtTemplate, NuxtTypeTemplate, NuxtServerTemplate, ResolvedNuxtTemplate } from './types/nuxt'
|
||||||
export type { RouterConfig, RouterConfigSerializable, RouterOptions } from './types/router'
|
export type { RouterConfig, RouterConfigSerializable, RouterOptions } from './types/router'
|
||||||
|
|
||||||
// Schema
|
// Schema
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user