mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
Merge branch '3.x' into test/3.x-vfs
This commit is contained in:
commit
59d8427048
2
.github/workflows/autofix-docs.yml
vendored
2
.github/workflows/autofix-docs.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
||||||
with:
|
with:
|
||||||
|
2
.github/workflows/autofix.yml
vendored
2
.github/workflows/autofix.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
||||||
with:
|
with:
|
||||||
|
2
.github/workflows/benchmark.yml
vendored
2
.github/workflows/benchmark.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
||||||
with:
|
with:
|
||||||
|
2
.github/workflows/changelog.yml
vendored
2
.github/workflows/changelog.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
|
23
.github/workflows/ci.yml
vendored
23
.github/workflows/ci.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
|||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
||||||
with:
|
with:
|
||||||
@ -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@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2
|
||||||
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@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10
|
uses: github/codeql-action/init@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12
|
||||||
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@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10
|
uses: github/codeql-action/analyze@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12
|
||||||
with:
|
with:
|
||||||
category: "/language:javascript-typescript"
|
category: "/language:javascript-typescript"
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ jobs:
|
|||||||
module: ["bundler", "node"]
|
module: ["bundler", "node"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
||||||
with:
|
with:
|
||||||
@ -138,7 +138,7 @@ jobs:
|
|||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
||||||
with:
|
with:
|
||||||
@ -162,7 +162,7 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- build
|
- build
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
||||||
with:
|
with:
|
||||||
@ -214,7 +214,7 @@ jobs:
|
|||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
||||||
with:
|
with:
|
||||||
@ -258,7 +258,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
|
||||||
@ -268,7 +267,7 @@ jobs:
|
|||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
@ -309,7 +308,7 @@ jobs:
|
|||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
|
2
.github/workflows/dependency-review.yml
vendored
2
.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@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
- name: 'Dependency Review'
|
- name: 'Dependency Review'
|
||||||
uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4
|
uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4
|
||||||
|
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@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
|
||||||
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@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
|
|
||||||
- name: Lychee link checker
|
- name: Lychee link checker
|
||||||
uses: lycheeverse/lychee-action@897f08a07f689df1a43076f4374af272f66a6dd1 # for v1.8.0
|
uses: lycheeverse/lychee-action@e360f3c89142a5391e094404ea45e5494f1317dd # for v1.8.0
|
||||||
with:
|
with:
|
||||||
# arguments with file types to check
|
# arguments with file types to check
|
||||||
args: >-
|
args: >-
|
||||||
|
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
||||||
with:
|
with:
|
||||||
|
2
.github/workflows/lint-sherif.yml
vendored
2
.github/workflows/lint-sherif.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
|
||||||
with:
|
with:
|
||||||
|
2
.github/workflows/lint-workflows.yml
vendored
2
.github/workflows/lint-workflows.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
# 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.3@sha256:7617f05bd698cd2f1c3aedc05bc733ccec92cca0738f3e8722c32c5b42c70ae6
|
uses: docker://rhysd/actionlint:1.7.3@sha256:7617f05bd698cd2f1c3aedc05bc733ccec92cca0738f3e8722c32c5b42c70ae6
|
||||||
|
4
.github/workflows/release-pr.yml
vendored
4
.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,7 +48,7 @@ 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@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
with:
|
with:
|
||||||
ref: ${{ steps.pr.outputs.head_sha }}
|
ref: ${{ steps.pr.outputs.head_sha }}
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -19,7 +19,7 @@ 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@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
|
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@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
- 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@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
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@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2
|
||||||
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@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3.26.10
|
uses: github/codeql-action/upload-sarif@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12
|
||||||
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@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
|
||||||
- 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'
|
||||||
|
@ -64,6 +64,10 @@ By default, the workload gets distributed to the workers with the round robin st
|
|||||||
|
|
||||||
:read-more{to="https://nitro.unjs.io/deploy/node" title="the Nitro documentation for node-server preset"}
|
:read-more{to="https://nitro.unjs.io/deploy/node" title="the Nitro documentation for node-server preset"}
|
||||||
|
|
||||||
|
::tip{icon="i-ph-video" to="https://www.youtube.com/watch?v=0x1H6K5yOfs" target="\_blank"}
|
||||||
|
Watch Daniel Roe's short video on the topic.
|
||||||
|
::
|
||||||
|
|
||||||
## Static Hosting
|
## Static Hosting
|
||||||
|
|
||||||
There are two ways to deploy a Nuxt application to any static hosting services:
|
There are two ways to deploy a Nuxt application to any static hosting services:
|
||||||
|
@ -4,7 +4,6 @@ description: 'Learn how to upgrade to the latest Nuxt version.'
|
|||||||
navigation.icon: i-ph-arrow-circle-up
|
navigation.icon: i-ph-arrow-circle-up
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## Upgrading Nuxt
|
## Upgrading Nuxt
|
||||||
|
|
||||||
### Latest release
|
### Latest release
|
||||||
@ -179,6 +178,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`
|
||||||
@ -467,7 +467,7 @@ We have already proactively migrated the public Nuxt modules which we are aware
|
|||||||
|
|
||||||
However, if you are a module author using the `builder:watch` hook and wishing to remain backwards/forwards compatible, you can use the following code to ensure that your code works the same in both Nuxt v3 and Nuxt v4:
|
However, if you are a module author using the `builder:watch` hook and wishing to remain backwards/forwards compatible, you can use the following code to ensure that your code works the same in both Nuxt v3 and Nuxt v4:
|
||||||
|
|
||||||
```diff
|
```diff
|
||||||
+ import { relative, resolve } from 'node:fs'
|
+ import { relative, resolve } from 'node:fs'
|
||||||
// ...
|
// ...
|
||||||
nuxt.hook('builder:watch', async (event, path) => {
|
nuxt.hook('builder:watch', async (event, path) => {
|
||||||
|
@ -4,7 +4,6 @@ description: Nuxt is configured with sensible defaults to make you productive.
|
|||||||
navigation.icon: i-ph-gear
|
navigation.icon: i-ph-gear
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
By default, Nuxt is configured to cover most use cases. The [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt-config) file can override or extend this default configuration.
|
By default, Nuxt is configured to cover most use cases. The [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt-config) file can override or extend this default configuration.
|
||||||
|
|
||||||
## Nuxt Configuration
|
## Nuxt Configuration
|
||||||
|
@ -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/
|
||||||
|
@ -33,7 +33,7 @@ Nuxt auto-imports functions and composables to perform [data fetching](/docs/get
|
|||||||
|
|
||||||
```vue twoslash
|
```vue twoslash
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
/* useAsyncData() and $fetch() are auto-imported */
|
/* useFetch() is auto-imported */
|
||||||
const { data, refresh, status } = await useFetch('/api/hello')
|
const { data, refresh, status } = await useFetch('/api/hello')
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
@ -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:
|
||||||
@ -82,6 +82,10 @@ const MyButton = resolveComponent('MyButton')
|
|||||||
If you are using `resolveComponent` to handle dynamic components, make sure not to insert anything but the name of the component, which must be a string and not a variable.
|
If you are using `resolveComponent` to handle dynamic components, make sure not to insert anything but the name of the component, which must be a string and not a variable.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
::tip{icon="i-ph-video" to="https://www.youtube.com/watch?v=4kq8E5IUM2U" target="\_blank"}
|
||||||
|
Watch Daniel Roe's short video about `resolveComponent`.
|
||||||
|
::
|
||||||
|
|
||||||
Alternatively, though not recommended, you can register all your components globally, which will create async chunks for all your components and make them available throughout your application.
|
Alternatively, though not recommended, you can register all your components globally, which will create async chunks for all your components and make them available throughout your application.
|
||||||
|
|
||||||
```diff
|
```diff
|
||||||
@ -281,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]
|
||||||
@ -355,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]
|
||||||
@ -385,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({
|
||||||
|
@ -347,6 +347,22 @@ export default defineEventHandler((event) => {
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Forwarding Context & Headers
|
||||||
|
|
||||||
|
By default, neither the headers from the incoming request nor the request context are forwarded when
|
||||||
|
making fetch requests in server routes. You can use `event.$fetch` to forward the request context and headers when making fetch requests in server routes.
|
||||||
|
|
||||||
|
```ts [server/api/forward.ts]
|
||||||
|
export default defineEventHandler((event) => {
|
||||||
|
return event.$fetch('/api/forwarded')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
::note
|
||||||
|
Headers that are **not meant to be forwarded** will **not be included** in the request. These headers include, for example:
|
||||||
|
`transfer-encoding`, `connection`, `keep-alive`, `upgrade`, `expect`, `host`, `accept`
|
||||||
|
::
|
||||||
|
|
||||||
## Advanced Usage
|
## Advanced Usage
|
||||||
|
|
||||||
### Nitro Config
|
### Nitro Config
|
||||||
|
@ -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
|
||||||
|
52
docs/3.api/2.composables/use-request-fetch.md
Normal file
52
docs/3.api/2.composables/use-request-fetch.md
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
title: 'useRequestFetch'
|
||||||
|
description: 'Forward the request context and headers for server-side fetch requests with the useRequestFetch composable.'
|
||||||
|
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
|
||||||
|
---
|
||||||
|
|
||||||
|
You can use `useRequestFetch` to forward the request context and headers when making server-side fetch requests.
|
||||||
|
|
||||||
|
When making a client-side fetch request, the browser automatically sends the necessary headers.
|
||||||
|
However, when making a request during server-side rendering, because the request is made on the server, we need to forward the headers manually.
|
||||||
|
|
||||||
|
::note
|
||||||
|
Headers that are **not meant to be forwarded** will **not be included** in the request. These headers include, for example:
|
||||||
|
`transfer-encoding`, `connection`, `keep-alive`, `upgrade`, `expect`, `host`, `accept`
|
||||||
|
::
|
||||||
|
|
||||||
|
::tip
|
||||||
|
The [`useFetch`](/docs/api/composables/use-fetch) composable uses `useRequestFetch` under the hood to automatically forward the request context and headers.
|
||||||
|
::
|
||||||
|
|
||||||
|
::code-group
|
||||||
|
|
||||||
|
```vue [pages/index.vue]
|
||||||
|
<script setup lang="ts">
|
||||||
|
// This will forward the user's headers to the `/api/foo` event handler
|
||||||
|
// Result: { cookies: { foo: 'bar' } }
|
||||||
|
const requestFetch = useRequestFetch()
|
||||||
|
const { data: forwarded } = await useAsyncData(() => requestFetch('/api/cookies'))
|
||||||
|
|
||||||
|
// This will NOT forward anything
|
||||||
|
// Result: { cookies: {} }
|
||||||
|
const { data: notForwarded } = await useAsyncData(() => $fetch('/api/cookies'))
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts [server/api/cookies.ts]
|
||||||
|
export default defineEventHandler((event) => {
|
||||||
|
const cookies = parseCookies(event)
|
||||||
|
|
||||||
|
return { cookies }
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
::tip
|
||||||
|
In the browser during client-side navigation, `useRequestFetch` will behave just like regular [`$fetch`](/docs/api/utils/dollarfetch).
|
||||||
|
::
|
32
package.json
32
package.json
@ -39,30 +39,30 @@
|
|||||||
"@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.16.11",
|
||||||
"@vue/compiler-core": "3.5.10",
|
"@vue/compiler-core": "3.5.11",
|
||||||
"@vue/compiler-dom": "3.5.10",
|
"@vue/compiler-dom": "3.5.11",
|
||||||
"@vue/shared": "3.5.10",
|
"@vue/shared": "3.5.11",
|
||||||
"magic-string": "^0.30.11",
|
"magic-string": "^0.30.11",
|
||||||
"nuxt": "workspace:*",
|
"nuxt": "workspace:*",
|
||||||
"ohash": "1.1.4",
|
"ohash": "1.1.4",
|
||||||
"postcss": "8.4.47",
|
"postcss": "8.4.47",
|
||||||
"rollup": "4.24.0",
|
"rollup": "4.24.0",
|
||||||
"send": ">=0.19.0",
|
"send": ">=0.19.0",
|
||||||
"typescript": "5.6.2",
|
"typescript": "5.6.3",
|
||||||
"ufo": "1.5.4",
|
"ufo": "1.5.4",
|
||||||
"vite": "5.4.8",
|
"vite": "5.4.8",
|
||||||
"vue": "3.5.10"
|
"vue": "3.5.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "9.11.1",
|
"@eslint/js": "9.12.0",
|
||||||
"@nuxt/eslint-config": "0.5.7",
|
"@nuxt/eslint-config": "0.5.7",
|
||||||
"@nuxt/kit": "workspace:*",
|
"@nuxt/kit": "workspace:*",
|
||||||
"@nuxt/test-utils": "3.14.2",
|
"@nuxt/test-utils": "3.14.3",
|
||||||
"@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.16.11",
|
||||||
"@types/semver": "7.5.8",
|
"@types/semver": "7.5.8",
|
||||||
"@unhead/schema": "1.11.7",
|
"@unhead/schema": "1.11.7",
|
||||||
"@unhead/vue": "1.11.7",
|
"@unhead/vue": "1.11.7",
|
||||||
@ -76,36 +76,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.12.0",
|
||||||
"eslint-plugin-no-only-tests": "3.3.0",
|
"eslint-plugin-no-only-tests": "3.3.0",
|
||||||
"eslint-plugin-perfectionist": "3.8.0",
|
"eslint-plugin-perfectionist": "3.8.0",
|
||||||
"eslint-typegen": "0.3.2",
|
"eslint-typegen": "0.3.2",
|
||||||
"h3": "1.12.0",
|
"h3": "1.12.0",
|
||||||
"happy-dom": "15.7.4",
|
"happy-dom": "15.7.4",
|
||||||
"jiti": "2.3.1",
|
"jiti": "2.3.3",
|
||||||
"markdownlint-cli": "0.42.0",
|
"markdownlint-cli": "0.42.0",
|
||||||
"nitropack": "2.9.7",
|
"nitropack": "2.9.7",
|
||||||
"nuxi": "3.14.0",
|
"nuxi": "3.14.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.0",
|
||||||
"rimraf": "6.0.1",
|
"rimraf": "6.0.1",
|
||||||
"semver": "7.6.3",
|
"semver": "7.6.3",
|
||||||
"sherif": "1.0.0",
|
"sherif": "1.0.0",
|
||||||
"std-env": "3.7.0",
|
"std-env": "3.7.0",
|
||||||
"tinyexec": "0.3.0",
|
"tinyexec": "0.3.0",
|
||||||
"tinyglobby": "0.2.9",
|
"tinyglobby": "0.2.9",
|
||||||
"typescript": "5.6.2",
|
"typescript": "5.6.3",
|
||||||
"ufo": "1.5.4",
|
"ufo": "1.5.4",
|
||||||
"vitest": "2.1.2",
|
"vitest": "2.1.2",
|
||||||
"vitest-environment-nuxt": "1.0.1",
|
"vitest-environment-nuxt": "1.0.1",
|
||||||
"vue": "3.5.10",
|
"vue": "3.5.11",
|
||||||
"vue-router": "4.4.5",
|
"vue-router": "4.4.5",
|
||||||
"vue-tsc": "2.1.6"
|
"vue-tsc": "2.1.6"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.12.0",
|
"packageManager": "pnpm@9.12.1",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.10.0 || >=18.0.0"
|
"node": "^16.10.0 || >=18.0.0"
|
||||||
},
|
},
|
||||||
|
@ -34,12 +34,12 @@
|
|||||||
"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.3.1",
|
"jiti": "^2.3.3",
|
||||||
"klona": "^2.0.6",
|
"klona": "^2.0.6",
|
||||||
"knitwork": "^1.1.0",
|
"knitwork": "^1.1.0",
|
||||||
"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",
|
||||||
|
@ -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.
|
||||||
|
@ -69,10 +69,10 @@
|
|||||||
"@unhead/shared": "^1.11.7",
|
"@unhead/shared": "^1.11.7",
|
||||||
"@unhead/ssr": "^1.11.7",
|
"@unhead/ssr": "^1.11.7",
|
||||||
"@unhead/vue": "^1.11.7",
|
"@unhead/vue": "^1.11.7",
|
||||||
"@vue/shared": "^3.5.10",
|
"@vue/shared": "^3.5.11",
|
||||||
"acorn": "8.12.1",
|
"acorn": "8.12.1",
|
||||||
"c12": "^2.0.1",
|
"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",
|
||||||
@ -88,20 +88,20 @@
|
|||||||
"hookable": "^5.5.3",
|
"hookable": "^5.5.3",
|
||||||
"ignore": "^6.0.2",
|
"ignore": "^6.0.2",
|
||||||
"impound": "^0.1.0",
|
"impound": "^0.1.0",
|
||||||
"jiti": "^2.3.1",
|
"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.11",
|
||||||
"mlly": "^1.7.1",
|
"mlly": "^1.7.2",
|
||||||
"nanotar": "^0.1.1",
|
"nanotar": "^0.1.1",
|
||||||
"nitropack": "^2.9.7",
|
"nitropack": "^2.9.7",
|
||||||
"nuxi": "^3.14.0",
|
"nuxi": "^3.14.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",
|
||||||
@ -119,7 +119,7 @@
|
|||||||
"unplugin-vue-router": "^0.10.8",
|
"unplugin-vue-router": "^0.10.8",
|
||||||
"unstorage": "^1.12.0",
|
"unstorage": "^1.12.0",
|
||||||
"untyped": "^1.5.1",
|
"untyped": "^1.5.1",
|
||||||
"vue": "^3.5.10",
|
"vue": "^3.5.11",
|
||||||
"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"
|
||||||
@ -130,7 +130,7 @@
|
|||||||
"@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.11",
|
||||||
"unbuild": "latest",
|
"unbuild": "latest",
|
||||||
"vite": "5.4.8",
|
"vite": "5.4.8",
|
||||||
"vitest": "2.1.2"
|
"vitest": "2.1.2"
|
||||||
|
@ -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(() => {}),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
@ -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, addWebpackPlugin, defineNuxtModule, logger, resolveAlias, resolvePath, updateTemplates } from '@nuxt/kit'
|
import { addBuildPlugin, addPluginTemplate, addTemplate, addTypeTemplate, addVitePlugin, defineNuxtModule, 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'
|
||||||
@ -9,8 +9,8 @@ import { scanComponents } from './scan'
|
|||||||
|
|
||||||
import { ClientFallbackAutoIdPlugin } from './plugins/client-fallback-auto-id'
|
import { ClientFallbackAutoIdPlugin } from './plugins/client-fallback-auto-id'
|
||||||
import { LoaderPlugin } from './plugins/loader'
|
import { LoaderPlugin } from './plugins/loader'
|
||||||
import { componentsChunkPlugin, islandsTransform } from './plugins/islands-transform'
|
import { ComponentsChunkPlugin, IslandsTransformPlugin } from './plugins/islands-transform'
|
||||||
import { createTransformPlugin } from './plugins/transform'
|
import { TransformPlugin } from './plugins/transform'
|
||||||
import { TreeShakeTemplatePlugin } from './plugins/tree-shake'
|
import { TreeShakeTemplatePlugin } from './plugins/tree-shake'
|
||||||
import { ComponentNamePlugin } from './plugins/component-names'
|
import { ComponentNamePlugin } from './plugins/component-names'
|
||||||
|
|
||||||
@ -134,14 +134,8 @@ export default defineNuxtModule<ComponentsOptions>({
|
|||||||
addTemplate(componentsMetadataTemplate)
|
addTemplate(componentsMetadataTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
const TransformPluginServer = createTransformPlugin(nuxt, getComponents, 'server')
|
addBuildPlugin(TransformPlugin(nuxt, getComponents, 'server'), { server: true, client: false })
|
||||||
const TransformPluginClient = createTransformPlugin(nuxt, getComponents, 'client')
|
addBuildPlugin(TransformPlugin(nuxt, getComponents, 'client'), { server: false, client: true })
|
||||||
|
|
||||||
addVitePlugin(() => TransformPluginServer.vite(), { server: true, client: false })
|
|
||||||
addVitePlugin(() => TransformPluginClient.vite(), { server: false, client: true })
|
|
||||||
|
|
||||||
addWebpackPlugin(() => TransformPluginServer.webpack(), { server: true, client: false })
|
|
||||||
addWebpackPlugin(() => TransformPluginClient.webpack(), { 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) => {
|
||||||
@ -219,37 +213,52 @@ export default defineNuxtModule<ComponentsOptions>({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
nuxt.hook('vite:extendConfig', (config, { isClient, isServer }) => {
|
if (nuxt.options.experimental.treeshakeClientOnly) {
|
||||||
const mode = isClient ? 'client' : 'server'
|
addBuildPlugin(TreeShakeTemplatePlugin({ sourcemap: !!nuxt.options.sourcemap.server, getComponents }), { client: false })
|
||||||
|
}
|
||||||
|
|
||||||
config.plugins = config.plugins || []
|
if (nuxt.options.experimental.clientFallback) {
|
||||||
if (nuxt.options.experimental.treeshakeClientOnly && isServer) {
|
addBuildPlugin(ClientFallbackAutoIdPlugin({ sourcemap: !!nuxt.options.sourcemap.client, rootDir: nuxt.options.rootDir }), { server: false })
|
||||||
config.plugins.push(TreeShakeTemplatePlugin.vite({
|
addBuildPlugin(ClientFallbackAutoIdPlugin({ sourcemap: !!nuxt.options.sourcemap.server, rootDir: nuxt.options.rootDir }), { client: false })
|
||||||
sourcemap: !!nuxt.options.sourcemap[mode],
|
}
|
||||||
getComponents,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
if (nuxt.options.experimental.clientFallback) {
|
|
||||||
config.plugins.push(ClientFallbackAutoIdPlugin.vite({
|
|
||||||
sourcemap: !!nuxt.options.sourcemap[mode],
|
|
||||||
rootDir: nuxt.options.rootDir,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
config.plugins.push(LoaderPlugin.vite({
|
|
||||||
sourcemap: !!nuxt.options.sourcemap[mode],
|
|
||||||
getComponents,
|
|
||||||
mode,
|
|
||||||
transform: typeof nuxt.options.components === 'object' && !Array.isArray(nuxt.options.components) ? nuxt.options.components.transform : undefined,
|
|
||||||
experimentalComponentIslands: !!nuxt.options.experimental.componentIslands,
|
|
||||||
}))
|
|
||||||
|
|
||||||
if (nuxt.options.experimental.componentIslands) {
|
const sharedLoaderOptions = {
|
||||||
const selectiveClient = typeof nuxt.options.experimental.componentIslands === 'object' && nuxt.options.experimental.componentIslands.selectiveClient
|
getComponents,
|
||||||
|
transform: typeof nuxt.options.components === 'object' && !Array.isArray(nuxt.options.components) ? nuxt.options.components.transform : undefined,
|
||||||
|
experimentalComponentIslands: !!nuxt.options.experimental.componentIslands,
|
||||||
|
}
|
||||||
|
|
||||||
|
addBuildPlugin(LoaderPlugin({ ...sharedLoaderOptions, sourcemap: !!nuxt.options.sourcemap.client, mode: 'client' }), { server: false })
|
||||||
|
addBuildPlugin(LoaderPlugin({ ...sharedLoaderOptions, sourcemap: !!nuxt.options.sourcemap.server, mode: 'server' }), { client: false })
|
||||||
|
|
||||||
|
if (nuxt.options.experimental.componentIslands) {
|
||||||
|
const selectiveClient = typeof nuxt.options.experimental.componentIslands === 'object' && nuxt.options.experimental.componentIslands.selectiveClient
|
||||||
|
|
||||||
|
addVitePlugin({
|
||||||
|
name: 'nuxt-server-component-hmr',
|
||||||
|
handleHotUpdate (ctx) {
|
||||||
|
const components = getComponents()
|
||||||
|
const filePath = normalize(ctx.file)
|
||||||
|
const comp = components.find(c => c.filePath === filePath)
|
||||||
|
if (comp?.mode === 'server') {
|
||||||
|
ctx.server.ws.send({
|
||||||
|
event: `nuxt-server-component:${comp.pascalName}`,
|
||||||
|
type: 'custom',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, { server: false })
|
||||||
|
|
||||||
|
addBuildPlugin(IslandsTransformPlugin({ getComponents, selectiveClient }), { client: false })
|
||||||
|
|
||||||
|
// TODO: refactor this
|
||||||
|
nuxt.hook('vite:extendConfig', (config, { isClient }) => {
|
||||||
|
config.plugins = config.plugins || []
|
||||||
|
|
||||||
if (isClient && selectiveClient) {
|
if (isClient && selectiveClient) {
|
||||||
writeFileSync(join(nuxt.options.buildDir, 'components-chunk.mjs'), 'export const paths = {}')
|
writeFileSync(join(nuxt.options.buildDir, 'components-chunk.mjs'), 'export const paths = {}')
|
||||||
if (!nuxt.options.dev) {
|
if (!nuxt.options.dev) {
|
||||||
config.plugins.push(componentsChunkPlugin.vite({
|
config.plugins.push(ComponentsChunkPlugin.vite({
|
||||||
getComponents,
|
getComponents,
|
||||||
buildDir: nuxt.options.buildDir,
|
buildDir: nuxt.options.buildDir,
|
||||||
}))
|
}))
|
||||||
@ -263,65 +272,18 @@ export default defineNuxtModule<ComponentsOptions>({
|
|||||||
)}`)
|
)}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if (isServer) {
|
nuxt.hook('webpack:config', (configs) => {
|
||||||
config.plugins.push(islandsTransform.vite({
|
configs.forEach((config) => {
|
||||||
getComponents,
|
const mode = config.name === 'client' ? 'client' : 'server'
|
||||||
selectiveClient,
|
config.plugins = config.plugins || []
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isServer && nuxt.options.experimental.componentIslands) {
|
|
||||||
config.plugins.push({
|
|
||||||
name: 'nuxt-server-component-hmr',
|
|
||||||
handleHotUpdate (ctx) {
|
|
||||||
const components = getComponents()
|
|
||||||
const filePath = normalize(ctx.file)
|
|
||||||
const comp = components.find(c => c.filePath === filePath)
|
|
||||||
if (comp?.mode === 'server') {
|
|
||||||
ctx.server.ws.send({
|
|
||||||
event: `nuxt-server-component:${comp.pascalName}`,
|
|
||||||
type: 'custom',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
nuxt.hook('webpack:config', (configs) => {
|
|
||||||
configs.forEach((config) => {
|
|
||||||
const mode = config.name === 'client' ? 'client' : 'server'
|
|
||||||
config.plugins = config.plugins || []
|
|
||||||
if (nuxt.options.experimental.treeshakeClientOnly && mode === 'server') {
|
|
||||||
config.plugins.push(TreeShakeTemplatePlugin.webpack({
|
|
||||||
sourcemap: !!nuxt.options.sourcemap[mode],
|
|
||||||
getComponents,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
if (nuxt.options.experimental.clientFallback) {
|
|
||||||
config.plugins.push(ClientFallbackAutoIdPlugin.webpack({
|
|
||||||
sourcemap: !!nuxt.options.sourcemap[mode],
|
|
||||||
rootDir: nuxt.options.rootDir,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
config.plugins.push(LoaderPlugin.webpack({
|
|
||||||
sourcemap: !!nuxt.options.sourcemap[mode],
|
|
||||||
getComponents,
|
|
||||||
mode,
|
|
||||||
transform: typeof nuxt.options.components === 'object' && !Array.isArray(nuxt.options.components) ? nuxt.options.components.transform : undefined,
|
|
||||||
experimentalComponentIslands: !!nuxt.options.experimental.componentIslands,
|
|
||||||
}))
|
|
||||||
|
|
||||||
if (nuxt.options.experimental.componentIslands) {
|
if (mode !== 'server') {
|
||||||
if (mode === 'server') {
|
|
||||||
config.plugins.push(islandsTransform.webpack({
|
|
||||||
getComponents,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
writeFileSync(join(nuxt.options.buildDir, 'components-chunk.mjs'), 'export const paths = {}')
|
writeFileSync(join(nuxt.options.buildDir, 'components-chunk.mjs'), 'export const paths = {}')
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -12,7 +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
|
||||||
export const ClientFallbackAutoIdPlugin = createUnplugin((options: LoaderOptions) => {
|
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 || []
|
||||||
|
|
||||||
|
@ -35,14 +35,14 @@ 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>`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const islandsTransform = createUnplugin((options: ServerOnlyComponentTransformPluginOptions, meta) => {
|
export const IslandsTransformPlugin = (options: ServerOnlyComponentTransformPluginOptions) => createUnplugin((_options, meta) => {
|
||||||
const isVite = meta.framework === 'vite'
|
const isVite = meta.framework === 'vite'
|
||||||
return {
|
return {
|
||||||
name: 'server-only-component-transform',
|
name: 'nuxt:server-only-component-transform',
|
||||||
enforce: 'pre',
|
enforce: 'pre',
|
||||||
transformInclude (id) {
|
transformInclude (id) {
|
||||||
if (!isVue(id)) { return false }
|
if (!isVue(id)) { return false }
|
||||||
if (options.selectiveClient === 'deep') { return true }
|
if (isVite && options.selectiveClient === 'deep') { return true }
|
||||||
const components = options.getComponents()
|
const components = options.getComponents()
|
||||||
|
|
||||||
const islands = components.filter(component =>
|
const islands = components.filter(component =>
|
||||||
@ -70,54 +70,68 @@ export const islandsTransform = createUnplugin((options: ServerOnlyComponentTran
|
|||||||
|
|
||||||
const ast = parse(template[0])
|
const ast = parse(template[0])
|
||||||
await walk(ast, (node) => {
|
await walk(ast, (node) => {
|
||||||
if (node.type === ELEMENT_NODE) {
|
if (node.type !== ELEMENT_NODE) {
|
||||||
if (node.name === 'slot') {
|
return
|
||||||
const { attributes, children, loc } = node
|
|
||||||
|
|
||||||
const slotName = attributes.name ?? 'default'
|
|
||||||
|
|
||||||
if (attributes.name) { delete attributes.name }
|
|
||||||
if (attributes['v-bind']) {
|
|
||||||
attributes._bind = extractAttributes(attributes, ['v-bind'])['v-bind']!
|
|
||||||
}
|
|
||||||
const teleportAttributes = extractAttributes(attributes, ['v-if', 'v-else-if', 'v-else'])
|
|
||||||
const bindings = getPropsToString(attributes)
|
|
||||||
// add the wrapper
|
|
||||||
s.appendLeft(startingIndex + loc[0].start, `<NuxtTeleportSsrSlot${attributeToString(teleportAttributes)} name="${slotName}" :props="${bindings}">`)
|
|
||||||
|
|
||||||
if (children.length) {
|
|
||||||
// pass slot fallback to NuxtTeleportSsrSlot fallback
|
|
||||||
const attrString = attributeToString(attributes)
|
|
||||||
const slice = code.slice(startingIndex + loc[0].end, startingIndex + loc[1].start).replaceAll(/:?key="[^"]"/g, '')
|
|
||||||
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 {
|
|
||||||
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.appendRight(startingIndex + loc[1].end, '</NuxtTeleportSsrSlot>')
|
|
||||||
} else if (options.selectiveClient && ('nuxt-client' in node.attributes || ':nuxt-client' in node.attributes)) {
|
|
||||||
hasNuxtClient = true
|
|
||||||
const { loc, attributes } = node
|
|
||||||
const attributeValue = attributes[':nuxt-client'] || attributes['nuxt-client'] || 'true'
|
|
||||||
if (isVite) {
|
|
||||||
const uid = hash(id + node.loc[0].start + node.loc[0].end)
|
|
||||||
const wrapperAttributes = extractAttributes(attributes, ['v-if', 'v-else-if', 'v-else'])
|
|
||||||
|
|
||||||
let startTag = code.slice(startingIndex + loc[0].start, startingIndex + loc[0].end).replace(NUXTCLIENT_ATTR_RE, '')
|
|
||||||
if (wrapperAttributes) {
|
|
||||||
startTag = startTag.replaceAll(EXTRACTED_ATTRS_RE, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
s.appendLeft(startingIndex + loc[0].start, `<NuxtTeleportIslandComponent${attributeToString(wrapperAttributes)} to="${node.name}-${uid}" :nuxt-client="${attributeValue}">`)
|
|
||||||
s.overwrite(startingIndex + loc[0].start, startingIndex + loc[0].end, startTag)
|
|
||||||
s.appendRight(startingIndex + loc[1].end, '</NuxtTeleportIslandComponent>')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (node.name === 'slot') {
|
||||||
|
const { attributes, children, loc } = node
|
||||||
|
|
||||||
|
const slotName = attributes.name ?? 'default'
|
||||||
|
|
||||||
|
if (attributes.name) { delete attributes.name }
|
||||||
|
if (attributes['v-bind']) {
|
||||||
|
attributes._bind = extractAttributes(attributes, ['v-bind'])['v-bind']!
|
||||||
|
}
|
||||||
|
const teleportAttributes = extractAttributes(attributes, ['v-if', 'v-else-if', 'v-else'])
|
||||||
|
const bindings = getPropsToString(attributes)
|
||||||
|
// add the wrapper
|
||||||
|
s.appendLeft(startingIndex + loc[0].start, `<NuxtTeleportSsrSlot${attributeToString(teleportAttributes)} name="${slotName}" :props="${bindings}">`)
|
||||||
|
|
||||||
|
if (children.length) {
|
||||||
|
// pass slot fallback to NuxtTeleportSsrSlot fallback
|
||||||
|
const attrString = attributeToString(attributes)
|
||||||
|
const slice = code.slice(startingIndex + loc[0].end, startingIndex + loc[1].start).replaceAll(/:?key="[^"]"/g, '')
|
||||||
|
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 {
|
||||||
|
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.appendRight(startingIndex + loc[1].end, '</NuxtTeleportSsrSlot>')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!('nuxt-client' in node.attributes) && !(':nuxt-client' in node.attributes)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasNuxtClient = true
|
||||||
|
|
||||||
|
if (!isVite || !options.selectiveClient) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { loc, attributes } = node
|
||||||
|
const attributeValue = attributes[':nuxt-client'] || attributes['nuxt-client'] || 'true'
|
||||||
|
|
||||||
|
const uid = hash(id + node.loc[0].start + node.loc[0].end)
|
||||||
|
const wrapperAttributes = extractAttributes(attributes, ['v-if', 'v-else-if', 'v-else'])
|
||||||
|
|
||||||
|
let startTag = code.slice(startingIndex + loc[0].start, startingIndex + loc[0].end).replace(NUXTCLIENT_ATTR_RE, '')
|
||||||
|
if (wrapperAttributes) {
|
||||||
|
startTag = startTag.replaceAll(EXTRACTED_ATTRS_RE, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
s.appendLeft(startingIndex + loc[0].start, `<NuxtTeleportIslandComponent${attributeToString(wrapperAttributes)} to="${node.name}-${uid}" :nuxt-client="${attributeValue}">`)
|
||||||
|
s.overwrite(startingIndex + loc[0].start, startingIndex + loc[0].end, startTag)
|
||||||
|
s.appendRight(startingIndex + loc[1].end, '</NuxtTeleportIslandComponent>')
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!isVite && hasNuxtClient) {
|
if (hasNuxtClient) {
|
||||||
console.warn(`nuxt-client attribute and client components within islands is only supported with Vite. file: ${id}`)
|
if (!options.selectiveClient) {
|
||||||
|
console.warn(`The \`nuxt-client\` attribute and client components within islands are only supported when \`experimental.componentIslands.selectiveClient\` is enabled. file: ${id}`)
|
||||||
|
} else if (!isVite) {
|
||||||
|
console.warn(`The \`nuxt-client\` attribute and client components within islands are only supported with Vite. file: ${id}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.hasChanged()) {
|
if (s.hasChanged()) {
|
||||||
@ -164,10 +178,10 @@ function getPropsToString (bindings: Record<string, string>): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const componentsChunkPlugin = createUnplugin((options: ComponentChunkOptions) => {
|
export const ComponentsChunkPlugin = createUnplugin((options: ComponentChunkOptions) => {
|
||||||
const { buildDir } = options
|
const { buildDir } = options
|
||||||
return {
|
return {
|
||||||
name: 'componentsChunkPlugin',
|
name: 'nuxt:components-chunk',
|
||||||
vite: {
|
vite: {
|
||||||
async config (config) {
|
async config (config) {
|
||||||
const components = options.getComponents()
|
const components = options.getComponents()
|
||||||
|
@ -2,7 +2,7 @@ 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, resolve } 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'
|
||||||
@ -17,10 +17,11 @@ interface LoaderOptions {
|
|||||||
experimentalComponentIslands?: boolean
|
experimentalComponentIslands?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LoaderPlugin = createUnplugin((options: LoaderOptions) => {
|
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 serverComponentRuntime = resolve(distDir, 'components/runtime/server-component')
|
||||||
|
const nuxt = tryUseNuxt()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'nuxt:components-loader',
|
name: 'nuxt:components-loader',
|
||||||
@ -34,7 +35,7 @@ export const LoaderPlugin = createUnplugin((options: LoaderOptions) => {
|
|||||||
}
|
}
|
||||||
return isVue(id, { type: ['template', 'script'] }) || !!id.match(/\.[tj]sx$/)
|
return isVue(id, { type: ['template', 'script'] }) || !!id.match(/\.[tj]sx$/)
|
||||||
},
|
},
|
||||||
transform (code) {
|
transform (code, id) {
|
||||||
const components = options.getComponents()
|
const components = options.getComponents()
|
||||||
|
|
||||||
let num = 0
|
let num = 0
|
||||||
@ -46,10 +47,14 @@ export const LoaderPlugin = createUnplugin((options: LoaderOptions) => {
|
|||||||
s.replace(/(?<=[ (])_?resolveComponent\(\s*["'](lazy-|Lazy(?=[A-Z]))?([^'"]*)["'][^)]*\)/g, (full: string, lazy: string, name: string) => {
|
s.replace(/(?<=[ (])_?resolveComponent\(\s*["'](lazy-|Lazy(?=[A-Z]))?([^'"]*)["'][^)]*\)/g, (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)
|
||||||
|
@ -12,7 +12,7 @@ import type { getComponentsT } from '../module'
|
|||||||
|
|
||||||
const COMPONENT_QUERY_RE = /[?&]nuxt_component=/
|
const COMPONENT_QUERY_RE = /[?&]nuxt_component=/
|
||||||
|
|
||||||
export function createTransformPlugin (nuxt: Nuxt, getComponents: getComponentsT, mode: 'client' | 'server' | 'all') {
|
export function TransformPlugin (nuxt: Nuxt, getComponents: getComponentsT, mode: 'client' | 'server' | 'all') {
|
||||||
const serverComponentRuntime = resolve(distDir, 'components/runtime/server-component')
|
const serverComponentRuntime = resolve(distDir, 'components/runtime/server-component')
|
||||||
const componentUnimport = createUnimport({
|
const componentUnimport = createUnimport({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -20,7 +20,7 @@ const PLACEHOLDER_EXACT_RE = /^(?:fallback|placeholder)$/
|
|||||||
const CLIENT_ONLY_NAME_RE = /^(?:_unref\()?(?:_component_)?(?:Lazy|lazy_)?(?:client_only|ClientOnly\)?)$/
|
const CLIENT_ONLY_NAME_RE = /^(?:_unref\()?(?:_component_)?(?:Lazy|lazy_)?(?:client_only|ClientOnly\)?)$/
|
||||||
const PARSER_OPTIONS = { sourceType: 'module', ecmaVersion: 'latest' }
|
const PARSER_OPTIONS = { sourceType: 'module', ecmaVersion: 'latest' }
|
||||||
|
|
||||||
export const TreeShakeTemplatePlugin = createUnplugin((options: TreeShakeTemplatePluginOptions) => {
|
export const TreeShakeTemplatePlugin = (options: TreeShakeTemplatePluginOptions) => createUnplugin(() => {
|
||||||
const regexpMap = new WeakMap<Component[], [RegExp, RegExp, string[]]>()
|
const regexpMap = new WeakMap<Component[], [RegExp, RegExp, string[]]>()
|
||||||
return {
|
return {
|
||||||
name: 'nuxt:tree-shake-template',
|
name: 'nuxt:tree-shake-template',
|
||||||
|
@ -33,9 +33,7 @@ import { version } from '../../package.json'
|
|||||||
import { scriptsStubsPreset } from '../imports/presets'
|
import { scriptsStubsPreset } from '../imports/presets'
|
||||||
import { resolveTypePath } from './utils/types'
|
import { resolveTypePath } from './utils/types'
|
||||||
import { nuxtImportProtections } from './plugins/import-protection'
|
import { nuxtImportProtections } from './plugins/import-protection'
|
||||||
import type { UnctxTransformPluginOptions } from './plugins/unctx'
|
|
||||||
import { UnctxTransformPlugin } from './plugins/unctx'
|
import { UnctxTransformPlugin } from './plugins/unctx'
|
||||||
import type { TreeShakeComposablesPluginOptions } from './plugins/tree-shake'
|
|
||||||
import { TreeShakeComposablesPlugin } from './plugins/tree-shake'
|
import { TreeShakeComposablesPlugin } from './plugins/tree-shake'
|
||||||
import { DevOnlyPlugin } from './plugins/dev-only'
|
import { DevOnlyPlugin } from './plugins/dev-only'
|
||||||
import { LayerAliasingPlugin } from './plugins/layer-aliasing'
|
import { LayerAliasingPlugin } from './plugins/layer-aliasing'
|
||||||
@ -270,58 +268,45 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
|
|
||||||
if (nuxt.options.experimental.localLayerAliases) {
|
if (nuxt.options.experimental.localLayerAliases) {
|
||||||
// Add layer aliasing support for ~, ~~, @ and @@ aliases
|
// Add layer aliasing support for ~, ~~, @ and @@ aliases
|
||||||
addVitePlugin(() => LayerAliasingPlugin.vite({
|
addBuildPlugin(LayerAliasingPlugin({
|
||||||
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
||||||
dev: nuxt.options.dev,
|
dev: nuxt.options.dev,
|
||||||
root: nuxt.options.srcDir,
|
root: nuxt.options.srcDir,
|
||||||
// skip top-level layer (user's project) as the aliases will already be correctly resolved
|
// skip top-level layer (user's project) as the aliases will already be correctly resolved
|
||||||
layers: nuxt.options._layers.slice(1),
|
layers: nuxt.options._layers.slice(1),
|
||||||
}))
|
}))
|
||||||
addWebpackPlugin(() => LayerAliasingPlugin.webpack({
|
|
||||||
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
|
||||||
dev: nuxt.options.dev,
|
|
||||||
root: nuxt.options.srcDir,
|
|
||||||
// skip top-level layer (user's project) as the aliases will already be correctly resolved
|
|
||||||
layers: nuxt.options._layers.slice(1),
|
|
||||||
transform: true,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nuxt.hook('modules:done', async () => {
|
nuxt.hook('modules:done', async () => {
|
||||||
// Add unctx transform
|
// Add unctx transform
|
||||||
const options = {
|
addBuildPlugin(UnctxTransformPlugin({
|
||||||
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
||||||
transformerOptions: {
|
transformerOptions: {
|
||||||
...nuxt.options.optimization.asyncTransforms,
|
...nuxt.options.optimization.asyncTransforms,
|
||||||
helperModule: await tryResolveModule('unctx', nuxt.options.modulesDir) ?? 'unctx',
|
helperModule: await tryResolveModule('unctx', nuxt.options.modulesDir) ?? 'unctx',
|
||||||
},
|
},
|
||||||
} satisfies UnctxTransformPluginOptions
|
}))
|
||||||
addVitePlugin(() => UnctxTransformPlugin.vite(options))
|
|
||||||
addWebpackPlugin(() => UnctxTransformPlugin.webpack(options))
|
|
||||||
|
|
||||||
// Add composable tree-shaking optimisations
|
// Add composable tree-shaking optimisations
|
||||||
const serverTreeShakeOptions: TreeShakeComposablesPluginOptions = {
|
if (Object.keys(nuxt.options.optimization.treeShake.composables.server).length) {
|
||||||
sourcemap: !!nuxt.options.sourcemap.server,
|
addBuildPlugin(TreeShakeComposablesPlugin({
|
||||||
composables: nuxt.options.optimization.treeShake.composables.server,
|
sourcemap: !!nuxt.options.sourcemap.server,
|
||||||
|
composables: nuxt.options.optimization.treeShake.composables.server,
|
||||||
|
}), { client: false })
|
||||||
}
|
}
|
||||||
if (Object.keys(serverTreeShakeOptions.composables).length) {
|
if (Object.keys(nuxt.options.optimization.treeShake.composables.client).length) {
|
||||||
addVitePlugin(() => TreeShakeComposablesPlugin.vite(serverTreeShakeOptions), { client: false })
|
addBuildPlugin(TreeShakeComposablesPlugin({
|
||||||
addWebpackPlugin(() => TreeShakeComposablesPlugin.webpack(serverTreeShakeOptions), { client: false })
|
sourcemap: !!nuxt.options.sourcemap.client,
|
||||||
}
|
composables: nuxt.options.optimization.treeShake.composables.client,
|
||||||
const clientTreeShakeOptions: TreeShakeComposablesPluginOptions = {
|
}), { server: false })
|
||||||
sourcemap: !!nuxt.options.sourcemap.client,
|
|
||||||
composables: nuxt.options.optimization.treeShake.composables.client,
|
|
||||||
}
|
|
||||||
if (Object.keys(clientTreeShakeOptions.composables).length) {
|
|
||||||
addVitePlugin(() => TreeShakeComposablesPlugin.vite(clientTreeShakeOptions), { server: false })
|
|
||||||
addWebpackPlugin(() => TreeShakeComposablesPlugin.webpack(clientTreeShakeOptions), { server: false })
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!nuxt.options.dev) {
|
if (!nuxt.options.dev) {
|
||||||
// DevOnly component tree-shaking - build time only
|
// DevOnly component tree-shaking - build time only
|
||||||
addVitePlugin(() => DevOnlyPlugin.vite({ sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client }))
|
addBuildPlugin(DevOnlyPlugin({
|
||||||
addWebpackPlugin(() => DevOnlyPlugin.webpack({ sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client }))
|
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nuxt.options.dev) {
|
if (nuxt.options.dev) {
|
||||||
|
@ -10,7 +10,7 @@ interface DevOnlyPluginOptions {
|
|||||||
const DEVONLY_COMP_SINGLE_RE = /<(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>[\s\S]*?<\/(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>/
|
const DEVONLY_COMP_SINGLE_RE = /<(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>[\s\S]*?<\/(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>/
|
||||||
const DEVONLY_COMP_RE = /<(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>[\s\S]*?<\/(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>/g
|
const DEVONLY_COMP_RE = /<(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>[\s\S]*?<\/(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>/g
|
||||||
|
|
||||||
export const DevOnlyPlugin = createUnplugin((options: DevOnlyPluginOptions) => {
|
export const DevOnlyPlugin = (options: DevOnlyPluginOptions) => createUnplugin(() => {
|
||||||
return {
|
return {
|
||||||
name: 'nuxt:server-devonly:transform',
|
name: 'nuxt:server-devonly:transform',
|
||||||
enforce: 'pre',
|
enforce: 'pre',
|
||||||
|
@ -6,7 +6,6 @@ import MagicString from 'magic-string'
|
|||||||
|
|
||||||
interface LayerAliasingOptions {
|
interface LayerAliasingOptions {
|
||||||
sourcemap?: boolean
|
sourcemap?: boolean
|
||||||
transform?: boolean
|
|
||||||
root: string
|
root: string
|
||||||
dev: boolean
|
dev: boolean
|
||||||
layers: NuxtConfigLayer[]
|
layers: NuxtConfigLayer[]
|
||||||
@ -15,7 +14,7 @@ interface LayerAliasingOptions {
|
|||||||
const ALIAS_RE = /(?<=['"])[~@]{1,2}(?=\/)/g
|
const ALIAS_RE = /(?<=['"])[~@]{1,2}(?=\/)/g
|
||||||
const ALIAS_RE_SINGLE = /(?<=['"])[~@]{1,2}(?=\/)/
|
const ALIAS_RE_SINGLE = /(?<=['"])[~@]{1,2}(?=\/)/
|
||||||
|
|
||||||
export const LayerAliasingPlugin = createUnplugin((options: LayerAliasingOptions) => {
|
export const LayerAliasingPlugin = (options: LayerAliasingOptions) => createUnplugin((_options, meta) => {
|
||||||
const aliases: Record<string, Record<string, string>> = {}
|
const aliases: Record<string, Record<string, string>> = {}
|
||||||
for (const layer of options.layers) {
|
for (const layer of options.layers) {
|
||||||
const srcDir = layer.config.srcDir || layer.cwd
|
const srcDir = layer.config.srcDir || layer.cwd
|
||||||
@ -52,12 +51,13 @@ export const LayerAliasingPlugin = createUnplugin((options: LayerAliasingOptions
|
|||||||
|
|
||||||
// webpack-only transform
|
// webpack-only transform
|
||||||
transformInclude: (id) => {
|
transformInclude: (id) => {
|
||||||
if (!options.transform) { return false }
|
if (meta.framework === 'vite') { return false }
|
||||||
|
|
||||||
const _id = normalize(id)
|
const _id = normalize(id)
|
||||||
return layers.some(dir => _id.startsWith(dir))
|
return layers.some(dir => _id.startsWith(dir))
|
||||||
},
|
},
|
||||||
transform (code, id) {
|
transform (code, id) {
|
||||||
if (!options.transform) { return }
|
if (meta.framework === 'vite') { return }
|
||||||
|
|
||||||
const _id = normalize(id)
|
const _id = normalize(id)
|
||||||
const layer = layers.find(l => _id.startsWith(l))
|
const layer = layers.find(l => _id.startsWith(l))
|
||||||
|
@ -5,12 +5,12 @@ import { isJS, isVue } from '../utils'
|
|||||||
|
|
||||||
type ImportPath = string
|
type ImportPath = string
|
||||||
|
|
||||||
export interface TreeShakeComposablesPluginOptions {
|
interface TreeShakeComposablesPluginOptions {
|
||||||
sourcemap?: boolean
|
sourcemap?: boolean
|
||||||
composables: Record<ImportPath, string[]>
|
composables: Record<ImportPath, string[]>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TreeShakeComposablesPlugin = createUnplugin((options: TreeShakeComposablesPluginOptions) => {
|
export const TreeShakeComposablesPlugin = (options: TreeShakeComposablesPluginOptions) => createUnplugin(() => {
|
||||||
/**
|
/**
|
||||||
* @todo Use the options import-path to tree-shake composables in a safer way.
|
* @todo Use the options import-path to tree-shake composables in a safer way.
|
||||||
*/
|
*/
|
||||||
|
@ -6,12 +6,12 @@ import { isJS, isVue } from '../utils'
|
|||||||
|
|
||||||
const TRANSFORM_MARKER = '/* _processed_nuxt_unctx_transform */\n'
|
const TRANSFORM_MARKER = '/* _processed_nuxt_unctx_transform */\n'
|
||||||
|
|
||||||
export interface UnctxTransformPluginOptions {
|
interface UnctxTransformPluginOptions {
|
||||||
sourcemap?: boolean
|
sourcemap?: boolean
|
||||||
transformerOptions: TransformerOptions
|
transformerOptions: TransformerOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UnctxTransformPlugin = createUnplugin((options: UnctxTransformPluginOptions) => {
|
export const UnctxTransformPlugin = (options: UnctxTransformPluginOptions) => createUnplugin(() => {
|
||||||
const transformer = createTransformer(options.transformerOptions)
|
const transformer = createTransformer(options.transformerOptions)
|
||||||
return {
|
return {
|
||||||
name: 'unctx:transform',
|
name: 'unctx:transform',
|
||||||
|
@ -8,6 +8,7 @@ import { hash } from 'ohash'
|
|||||||
import { camelCase } from 'scule'
|
import { camelCase } from 'scule'
|
||||||
import { filename } from 'pathe/utils'
|
import { filename } from 'pathe/utils'
|
||||||
import type { NuxtTemplate, NuxtTypeTemplate } from 'nuxt/schema'
|
import type { NuxtTemplate, NuxtTypeTemplate } from 'nuxt/schema'
|
||||||
|
import type { Nitro } from 'nitropack'
|
||||||
|
|
||||||
import { annotatePlugins, checkForCircularDependencies } from './app'
|
import { annotatePlugins, checkForCircularDependencies } from './app'
|
||||||
|
|
||||||
@ -512,6 +513,7 @@ export const nuxtConfigTemplate: NuxtTemplate = {
|
|||||||
`export const outdatedBuildInterval = ${ctx.nuxt.options.experimental.checkOutdatedBuildInterval}`,
|
`export const outdatedBuildInterval = ${ctx.nuxt.options.experimental.checkOutdatedBuildInterval}`,
|
||||||
`export const multiApp = ${!!ctx.nuxt.options.future.multiApp}`,
|
`export const multiApp = ${!!ctx.nuxt.options.future.multiApp}`,
|
||||||
`export const chunkErrorEvent = ${ctx.nuxt.options.experimental.emitRouteChunkError ? ctx.nuxt.options.builder === '@nuxt/vite-builder' ? '"vite:preloadError"' : '"nuxt:preloadError"' : 'false'}`,
|
`export const chunkErrorEvent = ${ctx.nuxt.options.experimental.emitRouteChunkError ? ctx.nuxt.options.builder === '@nuxt/vite-builder' ? '"vite:preloadError"' : '"nuxt:preloadError"' : 'false'}`,
|
||||||
|
`export const crawlLinks = ${!!((ctx.nuxt as any)._nitro as Nitro).options.prerender.crawlLinks}`,
|
||||||
].join('\n\n')
|
].join('\n\n')
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { existsSync, readdirSync } from 'node:fs'
|
import { existsSync, readdirSync } from 'node:fs'
|
||||||
import { mkdir, readFile } from 'node:fs/promises'
|
import { mkdir, readFile } from 'node:fs/promises'
|
||||||
import { addBuildPlugin, addComponent, addPlugin, addTemplate, addTypeTemplate, addVitePlugin, addWebpackPlugin, defineNuxtModule, findPath, logger, resolvePath, updateTemplates, useNitro } from '@nuxt/kit'
|
import { addBuildPlugin, addComponent, addPlugin, addTemplate, addTypeTemplate, defineNuxtModule, findPath, logger, resolvePath, updateTemplates, useNitro } from '@nuxt/kit'
|
||||||
import { dirname, join, relative, resolve } from 'pathe'
|
import { dirname, join, relative, resolve } from 'pathe'
|
||||||
import { genImport, genObjectFromRawEntries, genString } from 'knitwork'
|
import { genImport, genObjectFromRawEntries, genString } from 'knitwork'
|
||||||
import { joinURL } from 'ufo'
|
import { joinURL } from 'ufo'
|
||||||
@ -8,6 +8,7 @@ import type { Nuxt, NuxtApp, NuxtPage } from 'nuxt/schema'
|
|||||||
import { createRoutesContext } from 'unplugin-vue-router'
|
import { createRoutesContext } from 'unplugin-vue-router'
|
||||||
import { resolveOptions } from 'unplugin-vue-router/options'
|
import { resolveOptions } from 'unplugin-vue-router/options'
|
||||||
import type { EditableTreeNode, Options as TypedRouterOptions } from 'unplugin-vue-router'
|
import type { EditableTreeNode, Options as TypedRouterOptions } from 'unplugin-vue-router'
|
||||||
|
import { createRouter as createRadixRouter, toRouteMatcher } from 'radix3'
|
||||||
|
|
||||||
import type { NitroRouteConfig } from 'nitropack'
|
import type { NitroRouteConfig } from 'nitropack'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
@ -15,7 +16,6 @@ import { distDir } from '../dirs'
|
|||||||
import { resolveTypePath } from '../core/utils/types'
|
import { resolveTypePath } from '../core/utils/types'
|
||||||
import { normalizeRoutes, resolvePagesRoutes, resolveRoutePaths } from './utils'
|
import { normalizeRoutes, resolvePagesRoutes, resolveRoutePaths } from './utils'
|
||||||
import { extractRouteRules, getMappedPages } from './route-rules'
|
import { extractRouteRules, getMappedPages } from './route-rules'
|
||||||
import type { PageMetaPluginOptions } from './plugins/page-meta'
|
|
||||||
import { PageMetaPlugin } from './plugins/page-meta'
|
import { PageMetaPlugin } from './plugins/page-meta'
|
||||||
import { RouteInjectionPlugin } from './plugins/route-injection'
|
import { RouteInjectionPlugin } from './plugins/route-injection'
|
||||||
|
|
||||||
@ -273,7 +273,7 @@ export default defineNuxtModule({
|
|||||||
|
|
||||||
nuxt.hook('app:resolve', (app) => {
|
nuxt.hook('app:resolve', (app) => {
|
||||||
const nitro = useNitro()
|
const nitro = useNitro()
|
||||||
if (nitro.options.prerender.crawlLinks) {
|
if (nitro.options.prerender.crawlLinks || Object.values(nitro.options.routeRules).some(rule => rule.prerender)) {
|
||||||
app.plugins.push({
|
app.plugins.push({
|
||||||
src: resolve(runtimeDir, 'plugins/prerender.server'),
|
src: resolve(runtimeDir, 'plugins/prerender.server'),
|
||||||
mode: 'server',
|
mode: 'server',
|
||||||
@ -311,7 +311,20 @@ export default defineNuxtModule({
|
|||||||
})
|
})
|
||||||
|
|
||||||
nuxt.hook('nitro:build:before', (nitro) => {
|
nuxt.hook('nitro:build:before', (nitro) => {
|
||||||
if (nuxt.options.dev || !nitro.options.static || nuxt.options.router.options.hashMode || !nitro.options.prerender.crawlLinks) { return }
|
if (nuxt.options.dev || nuxt.options.router.options.hashMode) { return }
|
||||||
|
|
||||||
|
// Inject page patterns that explicitly match `prerender: true` route rule
|
||||||
|
if (!nitro.options.static && !nitro.options.prerender.crawlLinks) {
|
||||||
|
const routeRulesMatcher = toRouteMatcher(createRadixRouter({ routes: nitro.options.routeRules }))
|
||||||
|
for (const route of prerenderRoutes) {
|
||||||
|
const rules = defu({} as Record<string, any>, ...routeRulesMatcher.matchAll(route).reverse())
|
||||||
|
if (rules.prerender) {
|
||||||
|
nitro.options.prerender.routes.push(route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nitro.options.static || !nitro.options.prerender.crawlLinks) { return }
|
||||||
|
|
||||||
// Only hint the first route when `ssr: true` and no routes are provided
|
// Only hint the first route when `ssr: true` and no routes are provided
|
||||||
// as the rest will be injected at runtime when this is prerendered
|
// as the rest will be injected at runtime when this is prerendered
|
||||||
@ -418,13 +431,11 @@ export default defineNuxtModule({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract macros from pages
|
// Extract macros from pages
|
||||||
const pageMetaOptions: PageMetaPluginOptions = {
|
|
||||||
dev: nuxt.options.dev,
|
|
||||||
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
|
||||||
}
|
|
||||||
nuxt.hook('modules:done', () => {
|
nuxt.hook('modules:done', () => {
|
||||||
addVitePlugin(() => PageMetaPlugin.vite(pageMetaOptions))
|
addBuildPlugin(PageMetaPlugin({
|
||||||
addWebpackPlugin(() => PageMetaPlugin.webpack(pageMetaOptions))
|
dev: nuxt.options.dev,
|
||||||
|
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
||||||
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add prefetching support for middleware & layouts
|
// Add prefetching support for middleware & layouts
|
||||||
|
@ -10,7 +10,7 @@ import MagicString from 'magic-string'
|
|||||||
import { isAbsolute } from 'pathe'
|
import { isAbsolute } from 'pathe'
|
||||||
import { logger } from '@nuxt/kit'
|
import { logger } from '@nuxt/kit'
|
||||||
|
|
||||||
export interface PageMetaPluginOptions {
|
interface PageMetaPluginOptions {
|
||||||
dev?: boolean
|
dev?: boolean
|
||||||
sourcemap?: boolean
|
sourcemap?: boolean
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ if (import.meta.webpackHot) {
|
|||||||
})
|
})
|
||||||
}`
|
}`
|
||||||
|
|
||||||
export const PageMetaPlugin = createUnplugin((options: PageMetaPluginOptions) => {
|
export const PageMetaPlugin = (options: PageMetaPluginOptions) => createUnplugin(() => {
|
||||||
return {
|
return {
|
||||||
name: 'nuxt:pages-macros-transform',
|
name: 'nuxt:pages-macros-transform',
|
||||||
enforce: 'post',
|
enforce: 'post',
|
||||||
|
@ -1,20 +1,31 @@
|
|||||||
import type { RouteRecordRaw } from 'vue-router'
|
import type { RouteRecordRaw } from 'vue-router'
|
||||||
import { joinURL } from 'ufo'
|
import { joinURL } from 'ufo'
|
||||||
|
import { createRouter as createRadixRouter, toRouteMatcher } from 'radix3'
|
||||||
|
import defu from 'defu'
|
||||||
|
|
||||||
import { defineNuxtPlugin } from '#app/nuxt'
|
import { defineNuxtPlugin, useRuntimeConfig } from '#app/nuxt'
|
||||||
import { prerenderRoutes } from '#app/composables/ssr'
|
import { prerenderRoutes } from '#app/composables/ssr'
|
||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
import _routes from '#build/routes'
|
import _routes from '#build/routes'
|
||||||
import routerOptions from '#build/router.options'
|
import routerOptions from '#build/router.options'
|
||||||
|
// @ts-expect-error virtual file
|
||||||
|
import { crawlLinks } from '#build/nuxt.config.mjs'
|
||||||
|
|
||||||
let routes: string[]
|
let routes: string[]
|
||||||
|
|
||||||
|
let _routeRulesMatcher: undefined | ReturnType<typeof toRouteMatcher> = undefined
|
||||||
|
|
||||||
export default defineNuxtPlugin(async () => {
|
export default defineNuxtPlugin(async () => {
|
||||||
if (!import.meta.server || !import.meta.prerender || routerOptions.hashMode) {
|
if (!import.meta.server || !import.meta.prerender || routerOptions.hashMode) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (routes && !routes.length) { return }
|
if (routes && !routes.length) { return }
|
||||||
|
|
||||||
|
const routeRules = useRuntimeConfig().nitro!.routeRules
|
||||||
|
if (!crawlLinks && routeRules && Object.values(routeRules).some(r => r.prerender)) {
|
||||||
|
_routeRulesMatcher = toRouteMatcher(createRadixRouter({ routes: routeRules }))
|
||||||
|
}
|
||||||
|
|
||||||
routes ||= Array.from(processRoutes(await routerOptions.routes?.(_routes) ?? _routes))
|
routes ||= Array.from(processRoutes(await routerOptions.routes?.(_routes) ?? _routes))
|
||||||
const batch = routes.splice(0, 10)
|
const batch = routes.splice(0, 10)
|
||||||
prerenderRoutes(batch)
|
prerenderRoutes(batch)
|
||||||
@ -24,10 +35,14 @@ export default defineNuxtPlugin(async () => {
|
|||||||
|
|
||||||
const OPTIONAL_PARAM_RE = /^\/?:.*(?:\?|\(\.\*\)\*)$/
|
const OPTIONAL_PARAM_RE = /^\/?:.*(?:\?|\(\.\*\)\*)$/
|
||||||
|
|
||||||
|
function shouldPrerender (path: string) {
|
||||||
|
return !_routeRulesMatcher || defu({} as Record<string, any>, ..._routeRulesMatcher.matchAll(path).reverse()).prerender
|
||||||
|
}
|
||||||
|
|
||||||
function processRoutes (routes: RouteRecordRaw[], currentPath = '/', routesToPrerender = new Set<string>()) {
|
function processRoutes (routes: RouteRecordRaw[], currentPath = '/', routesToPrerender = new Set<string>()) {
|
||||||
for (const route of routes) {
|
for (const route of routes) {
|
||||||
// Add root of optional dynamic paths and catchalls
|
// Add root of optional dynamic paths and catchalls
|
||||||
if (OPTIONAL_PARAM_RE.test(route.path) && !route.children?.length) {
|
if (OPTIONAL_PARAM_RE.test(route.path) && !route.children?.length && shouldPrerender(currentPath)) {
|
||||||
routesToPrerender.add(currentPath)
|
routesToPrerender.add(currentPath)
|
||||||
}
|
}
|
||||||
// Skip dynamic paths
|
// Skip dynamic paths
|
||||||
@ -35,7 +50,9 @@ function processRoutes (routes: RouteRecordRaw[], currentPath = '/', routesToPre
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const fullPath = joinURL(currentPath, route.path)
|
const fullPath = joinURL(currentPath, route.path)
|
||||||
routesToPrerender.add(fullPath)
|
if (shouldPrerender(fullPath)) {
|
||||||
|
routesToPrerender.add(fullPath)
|
||||||
|
}
|
||||||
if (route.children) {
|
if (route.children) {
|
||||||
processRoutes(route.children, fullPath, routesToPrerender)
|
processRoutes(route.children, fullPath, routesToPrerender)
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import type { Component, Nuxt } from '@nuxt/schema'
|
|||||||
import { kebabCase } from 'scule'
|
import { kebabCase } from 'scule'
|
||||||
import { normalize } from 'pathe'
|
import { normalize } from 'pathe'
|
||||||
|
|
||||||
import { createTransformPlugin } from '../src/components/plugins/transform'
|
import { TransformPlugin } from '../src/components/plugins/transform'
|
||||||
|
|
||||||
describe('components:transform', () => {
|
describe('components:transform', () => {
|
||||||
it('should transform #components imports', async () => {
|
it('should transform #components imports', async () => {
|
||||||
@ -92,7 +92,7 @@ function createTransformer (components: Component[], mode: 'client' | 'server' |
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as Nuxt
|
} as Nuxt
|
||||||
const plugin = createTransformPlugin(stubNuxt, () => components, mode).vite()
|
const plugin = TransformPlugin(stubNuxt, () => components, mode).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)
|
||||||
|
@ -3,7 +3,7 @@ import type { Plugin } from 'vite'
|
|||||||
import { DevOnlyPlugin } from '../src/core/plugins/dev-only'
|
import { DevOnlyPlugin } from '../src/core/plugins/dev-only'
|
||||||
import { normalizeLineEndings } from './utils'
|
import { normalizeLineEndings } from './utils'
|
||||||
|
|
||||||
const pluginVite = DevOnlyPlugin.raw({}, { framework: 'vite' }) as Plugin
|
const pluginVite = DevOnlyPlugin({}).raw({}, { framework: 'vite' }) as Plugin
|
||||||
|
|
||||||
const viteTransform = async (source: string, id: string) => {
|
const viteTransform = async (source: string, id: string) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
|
@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest'
|
|||||||
import type { Plugin } from 'vite'
|
import type { Plugin } from 'vite'
|
||||||
import type { Component } from '@nuxt/schema'
|
import type { Component } from '@nuxt/schema'
|
||||||
import type { UnpluginOptions } from 'unplugin'
|
import type { UnpluginOptions } from 'unplugin'
|
||||||
import { islandsTransform } from '../src/components/plugins/islands-transform'
|
import { IslandsTransformPlugin } from '../src/components/plugins/islands-transform'
|
||||||
import { normalizeLineEndings } from './utils'
|
import { normalizeLineEndings } from './utils'
|
||||||
|
|
||||||
const getComponents = () => [{
|
const getComponents = () => [{
|
||||||
@ -18,16 +18,16 @@ const getComponents = () => [{
|
|||||||
preload: false,
|
preload: false,
|
||||||
}] as Component[]
|
}] as Component[]
|
||||||
|
|
||||||
const pluginWebpack = islandsTransform.raw({
|
const pluginWebpack = IslandsTransformPlugin({
|
||||||
getComponents,
|
getComponents,
|
||||||
selectiveClient: true,
|
selectiveClient: true,
|
||||||
}, { framework: 'webpack', webpack: { compiler: {} as any } })
|
}).raw({}, { framework: 'webpack', webpack: { compiler: {} as any } })
|
||||||
|
|
||||||
const viteTransform = async (source: string, id: string, selectiveClient = false) => {
|
const viteTransform = async (source: string, id: string, selectiveClient = false) => {
|
||||||
const vitePlugin = islandsTransform.raw({
|
const vitePlugin = IslandsTransformPlugin({
|
||||||
getComponents,
|
getComponents,
|
||||||
selectiveClient,
|
selectiveClient,
|
||||||
}, { framework: 'vite' }) as Plugin
|
}).raw({}, { framework: 'vite' }) as Plugin
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
const result = await (vitePlugin.transform! as Function)(source, id)
|
const result = await (vitePlugin.transform! as Function)(source, id)
|
||||||
@ -454,7 +454,7 @@ withDefaults(defineProps<{ things?: any[]; somethingElse?: string }>(), {
|
|||||||
"
|
"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
expect(spyOnWarn).toHaveBeenCalledWith('nuxt-client attribute and client components within islands is only supported with Vite. file: hello.server.vue')
|
expect(spyOnWarn).toHaveBeenCalledWith('The `nuxt-client` attribute and client components within islands are only supported with Vite. file: hello.server.vue')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
@ -27,7 +27,7 @@ function vuePlugin (options: Options) {
|
|||||||
|
|
||||||
const WithClientOnly = normalizeLineEndings(readFileSync(path.resolve(fixtureDir, './components/client/WithClientOnlySetup.vue')).toString())
|
const WithClientOnly = normalizeLineEndings(readFileSync(path.resolve(fixtureDir, './components/client/WithClientOnlySetup.vue')).toString())
|
||||||
|
|
||||||
const treeshakeTemplatePlugin = TreeShakeTemplatePlugin.raw({
|
const treeshakeTemplatePlugin = TreeShakeTemplatePlugin({
|
||||||
sourcemap: false,
|
sourcemap: false,
|
||||||
getComponents () {
|
getComponents () {
|
||||||
return [{
|
return [{
|
||||||
@ -52,7 +52,7 @@ const treeshakeTemplatePlugin = TreeShakeTemplatePlugin.raw({
|
|||||||
mode: 'client',
|
mode: 'client',
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
}, { framework: 'rollup' }) as Plugin
|
}).raw({}, { framework: 'rollup' }) as Plugin
|
||||||
|
|
||||||
const treeshake = async (source: string): Promise<string> => {
|
const treeshake = async (source: string): Promise<string> => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
|
@ -62,6 +62,6 @@ function transform (code: string, id = 'app.vue') {
|
|||||||
definePageMeta: ['middleware', 'validate'],
|
definePageMeta: ['middleware', 'validate'],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
const plugin = UnctxTransformPlugin.raw({ sourcemap: false, transformerOptions }, {} as any) as any
|
const plugin = UnctxTransformPlugin({ sourcemap: false, transformerOptions }).raw({}, {} as any) as any
|
||||||
return plugin.transformInclude(id) ? Promise.resolve(plugin.transform(code)).then((r: any) => r?.code.replace(/^ {6}/gm, '').trim()) : null
|
return plugin.transformInclude(id) ? Promise.resolve(plugin.transform(code)).then((r: any) => r?.code.replace(/^ {6}/gm, '').trim()) : null
|
||||||
}
|
}
|
||||||
|
@ -42,20 +42,20 @@
|
|||||||
"@unhead/schema": "1.11.7",
|
"@unhead/schema": "1.11.7",
|
||||||
"@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.11",
|
||||||
"@vue/compiler-sfc": "3.5.10",
|
"@vue/compiler-sfc": "3.5.11",
|
||||||
"@vue/language-core": "2.1.6",
|
"@vue/language-core": "2.1.6",
|
||||||
"c12": "2.0.1",
|
"c12": "2.0.1",
|
||||||
"esbuild-loader": "4.2.2",
|
"esbuild-loader": "4.2.2",
|
||||||
"h3": "1.12.0",
|
"h3": "1.12.0",
|
||||||
"ignore": "6.0.2",
|
"ignore": "6.0.2",
|
||||||
"nitropack": "2.9.7",
|
"nitropack": "2.9.7",
|
||||||
"ofetch": "1.4.0",
|
"ofetch": "1.4.1",
|
||||||
"unbuild": "latest",
|
"unbuild": "latest",
|
||||||
"unctx": "2.3.1",
|
"unctx": "2.3.1",
|
||||||
"unenv": "1.10.0",
|
"unenv": "1.10.0",
|
||||||
"vite": "5.4.8",
|
"vite": "5.4.8",
|
||||||
"vue": "3.5.10",
|
"vue": "3.5.11",
|
||||||
"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",
|
||||||
@ -68,7 +68,7 @@
|
|||||||
"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",
|
||||||
|
@ -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, ModuleSetupReturn, ModuleSetupInstallResult, NuxtModule, ResolvedModuleOptions } from './types/module'
|
export type { ModuleDefinition, ModuleMeta, ModuleOptions, ModuleSetupReturn, ModuleSetupInstallResult, 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
|
||||||
|
@ -42,6 +42,12 @@ export interface NuxtTemplate<Options = TemplateDefaultOptions> {
|
|||||||
write?: boolean
|
write?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface NuxtServerTemplate {
|
||||||
|
/** The target filename once the template is copied into the Nuxt buildDir */
|
||||||
|
filename: string
|
||||||
|
getContents: () => string | Promise<string>
|
||||||
|
}
|
||||||
|
|
||||||
export interface ResolvedNuxtTemplate<Options = TemplateDefaultOptions> extends NuxtTemplate<Options> {
|
export interface ResolvedNuxtTemplate<Options = TemplateDefaultOptions> extends NuxtTemplate<Options> {
|
||||||
filename: string
|
filename: string
|
||||||
dst: string
|
dst: string
|
||||||
|
@ -19,11 +19,11 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/lodash-es": "4.17.12",
|
"@types/lodash-es": "4.17.12",
|
||||||
"@unocss/reset": "0.63.2",
|
"@unocss/reset": "0.63.4",
|
||||||
"critters": "0.0.24",
|
"critters": "0.0.24",
|
||||||
"html-validate": "8.24.0",
|
"html-validate": "8.24.1",
|
||||||
"htmlnano": "2.1.1",
|
"htmlnano": "2.1.1",
|
||||||
"jiti": "2.3.1",
|
"jiti": "2.3.3",
|
||||||
"knitwork": "1.1.0",
|
"knitwork": "1.1.0",
|
||||||
"lodash-es": "4.17.21",
|
"lodash-es": "4.17.21",
|
||||||
"pathe": "1.1.2",
|
"pathe": "1.1.2",
|
||||||
@ -31,7 +31,7 @@
|
|||||||
"scule": "1.3.0",
|
"scule": "1.3.0",
|
||||||
"tinyexec": "0.3.0",
|
"tinyexec": "0.3.0",
|
||||||
"tinyglobby": "0.2.9",
|
"tinyglobby": "0.2.9",
|
||||||
"unocss": "0.63.2",
|
"unocss": "0.63.4",
|
||||||
"vite": "5.4.8"
|
"vite": "5.4.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
"@types/estree": "1.0.6",
|
"@types/estree": "1.0.6",
|
||||||
"rollup": "4.24.0",
|
"rollup": "4.24.0",
|
||||||
"unbuild": "latest",
|
"unbuild": "latest",
|
||||||
"vue": "3.5.10"
|
"vue": "3.5.11"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/kit": "workspace:*",
|
"@nuxt/kit": "workspace:*",
|
||||||
@ -47,14 +47,14 @@
|
|||||||
"externality": "^1.0.2",
|
"externality": "^1.0.2",
|
||||||
"get-port-please": "^3.1.2",
|
"get-port-please": "^3.1.2",
|
||||||
"h3": "^1.12.0",
|
"h3": "^1.12.0",
|
||||||
"jiti": "^2.3.1",
|
"jiti": "^2.3.3",
|
||||||
"knitwork": "^1.1.0",
|
"knitwork": "^1.1.0",
|
||||||
"magic-string": "^0.30.11",
|
"magic-string": "^0.30.11",
|
||||||
"mlly": "^1.7.1",
|
"mlly": "^1.7.2",
|
||||||
"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",
|
||||||
"postcss": "^8.4.47",
|
"postcss": "^8.4.47",
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
"std-env": "^3.7.0",
|
"std-env": "^3.7.0",
|
||||||
|
@ -39,12 +39,12 @@
|
|||||||
"fork-ts-checker-webpack-plugin": "^9.0.2",
|
"fork-ts-checker-webpack-plugin": "^9.0.2",
|
||||||
"h3": "^1.12.0",
|
"h3": "^1.12.0",
|
||||||
"hash-sum": "^2.0.0",
|
"hash-sum": "^2.0.0",
|
||||||
"jiti": "^2.3.1",
|
"jiti": "^2.3.3",
|
||||||
"lodash-es": "4.17.21",
|
"lodash-es": "4.17.21",
|
||||||
"magic-string": "^0.30.11",
|
"magic-string": "^0.30.11",
|
||||||
"memfs": "^4.12.0",
|
"memfs": "^4.13.0",
|
||||||
"mini-css-extract-plugin": "^2.9.1",
|
"mini-css-extract-plugin": "^2.9.1",
|
||||||
"mlly": "^1.7.1",
|
"mlly": "^1.7.2",
|
||||||
"ohash": "^1.1.4",
|
"ohash": "^1.1.4",
|
||||||
"pathe": "^1.1.2",
|
"pathe": "^1.1.2",
|
||||||
"pify": "^6.1.0",
|
"pify": "^6.1.0",
|
||||||
@ -76,7 +76,7 @@
|
|||||||
"@types/webpack-bundle-analyzer": "4.7.0",
|
"@types/webpack-bundle-analyzer": "4.7.0",
|
||||||
"@types/webpack-hot-middleware": "2.25.9",
|
"@types/webpack-hot-middleware": "2.25.9",
|
||||||
"unbuild": "latest",
|
"unbuild": "latest",
|
||||||
"vue": "3.5.10"
|
"vue": "3.5.11"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vue": "^3.3.4"
|
"vue": "^3.3.4"
|
||||||
|
2088
pnpm-lock.yaml
2088
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -618,6 +618,11 @@ describe('pages', () => {
|
|||||||
expect(status).toBe(200)
|
expect(status).toBe(200)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it.skipIf(isDev() || isWebpack /* TODO: fix bug with import.meta.prerender being undefined in webpack build */)('prerenders pages hinted with a route rule', async () => {
|
||||||
|
const html = await $fetch('/prerender/test')
|
||||||
|
expect(html).toContain('should be prerendered: true')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('nuxt composables', () => {
|
describe('nuxt composables', () => {
|
||||||
@ -1834,7 +1839,13 @@ describe.skipIf(isDev() || isWebpack)('inlining component styles', () => {
|
|||||||
|
|
||||||
it('does not load stylesheet for page styles', async () => {
|
it('does not load stylesheet for page styles', async () => {
|
||||||
const html: string = await $fetch<string>('/styles')
|
const html: string = await $fetch<string>('/styles')
|
||||||
expect(html.match(/<link [^>]*href="[^"]*\.css">(?: crossorigin)?/g)?.filter(m => m.includes('entry'))?.map(m => m.replace(/\.[^.]*\.css/, '.css'))).toMatchInlineSnapshot(`undefined`)
|
const cssFiles = html.match(/<link [^>]*href="[^"]*\.css"/g)
|
||||||
|
expect(cssFiles?.length).toBeGreaterThan(0)
|
||||||
|
expect(cssFiles?.filter(m => m.includes('entry'))?.map(m => m.replace(/\.[^.]*\.css/, '.css'))).toMatchInlineSnapshot(`
|
||||||
|
[
|
||||||
|
"<link rel="stylesheet" href="/_nuxt/entry.css"",
|
||||||
|
]
|
||||||
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('still downloads client-only styles', async () => {
|
it('still downloads client-only styles', async () => {
|
||||||
|
@ -40,7 +40,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
|
|||||||
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"213k"`)
|
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"213k"`)
|
||||||
|
|
||||||
const modules = await analyzeSizes(['node_modules/**/*'], serverDir)
|
const modules = await analyzeSizes(['node_modules/**/*'], serverDir)
|
||||||
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"1389k"`)
|
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"1390k"`)
|
||||||
|
|
||||||
const packages = modules.files
|
const packages = modules.files
|
||||||
.filter(m => m.endsWith('package.json'))
|
.filter(m => m.endsWith('package.json'))
|
||||||
|
1
test/fixtures/basic/nuxt.config.ts
vendored
1
test/fixtures/basic/nuxt.config.ts
vendored
@ -67,6 +67,7 @@ export default defineNuxtConfig({
|
|||||||
'/route-rules/middleware': { appMiddleware: 'route-rules-middleware' },
|
'/route-rules/middleware': { appMiddleware: 'route-rules-middleware' },
|
||||||
'/hydration/spa-redirection/**': { ssr: false },
|
'/hydration/spa-redirection/**': { ssr: false },
|
||||||
'/no-scripts': { experimentalNoScripts: true },
|
'/no-scripts': { experimentalNoScripts: true },
|
||||||
|
'/prerender/**': { prerender: true },
|
||||||
},
|
},
|
||||||
output: { dir: process.env.NITRO_OUTPUT_DIR },
|
output: { dir: process.env.NITRO_OUTPUT_DIR },
|
||||||
prerender: {
|
prerender: {
|
||||||
|
9
test/fixtures/basic/pages/prerender/test.vue
vendored
Normal file
9
test/fixtures/basic/pages/prerender/test.vue
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const wasPrerendered = useState(() => import.meta.prerender)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
should be prerendered: {{ wasPrerendered }}
|
||||||
|
</div>
|
||||||
|
</template>
|
Loading…
Reference in New Issue
Block a user