mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-21 21:25:11 +00:00
Merge branch 'main' into patch-21
This commit is contained in:
commit
2df3351048
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@ -1,6 +1,6 @@
|
|||||||
name: "\U0001F41E Bug report"
|
name: "\U0001F41E Bug report"
|
||||||
description: Create a report to help us improve Nuxt
|
description: Create a report to help us improve Nuxt
|
||||||
labels: ["pending triage", "3.x"]
|
labels: ["pending triage"]
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
|
7
.github/ISSUE_TEMPLATE/config.yml
vendored
7
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,11 +1,8 @@
|
|||||||
blank_issues_enabled: true
|
blank_issues_enabled: true
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: 📚 Nuxt 3 Documentation
|
- name: 📚 Nuxt Documentation
|
||||||
url: https://nuxt.com/docs
|
url: https://nuxt.com/docs
|
||||||
about: Check the documentation for usage of Nuxt 3
|
about: Check the documentation for usage of Nuxt
|
||||||
- name: 📚 Nuxt 2 Documentation
|
|
||||||
url: https://v2.nuxt.com
|
|
||||||
about: Check the documentation for usage of Nuxt 2
|
|
||||||
- name: 💬 Discussions
|
- name: 💬 Discussions
|
||||||
url: https://github.com/nuxt/nuxt/discussions
|
url: https://github.com/nuxt/nuxt/discussions
|
||||||
about: Use discussions if you have another issue, an idea for improvement or for asking questions.
|
about: Use discussions if you have another issue, an idea for improvement or for asking questions.
|
||||||
|
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@ -1,6 +1,6 @@
|
|||||||
name: "🚀 Feature request"
|
name: "🚀 Feature request"
|
||||||
description: Suggest a feature that will improve Nuxt
|
description: Suggest a feature that will improve Nuxt
|
||||||
labels: ["pending triage", "3.x"]
|
labels: ["pending triage"]
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
|
49
.github/ISSUE_TEMPLATE/z-bug-report-2.yml
vendored
49
.github/ISSUE_TEMPLATE/z-bug-report-2.yml
vendored
@ -1,49 +0,0 @@
|
|||||||
name: "\U0001F41E Bug report (Nuxt 2)"
|
|
||||||
description: Create a report to help us improve Nuxt
|
|
||||||
labels: ["pending triage", "2.x"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Please carefully read the contribution docs before creating a bug report
|
|
||||||
👉 https://nuxt.com/docs/community/reporting-bugs
|
|
||||||
|
|
||||||
Please use a template below to create a minimal reproduction
|
|
||||||
👉 https://stackblitz.com/github/nuxt/starter/tree/v2
|
|
||||||
👉 https://codesandbox.io/s/github/nuxt/starter/v2
|
|
||||||
- type: textarea
|
|
||||||
id: bug-env
|
|
||||||
attributes:
|
|
||||||
label: Environment
|
|
||||||
description: You can use `npx envinfo --system --npmPackages '{nuxt,@nuxt/*}' --binaries --browsers` to fill this section
|
|
||||||
placeholder: Environment
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: reproduction
|
|
||||||
attributes:
|
|
||||||
label: Reproduction
|
|
||||||
description: Please provide a link to a repo that can reproduce the problem you ran into. A [**minimal reproduction**](https://nuxt.com/docs/community/reporting-bugs#create-a-minimal-reproduction) is required unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem. If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "need reproduction" label. If no reproduction is provided we might close it.
|
|
||||||
placeholder: Reproduction
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: bug-description
|
|
||||||
attributes:
|
|
||||||
label: Describe the bug
|
|
||||||
description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks!
|
|
||||||
placeholder: Bug description
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: additonal
|
|
||||||
attributes:
|
|
||||||
label: Additional context
|
|
||||||
description: If applicable, add any other context about the problem here
|
|
||||||
- type: textarea
|
|
||||||
id: logs
|
|
||||||
attributes:
|
|
||||||
label: Logs
|
|
||||||
description: |
|
|
||||||
Optional if provided reproduction. Please try not to insert an image but copy paste the log text.
|
|
||||||
render: shell-script
|
|
5
.github/workflows/autofix-docs.yml
vendored
5
.github/workflows/autofix-docs.yml
vendored
@ -27,7 +27,10 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Build (stub)
|
||||||
|
run: pnpm dev:prepare
|
||||||
|
|
||||||
- name: Lint (docs)
|
- name: Lint (docs)
|
||||||
run: pnpm lint:docs:fix
|
run: pnpm lint:docs:fix
|
||||||
|
|
||||||
- uses: autofix-ci/action@dd55f44df8f7cdb7a6bf74c78677eb8acd40cd0a
|
- uses: autofix-ci/action@2891949f3779a1cafafae1523058501de3d4e944
|
||||||
|
2
.github/workflows/autofix.yml
vendored
2
.github/workflows/autofix.yml
vendored
@ -52,4 +52,4 @@ jobs:
|
|||||||
- name: Lint (code)
|
- name: Lint (code)
|
||||||
run: pnpm lint:fix
|
run: pnpm lint:fix
|
||||||
|
|
||||||
- uses: autofix-ci/action@dd55f44df8f7cdb7a6bf74c78677eb8acd40cd0a
|
- uses: autofix-ci/action@2891949f3779a1cafafae1523058501de3d4e944
|
||||||
|
1
.github/workflows/changelog.yml
vendored
1
.github/workflows/changelog.yml
vendored
@ -5,7 +5,6 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- 3.x
|
- 3.x
|
||||||
- 2.x
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
20
.github/workflows/ci.yml
vendored
20
.github/workflows/ci.yml
vendored
@ -57,7 +57,7 @@ jobs:
|
|||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
- name: Cache dist
|
- name: Cache dist
|
||||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
|
||||||
with:
|
with:
|
||||||
retention-days: 3
|
retention-days: 3
|
||||||
name: dist
|
name: dist
|
||||||
@ -85,19 +85,19 @@ jobs:
|
|||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10
|
uses: github/codeql-action/init@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11
|
||||||
with:
|
with:
|
||||||
languages: javascript
|
languages: javascript
|
||||||
queries: +security-and-quality
|
queries: +security-and-quality
|
||||||
|
|
||||||
- name: Restore dist cache
|
- name: Restore dist cache
|
||||||
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
path: packages
|
path: packages
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10
|
uses: github/codeql-action/analyze@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11
|
||||||
with:
|
with:
|
||||||
category: "/language:javascript"
|
category: "/language:javascript"
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ jobs:
|
|||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Restore dist cache
|
- name: Restore dist cache
|
||||||
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
path: packages
|
path: packages
|
||||||
@ -233,7 +233,7 @@ jobs:
|
|||||||
run: pnpm playwright-core install chromium
|
run: pnpm playwright-core install chromium
|
||||||
|
|
||||||
- name: Restore dist cache
|
- name: Restore dist cache
|
||||||
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
path: packages
|
path: packages
|
||||||
@ -254,6 +254,8 @@ jobs:
|
|||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
build-release:
|
build-release:
|
||||||
|
concurrency:
|
||||||
|
group: release
|
||||||
permissions:
|
permissions:
|
||||||
id-token: write
|
id-token: write
|
||||||
if: |
|
if: |
|
||||||
@ -283,7 +285,7 @@ jobs:
|
|||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Restore dist cache
|
- name: Restore dist cache
|
||||||
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
path: packages
|
path: packages
|
||||||
@ -295,6 +297,8 @@ jobs:
|
|||||||
NPM_CONFIG_PROVENANCE: true
|
NPM_CONFIG_PROVENANCE: true
|
||||||
|
|
||||||
release-pr:
|
release-pr:
|
||||||
|
concurrency:
|
||||||
|
group: release
|
||||||
permissions:
|
permissions:
|
||||||
id-token: write
|
id-token: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
@ -322,7 +326,7 @@ jobs:
|
|||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Restore dist cache
|
- name: Restore dist cache
|
||||||
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
path: packages
|
path: packages
|
||||||
|
3
.github/workflows/docs.yml
vendored
3
.github/workflows/docs.yml
vendored
@ -31,6 +31,9 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Build (stub)
|
||||||
|
run: pnpm dev:prepare
|
||||||
|
|
||||||
- name: Lint (docs)
|
- name: Lint (docs)
|
||||||
run: pnpm lint:docs
|
run: pnpm lint:docs
|
||||||
|
|
||||||
|
4
.github/workflows/release-pr.yml
vendored
4
.github/workflows/release-pr.yml
vendored
@ -14,6 +14,8 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
release-pr:
|
release-pr:
|
||||||
if: github.repository == 'nuxt/nuxt' && github.event.issue.pull_request && github.event.comment.body == '/trigger release'
|
if: github.repository == 'nuxt/nuxt' && github.event.issue.pull_request && github.event.comment.body == '/trigger release'
|
||||||
|
concurrency:
|
||||||
|
group: release
|
||||||
permissions:
|
permissions:
|
||||||
id-token: write
|
id-token: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
@ -44,7 +46,7 @@ jobs:
|
|||||||
if [[ $(date -d "$updated_at" +%s) -gt $(date -d "$COMMENT_AT" +%s) ]]; then
|
if [[ $(date -d "$updated_at" +%s) -gt $(date -d "$COMMENT_AT" +%s) ]]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "head_sha=$head_sha" >> $GITHUB_OUTPUT
|
echo "head_sha=$head_sha" >> $GITHUB_OUTPUT
|
||||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||||
with:
|
with:
|
||||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -12,6 +12,8 @@ permissions: {}
|
|||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
if: github.repository == 'nuxt/nuxt' && (startsWith(github.event.head_commit.message, 'v3.') || startsWith(github.event.head_commit.message, 'v4.'))
|
if: github.repository == 'nuxt/nuxt' && (startsWith(github.event.head_commit.message, 'v3.') || startsWith(github.event.head_commit.message, 'v4.'))
|
||||||
|
concurrency:
|
||||||
|
group: release
|
||||||
permissions:
|
permissions:
|
||||||
id-token: write
|
id-token: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
4
.github/workflows/scorecards.yml
vendored
4
.github/workflows/scorecards.yml
vendored
@ -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@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
|
||||||
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@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10
|
uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11
|
||||||
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/semantic-pull-requests.yml
vendored
2
.github/workflows/semantic-pull-requests.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
|||||||
name: Semantic pull request
|
name: Semantic pull request
|
||||||
steps:
|
steps:
|
||||||
- name: Validate PR title
|
- name: Validate PR title
|
||||||
uses: amannn/action-semantic-pull-request@cfb60706e18bc85e8aec535e3c577abe8f70378e # v5.5.2
|
uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5.5.3
|
||||||
with:
|
with:
|
||||||
scopes: |
|
scopes: |
|
||||||
kit
|
kit
|
||||||
|
1
CODEOWNERS
Normal file
1
CODEOWNERS
Normal file
@ -0,0 +1 @@
|
|||||||
|
* @danielroe
|
10
README.md
10
README.md
@ -34,7 +34,7 @@ It provides a number of features that make it easy to build fast, SEO-friendly,
|
|||||||
- 🏠 [Local Development](#local-development)
|
- 🏠 [Local Development](#local-development)
|
||||||
- ⛰️ [Nuxt 2](#nuxt-2)
|
- ⛰️ [Nuxt 2](#nuxt-2)
|
||||||
- 🛟 [Professional Support](#professional-support)
|
- 🛟 [Professional Support](#professional-support)
|
||||||
- 🔗 [Follow us](#follow-us)
|
- 🔗 [Follow Us](#follow-us)
|
||||||
- ⚖️ [License](#license)
|
- ⚖️ [License](#license)
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -101,18 +101,12 @@ Here are a few ways you can get involved:
|
|||||||
|
|
||||||
Follow the docs to [Set Up Your Local Development Environment](https://nuxt.com/docs/community/framework-contribution#setup) to contribute to the framework and documentation.
|
Follow the docs to [Set Up Your Local Development Environment](https://nuxt.com/docs/community/framework-contribution#setup) to contribute to the framework and documentation.
|
||||||
|
|
||||||
## <a name="nuxt-2">⛰️ Nuxt 2</a>
|
|
||||||
|
|
||||||
You can find the code for Nuxt 2 on the [`2.x` branch](https://github.com/nuxt/nuxt/tree/2.x) and the documentation at [v2.nuxt.com](https://v2.nuxt.com).
|
|
||||||
|
|
||||||
If you expect to be using Nuxt 2 beyond the EOL (End of Life) date (June 30, 2024), and still need a maintained version that can satisfy security and browser compatibility requirements, make sure to check out [HeroDevs’ NES (Never-Ending Support) Nuxt 2](https://www.herodevs.com/support/nuxt-nes?utm_source=nuxt-github&utm_medium=nuxt-readme).
|
|
||||||
|
|
||||||
## <a name="professional-support">🛟 Professional Support</a>
|
## <a name="professional-support">🛟 Professional Support</a>
|
||||||
|
|
||||||
- Technical audit & consulting: [Nuxt Experts](https://nuxt.com/enterprise/support)
|
- Technical audit & consulting: [Nuxt Experts](https://nuxt.com/enterprise/support)
|
||||||
- Custom development & more: [Nuxt Agencies Partners](https://nuxt.com/enterprise/agencies)
|
- Custom development & more: [Nuxt Agencies Partners](https://nuxt.com/enterprise/agencies)
|
||||||
|
|
||||||
## <a name="follow-us">🔗 Follow us</a>
|
## <a name="follow-us">🔗 Follow Us</a>
|
||||||
|
|
||||||
<p valign="center">
|
<p valign="center">
|
||||||
<a href="https://chat.nuxt.dev"><img width="20px" src="./.github/assets/discord.svg" alt="Discord"></a> <a href="https://twitter.nuxt.dev"><img width="20px" src="./.github/assets/twitter.svg" alt="Twitter"></a> <a href="https://github.nuxt.dev"><img width="20px" src="./.github/assets/github.svg" alt="GitHub"></a>
|
<a href="https://chat.nuxt.dev"><img width="20px" src="./.github/assets/discord.svg" alt="Discord"></a> <a href="https://twitter.nuxt.dev"><img width="20px" src="./.github/assets/twitter.svg" alt="Twitter"></a> <a href="https://github.nuxt.dev"><img width="20px" src="./.github/assets/github.svg" alt="GitHub"></a>
|
||||||
|
@ -76,7 +76,6 @@ Nuxt is composed of different [core packages](https://github.com/nuxt/nuxt/tree/
|
|||||||
- Command line interface: [nuxi](https://github.com/nuxt/nuxt/tree/main/packages/nuxi)
|
- Command line interface: [nuxi](https://github.com/nuxt/nuxt/tree/main/packages/nuxi)
|
||||||
- Server engine: [nitro](https://github.com/unjs/nitro)
|
- Server engine: [nitro](https://github.com/unjs/nitro)
|
||||||
- Development kit: [@nuxt/kit](https://github.com/nuxt/nuxt/tree/main/packages/kit)
|
- Development kit: [@nuxt/kit](https://github.com/nuxt/nuxt/tree/main/packages/kit)
|
||||||
- Nuxt 2 Bridge: [@nuxt/bridge](https://github.com/nuxt/bridge)
|
|
||||||
|
|
||||||
We recommend reading each concept to have a full vision of Nuxt capabilities and the scope of each package.
|
We recommend reading each concept to have a full vision of Nuxt capabilities and the scope of each package.
|
||||||
|
|
||||||
|
@ -85,13 +85,13 @@ export default defineNuxtConfig({
|
|||||||
|
|
||||||
## Hosting Providers
|
## Hosting Providers
|
||||||
|
|
||||||
Nuxt 3 can be deployed to several cloud providers with a minimal amount of configuration:
|
Nuxt can be deployed to several cloud providers with a minimal amount of configuration:
|
||||||
|
|
||||||
:read-more{to="/deploy"}
|
:read-more{to="/deploy"}
|
||||||
|
|
||||||
## Presets
|
## Presets
|
||||||
|
|
||||||
In addition to Node.js servers and static hosting services, a Nuxt 3 project can be deployed with several well-tested presets and minimal amount of configuration.
|
In addition to Node.js servers and static hosting services, a Nuxt project can be deployed with several well-tested presets and minimal amount of configuration.
|
||||||
|
|
||||||
You can explicitly set the desired preset in the [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt-config) file:
|
You can explicitly set the desired preset in the [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt-config) file:
|
||||||
|
|
||||||
@ -125,5 +125,5 @@ Accordingly, you should make sure that the following options are unchecked / dis
|
|||||||
With these settings, you can be sure that Cloudflare won't inject scripts into your Nuxt application that may cause unwanted side effects.
|
With these settings, you can be sure that Cloudflare won't inject scripts into your Nuxt application that may cause unwanted side effects.
|
||||||
|
|
||||||
::tip
|
::tip
|
||||||
Their location on the Cloudfalre dashboard sometimes changes so don't hesitate to look around.
|
Their location on the Cloudflare dashboard sometimes changes so don't hesitate to look around.
|
||||||
::
|
::
|
||||||
|
@ -35,11 +35,17 @@ bunx nuxi upgrade
|
|||||||
|
|
||||||
To use the latest Nuxt build and test features before their release, read about the [nightly release channel](/docs/guide/going-further/nightly-release-channel) guide.
|
To use the latest Nuxt build and test features before their release, read about the [nightly release channel](/docs/guide/going-further/nightly-release-channel) guide.
|
||||||
|
|
||||||
|
::alert{type="warning"}
|
||||||
|
The nightly release channel `latest` tag is currently tracking the Nuxt v4 branch, meaning that it is particularly likely to have breaking changes right now - be careful!
|
||||||
|
|
||||||
|
You can opt in to the 3.x branch nightly releases with `"nuxt": "npm:nuxt-nightly@3x"`.
|
||||||
|
::
|
||||||
|
|
||||||
## Testing Nuxt 4
|
## Testing Nuxt 4
|
||||||
|
|
||||||
Nuxt 4 is planned to be released **on or before June 14** (though obviously this is dependent on having enough time after Nitro's major release to be properly tested in the community, so be aware that this is not an exact date).
|
Nuxt 4 is planned to be released **on or before June 14** (though obviously this is dependent on having enough time after Nitro's major release to be properly tested in the community, so be aware that this is not an exact date).
|
||||||
|
|
||||||
Until then, it is possible to test many of Nuxt 4's breaking changes from Nuxt version 3.12 or via the nightly release channel.
|
Until then, it is possible to test many of Nuxt 4's breaking changes from Nuxt version 3.12+.
|
||||||
|
|
||||||
::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=r4wFKlcJK6c" target="_blank"}
|
::tip{icon="i-ph-video-duotone" to="https://www.youtube.com/watch?v=r4wFKlcJK6c" target="_blank"}
|
||||||
Watch a video from Alexander Lichter showing how to opt in to Nuxt 4's breaking changes already.
|
Watch a video from Alexander Lichter showing how to opt in to Nuxt 4's breaking changes already.
|
||||||
@ -47,7 +53,7 @@ Watch a video from Alexander Lichter showing how to opt in to Nuxt 4's breaking
|
|||||||
|
|
||||||
### Opting in to Nuxt 4
|
### Opting in to Nuxt 4
|
||||||
|
|
||||||
First, opt in to the nightly release channel [following these steps](/docs/guide/going-further/nightly-release-channel#opting-in).
|
First, upgrade Nuxt to the [latest release](https://github.com/nuxt/nuxt/releases).
|
||||||
|
|
||||||
Then you can set your `compatibilityVersion` to match Nuxt 4 behavior:
|
Then you can set your `compatibilityVersion` to match Nuxt 4 behavior:
|
||||||
|
|
||||||
@ -103,7 +109,8 @@ Nuxt now defaults to a new directory structure, with backwards compatibility (so
|
|||||||
|
|
||||||
* the new Nuxt default `srcDir` is `app/` by default, and most things are resolved from there.
|
* the new Nuxt default `srcDir` is `app/` by default, and most things are resolved from there.
|
||||||
* `serverDir` now defaults to `<rootDir>/server` rather than `<srcDir>/server`
|
* `serverDir` now defaults to `<rootDir>/server` rather than `<srcDir>/server`
|
||||||
* `modules` and `public` are resolved relative to `<rootDir>` by default
|
* `layers/`, `modules/` and `public/` are resolved relative to `<rootDir>` by default
|
||||||
|
* if using [Nuxt Content v2.13+](https://github.com/nuxt/content/pull/2649), `content/` is resolved relative to `<rootDir>`
|
||||||
* a new `dir.app` is added, which is the directory we look for `router.options.ts` and `spa-loading-template.html` - this defaults to `<srcDir>/`
|
* a new `dir.app` is added, which is the directory we look for `router.options.ts` and `spa-loading-template.html` - this defaults to `<srcDir>/`
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@ -125,6 +132,8 @@ app/
|
|||||||
app.config.ts
|
app.config.ts
|
||||||
app.vue
|
app.vue
|
||||||
router.options.ts
|
router.options.ts
|
||||||
|
content/
|
||||||
|
layers/
|
||||||
modules/
|
modules/
|
||||||
node_modules/
|
node_modules/
|
||||||
public/
|
public/
|
||||||
@ -150,7 +159,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`, `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.
|
||||||
|
|
||||||
However, migration is _not required_. If you wish to keep your current folder structure, Nuxt should auto-detect it. (If it does not, please raise an issue.) The one exception is that if you _already_ have a custom `srcDir`. In this case, you should be aware that your `modules/`, `public/` and `server/` folders will be resolved from your `rootDir` rather than from your custom `srcDir`. You can override this by configuring `dir.modules`, `dir.public` and `serverDir` if you need to.
|
However, migration is _not required_. If you wish to keep your current folder structure, Nuxt should auto-detect it. (If it does not, please raise an issue.) The one exception is that if you _already_ have a custom `srcDir`. In this case, you should be aware that your `modules/`, `public/` and `server/` folders will be resolved from your `rootDir` rather than from your custom `srcDir`. You can override this by configuring `dir.modules`, `dir.public` and `serverDir` if you need to.
|
||||||
|
|
||||||
@ -502,11 +511,11 @@ These options have been set to their current values for some time and we do not
|
|||||||
|
|
||||||
* `respectNoSSRHeader`is implementable in user-land with [server middleware](https://github.com/nuxt/nuxt/blob/c660b39447f0d5b8790c0826092638d321cd6821/packages/nuxt/src/core/runtime/nitro/no-ssr.ts#L8-L9)
|
* `respectNoSSRHeader`is implementable in user-land with [server middleware](https://github.com/nuxt/nuxt/blob/c660b39447f0d5b8790c0826092638d321cd6821/packages/nuxt/src/core/runtime/nitro/no-ssr.ts#L8-L9)
|
||||||
|
|
||||||
## Nuxt 2 vs Nuxt 3
|
## Nuxt 2 vs Nuxt 3+
|
||||||
|
|
||||||
In the table below, there is a quick comparison between 3 versions of Nuxt:
|
In the table below, there is a quick comparison between 3 versions of Nuxt:
|
||||||
|
|
||||||
Feature / Version | Nuxt 2 | Nuxt Bridge | Nuxt 3
|
Feature / Version | Nuxt 2 | Nuxt Bridge | Nuxt 3+
|
||||||
-------------------------|-----------------|------------------|---------
|
-------------------------|-----------------|------------------|---------
|
||||||
Vue | 2 | 2 | 3
|
Vue | 2 | 2 | 3
|
||||||
Stability | 😊 Stable | 😊 Stable | 😊 Stable
|
Stability | 😊 Stable | 😊 Stable | 😊 Stable
|
||||||
@ -524,9 +533,9 @@ Vite | ⚠️ Partial | 🚧 Partial | ✅
|
|||||||
Nuxi CLI | ❌ Old | ✅ nuxi | ✅ nuxi
|
Nuxi CLI | ❌ Old | ✅ nuxi | ✅ nuxi
|
||||||
Static sites | ✅ | ✅ | ✅
|
Static sites | ✅ | ✅ | ✅
|
||||||
|
|
||||||
## Nuxt 2 to Nuxt 3
|
## Nuxt 2 to Nuxt 3+
|
||||||
|
|
||||||
The migration guide provides a step-by-step comparison of Nuxt 2 features to Nuxt 3 features and guidance to adapt your current application.
|
The migration guide provides a step-by-step comparison of Nuxt 2 features to Nuxt 3+ features and guidance to adapt your current application.
|
||||||
|
|
||||||
::read-more{to="/docs/migration/overview"}
|
::read-more{to="/docs/migration/overview"}
|
||||||
Check out the **guide to migrating from Nuxt 2 to Nuxt 3**.
|
Check out the **guide to migrating from Nuxt 2 to Nuxt 3**.
|
||||||
@ -534,7 +543,7 @@ Check out the **guide to migrating from Nuxt 2 to Nuxt 3**.
|
|||||||
|
|
||||||
## Nuxt 2 to Nuxt Bridge
|
## Nuxt 2 to Nuxt Bridge
|
||||||
|
|
||||||
If you prefer to progressively migrate your Nuxt 2 application to Nuxt 3, you can use Nuxt Bridge. Nuxt Bridge is a compatibility layer that allows you to use Nuxt 3 features in Nuxt 2 with an opt-in mechanism.
|
If you prefer to progressively migrate your Nuxt 2 application to Nuxt 3, you can use Nuxt Bridge. Nuxt Bridge is a compatibility layer that allows you to use Nuxt 3+ features in Nuxt 2 with an opt-in mechanism.
|
||||||
|
|
||||||
::read-more{to="/docs/bridge/overview"}
|
::read-more{to="/docs/bridge/overview"}
|
||||||
**Migrate from Nuxt 2 to Nuxt Bridge**
|
**Migrate from Nuxt 2 to Nuxt Bridge**
|
||||||
|
@ -100,6 +100,6 @@ Well done! A browser window should automatically open for <http://localhost:3000
|
|||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
Now that you've created your Nuxt 3 project, you are ready to start building your application.
|
Now that you've created your Nuxt project, you are ready to start building your application.
|
||||||
|
|
||||||
:read-more{title="Nuxt Concepts" to="/docs/guide/concepts"}
|
:read-more{title="Nuxt Concepts" to="/docs/guide/concepts"}
|
||||||
|
@ -470,6 +470,4 @@ export default defineNuxtRouteMiddleware(to => {
|
|||||||
|
|
||||||
### Known issues
|
### Known issues
|
||||||
|
|
||||||
- View transitions may not work as expected with nested pages/layouts/async components owing to this upstream Vue bug: <https://github.com/vuejs/core/issues/5513>. If you make use of this pattern, you may need to delay adopting this experimental feature or implement it yourself. Feedback is very welcome.
|
|
||||||
|
|
||||||
- If you perform data fetching within your page setup functions, that you may wish to reconsider using this feature for the moment. (By design, View Transitions completely freeze DOM updates whilst they are taking place.) We're looking at restrict the View Transition to the final moments before `<Suspense>` resolves, but in the interim you may want to consider carefully whether to adopt this feature if this describes you.
|
- If you perform data fetching within your page setup functions, that you may wish to reconsider using this feature for the moment. (By design, View Transitions completely freeze DOM updates whilst they are taking place.) We're looking at restrict the View Transition to the final moments before `<Suspense>` resolves, but in the interim you may want to consider carefully whether to adopt this feature if this describes you.
|
||||||
|
@ -134,7 +134,7 @@ The `useAsyncData` composable is a great way to wrap and wait for multiple `$fet
|
|||||||
|
|
||||||
```vue
|
```vue
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { data: discounts, pending } = await useAsyncData('cart-discount', async () => {
|
const { data: discounts, status } = await useAsyncData('cart-discount', async () => {
|
||||||
const [coupons, offers] = await Promise.all([
|
const [coupons, offers] = await Promise.all([
|
||||||
$fetch('/cart/coupons'),
|
$fetch('/cart/coupons'),
|
||||||
$fetch('/cart/offers')
|
$fetch('/cart/offers')
|
||||||
@ -156,14 +156,13 @@ Read more about `useAsyncData`.
|
|||||||
`useFetch` and `useAsyncData` have the same return values listed below.
|
`useFetch` and `useAsyncData` have the same return values listed below.
|
||||||
|
|
||||||
- `data`: the result of the asynchronous function that is passed in.
|
- `data`: the result of the asynchronous function that is passed in.
|
||||||
- `pending`: a boolean indicating whether the data is still being fetched.
|
|
||||||
- `refresh`/`execute`: a function that can be used to refresh the data returned by the `handler` function.
|
- `refresh`/`execute`: a function that can be used to refresh the data returned by the `handler` function.
|
||||||
- `clear`: a function that can be used to set `data` to undefined, set `error` to `null`, set `pending` to `false`, set `status` to `idle`, and mark any currently pending requests as cancelled.
|
- `clear`: a function that can be used to set `data` to `undefined`, set `error` to `null`, set `status` to `idle`, and mark any currently pending requests as cancelled.
|
||||||
- `error`: an error object if the data fetching failed.
|
- `error`: an error object if the data fetching failed.
|
||||||
- `status`: a string indicating the status of the data request (`"idle"`, `"pending"`, `"success"`, `"error"`).
|
- `status`: a string indicating the status of the data request (`"idle"`, `"pending"`, `"success"`, `"error"`).
|
||||||
|
|
||||||
::note
|
::note
|
||||||
`data`, `pending`, `error` and `status` are Vue refs accessible with `.value` in `<script setup>`
|
`data`, `error` and `status` are Vue refs accessible with `.value` in `<script setup>`
|
||||||
::
|
::
|
||||||
|
|
||||||
By default, Nuxt waits until a `refresh` is finished before it can be executed again.
|
By default, Nuxt waits until a `refresh` is finished before it can be executed again.
|
||||||
@ -178,18 +177,18 @@ If you have not fetched data on the server (for example, with `server: false`),
|
|||||||
|
|
||||||
### Lazy
|
### Lazy
|
||||||
|
|
||||||
By default, data fetching composables will wait for the resolution of their asynchronous function before navigating to a new page by using Vue’s Suspense. This feature can be ignored on client-side navigation with the `lazy` option. In that case, you will have to manually handle loading state using the `pending` value.
|
By default, data fetching composables will wait for the resolution of their asynchronous function before navigating to a new page by using Vue’s Suspense. This feature can be ignored on client-side navigation with the `lazy` option. In that case, you will have to manually handle loading state using the `status` value.
|
||||||
|
|
||||||
```vue twoslash [app.vue]
|
```vue twoslash [app.vue]
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { pending, data: posts } = useFetch('/api/posts', {
|
const { status, data: posts } = useFetch('/api/posts', {
|
||||||
lazy: true
|
lazy: true
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- you will need to handle a loading state -->
|
<!-- you will need to handle a loading state -->
|
||||||
<div v-if="pending">
|
<div v-if="status === 'pending'">
|
||||||
Loading ...
|
Loading ...
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
@ -204,7 +203,7 @@ You can alternatively use [`useLazyFetch`](/docs/api/composables/use-lazy-fetch)
|
|||||||
|
|
||||||
```vue twoslash
|
```vue twoslash
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { pending, data: posts } = useLazyFetch('/api/posts')
|
const { status, data: posts } = useLazyFetch('/api/posts')
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -227,7 +226,7 @@ Combined with the `lazy` option, this can be useful for data that is not needed
|
|||||||
const articles = await useFetch('/api/article')
|
const articles = await useFetch('/api/article')
|
||||||
|
|
||||||
/* This call will only be performed on the client */
|
/* This call will only be performed on the client */
|
||||||
const { pending, data: comments } = useFetch('/api/comments', {
|
const { status, data: comments } = useFetch('/api/comments', {
|
||||||
lazy: true,
|
lazy: true,
|
||||||
server: false
|
server: false
|
||||||
})
|
})
|
||||||
@ -355,7 +354,7 @@ Sometimes you may need to compute an URL from reactive values, and refresh the d
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const id = ref(null)
|
const id = ref(null)
|
||||||
|
|
||||||
const { data, pending } = useLazyFetch('/api/user', {
|
const { data, status } = useLazyFetch('/api/user', {
|
||||||
query: {
|
query: {
|
||||||
user_id: id
|
user_id: id
|
||||||
}
|
}
|
||||||
@ -371,9 +370,11 @@ Every time a dependency changes, the data will be fetched using the newly constr
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const id = ref(null)
|
const id = ref(null)
|
||||||
|
|
||||||
const { data, pending, status } = useLazyFetch(() => `/api/users/${id.value}`, {
|
const { data, status } = useLazyFetch(() => `/api/users/${id.value}`, {
|
||||||
immediate: false
|
immediate: false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const pending = computed(() => status.value === 'pending');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -406,7 +407,7 @@ With that, you will need both the `status` to handle the fetch lifecycle, and `e
|
|||||||
|
|
||||||
```vue
|
```vue
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { data, error, execute, pending, status } = await useLazyFetch('/api/comments', {
|
const { data, error, execute, status } = await useLazyFetch('/api/comments', {
|
||||||
immediate: false
|
immediate: false
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@ -416,7 +417,7 @@ const { data, error, execute, pending, status } = await useLazyFetch('/api/comme
|
|||||||
<button @click="execute">Get data</button>
|
<button @click="execute">Get data</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="pending">
|
<div v-else-if="status === 'pending'">
|
||||||
Loading comments...
|
Loading comments...
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -494,7 +495,7 @@ onMounted(() => console.log(document.cookie))
|
|||||||
|
|
||||||
## Options API support
|
## Options API support
|
||||||
|
|
||||||
Nuxt 3 provides a way to perform `asyncData` fetching within the Options API. You must wrap your component definition within `defineNuxtComponent` for this to work.
|
Nuxt provides a way to perform `asyncData` fetching within the Options API. You must wrap your component definition within `defineNuxtComponent` for this to work.
|
||||||
|
|
||||||
```vue
|
```vue
|
||||||
<script>
|
<script>
|
||||||
|
@ -24,7 +24,7 @@ Read more about `useState` composable.
|
|||||||
|
|
||||||
::warning
|
::warning
|
||||||
Never define `const state = ref()` outside of `<script setup>` or `setup()` function.<br>
|
Never define `const state = ref()` outside of `<script setup>` or `setup()` function.<br>
|
||||||
Such state will be shared across all users visiting your website and can lead to memory leaks!
|
For example, doing `export myState = ref({})` would result in state shared across requests on the server and can lead to memory leaks.
|
||||||
::
|
::
|
||||||
|
|
||||||
::tip{icon="i-ph-check-circle-duotone"}
|
::tip{icon="i-ph-check-circle-duotone"}
|
||||||
|
@ -4,7 +4,7 @@ description: 'Learn how to catch and handle errors in Nuxt.'
|
|||||||
navigation.icon: i-ph-bug-beetle-duotone
|
navigation.icon: i-ph-bug-beetle-duotone
|
||||||
---
|
---
|
||||||
|
|
||||||
Nuxt 3 is a full-stack framework, which means there are several sources of unpreventable user runtime errors that can happen in different contexts:
|
Nuxt is a full-stack framework, which means there are several sources of unpreventable user runtime errors that can happen in different contexts:
|
||||||
|
|
||||||
- Errors during the Vue rendering lifecycle (SSR & CSR)
|
- Errors during the Vue rendering lifecycle (SSR & CSR)
|
||||||
- Server and client startup errors (SSR + CSR)
|
- Server and client startup errors (SSR + CSR)
|
||||||
|
@ -4,7 +4,7 @@ description: Nuxt provides a powerful system that allows you to extend the defau
|
|||||||
navigation.icon: i-ph-stack-duotone
|
navigation.icon: i-ph-stack-duotone
|
||||||
---
|
---
|
||||||
|
|
||||||
One of the core features of Nuxt 3 is the layers and extending support. You can extend a default Nuxt application to reuse components, utils, and configuration. The layers structure is almost identical to a standard Nuxt application which makes them easy to author and maintain.
|
One of the core features of Nuxt is the layers and extending support. You can extend a default Nuxt application to reuse components, utils, and configuration. The layers structure is almost identical to a standard Nuxt application which makes them easy to author and maintain.
|
||||||
|
|
||||||
## Use Cases
|
## Use Cases
|
||||||
|
|
||||||
|
@ -34,7 +34,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 */
|
/* useAsyncData() and $fetch() are auto-imported */
|
||||||
const { data, refresh, pending } = await useFetch('/api/hello')
|
const { data, refresh, status } = await useFetch('/api/hello')
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -105,6 +105,11 @@ Nuxt directly auto-imports files created in defined directories:
|
|||||||
|
|
||||||
:link-example{to="/docs/examples/features/auto-imports"}
|
:link-example{to="/docs/examples/features/auto-imports"}
|
||||||
|
|
||||||
|
::warning
|
||||||
|
**Auto-imported `ref` and `computed` won't be unwrapped in a component `<template>`.** :br
|
||||||
|
This is due to how Vue works with refs that aren't top-level to the template. You can read more about it [in the Vue documentation](https://vuejs.org/guide/essentials/reactivity-fundamentals.html#caveat-when-unwrapping-in-templates).
|
||||||
|
::
|
||||||
|
|
||||||
### Explicit Imports
|
### Explicit Imports
|
||||||
|
|
||||||
Nuxt exposes every auto-import with the `#imports` alias that can be used to make the import explicit if needed:
|
Nuxt exposes every auto-import with the `#imports` alias that can be used to make the import explicit if needed:
|
||||||
|
@ -39,7 +39,7 @@ Most applications need multiple pages and a way to navigate between them. This i
|
|||||||
|
|
||||||
## Differences with Nuxt 2 / Vue 2
|
## Differences with Nuxt 2 / Vue 2
|
||||||
|
|
||||||
Nuxt 3 is based on Vue 3. The new major Vue version introduces several changes that Nuxt takes advantage of:
|
Nuxt 3+ is based on Vue 3. The new major Vue version introduces several changes that Nuxt takes advantage of:
|
||||||
|
|
||||||
- Better performance
|
- Better performance
|
||||||
- Composition API
|
- Composition API
|
||||||
@ -89,15 +89,15 @@ const increment = () => count.value++
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
The goal of Nuxt 3 is to provide a great developer experience around the Composition API.
|
The goal of Nuxt is to provide a great developer experience around the Composition API.
|
||||||
|
|
||||||
- Use auto-imported [Reactivity functions](https://vuejs.org/api/reactivity-core.html) from Vue and Nuxt 3 [built-in composables](/docs/api/composables/use-async-data).
|
- Use auto-imported [Reactivity functions](https://vuejs.org/api/reactivity-core.html) from Vue and Nuxt [built-in composables](/docs/api/composables/use-async-data).
|
||||||
- Write your own auto-imported reusable functions in the [`composables/` directory](/docs/guide/directory-structure/composables).
|
- Write your own auto-imported reusable functions in the [`composables/` directory](/docs/guide/directory-structure/composables).
|
||||||
|
|
||||||
### TypeScript Support
|
### TypeScript Support
|
||||||
|
|
||||||
Both Vue 3 and Nuxt 3 are written in TypeScript. A fully typed codebase prevents mistakes and documents APIs usage. This doesn’t mean that you have to write your application in TypeScript to take advantage of it. With Nuxt 3, you can opt-in by renaming your file from `.js` to `.ts` , or add `<script setup lang="ts">` in a component.
|
Both Vue 3 and Nuxt 3+ are written in TypeScript. A fully typed codebase prevents mistakes and documents APIs usage. This doesn’t mean that you have to write your application in TypeScript to take advantage of it. With Nuxt 3, you can opt-in by renaming your file from `.js` to `.ts` , or add `<script setup lang="ts">` in a component.
|
||||||
|
|
||||||
::read-more{to="/docs/guide/concepts/typescript"}
|
::read-more{to="/docs/guide/concepts/typescript"}
|
||||||
Read the details about TypeScript in Nuxt 3
|
Read the details about TypeScript in Nuxt
|
||||||
::
|
::
|
||||||
|
@ -105,7 +105,7 @@ Hybrid rendering allows different caching rules per route using **Route Rules**
|
|||||||
|
|
||||||
Previously every route/page of a Nuxt application and server must use the same rendering mode, universal or client-side. In various cases, some pages could be generated at build time, while others should be client-side rendered. For example, think of a content website with an admin section. Every content page should be primarily static and generated once, but the admin section requires registration and behaves more like a dynamic application.
|
Previously every route/page of a Nuxt application and server must use the same rendering mode, universal or client-side. In various cases, some pages could be generated at build time, while others should be client-side rendered. For example, think of a content website with an admin section. Every content page should be primarily static and generated once, but the admin section requires registration and behaves more like a dynamic application.
|
||||||
|
|
||||||
Nuxt 3 includes route rules and hybrid rendering support. Using route rules you can define rules for a group of nuxt routes, change rendering mode or assign a cache strategy based on route!
|
Nuxt includes route rules and hybrid rendering support. Using route rules you can define rules for a group of nuxt routes, change rendering mode or assign a cache strategy based on route!
|
||||||
|
|
||||||
Nuxt server will automatically register corresponding middleware and wrap routes with cache handlers using [Nitro caching layer](https://nitro.unjs.io/guide/cache).
|
Nuxt server will automatically register corresponding middleware and wrap routes with cache handlers using [Nitro caching layer](https://nitro.unjs.io/guide/cache).
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ Note that Hybrid Rendering is not available when using [`nuxt generate`](/docs/a
|
|||||||
|
|
||||||
## Edge-Side Rendering
|
## Edge-Side Rendering
|
||||||
|
|
||||||
Edge-Side Rendering (ESR) is a powerful feature introduced in Nuxt 3 that allows the rendering of your Nuxt application closer to your users via edge servers of a Content Delivery Network (CDN). By leveraging ESR, you can ensure improved performance and reduced latency, thereby providing an enhanced user experience.
|
Edge-Side Rendering (ESR) is a powerful feature introduced in Nuxt that allows the rendering of your Nuxt application closer to your users via edge servers of a Content Delivery Network (CDN). By leveraging ESR, you can ensure improved performance and reduced latency, thereby providing an enhanced user experience.
|
||||||
|
|
||||||
With ESR, the rendering process is pushed to the 'edge' of the network - the CDN's edge servers. Note that ESR is more a deployment target than an actual rendering mode.
|
With ESR, the rendering process is pushed to the 'edge' of the network - the CDN's edge servers. Note that ESR is more a deployment target than an actual rendering mode.
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: Server Engine
|
title: Server Engine
|
||||||
description: 'Nuxt 3 is powered by a new server engine: Nitro.'
|
description: 'Nuxt is powered by a new server engine: Nitro.'
|
||||||
---
|
---
|
||||||
|
|
||||||
While building Nuxt 3, we created a new server engine: [Nitro](https://nitro.unjs.io).
|
While building Nuxt 3, we created a new server engine: [Nitro](https://nitro.unjs.io).
|
||||||
@ -53,7 +53,7 @@ Nitro produces a standalone server dist that is independent of `node_modules`.
|
|||||||
|
|
||||||
The server in Nuxt 2 is not standalone and requires part of Nuxt core to be involved by running `nuxt start` (with the [`nuxt-start`](https://www.npmjs.com/package/nuxt-start) or [`nuxt`](https://www.npmjs.com/package/nuxt) distributions) or custom programmatic usage, which is fragile and prone to breakage and not suitable for serverless and service-worker environments.
|
The server in Nuxt 2 is not standalone and requires part of Nuxt core to be involved by running `nuxt start` (with the [`nuxt-start`](https://www.npmjs.com/package/nuxt-start) or [`nuxt`](https://www.npmjs.com/package/nuxt) distributions) or custom programmatic usage, which is fragile and prone to breakage and not suitable for serverless and service-worker environments.
|
||||||
|
|
||||||
Nuxt 3 generates this dist when running `nuxt build` into a [`.output`](/docs/guide/directory-structure/output) directory.
|
Nuxt generates this dist when running `nuxt build` into a [`.output`](/docs/guide/directory-structure/output) directory.
|
||||||
|
|
||||||
The output contains runtime code to run your Nuxt server in any environment (including experimental browser service workers!) and serve your static files, making it a true hybrid framework for the JAMstack. In addition, Nuxt implements a native storage layer, supporting multi-source drivers and local assets.
|
The output contains runtime code to run your Nuxt server in any environment (including experimental browser service workers!) and serve your static files, making it a true hybrid framework for the JAMstack. In addition, Nuxt implements a native storage layer, supporting multi-source drivers and local assets.
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 'ES Modules'
|
title: 'ES Modules'
|
||||||
description: "Nuxt 3 (and Bridge) uses Native ES Modules."
|
description: "Nuxt uses native ES modules."
|
||||||
---
|
---
|
||||||
|
|
||||||
This guide helps explain what ES Modules are and how to make a Nuxt app (or upstream library) compatible with ESM.
|
This guide helps explain what ES Modules are and how to make a Nuxt app (or upstream library) compatible with ESM.
|
||||||
@ -190,7 +190,7 @@ import { default as pkg } from 'cjs-pkg'
|
|||||||
import('cjs-pkg').then(m => m.default || m).then(console.log)
|
import('cjs-pkg').then(m => m.default || m).then(console.log)
|
||||||
```
|
```
|
||||||
|
|
||||||
For handling more complex situations and more safety, we recommend and internally use [mlly](https://github.com/unjs/mlly) in Nuxt 3 that can preserve named exports.
|
For handling more complex situations and more safety, we recommend and internally use [mlly](https://github.com/unjs/mlly) in Nuxt that can preserve named exports.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { interopDefault } from 'mlly'
|
import { interopDefault } from 'mlly'
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: 'TypeScript'
|
title: 'TypeScript'
|
||||||
description: "Nuxt 3 is fully typed and provides helpful shortcuts to ensure you have access to accurate type information when you are coding."
|
description: "Nuxt is fully typed and provides helpful shortcuts to ensure you have access to accurate type information when you are coding."
|
||||||
---
|
---
|
||||||
|
|
||||||
## Type-checking
|
## Type-checking
|
||||||
@ -9,10 +9,6 @@ By default, Nuxt doesn't check types when you run [`nuxi dev`](/docs/api/command
|
|||||||
|
|
||||||
To enable type-checking at build or development time, install `vue-tsc` and `typescript` as development dependency:
|
To enable type-checking at build or development time, install `vue-tsc` and `typescript` as development dependency:
|
||||||
|
|
||||||
::alert{type="warning"}
|
|
||||||
You may experience issues with the latest `vue-tsc` and `vite-plugin-checker`, used internally when type checking. For now, you may need to stay on v1 of `vue-tsc`, and follow these upstream issues for updates: [fi3ework/vite-plugin-checker#306](https://github.com/fi3ework/vite-plugin-checker/issues/306) and [vuejs/language-tools#3969](https://github.com/vuejs/language-tools/issues/3969).
|
|
||||||
::
|
|
||||||
|
|
||||||
::code-group
|
::code-group
|
||||||
|
|
||||||
```bash [yarn]
|
```bash [yarn]
|
||||||
@ -55,7 +51,7 @@ When you run `nuxi dev` or `nuxi build`, Nuxt generates the following files for
|
|||||||
|
|
||||||
### `.nuxt/nuxt.d.ts`
|
### `.nuxt/nuxt.d.ts`
|
||||||
|
|
||||||
This file contains the types of any modules you are using, as well as the key types that Nuxt 3 requires. Your IDE should recognize these types automatically.
|
This file contains the types of any modules you are using, as well as the key types that Nuxt requires. Your IDE should recognize these types automatically.
|
||||||
|
|
||||||
Some of the references in the file are to files that are only generated within your `buildDir` (`.nuxt`) and therefore for full typings, you will need to run `nuxi dev` or `nuxi build`.
|
Some of the references in the file are to files that are only generated within your `buildDir` (`.nuxt`) and therefore for full typings, you will need to run `nuxi dev` or `nuxi build`.
|
||||||
|
|
||||||
@ -84,7 +80,7 @@ In case you need to extend options provided by `./.nuxt/tsconfig.json` further,
|
|||||||
|
|
||||||
TypeScript comes with certain checks to give you more safety and analysis of your program.
|
TypeScript comes with certain checks to give you more safety and analysis of your program.
|
||||||
|
|
||||||
[Strict checks](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html#getting-stricter-checks) are enabled by default in Nuxt 3 to give you greater type safety.
|
[Strict checks](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html#getting-stricter-checks) are enabled by default in Nuxt to give you greater type safety.
|
||||||
|
|
||||||
If you are currently converting your codebase to TypeScript, you may want to temporarily disable strict checks by setting `strict` to `false` in your `nuxt.config`:
|
If you are currently converting your codebase to TypeScript, you may want to temporarily disable strict checks by setting `strict` to `false` in your `nuxt.config`:
|
||||||
|
|
||||||
|
@ -376,6 +376,10 @@ Server-only components use [`<NuxtIsland>`](/docs/api/components/nuxt-island) un
|
|||||||
Server components (and islands) must have a single root element. (HTML comments are considered elements as well.)
|
Server components (and islands) must have a single root element. (HTML comments are considered elements as well.)
|
||||||
::
|
::
|
||||||
|
|
||||||
|
::alert{type=warning}
|
||||||
|
Be careful when nesting islands within other islands as each island adds some extra overhead.
|
||||||
|
::
|
||||||
|
|
||||||
::alert{type=warning}
|
::alert{type=warning}
|
||||||
Most features for server-only components and island components, such as slots and client components, are only available for single file components.
|
Most features for server-only components and island components, such as slots and client components, are only available for single file components.
|
||||||
::
|
::
|
||||||
|
@ -342,7 +342,7 @@ Learn more about `<NuxtLink>` usage.
|
|||||||
|
|
||||||
## Programmatic Navigation
|
## Programmatic Navigation
|
||||||
|
|
||||||
Nuxt 3 allows programmatic navigation through the `navigateTo()` utility method. Using this utility method, you will be able to programmatically navigate the user in your app. This is great for taking input from the user and navigating them dynamically throughout your application. In this example, we have a simple method called `navigate()` that gets called when the user submits a search form.
|
Nuxt allows programmatic navigation through the `navigateTo()` utility method. Using this utility method, you will be able to programmatically navigate the user in your app. This is great for taking input from the user and navigating them dynamically throughout your application. In this example, we have a simple method called `navigate()` that gets called when the user submits a search form.
|
||||||
|
|
||||||
::note
|
::note
|
||||||
Ensure to always `await` on `navigateTo` or chain its result by returning from functions.
|
Ensure to always `await` on `navigateTo` or chain its result by returning from functions.
|
||||||
|
@ -34,7 +34,7 @@ npx nuxi dev --dotenv .env.local
|
|||||||
When updating `.env` in development mode, the Nuxt instance is automatically restarted to apply new values to the `process.env`.
|
When updating `.env` in development mode, the Nuxt instance is automatically restarted to apply new values to the `process.env`.
|
||||||
|
|
||||||
::important
|
::important
|
||||||
In your application code, you should use [Runtime Config](https://nuxt.com/docs/guide/going-further/runtime-config) instead of plain env variables.
|
In your application code, you should use [Runtime Config](/docs/guide/going-further/runtime-config) instead of plain env variables.
|
||||||
::
|
::
|
||||||
|
|
||||||
## Production
|
## Production
|
||||||
|
@ -5,7 +5,7 @@ description: Expose reactive configuration within your application with the App
|
|||||||
navigation.icon: i-ph-file-duotone
|
navigation.icon: i-ph-file-duotone
|
||||||
---
|
---
|
||||||
|
|
||||||
Nuxt 3 provides an `app.config` config file to expose reactive configuration within your application with the ability to update it at runtime within lifecycle or using a nuxt plugin and editing it with HMR (hot-module-replacement).
|
Nuxt provides an `app.config` config file to expose reactive configuration within your application with the ability to update it at runtime within lifecycle or using a nuxt plugin and editing it with HMR (hot-module-replacement).
|
||||||
|
|
||||||
You can easily provide runtime app configuration using `app.config.ts` file. It can have either of `.ts`, `.js`, or `.mjs` extensions.
|
You can easily provide runtime app configuration using `app.config.ts` file. It can have either of `.ts`, `.js`, or `.mjs` extensions.
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ The behavior is different between the client-side and server-side:
|
|||||||
|
|
||||||
- On client-side, only keys in `runtimeConfig.public` are available, and the object is both writable and reactive.
|
- On client-side, only keys in `runtimeConfig.public` are available, and the object is both writable and reactive.
|
||||||
|
|
||||||
- On server-side, the entire runtime config is available on the server-side, but it is read-only to avoid context sharing.
|
- On server-side, the entire runtime config is available, but it is read-only to avoid context sharing.
|
||||||
::
|
::
|
||||||
|
|
||||||
```vue [pages/index.vue]
|
```vue [pages/index.vue]
|
||||||
|
@ -15,6 +15,12 @@ The build and publishing method and quality of these 'nightly' releases are the
|
|||||||
Features that are only available on the nightly release channel are marked with an alert in the documentation.
|
Features that are only available on the nightly release channel are marked with an alert in the documentation.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
::alert{type="warning"}
|
||||||
|
The `latest` nightly release channel is currently tracking the Nuxt v4 branch, meaning that it is particularly likely to have breaking changes right now - be careful!
|
||||||
|
|
||||||
|
You can opt in to the 3.x branch nightly releases with `"nuxt": "npm:nuxt-nightly@3x"`.
|
||||||
|
::
|
||||||
|
|
||||||
## Opting In
|
## Opting In
|
||||||
|
|
||||||
Update `nuxt` dependency inside `package.json`:
|
Update `nuxt` dependency inside `package.json`:
|
||||||
|
@ -13,10 +13,25 @@ With modules, you can encapsulate, properly test, and share custom solutions as
|
|||||||
|
|
||||||
We recommend you get started with Nuxt Modules using our [starter template](https://github.com/nuxt/starter/tree/module):
|
We recommend you get started with Nuxt Modules using our [starter template](https://github.com/nuxt/starter/tree/module):
|
||||||
|
|
||||||
```bash [Terminal]
|
::code-group
|
||||||
|
|
||||||
|
```bash [npm]
|
||||||
npx nuxi init -t module my-module
|
npx nuxi init -t module my-module
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```bash [yarn]
|
||||||
|
yarn dlx nuxi init -t module my-module
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [pnpm]
|
||||||
|
pnpm dlx nuxi init -t module my-module
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash [bun]
|
||||||
|
bunx nuxi init -t module my-module
|
||||||
|
```
|
||||||
|
::
|
||||||
|
|
||||||
This will create a `my-module` project with all the boilerplate necessary to develop and publish your module.
|
This will create a `my-module` project with all the boilerplate necessary to develop and publish your module.
|
||||||
|
|
||||||
**Next steps:**
|
**Next steps:**
|
||||||
|
@ -24,7 +24,7 @@ Let's pretend here that:
|
|||||||
- If the API responds with a `401` status code, we redirect the user to the `/login` page
|
- If the API responds with a `401` status code, we redirect the user to the `/login` page
|
||||||
|
|
||||||
```ts [plugins/api.ts]
|
```ts [plugins/api.ts]
|
||||||
export default defineNuxtPlugin(() => {
|
export default defineNuxtPlugin((nuxtApp) => {
|
||||||
const { session } = useUserSession()
|
const { session } = useUserSession()
|
||||||
|
|
||||||
const api = $fetch.create({
|
const api = $fetch.create({
|
||||||
@ -43,7 +43,7 @@ export default defineNuxtPlugin(() => {
|
|||||||
},
|
},
|
||||||
async onResponseError({ response }) {
|
async onResponseError({ response }) {
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
await navigateTo('/login')
|
await nuxtApp.runWithContext(() => navigateTo('/login'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: '<NuxtRouteAnnouncer>'
|
title: '<NuxtRouteAnnouncer>'
|
||||||
description: 'Add a hidden element with the page title for assistive technologies.'
|
description: 'The <NuxtRouteAnnouncer> component adds a hidden element with the page title to announce route changes to assistive technologies.'
|
||||||
navigation:
|
navigation:
|
||||||
badge: New
|
badge: New
|
||||||
links:
|
links:
|
||||||
@ -16,7 +16,7 @@ This component is available in Nuxt v3.12+.
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Add `<NuxtRouteAnnouncer/>` in your [`app.vue`](/docs/guide/directory-structure/app) or [`layouts/`](/docs/guide/directory-structure/layouts) to enhance accessibility by informing assistive technologies about page's title changes. This ensures that navigational changes are announced to users relying on screen readers.
|
Add `<NuxtRouteAnnouncer/>` in your [`app.vue`](/docs/guide/directory-structure/app) or [`layouts/`](/docs/guide/directory-structure/layouts) to enhance accessibility by informing assistive technologies about page title changes. This ensures that navigational changes are announced to users relying on screen readers.
|
||||||
|
|
||||||
```vue [app.vue]
|
```vue [app.vue]
|
||||||
<template>
|
<template>
|
||||||
@ -29,7 +29,7 @@ Add `<NuxtRouteAnnouncer/>` in your [`app.vue`](/docs/guide/directory-structure/
|
|||||||
|
|
||||||
## Slots
|
## Slots
|
||||||
|
|
||||||
You can pass custom HTML or components through the route announcer default slot.
|
You can pass custom HTML or components through the route announcer's default slot.
|
||||||
|
|
||||||
```vue
|
```vue
|
||||||
<template>
|
<template>
|
||||||
@ -43,7 +43,7 @@ You can pass custom HTML or components through the route announcer default slot.
|
|||||||
|
|
||||||
## Props
|
## Props
|
||||||
|
|
||||||
- `atomic`: Controls if screen readers announce only changes or the entire content. Set to true for full content readout on updates, false for changes only. (default `false`)
|
- `atomic`: Controls if screen readers only announce changes or the entire content. Set to true for full content readouts on updates, false for changes only. (default `false`)
|
||||||
- `politeness`: Sets the urgency for screen reader announcements: `off` (disable the announcement), `polite` (waits for silence), or `assertive` (interrupts immediately). (default `polite`)
|
- `politeness`: Sets the urgency for screen reader announcements: `off` (disable the announcement), `polite` (waits for silence), or `assertive` (interrupts immediately). (default `polite`)
|
||||||
|
|
||||||
::callout
|
::callout
|
||||||
|
@ -111,7 +111,7 @@ When you need to overwrite this behavior you can use the `rel` and `noRel` props
|
|||||||
|
|
||||||
When not using `external`, `<NuxtLink>` supports all Vue Router's [`RouterLink` props](https://router.vuejs.org/api/interfaces/RouterLinkProps.html)
|
When not using `external`, `<NuxtLink>` supports all Vue Router's [`RouterLink` props](https://router.vuejs.org/api/interfaces/RouterLinkProps.html)
|
||||||
|
|
||||||
- `to`: Any URL or a [route location object](https://router.vuejs.org/api/interfaces/RouteLocation.html) from Vue Router
|
- `to`: Any URL or a [route location object](https://router.vuejs.org/api/#RouteLocation) from Vue Router
|
||||||
- `custom`: Whether `<NuxtLink>` should wrap its content in an `<a>` element. It allows taking full control of how a link is rendered and how navigation works when it is clicked. Works the same as [Vue Router's `custom` prop](https://router.vuejs.org/api/interfaces/RouterLinkProps.html#Properties-custom)
|
- `custom`: Whether `<NuxtLink>` should wrap its content in an `<a>` element. It allows taking full control of how a link is rendered and how navigation works when it is clicked. Works the same as [Vue Router's `custom` prop](https://router.vuejs.org/api/interfaces/RouterLinkProps.html#Properties-custom)
|
||||||
- `exactActiveClass`: A class to apply on exact active links. Works the same as [Vue Router's `exact-active-class` prop](https://router.vuejs.org/api/interfaces/RouterLinkProps.html#Properties-exactActiveClass) on internal links. Defaults to Vue Router's default `"router-link-exact-active"`)
|
- `exactActiveClass`: A class to apply on exact active links. Works the same as [Vue Router's `exact-active-class` prop](https://router.vuejs.org/api/interfaces/RouterLinkProps.html#Properties-exactActiveClass) on internal links. Defaults to Vue Router's default `"router-link-exact-active"`)
|
||||||
- `replace`: Works the same as [Vue Router's `replace` prop](https://router.vuejs.org/api/interfaces/RouteLocationOptions.html#Properties-replace) on internal links
|
- `replace`: Works the same as [Vue Router's `replace` prop](https://router.vuejs.org/api/interfaces/RouteLocationOptions.html#Properties-replace) on internal links
|
||||||
@ -140,7 +140,7 @@ Defaults can be overwritten, see [overwriting defaults](#overwriting-defaults) i
|
|||||||
|
|
||||||
### In Nuxt Config
|
### In Nuxt Config
|
||||||
|
|
||||||
You can overwrite some `<NuxtLink>` defaults in your [`nuxt.config`](https://nuxt.com/docs/api/nuxt-config#defaults)
|
You can overwrite some `<NuxtLink>` defaults in your [`nuxt.config`](/docs/api/nuxt-config#defaults)
|
||||||
|
|
||||||
::important
|
::important
|
||||||
These options will likely be moved elsewhere in the future, such as into `app.config` or into the `app/` directory.
|
These options will likely be moved elsewhere in the future, such as into `app.config` or into the `app/` directory.
|
||||||
|
@ -18,7 +18,7 @@ Within your pages, components, and plugins you can use useAsyncData to get acces
|
|||||||
|
|
||||||
```vue [pages/index.vue]
|
```vue [pages/index.vue]
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { data, pending, error, refresh, clear } = await useAsyncData(
|
const { data, status, error, refresh, clear } = await useAsyncData(
|
||||||
'mountains',
|
'mountains',
|
||||||
() => $fetch('https://api.nuxtjs.dev/mountains')
|
() => $fetch('https://api.nuxtjs.dev/mountains')
|
||||||
)
|
)
|
||||||
@ -30,7 +30,7 @@ If you're using a custom useAsyncData wrapper, do not await it in the composable
|
|||||||
::
|
::
|
||||||
|
|
||||||
::note
|
::note
|
||||||
`data`, `pending`, `status` and `error` are Vue refs and they should be accessed with `.value` when used within the `<script setup>`, while `refresh`/`execute` and `clear` are plain functions.
|
`data`, `status` and `error` are Vue refs and they should be accessed with `.value` when used within the `<script setup>`, while `refresh`/`execute` and `clear` are plain functions.
|
||||||
::
|
::
|
||||||
|
|
||||||
### Watch Params
|
### Watch Params
|
||||||
@ -92,11 +92,10 @@ Learn how to use `transform` and `getCachedData` to avoid superfluous calls to a
|
|||||||
## Return Values
|
## Return Values
|
||||||
|
|
||||||
- `data`: the result of the asynchronous function that is passed in.
|
- `data`: the result of the asynchronous function that is passed in.
|
||||||
- `pending`: a boolean indicating whether the data is still being fetched.
|
|
||||||
- `refresh`/`execute`: a function that can be used to refresh the data returned by the `handler` function.
|
- `refresh`/`execute`: a function that can be used to refresh the data returned by the `handler` function.
|
||||||
- `error`: an error object if the data fetching failed.
|
- `error`: an error object if the data fetching failed.
|
||||||
- `status`: a string indicating the status of the data request (`"idle"`, `"pending"`, `"success"`, `"error"`).
|
- `status`: a string indicating the status of the data request (`"idle"`, `"pending"`, `"success"`, `"error"`).
|
||||||
- `clear`: a function which will set `data` to `undefined`, set `error` to `null`, set `pending` to `false`, set `status` to `'idle'`, and mark any currently pending requests as cancelled.
|
- `clear`: a function which will set `data` to `undefined`, set `error` to `null`, set `status` to `'idle'`, and mark any currently pending requests as cancelled.
|
||||||
|
|
||||||
By default, Nuxt waits until a `refresh` is finished before it can be executed again.
|
By default, Nuxt waits until a `refresh` is finished before it can be executed again.
|
||||||
|
|
||||||
@ -132,7 +131,6 @@ type AsyncDataOptions<DataT> = {
|
|||||||
|
|
||||||
type AsyncData<DataT, ErrorT> = {
|
type AsyncData<DataT, ErrorT> = {
|
||||||
data: Ref<DataT | null>
|
data: Ref<DataT | null>
|
||||||
pending: Ref<boolean>
|
|
||||||
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
|
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
|
||||||
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
|
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
|
||||||
clear: () => void
|
clear: () => void
|
||||||
|
@ -46,7 +46,7 @@ counter.value = counter.value || Math.round(Math.random() * 1000)
|
|||||||
:link-example{to="/docs/examples/advanced/use-cookie"}
|
:link-example{to="/docs/examples/advanced/use-cookie"}
|
||||||
|
|
||||||
::note
|
::note
|
||||||
Refresh `useCookie` values manually when a cookie has changed with [`refreshCookie`](/api/utils/refresh-cookie).
|
Refresh `useCookie` values manually when a cookie has changed with [`refreshCookie`](/docs/api/utils/refresh-cookie).
|
||||||
::
|
::
|
||||||
|
|
||||||
## Options
|
## Options
|
||||||
@ -153,7 +153,7 @@ Specifies the `boolean` or `string` value for [watch](https://vuejs.org/api/reac
|
|||||||
- `false` - Will not watch cookie ref data changes.
|
- `false` - Will not watch cookie ref data changes.
|
||||||
|
|
||||||
::note
|
::note
|
||||||
Refresh `useCookie` values manually when a cookie has changed with [`refreshCookie`](/api/utils/refresh-cookie).
|
Refresh `useCookie` values manually when a cookie has changed with [`refreshCookie`](/docs/api/utils/refresh-cookie).
|
||||||
::
|
::
|
||||||
|
|
||||||
**Example 1:**
|
**Example 1:**
|
||||||
|
@ -19,7 +19,7 @@ It automatically generates a key based on URL and fetch options, provides type h
|
|||||||
|
|
||||||
```vue [pages/modules.vue]
|
```vue [pages/modules.vue]
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { data, pending, error, refresh, clear } = await useFetch('/api/modules', {
|
const { data, status, error, refresh, clear } = await useFetch('/api/modules', {
|
||||||
pick: ['title']
|
pick: ['title']
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@ -30,14 +30,14 @@ If you're using a custom useFetch wrapper, do not await it in the composable, as
|
|||||||
::
|
::
|
||||||
|
|
||||||
::note
|
::note
|
||||||
`data`, `pending`, `status` and `error` are Vue refs and they should be accessed with `.value` when used within the `<script setup>`, while `refresh`/`execute` and `clear` are plain functions..
|
`data`, `status` and `error` are Vue refs and they should be accessed with `.value` when used within the `<script setup>`, while `refresh`/`execute` and `clear` are plain functions..
|
||||||
::
|
::
|
||||||
|
|
||||||
Using the `query` option, you can add search parameters to your query. This option is extended from [unjs/ofetch](https://github.com/unjs/ofetch) and is using [unjs/ufo](https://github.com/unjs/ufo) to create the URL. Objects are automatically stringified.
|
Using the `query` option, you can add search parameters to your query. This option is extended from [unjs/ofetch](https://github.com/unjs/ofetch) and is using [unjs/ufo](https://github.com/unjs/ufo) to create the URL. Objects are automatically stringified.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
const param1 = ref('value1')
|
const param1 = ref('value1')
|
||||||
const { data, pending, error, refresh } = await useFetch('/api/modules', {
|
const { data, status, error, refresh } = await useFetch('/api/modules', {
|
||||||
query: { param1, param2: 'value2' }
|
query: { param1, param2: 'value2' }
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
@ -47,7 +47,7 @@ The above example results in `https://api.nuxt.com/modules?param1=value1¶m2=
|
|||||||
You can also use [interceptors](https://github.com/unjs/ofetch#%EF%B8%8F-interceptors):
|
You can also use [interceptors](https://github.com/unjs/ofetch#%EF%B8%8F-interceptors):
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
const { data, pending, 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 || {}
|
options.headers = options.headers || {}
|
||||||
@ -128,11 +128,10 @@ Learn how to use `transform` and `getCachedData` to avoid superfluous calls to a
|
|||||||
## Return Values
|
## Return Values
|
||||||
|
|
||||||
- `data`: the result of the asynchronous function that is passed in.
|
- `data`: the result of the asynchronous function that is passed in.
|
||||||
- `pending`: a boolean indicating whether the data is still being fetched.
|
|
||||||
- `refresh`/`execute`: a function that can be used to refresh the data returned by the `handler` function.
|
- `refresh`/`execute`: a function that can be used to refresh the data returned by the `handler` function.
|
||||||
- `error`: an error object if the data fetching failed.
|
- `error`: an error object if the data fetching failed.
|
||||||
- `status`: a string indicating the status of the data request (`"idle"`, `"pending"`, `"success"`, `"error"`).
|
- `status`: a string indicating the status of the data request (`"idle"`, `"pending"`, `"success"`, `"error"`).
|
||||||
- `clear`: a function which will set `data` to `undefined`, set `error` to `null`, set `pending` to `false`, set `status` to `'idle'`, and mark any currently pending requests as cancelled.
|
- `clear`: a function which will set `data` to `undefined`, set `error` to `null`, set `status` to `'idle'`, and mark any currently pending requests as cancelled.
|
||||||
|
|
||||||
By default, Nuxt waits until a `refresh` is finished before it can be executed again.
|
By default, Nuxt waits until a `refresh` is finished before it can be executed again.
|
||||||
|
|
||||||
@ -170,7 +169,6 @@ type UseFetchOptions<DataT> = {
|
|||||||
|
|
||||||
type AsyncData<DataT, ErrorT> = {
|
type AsyncData<DataT, ErrorT> = {
|
||||||
data: Ref<DataT | null>
|
data: Ref<DataT | null>
|
||||||
pending: Ref<boolean>
|
|
||||||
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
|
refresh: (opts?: AsyncDataExecuteOptions) => Promise<void>
|
||||||
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
|
execute: (opts?: AsyncDataExecuteOptions) => Promise<void>
|
||||||
clear: () => void
|
clear: () => void
|
||||||
|
@ -23,9 +23,9 @@ By default, [`useAsyncData`](/docs/api/composables/use-async-data) blocks naviga
|
|||||||
```vue [pages/index.vue]
|
```vue [pages/index.vue]
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
/* Navigation will occur before fetching is complete.
|
/* Navigation will occur before fetching is complete.
|
||||||
Handle pending and error states directly within your component's template
|
Handle 'pending' and 'error' states directly within your component's template
|
||||||
*/
|
*/
|
||||||
const { pending, data: count } = await useLazyAsyncData('count', () => $fetch('/api/count'))
|
const { status, data: count } = await useLazyAsyncData('count', () => $fetch('/api/count'))
|
||||||
|
|
||||||
watch(count, (newCount) => {
|
watch(count, (newCount) => {
|
||||||
// Because count might start out null, you won't have access
|
// Because count might start out null, you won't have access
|
||||||
@ -35,7 +35,7 @@ watch(count, (newCount) => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
{{ pending ? 'Loading' : count }}
|
{{ status === 'pending' ? 'Loading' : count }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
@ -23,9 +23,9 @@ By default, [`useFetch`](/docs/api/composables/use-fetch) blocks navigation unti
|
|||||||
```vue [pages/index.vue]
|
```vue [pages/index.vue]
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
/* Navigation will occur before fetching is complete.
|
/* Navigation will occur before fetching is complete.
|
||||||
Handle pending and error states directly within your component's template
|
* Handle 'pending' and 'error' states directly within your component's template
|
||||||
*/
|
*/
|
||||||
const { pending, data: posts } = await useLazyFetch('/api/posts')
|
const { status, data: posts } = await useLazyFetch('/api/posts')
|
||||||
watch(posts, (newPosts) => {
|
watch(posts, (newPosts) => {
|
||||||
// Because posts might start out null, you won't have access
|
// Because posts might start out null, you won't have access
|
||||||
// to its contents immediately, but you can watch it.
|
// to its contents immediately, but you can watch it.
|
||||||
@ -33,7 +33,7 @@ watch(posts, (newPosts) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="pending">
|
<div v-if="status === 'pending'">
|
||||||
Loading ...
|
Loading ...
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
@ -8,7 +8,7 @@ links:
|
|||||||
size: xs
|
size: xs
|
||||||
---
|
---
|
||||||
|
|
||||||
`useNuxtApp` is a built-in composable that provides a way to access shared runtime context of Nuxt, also known as the [Nuxt context](/docs/guide/going-further/nuxt-app#the-nuxt-context), which is available on both client and server side. It helps you access the Vue app instance, runtime hooks, runtime config variables and internal states, such as `ssrContext` and `payload`.
|
`useNuxtApp` is a built-in composable that provides a way to access shared runtime context of Nuxt, also known as the [Nuxt context](/docs/guide/going-further/nuxt-app#the-nuxt-context), which is available on both client and server side (but not within Nitro routes). It helps you access the Vue app instance, runtime hooks, runtime config variables and internal states, such as `ssrContext` and `payload`.
|
||||||
|
|
||||||
```vue [app.vue]
|
```vue [app.vue]
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -252,7 +252,7 @@ For a better description of what Vue actually does, see [unjs/unctx#2 (comment)]
|
|||||||
|
|
||||||
This is where `runWithContext` can be used to restore context, similarly to how `<script setup>` works.
|
This is where `runWithContext` can be used to restore context, similarly to how `<script setup>` works.
|
||||||
|
|
||||||
Nuxt 3 internally uses [unjs/unctx](https://github.com/unjs/unctx) to support composables similar to Vue for plugins and middleware. This enables composables like `navigateTo()` to work without directly passing `nuxtApp` to them - bringing the DX and performance benefits of Composition API to the whole Nuxt framework.
|
Nuxt internally uses [unjs/unctx](https://github.com/unjs/unctx) to support composables similar to Vue for plugins and middleware. This enables composables like `navigateTo()` to work without directly passing `nuxtApp` to them - bringing the DX and performance benefits of Composition API to the whole Nuxt framework.
|
||||||
|
|
||||||
Nuxt composables have the same design as the Vue Composition API and therefore need a similar solution to magically do this transform. Check out [unjs/unctx#2](https://github.com/unjs/unctx/issues/2) (proposal), [unjs/unctx#4](https://github.com/unjs/unctx/pull/4) (transform implementation), and [nuxt/framework#3884](https://github.com/nuxt/framework/pull/3884) (Integration to Nuxt).
|
Nuxt composables have the same design as the Vue Composition API and therefore need a similar solution to magically do this transform. Check out [unjs/unctx#2](https://github.com/unjs/unctx/issues/2) (proposal), [unjs/unctx#4](https://github.com/unjs/unctx/pull/4) (transform implementation), and [nuxt/framework#3884](https://github.com/nuxt/framework/pull/3884) (Integration to Nuxt).
|
||||||
|
|
||||||
|
@ -48,4 +48,4 @@ Apart from dynamic parameters and query parameters, `useRoute()` also provides t
|
|||||||
Browsers don't send [URL fragments](https://url.spec.whatwg.org/#concept-url-fragment) (for example `#foo`) when making requests. So using `route.fullPath` in your template can trigger hydration issues because this will include the fragment on client but not the server.
|
Browsers don't send [URL fragments](https://url.spec.whatwg.org/#concept-url-fragment) (for example `#foo`) when making requests. So using `route.fullPath` in your template can trigger hydration issues because this will include the fragment on client but not the server.
|
||||||
::
|
::
|
||||||
|
|
||||||
:read-more{icon="i-simple-icons-vuedotjs" to="https://router.vuejs.org/api/interfaces/RouteLocationNormalizedLoaded.html"}
|
:read-more{icon="i-simple-icons-vuedotjs" to="https://router.vuejs.org/api/#RouteLocationNormalizedLoaded"}
|
||||||
|
@ -28,7 +28,7 @@ interface RouteMiddleware {
|
|||||||
|
|
||||||
A function that takes two Vue Router's route location objects as parameters: the next route `to` as the first, and the current route `from` as the second.
|
A function that takes two Vue Router's route location objects as parameters: the next route `to` as the first, and the current route `from` as the second.
|
||||||
|
|
||||||
Learn more about available properties of `RouteLocationNormalized` in the **[Vue Router docs](https://router.vuejs.org/api/interfaces/RouteLocationNormalized.html)**.
|
Learn more about available properties of `RouteLocationNormalized` in the **[Vue Router docs](https://router.vuejs.org/api/#RouteLocationNormalized)**.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ Use [`useState`](/docs/api/composables/use-state) in combination with `navigateT
|
|||||||
```ts [middleware/auth.ts]
|
```ts [middleware/auth.ts]
|
||||||
export default defineNuxtRouteMiddleware((to, from) => {
|
export default defineNuxtRouteMiddleware((to, from) => {
|
||||||
const auth = useState('auth')
|
const auth = useState('auth')
|
||||||
|
|
||||||
if (!auth.value.isAuthenticated) {
|
if (!auth.value.isAuthenticated) {
|
||||||
return navigateTo('/login')
|
return navigateTo('/login')
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,17 @@ links:
|
|||||||
---
|
---
|
||||||
|
|
||||||
::note
|
::note
|
||||||
`navigateTo` is available on both server side and client side.
|
`navigateTo` is available on both client and server side (but not within Nitro routes).
|
||||||
::
|
::
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
`navigateTo` is available on both server side and client side. It can be used within the [Nuxt context](/docs/guide/going-further/nuxt-app#the-nuxt-context), or directly, to perform page navigation.
|
`navigateTo` is available on both server side and client side. It can be used within the [Nuxt context](/docs/guide/going-further/nuxt-app#the-nuxt-context), or directly, to perform page navigation.
|
||||||
|
|
||||||
|
::tip
|
||||||
|
To send a redirect from a server endpoint, use [`sendRedirect`](https://h3.unjs.io/utils/response#sendredirectevent-location-code) instead.
|
||||||
|
::
|
||||||
|
|
||||||
### Within a Vue Component
|
### Within a Vue Component
|
||||||
|
|
||||||
```vue
|
```vue
|
||||||
@ -115,7 +119,7 @@ Make sure to always use `await` or `return` on result of `navigateTo` when calli
|
|||||||
|
|
||||||
### `to`
|
### `to`
|
||||||
|
|
||||||
**Type**: [`RouteLocationRaw`](https://router.vuejs.org/api/interfaces/RouteLocation.html) | `undefined` | `null`
|
**Type**: [`RouteLocationRaw`](https://router.vuejs.org/api/interfaces/RouteLocationOptions.html#Interface-RouteLocationOptions) | `undefined` | `null`
|
||||||
|
|
||||||
**Default**: `'/'`
|
**Default**: `'/'`
|
||||||
|
|
||||||
|
@ -58,13 +58,13 @@ This example below refreshes only data where the key matches to `count`.
|
|||||||
|
|
||||||
```vue [pages/some-page.vue]
|
```vue [pages/some-page.vue]
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { pending, data: count } = await useLazyAsyncData('count', () => $fetch('/api/count'))
|
const { status, data: count } = await useLazyAsyncData('count', () => $fetch('/api/count'))
|
||||||
const refresh = () => refreshNuxtData('count')
|
const refresh = () => refreshNuxtData('count')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
{{ pending ? 'Loading' : count }}
|
{{ status === 'pending' ? 'Loading' : count }}
|
||||||
</div>
|
</div>
|
||||||
<button @click="refresh">Refresh</button>
|
<button @click="refresh">Refresh</button>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "nuxi upgrade"
|
title: "nuxi upgrade"
|
||||||
description: The upgrade command upgrades Nuxt 3 to the latest version.
|
description: The upgrade command upgrades Nuxt to the latest version.
|
||||||
links:
|
links:
|
||||||
- label: Source
|
- label: Source
|
||||||
icon: i-simple-icons-github
|
icon: i-simple-icons-github
|
||||||
@ -12,7 +12,7 @@ links:
|
|||||||
npx nuxi upgrade [--force|-f]
|
npx nuxi upgrade [--force|-f]
|
||||||
```
|
```
|
||||||
|
|
||||||
The `upgrade` command upgrades Nuxt 3 to the latest version.
|
The `upgrade` command upgrades Nuxt to the latest version.
|
||||||
|
|
||||||
Option | Default | Description
|
Option | Default | Description
|
||||||
-------------------------|-----------------|------------------
|
-------------------------|-----------------|------------------
|
||||||
|
@ -8,7 +8,7 @@ links:
|
|||||||
size: xs
|
size: xs
|
||||||
---
|
---
|
||||||
|
|
||||||
Nitro is an open source TypeScript framework to build ultra-fast web servers. Nuxt 3 (and, optionally, Nuxt Bridge) uses Nitro as its server engine. You can use `useNitro` to access the Nitro instance, `addServerHandler` to add a server handler, `addDevServerHandler` to add a server handler to be used only in development mode, `addServerPlugin` to add a plugin to extend Nitro's runtime behavior, and `addPrerenderRoutes` to add routes to be prerendered by Nitro.
|
Nitro is an open source TypeScript framework to build ultra-fast web servers. Nuxt uses Nitro as its server engine. You can use `useNitro` to access the Nitro instance, `addServerHandler` to add a server handler, `addDevServerHandler` to add a server handler to be used only in development mode, `addServerPlugin` to add a plugin to extend Nitro's runtime behavior, and `addPrerenderRoutes` to add routes to be prerendered by Nitro.
|
||||||
|
|
||||||
## `addServerHandler`
|
## `addServerHandler`
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ links:
|
|||||||
|
|
||||||
## `extendPages`
|
## `extendPages`
|
||||||
|
|
||||||
In Nuxt 3, routes are automatically generated based on the structure of the files in the `pages` directory. However, there may be scenarios where you'd want to customize these routes. For instance, you might need to add a route for a dynamic page not generated by Nuxt, remove an existing route, or modify the configuration of a route. For such customizations, Nuxt 3 offers the `extendPages` feature, which allows you to extend and alter the pages configuration.
|
In Nuxt 3, routes are automatically generated based on the structure of the files in the `pages` directory. However, there may be scenarios where you'd want to customize these routes. For instance, you might need to add a route for a dynamic page not generated by Nuxt, remove an existing route, or modify the configuration of a route. For such customizations, Nuxt offers the `extendPages` feature, which allows you to extend and alter the pages configuration.
|
||||||
|
|
||||||
::tip{icon="i-ph-video-duotone" to="https://vueschool.io/lessons/extend-and-alter-nuxt-pages?friend=nuxt" target="_blank"}
|
::tip{icon="i-ph-video-duotone" to="https://vueschool.io/lessons/extend-and-alter-nuxt-pages?friend=nuxt" target="_blank"}
|
||||||
Watch Vue School video about extendPages.
|
Watch Vue School video about extendPages.
|
||||||
|
@ -15,7 +15,7 @@ Layouts is used to be a wrapper around your pages. It can be used to wrap your p
|
|||||||
Register template as layout and add it to the layouts.
|
Register template as layout and add it to the layouts.
|
||||||
|
|
||||||
::note
|
::note
|
||||||
In Nuxt 2 `error` layout can also be registered using this utility. In Nuxt 3 `error` layout [replaced](/docs/getting-started/error-handling#rendering-an-error-page) with `error.vue` page in project root.
|
In Nuxt 2 `error` layout can also be registered using this utility. In Nuxt 3+ `error` layout [replaced](/docs/getting-started/error-handling#rendering-an-error-page) with `error.vue` page in project root.
|
||||||
::
|
::
|
||||||
|
|
||||||
### Type
|
### Type
|
||||||
|
@ -30,7 +30,7 @@ Hook | Arguments | Environment | Description
|
|||||||
`page:loading:end` | - | Client | Called after `page:finish`
|
`page:loading:end` | - | Client | Called after `page:finish`
|
||||||
`page:transition:finish`| `pageComponent?` | Client | After page transition [onAfterLeave](https://vuejs.org/guide/built-ins/transition.html#javascript-hooks) event.
|
`page:transition:finish`| `pageComponent?` | Client | After page transition [onAfterLeave](https://vuejs.org/guide/built-ins/transition.html#javascript-hooks) event.
|
||||||
`dev:ssr-logs` | `logs` | Client | Called with an array of server-side logs that have been passed to the client (if `features.devLogs` is enabled).
|
`dev:ssr-logs` | `logs` | Client | Called with an array of server-side logs that have been passed to the client (if `features.devLogs` is enabled).
|
||||||
`page:view-transition:start` | `transition` | Client | Called after `document.startViewTransition` is called when [experimental viewTransition support is enabled](https://nuxt.com/docs/getting-started/transitions#view-transitions-api-experimental).
|
`page:view-transition:start` | `transition` | Client | Called after `document.startViewTransition` is called when [experimental viewTransition support is enabled](/docs/getting-started/transitions#view-transitions-api-experimental).
|
||||||
|
|
||||||
## Nuxt Hooks (build time)
|
## Nuxt Hooks (build time)
|
||||||
|
|
||||||
|
@ -30,4 +30,4 @@ And finally, just ask the question! There's no need to [ask permission to ask a
|
|||||||
|
|
||||||
Something isn't working the way that the docs say that it should. You're not sure if it's a bug. You've searched through the [open issues](https://github.com/nuxt/nuxt/issues) and [discussions](https://github.com/nuxt/nuxt/discussions) but you can't find anything. (if there is a closed issue, please create a new one)
|
Something isn't working the way that the docs say that it should. You're not sure if it's a bug. You've searched through the [open issues](https://github.com/nuxt/nuxt/issues) and [discussions](https://github.com/nuxt/nuxt/discussions) but you can't find anything. (if there is a closed issue, please create a new one)
|
||||||
|
|
||||||
We recommend taking a look at [how to report bugs](/docs/community/reporting-bugs). Nuxt 3 is still in active development, and every issue helps make it better.
|
We recommend taking a look at [how to report bugs](/docs/community/reporting-bugs). Nuxt is still in active development, and every issue helps make it better.
|
||||||
|
@ -22,31 +22,25 @@ Search through the [open issues](https://github.com/nuxt/nuxt/issues) and [discu
|
|||||||
|
|
||||||
It's important to be able to reproduce the bug reliably - in a minimal way and apart from the rest of your project. This narrows down what could be causing the issue and makes it possible for someone not only to find the cause, but also to test a potential solution.
|
It's important to be able to reproduce the bug reliably - in a minimal way and apart from the rest of your project. This narrows down what could be causing the issue and makes it possible for someone not only to find the cause, but also to test a potential solution.
|
||||||
|
|
||||||
Start with the Nuxt 3 or Nuxt Bridge sandbox and add the **minimum** amount of code necessary to reproduce the bug you're experiencing.
|
Start with the Nuxt sandbox and add the **minimum** amount of code necessary to reproduce the bug you're experiencing.
|
||||||
|
|
||||||
::note
|
::note
|
||||||
If your issue concerns Vue 3 or Vite, please try to reproduce it first with the Vue 3 SSR starter.
|
If your issue concerns Vue or Vite, please try to reproduce it first with the Vue SSR starter.
|
||||||
::
|
::
|
||||||
|
|
||||||
**Nuxt 3**:
|
**Nuxt**:
|
||||||
|
|
||||||
::card-group
|
::card-group
|
||||||
:card{title="Nuxt 3 on StackBlitz" icon="i-simple-icons-stackblitz" to="https://nuxt.new/s/v3" target="_blank"}
|
:card{title="Nuxt on StackBlitz" icon="i-simple-icons-stackblitz" to="https://nuxt.new/s/v3" target="_blank"}
|
||||||
:card{title="Nuxt 3 on CodeSandbox" icon="i-simple-icons-codesandbox" to="https://nuxt.new/c/v3" target="_blank"}
|
:card{title="Nuxt on CodeSandbox" icon="i-simple-icons-codesandbox" to="https://nuxt.new/c/v3" target="_blank"}
|
||||||
::
|
::
|
||||||
|
|
||||||
**Nuxt Bridge**:
|
**Vue**:
|
||||||
|
|
||||||
::card-group
|
::card-group
|
||||||
:card{title="Nuxt Bridge on CodeSandbox" icon="i-simple-icons-codesandbox" to="https://codesandbox.io/s/github/nuxt/starter/v2-bridge-codesandbox" target="_blank"}
|
:card{title="Vue SSR on StackBlitz" icon="i-simple-icons-stackblitz" to="https://stackblitz.com/github/nuxt-contrib/vue3-ssr-starter/tree/main?terminal=dev" target="_blank"}
|
||||||
::
|
:card{title="Vue SSR on CodeSandbox" icon="i-simple-icons-codesandbox" to="https://codesandbox.io/s/github/nuxt-contrib/vue3-ssr-starter/main" target="_blank"}
|
||||||
|
:card{title="Vue SSR Template on GitHub" icon="i-simple-icons-github" to="https://github.com/nuxt-contrib/vue3-ssr-starter/generate" target="_blank"}
|
||||||
**Vue 3**:
|
|
||||||
|
|
||||||
::card-group
|
|
||||||
:card{title="Vue 3 SSR on StackBlitz" icon="i-simple-icons-stackblitz" to="https://stackblitz.com/github/nuxt-contrib/vue3-ssr-starter/tree/main?terminal=dev" target="_blank"}
|
|
||||||
:card{title="Vue 3 SSR on CodeSandbox" icon="i-simple-icons-codesandbox" to="https://codesandbox.io/s/github/nuxt-contrib/vue3-ssr-starter/main" target="_blank"}
|
|
||||||
:card{title="Vue 3 SSR Template on GitHub" icon="i-simple-icons-github" to="https://github.com/nuxt-contrib/vue3-ssr-starter/generate" target="_blank"}
|
|
||||||
::
|
::
|
||||||
|
|
||||||
Once you've reproduced the issue, remove as much code from your reproduction as you can (while still recreating the bug). The time spent making the reproduction as minimal as possible will make a huge difference to whoever sets out to fix the issue.
|
Once you've reproduced the issue, remove as much code from your reproduction as you can (while still recreating the bug). The time spent making the reproduction as minimal as possible will make a huge difference to whoever sets out to fix the issue.
|
||||||
|
@ -32,7 +32,7 @@ Milestone | Expected date | Notes
|
|||||||
-------------|---------------|------------------------------------------------------------------------|-----------------------
|
-------------|---------------|------------------------------------------------------------------------|-----------------------
|
||||||
SEO & PWA | 2024 | [nuxt/nuxt#18395](https://github.com/nuxt/nuxt/discussions/18395) | Migrating from [nuxt-community/pwa-module](https://github.com/nuxt-community/pwa-module) for built-in SEO utils and service worker support
|
SEO & PWA | 2024 | [nuxt/nuxt#18395](https://github.com/nuxt/nuxt/discussions/18395) | Migrating from [nuxt-community/pwa-module](https://github.com/nuxt-community/pwa-module) for built-in SEO utils and service worker support
|
||||||
Assets | 2024 | [nuxt/nuxt#22012](https://github.com/nuxt/nuxt/discussions/22012) | Allow developers and modules to handle loading third-party assets.
|
Assets | 2024 | [nuxt/nuxt#22012](https://github.com/nuxt/nuxt/discussions/22012) | Allow developers and modules to handle loading third-party assets.
|
||||||
Translations | - | [nuxt/translations#4](https://github.com/nuxt/translations/discussions/4) ([request access](https://github.com/nuxt/nuxt/discussions/16054)) | A collaborative project for a stable translation process for Nuxt 3 docs. Currently pending for ideas and documentation tooling support (content v2 with remote sources).
|
Translations | - | [nuxt/translations#4](https://github.com/nuxt/translations/discussions/4) ([request access](https://github.com/nuxt/nuxt/discussions/16054)) | A collaborative project for a stable translation process for Nuxt docs. Currently pending for ideas and documentation tooling support (content v2 with remote sources).
|
||||||
|
|
||||||
## Core Modules Roadmap
|
## Core Modules Roadmap
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ Module | Status | Nuxt Support | Repos
|
|||||||
------------------------------------|---------------------|--------------|------------|-------------------
|
------------------------------------|---------------------|--------------|------------|-------------------
|
||||||
[Scripts](https://scripts.nuxt.com) | Public Preview | 3.x | [nuxt/scripts](https://github.com/nuxt/scripts) | Easy 3rd party script management.
|
[Scripts](https://scripts.nuxt.com) | Public Preview | 3.x | [nuxt/scripts](https://github.com/nuxt/scripts) | Easy 3rd party script management.
|
||||||
A11y | Planned | 3.x | `nuxt/a11y` to be announced | Accessibility hinting and utilities [nuxt/nuxt#23255](https://github.com/nuxt/nuxt/issues/23255)
|
A11y | Planned | 3.x | `nuxt/a11y` to be announced | Accessibility hinting and utilities [nuxt/nuxt#23255](https://github.com/nuxt/nuxt/issues/23255)
|
||||||
Auth | Planned | 3.x | `nuxt/auth` to be announced | Nuxt 3 support is planned after session support.
|
Auth | Planned | 3.x | `nuxt/auth` to be announced | Support is planned after session support.
|
||||||
Hints | Planned | 3.x | `nuxt/hints` to be announced | Guidance and suggestions for enhancing development practices.
|
Hints | Planned | 3.x | `nuxt/hints` to be announced | Guidance and suggestions for enhancing development practices.
|
||||||
|
|
||||||
## Release Cycle
|
## Release Cycle
|
||||||
@ -59,13 +59,13 @@ The current active version of [Nuxt](https://nuxt.com) is **v3** which is availa
|
|||||||
|
|
||||||
Nuxt 2 is in maintenance mode and is available on npm with the `2x` tag. It will reach End of Life (EOL) on June 30, 2024.
|
Nuxt 2 is in maintenance mode and is available on npm with the `2x` tag. It will reach End of Life (EOL) on June 30, 2024.
|
||||||
|
|
||||||
Each active version has its own nightly releases which are generated automatically. For more about enabling the Nuxt 3 nightly release channel, see [the nightly release channel docs](/docs/guide/going-further/nightly-release-channel).
|
Each active version has its own nightly releases which are generated automatically. For more about enabling the Nuxt nightly release channel, see [the nightly release channel docs](/docs/guide/going-further/nightly-release-channel).
|
||||||
|
|
||||||
Release | | Initial release | End Of Life | Docs
|
Release | | Initial release | End Of Life | Docs
|
||||||
----------------------------------------|---------------------------------------------------------------------------------------------------|-----------------|--------------|-------
|
----------------------------------------|---------------------------------------------------------------------------------------------------|-----------------|--------------|-------
|
||||||
**4.x** (scheduled) | | 2024 Q2 | |
|
**4.x** (scheduled) | | 2024 Q3 | |
|
||||||
**3.x** (stable) | <a href="https://npmjs.com/package/nuxt"><img alt="Nuxt latest 3.x version" src="https://flat.badgen.net/npm/v/nuxt?label=" class="not-prose"></a> | 2022-11-16 | TBA | [nuxt.com](/docs)
|
**3.x** (stable) | <a href="https://npmjs.com/package/nuxt"><img alt="Nuxt latest 3.x version" src="https://flat.badgen.net/npm/v/nuxt?label=" class="not-prose"></a> | 2022-11-16 | TBA | [nuxt.com](/docs)
|
||||||
**2.x** (maintenance) | <a href="https://www.npmjs.com/package/nuxt?activeTab=versions"><img alt="Nuxt 2.x version" src="https://flat.badgen.net/npm/v/nuxt/2x?label=" class="not-prose"></a> | 2018-09-21 | 2024-06-30 | [v2.nuxt.com](https://v2.nuxt.com/docs)
|
**2.x** (unsupported) | <a href="https://www.npmjs.com/package/nuxt?activeTab=versions"><img alt="Nuxt 2.x version" src="https://flat.badgen.net/npm/v/nuxt/2x?label=" class="not-prose"></a> | 2018-09-21 | 2024-06-30 | [v2.nuxt.com](https://v2.nuxt.com/docs)
|
||||||
**1.x** (unsupported) | <a href="https://www.npmjs.com/package/nuxt?activeTab=versions"><img alt="Nuxt 1.x version" src="https://flat.badgen.net/npm/v/nuxt/1x?label=" class="not-prose"></a> | 2018-01-08 | 2019-09-21 |
|
**1.x** (unsupported) | <a href="https://www.npmjs.com/package/nuxt?activeTab=versions"><img alt="Nuxt 1.x version" src="https://flat.badgen.net/npm/v/nuxt/1x?label=" class="not-prose"></a> | 2018-01-08 | 2019-09-21 |
|
||||||
|
|
||||||
### Support Status
|
### Support Status
|
||||||
|
@ -44,7 +44,7 @@ Use of `defineNuxtRouteMiddleware` is not supported outside of the middleware di
|
|||||||
|
|
||||||
## definePageMeta
|
## definePageMeta
|
||||||
|
|
||||||
You can also use [`definePageMeta`](https://nuxt.com/docs/api/utils/define-page-meta) in Nuxt Bridge.
|
You can also use [`definePageMeta`](/docs/api/utils/define-page-meta) in Nuxt Bridge.
|
||||||
|
|
||||||
You can be enabled with the `macros.pageMeta` option in your configuration file
|
You can be enabled with the `macros.pageMeta` option in your configuration file
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Nuxt 3 Examples
|
# Nuxt Examples
|
||||||
|
|
||||||
- 👉 See examples in your browser at https://nuxt.com/docs/examples
|
- 👉 See examples in your browser at https://nuxt.com/docs/examples
|
||||||
- 👉 View on GitHub at https://github.com/nuxt/examples
|
- 👉 View on GitHub at https://github.com/nuxt/examples
|
||||||
|
41
package.json
41
package.json
@ -34,22 +34,25 @@
|
|||||||
"typecheck:docs": "DOCS_TYPECHECK=true pnpm nuxi prepare && nuxt-content-twoslash verify --content-dir docs"
|
"typecheck:docs": "DOCS_TYPECHECK=true pnpm nuxi prepare && nuxt-content-twoslash verify --content-dir docs"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28648657.9a717203",
|
|
||||||
"typescript": "5.5.2",
|
|
||||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
|
||||||
"@nuxt/kit": "workspace:*",
|
"@nuxt/kit": "workspace:*",
|
||||||
"@nuxt/schema": "workspace:*",
|
"@nuxt/schema": "workspace:*",
|
||||||
"@nuxt/ui-templates": "workspace:*",
|
"@nuxt/ui-templates": "workspace:*",
|
||||||
"@nuxt/vite-builder": "workspace:*",
|
"@nuxt/vite-builder": "workspace:*",
|
||||||
"@nuxt/webpack-builder": "workspace:*",
|
"@nuxt/webpack-builder": "workspace:*",
|
||||||
|
"c12": "2.0.0-beta.1",
|
||||||
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
|
"jiti": "2.0.0-beta.3",
|
||||||
"magic-string": "^0.30.10",
|
"magic-string": "^0.30.10",
|
||||||
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||||
"nuxt": "workspace:*",
|
"nuxt": "workspace:*",
|
||||||
"rollup": "^4.18.0",
|
"rollup": "^4.18.0",
|
||||||
"vite": "5.3.1",
|
"typescript": "5.5.3",
|
||||||
"vue": "3.4.30"
|
"unbuild": "3.0.0-rc.6",
|
||||||
|
"vite": "5.3.3",
|
||||||
|
"vue": "3.4.31"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "9.5.0",
|
"@eslint/js": "9.6.0",
|
||||||
"@nuxt/eslint-config": "0.3.13",
|
"@nuxt/eslint-config": "0.3.13",
|
||||||
"@nuxt/kit": "workspace:*",
|
"@nuxt/kit": "workspace:*",
|
||||||
"@nuxt/test-utils": "3.13.1",
|
"@nuxt/test-utils": "3.13.1",
|
||||||
@ -58,41 +61,43 @@
|
|||||||
"@types/eslint__js": "8.42.3",
|
"@types/eslint__js": "8.42.3",
|
||||||
"@types/node": "20.14.9",
|
"@types/node": "20.14.9",
|
||||||
"@types/semver": "7.5.8",
|
"@types/semver": "7.5.8",
|
||||||
"@unhead/schema": "1.9.14",
|
"@unhead/schema": "1.9.15",
|
||||||
"@vitejs/plugin-vue": "5.0.4",
|
"@vitejs/plugin-vue": "5.0.5",
|
||||||
"@vitest/coverage-v8": "1.6.0",
|
"@vitest/coverage-v8": "1.6.0",
|
||||||
"@vue/test-utils": "2.4.6",
|
"@vue/test-utils": "2.4.6",
|
||||||
|
"autoprefixer": "10.4.19",
|
||||||
"case-police": "0.6.1",
|
"case-police": "0.6.1",
|
||||||
"changelogen": "0.5.5",
|
"changelogen": "0.5.5",
|
||||||
"consola": "3.2.3",
|
"consola": "3.2.3",
|
||||||
|
"cssnano": "7.0.4",
|
||||||
"devalue": "5.0.0",
|
"devalue": "5.0.0",
|
||||||
"eslint": "9.5.0",
|
"eslint": "9.6.0",
|
||||||
"eslint-plugin-no-only-tests": "3.1.0",
|
"eslint-plugin-no-only-tests": "3.1.0",
|
||||||
"eslint-plugin-perfectionist": "2.11.0",
|
"eslint-plugin-perfectionist": "2.11.0",
|
||||||
"eslint-typegen": "0.2.4",
|
"eslint-typegen": "0.2.4",
|
||||||
"execa": "9.3.0",
|
"execa": "9.3.0",
|
||||||
"globby": "14.0.1",
|
"globby": "14.0.2",
|
||||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
"happy-dom": "14.12.3",
|
"happy-dom": "14.12.3",
|
||||||
"jiti": "1.21.6",
|
"jiti": "2.0.0-beta.3",
|
||||||
"markdownlint-cli": "0.41.0",
|
"markdownlint-cli": "0.41.0",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28648657.9a717203",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||||
"nuxi": "3.12.0",
|
"nuxi": "3.12.0",
|
||||||
"nuxt": "workspace:*",
|
"nuxt": "workspace:*",
|
||||||
"nuxt-content-twoslash": "0.0.10",
|
"nuxt-content-twoslash": "0.1.0",
|
||||||
"ofetch": "1.3.4",
|
"ofetch": "1.3.4",
|
||||||
"pathe": "1.1.2",
|
"pathe": "1.1.2",
|
||||||
"playwright-core": "1.45.0",
|
"playwright-core": "1.45.1",
|
||||||
"rimraf": "5.0.7",
|
"rimraf": "6.0.0",
|
||||||
"semver": "7.6.2",
|
"semver": "7.6.2",
|
||||||
"std-env": "3.7.0",
|
"std-env": "3.7.0",
|
||||||
"typescript": "5.5.2",
|
"typescript": "5.5.3",
|
||||||
"ufo": "1.5.3",
|
"ufo": "1.5.3",
|
||||||
"vitest": "1.6.0",
|
"vitest": "1.6.0",
|
||||||
"vitest-environment-nuxt": "1.0.0",
|
"vitest-environment-nuxt": "1.0.0",
|
||||||
"vue": "3.4.30",
|
"vue": "3.4.31",
|
||||||
"vue-router": "4.4.0",
|
"vue-router": "4.4.0",
|
||||||
"vue-tsc": "2.0.22"
|
"vue-tsc": "2.0.26"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.4.0",
|
"packageManager": "pnpm@9.4.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
7
packages/kit/index.d.ts
vendored
7
packages/kit/index.d.ts
vendored
@ -1,7 +0,0 @@
|
|||||||
/* eslint-disable no-var */
|
|
||||||
declare global {
|
|
||||||
var __NUXT_PREPATHS__: string[] | string | undefined
|
|
||||||
var __NUXT_PATHS__: string[] | string | undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
export {}
|
|
@ -27,18 +27,18 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/schema": "workspace:*",
|
"@nuxt/schema": "workspace:*",
|
||||||
"c12": "^1.11.1",
|
"c12": "^2.0.0-beta.1",
|
||||||
"consola": "^3.2.3",
|
"consola": "^3.2.3",
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
"destr": "^2.0.3",
|
"destr": "^2.0.3",
|
||||||
"globby": "^14.0.1",
|
"globby": "^14.0.2",
|
||||||
"hash-sum": "^2.0.0",
|
"hash-sum": "^2.0.0",
|
||||||
"ignore": "^5.3.1",
|
"ignore": "^5.3.1",
|
||||||
"jiti": "^1.21.6",
|
"jiti": "^2.0.0-beta.3",
|
||||||
"klona": "^2.0.6",
|
"klona": "^2.0.6",
|
||||||
"mlly": "^1.7.1",
|
"mlly": "^1.7.1",
|
||||||
"pathe": "^1.1.2",
|
"pathe": "^1.1.2",
|
||||||
"pkg-types": "^1.1.1",
|
"pkg-types": "^1.1.3",
|
||||||
"scule": "^1.3.0",
|
"scule": "^1.3.0",
|
||||||
"semver": "^7.6.2",
|
"semver": "^7.6.2",
|
||||||
"ufo": "^1.5.3",
|
"ufo": "^1.5.3",
|
||||||
@ -49,9 +49,9 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/hash-sum": "1.0.2",
|
"@types/hash-sum": "1.0.2",
|
||||||
"@types/semver": "7.5.8",
|
"@types/semver": "7.5.8",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28648657.9a717203",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||||
"unbuild": "latest",
|
"unbuild": "3.0.0-rc.6",
|
||||||
"vite": "5.3.1",
|
"vite": "5.3.3",
|
||||||
"vitest": "1.6.0",
|
"vitest": "1.6.0",
|
||||||
"webpack": "5.92.1"
|
"webpack": "5.92.1"
|
||||||
},
|
},
|
||||||
|
@ -1,37 +1,36 @@
|
|||||||
// Module
|
// Module
|
||||||
export * from './module/define'
|
export { defineNuxtModule } from './module/define'
|
||||||
export * from './module/install'
|
export { getDirectory, installModule, loadNuxtModuleInstance, normalizeModuleTranspilePath } from './module/install'
|
||||||
export * from './module/compatibility'
|
export { getNuxtModuleVersion, hasNuxtModule, hasNuxtModuleCompatibility } from './module/compatibility'
|
||||||
|
|
||||||
// Loader
|
// Loader
|
||||||
export * from './loader/config'
|
export { loadNuxtConfig } from './loader/config'
|
||||||
export * from './loader/schema'
|
export type { LoadNuxtConfigOptions } from './loader/config'
|
||||||
export * from './loader/nuxt'
|
export { extendNuxtSchema } from './loader/schema'
|
||||||
|
export { buildNuxt, loadNuxt } from './loader/nuxt'
|
||||||
|
export type { LoadNuxtOptions } from './loader/nuxt'
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
export * from './imports'
|
export { addImports, addImportsDir, addImportsSources } from './imports'
|
||||||
export { updateRuntimeConfig, useRuntimeConfig } from './runtime-config'
|
export { updateRuntimeConfig, useRuntimeConfig } from './runtime-config'
|
||||||
export * from './build'
|
export { addBuildPlugin, addVitePlugin, addWebpackPlugin, extendViteConfig, extendWebpackConfig } from './build'
|
||||||
export * from './compatibility'
|
export type { ExtendConfigOptions, ExtendViteConfigOptions, ExtendWebpackConfigOptions } from './build'
|
||||||
export * from './components'
|
export { assertNuxtCompatibility, checkNuxtCompatibility, getNuxtVersion, hasNuxtCompatibility, isNuxtMajorVersion, normalizeSemanticVersion, isNuxt2, isNuxt3 } from './compatibility'
|
||||||
export * from './context'
|
export { addComponent, addComponentsDir } from './components'
|
||||||
|
export type { AddComponentOptions } from './components'
|
||||||
|
export { nuxtCtx, tryUseNuxt, useNuxt } from './context'
|
||||||
export { isIgnored, resolveIgnorePatterns } from './ignore'
|
export { isIgnored, resolveIgnorePatterns } from './ignore'
|
||||||
export * from './layout'
|
export { addLayout } from './layout'
|
||||||
export * from './pages'
|
export { addRouteMiddleware, extendPages, extendRouteRules } from './pages'
|
||||||
export * from './plugin'
|
export type { AddRouteMiddlewareOptions, ExtendRouteRulesOptions } from './pages'
|
||||||
export * from './resolve'
|
export { addPlugin, addPluginTemplate, normalizePlugin } from './plugin'
|
||||||
export * from './nitro'
|
export type { AddPluginOptions } from './plugin'
|
||||||
|
export { createResolver, findPath, resolveAlias, resolveFiles, resolveNuxtModule, resolvePath } from './resolve'
|
||||||
|
export type { ResolvePathOptions, Resolver } from './resolve'
|
||||||
|
export { addServerHandler, addDevServerHandler, addServerPlugin, addPrerenderRoutes, useNitro, addServerImports, addServerImportsDir, addServerScanDir } from './nitro'
|
||||||
export { addTemplate, addTypeTemplate, normalizeTemplate, updateTemplates, writeTypes } from './template'
|
export { addTemplate, addTypeTemplate, normalizeTemplate, updateTemplates, writeTypes } from './template'
|
||||||
export * from './logger'
|
export { logger, useLogger } from './logger'
|
||||||
|
|
||||||
// Internal Utils
|
// Internal Utils
|
||||||
// TODO
|
export { resolveModule, tryResolveModule, importModule, tryImportModule, requireModule, tryRequireModule } from './internal/esm'
|
||||||
export {
|
export type { ImportModuleOptions, ResolveModuleOptions } from './internal/esm'
|
||||||
resolveModule,
|
|
||||||
requireModule,
|
|
||||||
importModule,
|
|
||||||
tryImportModule,
|
|
||||||
tryRequireModule,
|
|
||||||
} from './internal/cjs'
|
|
||||||
export type { ResolveModuleOptions, RequireModuleOptions } from './internal/cjs'
|
|
||||||
export { tryResolveModule } from './internal/esm'
|
|
||||||
|
@ -1,121 +0,0 @@
|
|||||||
import { pathToFileURL } from 'node:url'
|
|
||||||
import { normalize } from 'pathe'
|
|
||||||
import { interopDefault } from 'mlly'
|
|
||||||
import jiti from 'jiti'
|
|
||||||
|
|
||||||
// TODO: use create-require for jest environment
|
|
||||||
const _require = jiti(process.cwd(), { interopDefault: true, esmResolve: true })
|
|
||||||
|
|
||||||
/** @deprecated Do not use CJS utils */
|
|
||||||
export interface ResolveModuleOptions {
|
|
||||||
paths?: string | string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated Do not use CJS utils */
|
|
||||||
export interface RequireModuleOptions extends ResolveModuleOptions {
|
|
||||||
// TODO: use create-require for jest environment
|
|
||||||
// native?: boolean
|
|
||||||
/** Clear the require cache (force fresh require) but only if not within `node_modules` */
|
|
||||||
clearCache?: boolean
|
|
||||||
|
|
||||||
/** Automatically de-default the result of requiring the module. */
|
|
||||||
interopDefault?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated Do not use CJS utils */
|
|
||||||
function isNodeModules (id: string) {
|
|
||||||
// TODO: Follow symlinks
|
|
||||||
return /[/\\]node_modules[/\\]/.test(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated Do not use CJS utils */
|
|
||||||
function clearRequireCache (id: string) {
|
|
||||||
if (isNodeModules(id)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const entry = getRequireCacheItem(id)
|
|
||||||
|
|
||||||
if (!entry) {
|
|
||||||
delete _require.cache[id]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.parent) {
|
|
||||||
entry.parent.children = entry.parent.children.filter(e => e.id !== id)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const child of entry.children) {
|
|
||||||
clearRequireCache(child.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
delete _require.cache[id]
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated Do not use CJS utils */
|
|
||||||
function getRequireCacheItem (id: string) {
|
|
||||||
try {
|
|
||||||
return _require.cache[id]
|
|
||||||
} catch (e) {
|
|
||||||
// ignore issues accessing require.cache
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getNodeModulesPaths (paths?: string[] | string) {
|
|
||||||
return ([] as Array<string | undefined>).concat(
|
|
||||||
global.__NUXT_PREPATHS__,
|
|
||||||
paths || [],
|
|
||||||
process.cwd(),
|
|
||||||
global.__NUXT_PATHS__,
|
|
||||||
).filter(Boolean) as string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated Do not use CJS utils */
|
|
||||||
export function resolveModule (id: string, opts: ResolveModuleOptions = {}) {
|
|
||||||
return normalize(_require.resolve(id, {
|
|
||||||
paths: getNodeModulesPaths(opts.paths),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated Do not use CJS utils */
|
|
||||||
export function requireModule (id: string, opts: RequireModuleOptions = {}) {
|
|
||||||
// Resolve id
|
|
||||||
const resolvedPath = resolveModule(id, opts)
|
|
||||||
|
|
||||||
// Clear require cache if necessary
|
|
||||||
if (opts.clearCache && !isNodeModules(id)) {
|
|
||||||
clearRequireCache(resolvedPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to require
|
|
||||||
const requiredModule = _require(resolvedPath)
|
|
||||||
|
|
||||||
return requiredModule
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated Do not use CJS utils */
|
|
||||||
export function importModule (id: string, opts: RequireModuleOptions = {}) {
|
|
||||||
const resolvedPath = resolveModule(id, opts)
|
|
||||||
if (opts.interopDefault !== false) {
|
|
||||||
return import(pathToFileURL(resolvedPath).href).then(interopDefault)
|
|
||||||
}
|
|
||||||
return import(pathToFileURL(resolvedPath).href)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated Do not use CJS utils */
|
|
||||||
export function tryImportModule (id: string, opts: RequireModuleOptions = {}) {
|
|
||||||
try {
|
|
||||||
return importModule(id, opts).catch(() => undefined)
|
|
||||||
} catch {
|
|
||||||
// intentionally empty as this is a `try-` function
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated Do not use CJS utils */
|
|
||||||
export function tryRequireModule (id: string, opts: RequireModuleOptions = {}) {
|
|
||||||
try {
|
|
||||||
return requireModule(id, opts)
|
|
||||||
} catch {
|
|
||||||
// intentionally empty as this is a `try-` function
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,10 @@
|
|||||||
import { pathToFileURL } from 'node:url'
|
import { pathToFileURL } from 'node:url'
|
||||||
import { interopDefault, resolvePath } from 'mlly'
|
import { interopDefault, resolvePath, resolvePathSync } from 'mlly'
|
||||||
|
import { createJiti } from 'jiti'
|
||||||
|
|
||||||
|
export interface ResolveModuleOptions {
|
||||||
|
paths?: string | string[]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a module from a given root path using an algorithm patterned on
|
* Resolve a module from a given root path using an algorithm patterned on
|
||||||
@ -15,14 +20,52 @@ export async function tryResolveModule (id: string, url: string | string[] = imp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function importModule (id: string, url: string | string[] = import.meta.url) {
|
export function resolveModule (id: string, options?: ResolveModuleOptions) {
|
||||||
const resolvedPath = await resolvePath(id, { url })
|
return resolvePathSync(id, { url: options?.paths ?? [import.meta.url] })
|
||||||
return import(pathToFileURL(resolvedPath).href).then(interopDefault)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function tryImportModule (id: string, url = import.meta.url) {
|
export interface ImportModuleOptions extends ResolveModuleOptions {
|
||||||
|
/** Automatically de-default the result of requiring the module. */
|
||||||
|
interopDefault?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function importModule<T = unknown> (id: string, opts?: ImportModuleOptions) {
|
||||||
|
const resolvedPath = await resolveModule(id, opts)
|
||||||
|
return import(pathToFileURL(resolvedPath).href).then(r => opts?.interopDefault !== false ? interopDefault(r) : r) as Promise<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tryImportModule<T = unknown> (id: string, opts?: ImportModuleOptions) {
|
||||||
try {
|
try {
|
||||||
return importModule(id, url).catch(() => undefined)
|
return importModule<T>(id, opts).catch(() => undefined)
|
||||||
|
} catch {
|
||||||
|
// intentionally empty as this is a `try-` function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const warnings = new Set<string>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Please use `importModule` instead.
|
||||||
|
*/
|
||||||
|
export function requireModule<T = unknown> (id: string, opts?: ImportModuleOptions) {
|
||||||
|
if (!warnings.has(id)) {
|
||||||
|
// TODO: add more information on stack trace
|
||||||
|
console.warn('[@nuxt/kit] `requireModule` is deprecated. Please use `importModule` instead.')
|
||||||
|
warnings.add(id)
|
||||||
|
}
|
||||||
|
const resolvedPath = resolveModule(id, opts)
|
||||||
|
const jiti = createJiti(import.meta.url, {
|
||||||
|
interopDefault: opts?.interopDefault !== false,
|
||||||
|
})
|
||||||
|
return jiti(pathToFileURL(resolvedPath).href) as T
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Please use `tryImportModule` instead.
|
||||||
|
*/
|
||||||
|
export function tryRequireModule<T = unknown> (id: string, opts?: ImportModuleOptions) {
|
||||||
|
try {
|
||||||
|
return requireModule<T>(id, opts)
|
||||||
} catch {
|
} catch {
|
||||||
// intentionally empty as this is a `try-` function
|
// intentionally empty as this is a `try-` function
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
|
|||||||
|
|
||||||
const rootDir = pathToFileURL(opts.cwd!).href
|
const rootDir = pathToFileURL(opts.cwd!).href
|
||||||
|
|
||||||
const { loadNuxt } = await importModule((pkg as any)._name || pkg.name, rootDir)
|
const { loadNuxt } = await importModule<typeof import('nuxt')>((pkg as any)._name || pkg.name, { paths: rootDir })
|
||||||
const nuxt = await loadNuxt(opts)
|
const nuxt = await loadNuxt(opts)
|
||||||
return nuxt
|
return nuxt
|
||||||
}
|
}
|
||||||
@ -39,13 +39,6 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
|
|||||||
export async function buildNuxt (nuxt: Nuxt): Promise<any> {
|
export async function buildNuxt (nuxt: Nuxt): Promise<any> {
|
||||||
const rootDir = pathToFileURL(nuxt.options.rootDir).href
|
const rootDir = pathToFileURL(nuxt.options.rootDir).href
|
||||||
|
|
||||||
// Nuxt 3
|
const { build } = await tryImportModule<typeof import('nuxt')>('nuxt-nightly', { paths: rootDir }) || await importModule<typeof import('nuxt')>('nuxt', { paths: rootDir })
|
||||||
if (nuxt.options._majorVersion === 3) {
|
|
||||||
const { build } = await tryImportModule('nuxt-nightly', rootDir) || await tryImportModule('nuxt3', rootDir) || await importModule('nuxt', rootDir)
|
|
||||||
return build(nuxt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nuxt 2
|
|
||||||
const { build } = await tryImportModule('nuxt-edge', rootDir) || await importModule('nuxt', rootDir)
|
|
||||||
return build(nuxt)
|
return build(nuxt)
|
||||||
}
|
}
|
||||||
|
@ -112,11 +112,10 @@ function _defineNuxtModule<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call setup
|
// Call setup
|
||||||
const key = `nuxt:module:${uniqueKey || (Math.round(Math.random() * 10000))}`
|
const start = performance.now()
|
||||||
const mark = performance.mark(key)
|
|
||||||
const res = await module.setup?.call(null as any, _options, nuxt) ?? {}
|
const res = await module.setup?.call(null as any, _options, nuxt) ?? {}
|
||||||
const perf = performance.measure(key, mark.name)
|
const perf = performance.now() - start
|
||||||
const setupTime = Math.round((perf.duration * 100)) / 100
|
const setupTime = Math.round((perf * 100)) / 100
|
||||||
|
|
||||||
// Measure setup time
|
// Measure setup time
|
||||||
if (setupTime > 5000 && uniqueKey !== '@nuxt/telemetry') {
|
if (setupTime > 5000 && uniqueKey !== '@nuxt/telemetry') {
|
||||||
|
@ -2,10 +2,9 @@ import { existsSync, promises as fsp, lstatSync } from 'node:fs'
|
|||||||
import type { ModuleMeta, Nuxt, NuxtConfig, NuxtModule } from '@nuxt/schema'
|
import type { ModuleMeta, Nuxt, NuxtConfig, NuxtModule } from '@nuxt/schema'
|
||||||
import { dirname, isAbsolute, join, resolve } from 'pathe'
|
import { dirname, isAbsolute, join, resolve } from 'pathe'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
|
import { createJiti } from 'jiti'
|
||||||
import { useNuxt } from '../context'
|
import { useNuxt } from '../context'
|
||||||
import { requireModule } from '../internal/cjs'
|
import { resolveAlias } from '../resolve'
|
||||||
import { importModule } from '../internal/esm'
|
|
||||||
import { resolveAlias, resolvePath } from '../resolve'
|
|
||||||
import { logger } from '../logger'
|
import { logger } from '../logger'
|
||||||
|
|
||||||
const NODE_MODULES_RE = /[/\\]node_modules[/\\]/
|
const NODE_MODULES_RE = /[/\\]node_modules[/\\]/
|
||||||
@ -72,15 +71,20 @@ export const normalizeModuleTranspilePath = (p: string) => {
|
|||||||
|
|
||||||
export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, nuxt: Nuxt = useNuxt()) {
|
export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, nuxt: Nuxt = useNuxt()) {
|
||||||
let buildTimeModuleMeta: ModuleMeta = {}
|
let buildTimeModuleMeta: ModuleMeta = {}
|
||||||
|
|
||||||
|
const jiti = createJiti(nuxt.options.rootDir, {
|
||||||
|
interopDefault: true,
|
||||||
|
alias: nuxt.options.alias,
|
||||||
|
})
|
||||||
|
|
||||||
// Import if input is string
|
// Import if input is string
|
||||||
if (typeof nuxtModule === 'string') {
|
if (typeof nuxtModule === 'string') {
|
||||||
const paths = [join(nuxtModule, 'nuxt'), join(nuxtModule, 'module'), nuxtModule]
|
const paths = [join(nuxtModule, 'nuxt'), join(nuxtModule, 'module'), nuxtModule]
|
||||||
let error: unknown
|
let error: unknown
|
||||||
for (const path of paths) {
|
for (const path of paths) {
|
||||||
try {
|
try {
|
||||||
const src = await resolvePath(path, { fallbackToOriginal: true })
|
const src = jiti.esmResolve(path)
|
||||||
// Prefer ESM resolution if possible
|
nuxtModule = await jiti.import(src) as NuxtModule
|
||||||
nuxtModule = await importModule(src, nuxt.options.modulesDir).catch(() => null) ?? requireModule(src, { paths: nuxt.options.modulesDir })
|
|
||||||
|
|
||||||
// nuxt-module-builder generates a module.json with metadata including the version
|
// nuxt-module-builder generates a module.json with metadata including the version
|
||||||
const moduleMetadataPath = join(dirname(src), 'module.json')
|
const moduleMetadataPath = join(dirname(src), 'module.json')
|
||||||
@ -94,7 +98,7 @@ export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, n
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof nuxtModule !== 'function' && error) {
|
if (typeof nuxtModule !== 'function' && error) {
|
||||||
logger.error(`Error while requiring module \`${nuxtModule}\`: ${error}`)
|
logger.error(`Error while importing module \`${nuxtModule}\`: ${error}`)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import { readPackageJSON } from 'pkg-types'
|
|||||||
import { tryResolveModule } from './internal/esm'
|
import { tryResolveModule } from './internal/esm'
|
||||||
import { getDirectory } from './module/install'
|
import { getDirectory } from './module/install'
|
||||||
import { tryUseNuxt, useNuxt } from './context'
|
import { tryUseNuxt, useNuxt } from './context'
|
||||||
import { getNodeModulesPaths } from './internal/cjs'
|
|
||||||
import { resolveNuxtModule } from './resolve'
|
import { resolveNuxtModule } from './resolve'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -191,6 +190,7 @@ export async function _generateTypes (nuxt: Nuxt) {
|
|||||||
'ESNext',
|
'ESNext',
|
||||||
'dom',
|
'dom',
|
||||||
'dom.iterable',
|
'dom.iterable',
|
||||||
|
'webworker',
|
||||||
],
|
],
|
||||||
/* JSX support for Vue */
|
/* JSX support for Vue */
|
||||||
jsx: 'preserve',
|
jsx: 'preserve',
|
||||||
@ -265,7 +265,7 @@ export async function _generateTypes (nuxt: Nuxt) {
|
|||||||
await Promise.all([...nuxt.options.modules, ...nuxt.options._modules].map(async (id) => {
|
await Promise.all([...nuxt.options.modules, ...nuxt.options._modules].map(async (id) => {
|
||||||
if (typeof id !== 'string') { return }
|
if (typeof id !== 'string') { return }
|
||||||
|
|
||||||
const pkg = await readPackageJSON(id, { url: getNodeModulesPaths(nuxt.options.modulesDir) }).catch(() => null)
|
const pkg = await readPackageJSON(id, { url: nuxt.options.modulesDir }).catch(() => null)
|
||||||
references.push(({ types: pkg?.name || id }))
|
references.push(({ types: pkg?.name || id }))
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
2
packages/nuxt/index.d.ts
vendored
2
packages/nuxt/index.d.ts
vendored
@ -2,8 +2,6 @@
|
|||||||
declare global {
|
declare global {
|
||||||
var __NUXT_VERSION__: string
|
var __NUXT_VERSION__: string
|
||||||
var __NUXT_ASYNC_CONTEXT__: boolean
|
var __NUXT_ASYNC_CONTEXT__: boolean
|
||||||
var __NUXT_PREPATHS__: string[] | string | undefined
|
|
||||||
var __NUXT_PATHS__: string[] | string | undefined
|
|
||||||
|
|
||||||
interface Navigator {
|
interface Navigator {
|
||||||
connection?: {
|
connection?: {
|
||||||
|
@ -60,42 +60,44 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/devalue": "^2.0.2",
|
"@nuxt/devalue": "^2.0.2",
|
||||||
"@nuxt/devtools": "^1.3.6",
|
"@nuxt/devtools": "^1.3.9",
|
||||||
"@nuxt/kit": "workspace:*",
|
"@nuxt/kit": "workspace:*",
|
||||||
"@nuxt/schema": "workspace:*",
|
"@nuxt/schema": "workspace:*",
|
||||||
"@nuxt/telemetry": "^2.5.4",
|
"@nuxt/telemetry": "^2.5.4",
|
||||||
"@nuxt/vite-builder": "workspace:*",
|
"@nuxt/vite-builder": "workspace:*",
|
||||||
"@unhead/dom": "^1.9.14",
|
"@unhead/dom": "^1.9.15",
|
||||||
"@unhead/ssr": "^1.9.14",
|
"@unhead/ssr": "^1.9.15",
|
||||||
"@unhead/vue": "^1.9.14",
|
"@unhead/vue": "^1.9.15",
|
||||||
"@vue/shared": "^3.4.30",
|
"@vue/shared": "^3.4.31",
|
||||||
"acorn": "8.12.0",
|
"acorn": "8.12.1",
|
||||||
"c12": "^1.11.1",
|
"c12": "^2.0.0-beta.1",
|
||||||
"chokidar": "^3.6.0",
|
"chokidar": "^3.6.0",
|
||||||
|
"compatx": "^0.1.8",
|
||||||
|
"consola": "^3.2.3",
|
||||||
"cookie-es": "^1.1.0",
|
"cookie-es": "^1.1.0",
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
"destr": "^2.0.3",
|
"destr": "^2.0.3",
|
||||||
"devalue": "^5.0.0",
|
"devalue": "^5.0.0",
|
||||||
"esbuild": "^0.21.5",
|
"esbuild": "^0.23.0",
|
||||||
"escape-string-regexp": "^5.0.0",
|
"escape-string-regexp": "^5.0.0",
|
||||||
"estree-walker": "^3.0.3",
|
"estree-walker": "^3.0.3",
|
||||||
"globby": "^14.0.1",
|
"globby": "^14.0.2",
|
||||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
"hookable": "^5.5.3",
|
"hookable": "^5.5.3",
|
||||||
"ignore": "^5.3.1",
|
"ignore": "^5.3.1",
|
||||||
"jiti": "^1.21.6",
|
"jiti": "^2.0.0-beta.3",
|
||||||
"klona": "^2.0.6",
|
"klona": "^2.0.6",
|
||||||
"knitwork": "^1.1.0",
|
"knitwork": "^1.1.0",
|
||||||
"magic-string": "^0.30.10",
|
"magic-string": "^0.30.10",
|
||||||
"mlly": "^1.7.1",
|
"mlly": "^1.7.1",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28648657.9a717203",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||||
"nuxi": "^3.12.0",
|
"nuxi": "^3.12.0",
|
||||||
"nypm": "^0.3.8",
|
"nypm": "^0.3.9",
|
||||||
"ofetch": "^1.3.4",
|
"ofetch": "^1.3.4",
|
||||||
"ohash": "^1.1.3",
|
"ohash": "^1.1.3",
|
||||||
"pathe": "^1.1.2",
|
"pathe": "^1.1.2",
|
||||||
"perfect-debounce": "^1.0.0",
|
"perfect-debounce": "^1.0.0",
|
||||||
"pkg-types": "^1.1.1",
|
"pkg-types": "^1.1.3",
|
||||||
"radix3": "^1.1.2",
|
"radix3": "^1.1.2",
|
||||||
"scule": "^1.3.0",
|
"scule": "^1.3.0",
|
||||||
"semver": "^7.6.2",
|
"semver": "^7.6.2",
|
||||||
@ -107,11 +109,11 @@
|
|||||||
"unctx": "^2.3.1",
|
"unctx": "^2.3.1",
|
||||||
"unenv": "^1.9.0",
|
"unenv": "^1.9.0",
|
||||||
"unimport": "^3.7.2",
|
"unimport": "^3.7.2",
|
||||||
"unplugin": "^1.10.1",
|
"unplugin": "^1.11.0",
|
||||||
"unplugin-vue-router": "^0.10.0",
|
"unplugin-vue-router": "^0.10.0",
|
||||||
"unstorage": "^1.10.2",
|
"unstorage": "^1.10.2",
|
||||||
"untyped": "^1.4.2",
|
"untyped": "^1.4.2",
|
||||||
"vue": "^3.4.30",
|
"vue": "^3.4.31",
|
||||||
"vue-bundle-renderer": "^2.1.0",
|
"vue-bundle-renderer": "^2.1.0",
|
||||||
"vue-devtools-stub": "^0.1.0",
|
"vue-devtools-stub": "^0.1.0",
|
||||||
"vue-router": "^4.4.0"
|
"vue-router": "^4.4.0"
|
||||||
@ -121,10 +123,10 @@
|
|||||||
"@nuxt/ui-templates": "1.3.4",
|
"@nuxt/ui-templates": "1.3.4",
|
||||||
"@parcel/watcher": "2.4.1",
|
"@parcel/watcher": "2.4.1",
|
||||||
"@types/estree": "1.0.5",
|
"@types/estree": "1.0.5",
|
||||||
"@vitejs/plugin-vue": "5.0.4",
|
"@vitejs/plugin-vue": "5.0.5",
|
||||||
"@vue/compiler-sfc": "3.4.30",
|
"@vue/compiler-sfc": "3.4.31",
|
||||||
"unbuild": "latest",
|
"unbuild": "3.0.0-rc.6",
|
||||||
"vite": "5.3.1",
|
"vite": "5.3.3",
|
||||||
"vitest": "1.6.0"
|
"vitest": "1.6.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
@ -310,7 +310,7 @@ export function useAsyncData<
|
|||||||
|
|
||||||
if (import.meta.dev && import.meta.server && !result) {
|
if (import.meta.dev && import.meta.server && !result) {
|
||||||
// @ts-expect-error private property
|
// @ts-expect-error private property
|
||||||
console.warn(`[nuxt] \`${options._functionName || 'useAsyncData'}\` should return a value that is not \`null\` or \`undefined\` or the request may be duplicated on the client side.`)
|
console.warn(`[nuxt] \`${options._functionName || 'useAsyncData'}\` must return a truthy value (for example, it should not be \`undefined\`, \`null\` or empty string) or the request may be duplicated on the client side.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
nuxtApp.payload.data[key] = result
|
nuxtApp.payload.data[key] = result
|
||||||
|
@ -23,6 +23,8 @@ export const preloadComponents = async (components: string | string[]) => {
|
|||||||
* @since 3.0.0
|
* @since 3.0.0
|
||||||
*/
|
*/
|
||||||
export const prefetchComponents = (components: string | string[]) => {
|
export const prefetchComponents = (components: string | string[]) => {
|
||||||
|
if (import.meta.server) { return }
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
return preloadComponents(components)
|
return preloadComponents(components)
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
/// <reference path="types/augments.d.ts" />
|
export { applyPlugin, applyPlugins, callWithNuxt, createNuxtApp, defineAppConfig, defineNuxtPlugin, definePayloadPlugin, isNuxtPlugin, registerPluginHooks, tryUseNuxtApp, useNuxtApp, useRuntimeConfig } from './nuxt'
|
||||||
|
export type { CreateOptions, NuxtApp, NuxtPayload, NuxtPluginIndicator, NuxtSSRContext, ObjectPlugin, Plugin, PluginEnvContext, PluginMeta, ResolvedPluginMeta, RuntimeNuxtHooks } from './nuxt'
|
||||||
|
|
||||||
export * from './nuxt'
|
export { defineNuxtComponent, useAsyncData, useLazyAsyncData, useNuxtData, refreshNuxtData, clearNuxtData, useHydration, callOnce, useState, clearNuxtState, clearError, createError, isNuxtError, showError, useError, useFetch, useLazyFetch, useCookie, refreshCookie, onPrehydrate, prerenderRoutes, useRequestHeaders, useRequestEvent, useRequestFetch, setResponseStatus, onNuxtReady, abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter, preloadComponents, prefetchComponents, preloadRouteComponents, isPrerendered, loadPayload, preloadPayload, definePayloadReducer, definePayloadReviver, getAppManifest, getRouteRules, reloadNuxtApp, useRequestURL, usePreviewMode, useId, useRouteAnnouncer, useHead, useSeoMeta, useServerSeoMeta } from './composables/index'
|
||||||
|
export type { AddRouteMiddlewareOptions, AsyncData, AsyncDataOptions, AsyncDataRequestStatus, CookieOptions, CookieRef, FetchResult, NuxtAppManifest, NuxtAppManifestMeta, NuxtError, ReloadNuxtAppOptions, RouteMiddleware, UseFetchOptions } from './composables/index'
|
||||||
|
|
||||||
export * from './composables/index'
|
export { defineNuxtLink } from './components/index'
|
||||||
|
export type { NuxtLinkOptions, NuxtLinkProps } from './components/index'
|
||||||
export * from './components/index'
|
export { _getAppConfig, updateAppConfig, useAppConfig } from './config'
|
||||||
export * from './config'
|
export { cancelIdleCallback, requestIdleCallback } from './compat/idle-callback'
|
||||||
export * from './compat/idle-callback'
|
export type { NuxtAppLiterals, NuxtIslandContext, NuxtIslandResponse, NuxtRenderHTMLContext, PageMeta } from './types'
|
||||||
export * from './types'
|
|
||||||
|
|
||||||
export const isVue2 = false
|
export const isVue2 = false
|
||||||
export const isVue3 = true
|
export const isVue3 = true
|
||||||
|
@ -121,6 +121,9 @@ interface _NuxtApp {
|
|||||||
/** @internal */
|
/** @internal */
|
||||||
_asyncData: Record<string, {
|
_asyncData: Record<string, {
|
||||||
data: Ref<unknown>
|
data: Ref<unknown>
|
||||||
|
/**
|
||||||
|
* @deprecated This may be removed in a future major version.
|
||||||
|
*/
|
||||||
pending: Ref<boolean>
|
pending: Ref<boolean>
|
||||||
error: Ref<Error | undefined>
|
error: Ref<Error | undefined>
|
||||||
status: Ref<AsyncDataRequestStatus>
|
status: Ref<AsyncDataRequestStatus>
|
||||||
|
@ -5,9 +5,9 @@ export default defineNuxtPlugin({
|
|||||||
setup (nuxtApp) {
|
setup (nuxtApp) {
|
||||||
nuxtApp.vueApp.mixin({
|
nuxtApp.vueApp.mixin({
|
||||||
beforeCreate () {
|
beforeCreate () {
|
||||||
const { _registeredComponents } = this.$nuxt.ssrContext
|
const { modules } = this.$nuxt.ssrContext
|
||||||
const { __moduleIdentifier } = this.$options
|
const { __moduleIdentifier } = this.$options
|
||||||
_registeredComponents.add(__moduleIdentifier)
|
modules.add(__moduleIdentifier)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -61,7 +61,7 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => {
|
|||||||
!components.some(c => c.pascalName === component.pascalName && c.mode === 'client')
|
!components.some(c => c.pascalName === component.pascalName && c.mode === 'client')
|
||||||
if (isServerOnly) {
|
if (isServerOnly) {
|
||||||
imports.add(genImport(serverComponentRuntime, [{ name: 'createServerComponent' }]))
|
imports.add(genImport(serverComponentRuntime, [{ name: 'createServerComponent' }]))
|
||||||
imports.add(`const ${identifier} = createServerComponent(${JSON.stringify(name)})`)
|
imports.add(`const ${identifier} = createServerComponent(${JSON.stringify(component.pascalName)})`)
|
||||||
if (!options.experimentalComponentIslands) {
|
if (!options.experimentalComponentIslands) {
|
||||||
logger.warn(`Standalone server components (\`${name}\`) are not yet supported without enabling \`experimental.componentIslands\`.`)
|
logger.warn(`Standalone server components (\`${name}\`) are not yet supported without enabling \`experimental.componentIslands\`.`)
|
||||||
}
|
}
|
||||||
|
@ -121,11 +121,7 @@ export default defineNuxtModule<ComponentsOptions>({
|
|||||||
// component-names.mjs
|
// component-names.mjs
|
||||||
addTemplate(componentNamesTemplate)
|
addTemplate(componentNamesTemplate)
|
||||||
// components.islands.mjs
|
// components.islands.mjs
|
||||||
if (nuxt.options.experimental.componentIslands) {
|
addTemplate({ ...componentsIslandsTemplate, filename: 'components.islands.mjs' })
|
||||||
addTemplate({ ...componentsIslandsTemplate, filename: 'components.islands.mjs' })
|
|
||||||
} else {
|
|
||||||
addTemplate({ filename: 'components.islands.mjs', getContents: () => 'export const islandComponents = {}' })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (componentOptions.generateMetadata) {
|
if (componentOptions.generateMetadata) {
|
||||||
addTemplate(componentsMetadataTemplate)
|
addTemplate(componentsMetadataTemplate)
|
||||||
|
@ -70,7 +70,11 @@ export const componentNamesTemplate: NuxtTemplate = {
|
|||||||
|
|
||||||
export const componentsIslandsTemplate: NuxtTemplate = {
|
export const componentsIslandsTemplate: NuxtTemplate = {
|
||||||
// components.islands.mjs'
|
// components.islands.mjs'
|
||||||
getContents ({ app }) {
|
getContents ({ app, nuxt }) {
|
||||||
|
if (!nuxt.options.experimental.componentIslands) {
|
||||||
|
return 'export const islandComponents = {}'
|
||||||
|
}
|
||||||
|
|
||||||
const components = app.components
|
const components = app.components
|
||||||
const pages = app.pages
|
const pages = app.pages
|
||||||
const islands = components.filter(component =>
|
const islands = components.filter(component =>
|
||||||
|
@ -60,7 +60,7 @@ export async function generateApp (nuxt: Nuxt, app: NuxtApp, options: { filter?:
|
|||||||
|
|
||||||
async function processTemplate (template: ResolvedNuxtTemplate) {
|
async function processTemplate (template: ResolvedNuxtTemplate) {
|
||||||
const fullPath = template.dst || resolve(nuxt.options.buildDir, template.filename!)
|
const fullPath = template.dst || resolve(nuxt.options.buildDir, template.filename!)
|
||||||
const mark = performance.mark(fullPath)
|
const start = performance.now()
|
||||||
const oldContents = nuxt.vfs[fullPath]
|
const oldContents = nuxt.vfs[fullPath]
|
||||||
const contents = await compileTemplate(template, templateContext).catch((e) => {
|
const contents = await compileTemplate(template, templateContext).catch((e) => {
|
||||||
logger.error(`Could not compile template \`${template.filename}\`.`)
|
logger.error(`Could not compile template \`${template.filename}\`.`)
|
||||||
@ -83,8 +83,8 @@ export async function generateApp (nuxt: Nuxt, app: NuxtApp, options: { filter?:
|
|||||||
changedTemplates.push(template)
|
changedTemplates.push(template)
|
||||||
}
|
}
|
||||||
|
|
||||||
const perf = performance.measure(fullPath, mark.name)
|
const perf = performance.now() - start
|
||||||
const setupTime = Math.round((perf.duration * 100)) / 100
|
const setupTime = Math.round((perf * 100)) / 100
|
||||||
|
|
||||||
if (nuxt.options.debug || setupTime > 500) {
|
if (nuxt.options.debug || setupTime > 500) {
|
||||||
logger.info(`Compiled \`${template.filename}\` in ${setupTime}ms`)
|
logger.info(`Compiled \`${template.filename}\` in ${setupTime}ms`)
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import { pathToFileURL } from 'node:url'
|
|
||||||
import type { EventType } from '@parcel/watcher'
|
import type { EventType } from '@parcel/watcher'
|
||||||
import type { FSWatcher } from 'chokidar'
|
import type { FSWatcher } from 'chokidar'
|
||||||
import chokidar from 'chokidar'
|
import { watch as chokidarWatch } from 'chokidar'
|
||||||
import { isIgnored, logger, tryResolveModule, useNuxt } from '@nuxt/kit'
|
import { importModule, isIgnored, logger, tryResolveModule, useNuxt } from '@nuxt/kit'
|
||||||
import { interopDefault } from 'mlly'
|
|
||||||
import { debounce } from 'perfect-debounce'
|
import { debounce } from 'perfect-debounce'
|
||||||
import { normalize, relative, resolve } from 'pathe'
|
import { normalize, relative, resolve } from 'pathe'
|
||||||
import type { Nuxt, NuxtBuilder } from 'nuxt/schema'
|
import type { Nuxt, NuxtBuilder } from 'nuxt/schema'
|
||||||
@ -77,7 +75,7 @@ async function watch (nuxt: Nuxt) {
|
|||||||
function createWatcher () {
|
function createWatcher () {
|
||||||
const nuxt = useNuxt()
|
const nuxt = useNuxt()
|
||||||
|
|
||||||
const watcher = chokidar.watch(nuxt.options._layers.map(i => i.config.srcDir as string).filter(Boolean), {
|
const watcher = chokidarWatch(nuxt.options._layers.map(i => i.config.srcDir as string).filter(Boolean), {
|
||||||
...nuxt.options.watchers.chokidar,
|
...nuxt.options.watchers.chokidar,
|
||||||
ignoreInitial: true,
|
ignoreInitial: true,
|
||||||
ignored: [
|
ignored: [
|
||||||
@ -110,7 +108,7 @@ function createGranularWatcher () {
|
|||||||
}
|
}
|
||||||
for (const dir of pathsToWatch) {
|
for (const dir of pathsToWatch) {
|
||||||
pending++
|
pending++
|
||||||
const watcher = chokidar.watch(dir, { ...nuxt.options.watchers.chokidar, ignoreInitial: false, depth: 0, ignored: [isIgnored, '**/node_modules'] })
|
const watcher = chokidarWatch(dir, { ...nuxt.options.watchers.chokidar, ignoreInitial: false, depth: 0, ignored: [isIgnored, '**/node_modules'] })
|
||||||
const watchers: Record<string, FSWatcher> = {}
|
const watchers: Record<string, FSWatcher> = {}
|
||||||
|
|
||||||
watcher.on('all', (event, path) => {
|
watcher.on('all', (event, path) => {
|
||||||
@ -123,7 +121,7 @@ function createGranularWatcher () {
|
|||||||
delete watchers[path]
|
delete watchers[path]
|
||||||
}
|
}
|
||||||
if (event === 'addDir' && path !== dir && !ignoredDirs.has(path) && !pathsToWatch.includes(path) && !(path in watchers) && !isIgnored(path)) {
|
if (event === 'addDir' && path !== dir && !ignoredDirs.has(path) && !pathsToWatch.includes(path) && !(path in watchers) && !isIgnored(path)) {
|
||||||
watchers[path] = chokidar.watch(path, { ...nuxt.options.watchers.chokidar, ignored: [isIgnored] })
|
watchers[path] = chokidarWatch(path, { ...nuxt.options.watchers.chokidar, ignored: [isIgnored] })
|
||||||
watchers[path].on('all', (event, p) => nuxt.callHook('builder:watch', event, normalize(p)))
|
watchers[path].on('all', (event, p) => nuxt.callHook('builder:watch', event, normalize(p)))
|
||||||
nuxt.hook('close', () => watchers[path]?.close())
|
nuxt.hook('close', () => watchers[path]?.close())
|
||||||
}
|
}
|
||||||
@ -151,7 +149,7 @@ async function createParcelWatcher () {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const { subscribe } = await import(pathToFileURL(watcherPath).href).then(interopDefault) as typeof import('@parcel/watcher')
|
const { subscribe } = await importModule<typeof import('@parcel/watcher')>(watcherPath)
|
||||||
for (const layer of nuxt.options._layers) {
|
for (const layer of nuxt.options._layers) {
|
||||||
if (!layer.config.srcDir) { continue }
|
if (!layer.config.srcDir) { continue }
|
||||||
const watcher = subscribe(layer.config.srcDir, (err, events) => {
|
const watcher = subscribe(layer.config.srcDir, (err, events) => {
|
||||||
@ -201,5 +199,5 @@ async function loadBuilder (nuxt: Nuxt, builder: string): Promise<NuxtBuilder> {
|
|||||||
if (!builderPath) {
|
if (!builderPath) {
|
||||||
throw new Error(`Loading \`${builder}\` builder failed. You can read more about the nuxt \`builder\` option at: \`https://nuxt.com/docs/api/nuxt-config#builder\``)
|
throw new Error(`Loading \`${builder}\` builder failed. You can read more about the nuxt \`builder\` option at: \`https://nuxt.com/docs/api/nuxt-config#builder\``)
|
||||||
}
|
}
|
||||||
return import(pathToFileURL(builderPath).href)
|
return importModule(builderPath)
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import { createRouter as createRadixRouter, exportMatcher, toRouteMatcher } from
|
|||||||
import { joinURL, withTrailingSlash } from 'ufo'
|
import { joinURL, withTrailingSlash } from 'ufo'
|
||||||
import { build, copyPublicAssets, createDevServer, createNitro, prepare, prerender, writeTypes } from 'nitro'
|
import { build, copyPublicAssets, createDevServer, createNitro, prepare, prerender, writeTypes } from 'nitro'
|
||||||
import type { Nitro, NitroConfig, NitroOptions } from 'nitro/types'
|
import type { Nitro, NitroConfig, NitroOptions } from 'nitro/types'
|
||||||
import { findPath, logger, resolveAlias, resolveIgnorePatterns, resolveNuxtModule, resolvePath } from '@nuxt/kit'
|
import { findPath, logger, resolveAlias, resolveIgnorePatterns, resolveNuxtModule } from '@nuxt/kit'
|
||||||
import escapeRE from 'escape-string-regexp'
|
import escapeRE from 'escape-string-regexp'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { dynamicEventHandler } from 'h3'
|
import { dynamicEventHandler } from 'h3'
|
||||||
@ -112,6 +112,9 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
generateTsConfig: true,
|
generateTsConfig: true,
|
||||||
tsconfigPath: 'tsconfig.server.json',
|
tsconfigPath: 'tsconfig.server.json',
|
||||||
tsConfig: {
|
tsConfig: {
|
||||||
|
compilerOptions: {
|
||||||
|
lib: ['esnext', 'webworker', 'dom.iterable'],
|
||||||
|
},
|
||||||
include: [
|
include: [
|
||||||
join(nuxt.options.buildDir, 'types/nitro-nuxt.d.ts'),
|
join(nuxt.options.buildDir, 'types/nitro-nuxt.d.ts'),
|
||||||
...modules.map(m => join(relativeWithDot(nuxt.options.buildDir, m), 'runtime/server')),
|
...modules.map(m => join(relativeWithDot(nuxt.options.buildDir, m), 'runtime/server')),
|
||||||
@ -405,7 +408,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const cacheDir = resolve(nuxt.options.buildDir, 'cache/nitro/prerender')
|
const cacheDir = resolve(nuxt.options.buildDir, 'cache/nitro/prerender')
|
||||||
const cacheDriverPath = await resolvePath(join(distDir, 'core/runtime/nitro/cache-driver'))
|
const cacheDriverPath = join(distDir, 'core/runtime/nitro/cache-driver.js')
|
||||||
await fsp.rm(cacheDir, { recursive: true, force: true }).catch(() => {})
|
await fsp.rm(cacheDir, { recursive: true, force: true }).catch(() => {})
|
||||||
nitro.options._config.storage = defu(nitro.options._config.storage, {
|
nitro.options._config.storage = defu(nitro.options._config.storage, {
|
||||||
'internal:nuxt:prerender': {
|
'internal:nuxt:prerender': {
|
||||||
@ -500,11 +503,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
for (const route of ['/200.html', '/404.html']) {
|
for (const route of ['/200.html', '/404.html']) {
|
||||||
routes.add(route)
|
routes.add(route)
|
||||||
}
|
}
|
||||||
if (nuxt.options.ssr) {
|
if (!nuxt.options.ssr) {
|
||||||
if (nitro.options.prerender.crawlLinks) {
|
|
||||||
routes.add('/')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
routes.add('/index.html')
|
routes.add('/index.html')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { existsSync } from 'node:fs'
|
import { existsSync } from 'node:fs'
|
||||||
import { rm } from 'node:fs/promises'
|
import { rm } from 'node:fs/promises'
|
||||||
import { dirname, join, normalize, relative, resolve } from 'pathe'
|
import { join, normalize, relative, resolve } from 'pathe'
|
||||||
import { createDebugger, createHooks } from 'hookable'
|
import { createDebugger, createHooks } from 'hookable'
|
||||||
import ignore from 'ignore'
|
import ignore from 'ignore'
|
||||||
import type { LoadNuxtOptions } from '@nuxt/kit'
|
import type { LoadNuxtOptions } from '@nuxt/kit'
|
||||||
@ -8,14 +8,20 @@ import { addBuildPlugin, addComponent, addPlugin, addRouteMiddleware, addServerP
|
|||||||
import { resolvePath as _resolvePath } from 'mlly'
|
import { resolvePath as _resolvePath } from 'mlly'
|
||||||
import type { Nuxt, NuxtHooks, NuxtModule, NuxtOptions } from 'nuxt/schema'
|
import type { Nuxt, NuxtHooks, NuxtModule, NuxtOptions } from 'nuxt/schema'
|
||||||
import type { PackageJson } from 'pkg-types'
|
import type { PackageJson } from 'pkg-types'
|
||||||
import { readPackageJSON, resolvePackageJSON } from 'pkg-types'
|
import { readPackageJSON } from 'pkg-types'
|
||||||
import { hash } from 'ohash'
|
import { hash } from 'ohash'
|
||||||
|
import consola from 'consola'
|
||||||
|
import { colorize } from 'consola/utils'
|
||||||
|
import { updateConfig } from 'c12/update'
|
||||||
|
import { formatDate, resolveCompatibilityDatesFromEnv } from 'compatx'
|
||||||
|
import type { DateString } from 'compatx'
|
||||||
|
|
||||||
import escapeRE from 'escape-string-regexp'
|
import escapeRE from 'escape-string-regexp'
|
||||||
import { withTrailingSlash, withoutLeadingSlash } from 'ufo'
|
import { withTrailingSlash, withoutLeadingSlash } from 'ufo'
|
||||||
|
|
||||||
import defu from 'defu'
|
import defu from 'defu'
|
||||||
import { gt, satisfies } from 'semver'
|
import { gt, satisfies } from 'semver'
|
||||||
|
import { hasTTY, isCI } from 'std-env'
|
||||||
import pagesModule from '../pages/module'
|
import pagesModule from '../pages/module'
|
||||||
import metaModule from '../head/module'
|
import metaModule from '../head/module'
|
||||||
import componentsModule from '../components/module'
|
import componentsModule from '../components/module'
|
||||||
@ -24,6 +30,7 @@ import importsModule from '../imports/module'
|
|||||||
import { distDir, pkgDir } from '../dirs'
|
import { distDir, pkgDir } from '../dirs'
|
||||||
import { version } from '../../package.json'
|
import { version } from '../../package.json'
|
||||||
import { scriptsStubsPreset } from '../imports/presets'
|
import { scriptsStubsPreset } from '../imports/presets'
|
||||||
|
import { resolveTypePath } from './utils/types'
|
||||||
import { ImportProtectionPlugin, nuxtImportProtections } from './plugins/import-protection'
|
import { ImportProtectionPlugin, nuxtImportProtections } from './plugins/import-protection'
|
||||||
import type { UnctxTransformPluginOptions } from './plugins/unctx'
|
import type { UnctxTransformPluginOptions } from './plugins/unctx'
|
||||||
import { UnctxTransformPlugin } from './plugins/unctx'
|
import { UnctxTransformPlugin } from './plugins/unctx'
|
||||||
@ -60,6 +67,9 @@ export function createNuxt (options: NuxtOptions): Nuxt {
|
|||||||
return nuxt
|
return nuxt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: update to nitro import
|
||||||
|
const fallbackCompatibilityDate = '2024-04-03' as DateString
|
||||||
|
|
||||||
const nightlies = {
|
const nightlies = {
|
||||||
'nitropack': 'nitropack-nightly',
|
'nitropack': 'nitropack-nightly',
|
||||||
'nitro': 'nitro-nightly',
|
'nitro': 'nitro-nightly',
|
||||||
@ -74,6 +84,8 @@ const keyDependencies = [
|
|||||||
'@nuxt/schema',
|
'@nuxt/schema',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
let warnedAboutCompatDate = false
|
||||||
|
|
||||||
async function initNuxt (nuxt: Nuxt) {
|
async function initNuxt (nuxt: Nuxt) {
|
||||||
// Register user hooks
|
// Register user hooks
|
||||||
for (const config of nuxt.options._layers.map(layer => layer.config).reverse()) {
|
for (const config of nuxt.options._layers.map(layer => layer.config).reverse()) {
|
||||||
@ -82,6 +94,73 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prompt to set compatibility date
|
||||||
|
nuxt.options.compatibilityDate = resolveCompatibilityDatesFromEnv(nuxt.options.compatibilityDate)
|
||||||
|
|
||||||
|
if (!nuxt.options.compatibilityDate.default) {
|
||||||
|
const todaysDate = formatDate(new Date())
|
||||||
|
nuxt.options.compatibilityDate.default = fallbackCompatibilityDate
|
||||||
|
|
||||||
|
const shouldShowPrompt = nuxt.options.dev && hasTTY && !isCI
|
||||||
|
if (!shouldShowPrompt) {
|
||||||
|
console.log(`Using \`${fallbackCompatibilityDate}\` as fallback compatibility date.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function promptAndUpdate () {
|
||||||
|
const result = await consola.prompt(`Do you want to update your ${colorize('cyan', 'nuxt.config')} to set ${colorize('cyan', `compatibilityDate: '${todaysDate}'`)}?`, {
|
||||||
|
type: 'confirm',
|
||||||
|
default: true,
|
||||||
|
})
|
||||||
|
if (result !== true) {
|
||||||
|
console.log(`Using \`${fallbackCompatibilityDate}\` as fallback compatibility date.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await updateConfig({
|
||||||
|
configFile: 'nuxt.config',
|
||||||
|
cwd: nuxt.options.rootDir,
|
||||||
|
async onCreate ({ configFile }) {
|
||||||
|
const shallCreate = await consola.prompt(`Do you want to create ${colorize('cyan', relative(nuxt.options.rootDir, configFile))}?`, {
|
||||||
|
type: 'confirm',
|
||||||
|
default: true,
|
||||||
|
})
|
||||||
|
if (shallCreate !== true) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return _getDefaultNuxtConfig()
|
||||||
|
},
|
||||||
|
onUpdate (config) {
|
||||||
|
config.compatibilityDate = todaysDate
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res?.configFile) {
|
||||||
|
nuxt.options.compatibilityDate = resolveCompatibilityDatesFromEnv(todaysDate)
|
||||||
|
consola.success(`Compatibility date set to \`${todaysDate}\` in \`${relative(nuxt.options.rootDir, res.configFile)}\``)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const message = err instanceof Error ? err.message : err
|
||||||
|
|
||||||
|
consola.error(`Failed to update config: ${message}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Using \`${fallbackCompatibilityDate}\` as fallback compatibility date.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
nuxt.hooks.hookOnce('nitro:init', (nitro) => {
|
||||||
|
if (warnedAboutCompatDate) { return }
|
||||||
|
|
||||||
|
nitro.hooks.hookOnce('compiled', () => {
|
||||||
|
warnedAboutCompatDate = true
|
||||||
|
// Print warning
|
||||||
|
console.info(`Nuxt now supports pinning the behavior of provider and deployment presets with a compatibility date. We recommend you specify a \`compatibilityDate\` in your \`nuxt.config\` file, or set an environment variable, such as \`COMPATIBILITY_DATE=${todaysDate}\`.`)
|
||||||
|
if (shouldShowPrompt) { promptAndUpdate() }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Restart Nuxt when layer directories are added or removed
|
// Restart Nuxt when layer directories are added or removed
|
||||||
const layersDir = withTrailingSlash(resolve(nuxt.options.rootDir, 'layers'))
|
const layersDir = withTrailingSlash(resolve(nuxt.options.rootDir, 'layers'))
|
||||||
nuxt.hook('builder:watch', (event, relativePath) => {
|
nuxt.hook('builder:watch', (event, relativePath) => {
|
||||||
@ -107,29 +186,16 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
// ignore packages that exist in `package.json` as these can be resolved by TypeScript
|
// ignore packages that exist in `package.json` as these can be resolved by TypeScript
|
||||||
if (dependencies.has(_pkg) && !(_pkg in nightlies)) { return [] }
|
if (dependencies.has(_pkg) && !(_pkg in nightlies)) { return [] }
|
||||||
|
|
||||||
async function resolveTypePath (path: string) {
|
|
||||||
try {
|
|
||||||
const r = await _resolvePath(path, { url: nuxt.options.modulesDir, conditions: ['types', 'import', 'require'] })
|
|
||||||
if (subpath) {
|
|
||||||
return r.replace(/(?:\.d)?\.[mc]?[jt]s$/, '')
|
|
||||||
}
|
|
||||||
const rootPath = await resolvePackageJSON(r)
|
|
||||||
return dirname(rootPath)
|
|
||||||
} catch {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// deduplicate types for nightly releases
|
// deduplicate types for nightly releases
|
||||||
if (_pkg in nightlies) {
|
if (_pkg in nightlies) {
|
||||||
const nightly = nightlies[_pkg as keyof typeof nightlies]
|
const nightly = nightlies[_pkg as keyof typeof nightlies]
|
||||||
const path = await resolveTypePath(nightly + subpath)
|
const path = await resolveTypePath(nightly + subpath, subpath, nuxt.options.modulesDir)
|
||||||
if (path) {
|
if (path) {
|
||||||
return [[pkg, [path]], [nightly + subpath, [path]]]
|
return [[pkg, [path]], [nightly + subpath, [path]]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const path = await resolveTypePath(_pkg + subpath)
|
const path = await resolveTypePath(_pkg + subpath, subpath, nuxt.options.modulesDir)
|
||||||
if (path) {
|
if (path) {
|
||||||
return [[pkg, [path]]]
|
return [[pkg, [path]]]
|
||||||
}
|
}
|
||||||
@ -420,21 +486,6 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add <NuxtIsland>
|
|
||||||
if (nuxt.options.experimental.componentIslands) {
|
|
||||||
addComponent({
|
|
||||||
name: 'NuxtIsland',
|
|
||||||
priority: 10, // built-in that we do not expect the user to override
|
|
||||||
filePath: resolve(nuxt.options.appDir, 'components/nuxt-island'),
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!nuxt.options.ssr && nuxt.options.experimental.componentIslands !== 'auto') {
|
|
||||||
nuxt.options.ssr = true
|
|
||||||
nuxt.options.nitro.routeRules ||= {}
|
|
||||||
nuxt.options.nitro.routeRules['/**'] = defu(nuxt.options.nitro.routeRules['/**'], { ssr: false })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add stubs for <NuxtImg> and <NuxtPicture>
|
// Add stubs for <NuxtImg> and <NuxtPicture>
|
||||||
for (const name of ['NuxtImg', 'NuxtPicture']) {
|
for (const name of ['NuxtImg', 'NuxtPicture']) {
|
||||||
addComponent({
|
addComponent({
|
||||||
@ -447,38 +498,6 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add prerender payload support
|
|
||||||
if (!nuxt.options.dev && nuxt.options.experimental.payloadExtraction) {
|
|
||||||
addPlugin(resolve(nuxt.options.appDir, 'plugins/payload.client'))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add experimental cross-origin prefetch support using Speculation Rules API
|
|
||||||
if (nuxt.options.experimental.crossOriginPrefetch) {
|
|
||||||
addPlugin(resolve(nuxt.options.appDir, 'plugins/cross-origin-prefetch.client'))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add experimental page reload support
|
|
||||||
if (nuxt.options.experimental.emitRouteChunkError === 'automatic') {
|
|
||||||
addPlugin(resolve(nuxt.options.appDir, 'plugins/chunk-reload.client'))
|
|
||||||
}
|
|
||||||
// Add experimental session restoration support
|
|
||||||
if (nuxt.options.experimental.restoreState) {
|
|
||||||
addPlugin(resolve(nuxt.options.appDir, 'plugins/restore-state.client'))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add experimental automatic view transition api support
|
|
||||||
if (nuxt.options.experimental.viewTransition) {
|
|
||||||
addPlugin(resolve(nuxt.options.appDir, 'plugins/view-transitions.client'))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add experimental support for custom types in JSON payload
|
|
||||||
if (nuxt.options.experimental.renderJsonPayloads) {
|
|
||||||
nuxt.hooks.hook('modules:done', () => {
|
|
||||||
addPlugin(resolve(nuxt.options.appDir, 'plugins/revive-payload.client'))
|
|
||||||
addPlugin(resolve(nuxt.options.appDir, 'plugins/revive-payload.server'))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Track components used to render for webpack
|
// Track components used to render for webpack
|
||||||
if (nuxt.options.builder === '@nuxt/webpack-builder') {
|
if (nuxt.options.builder === '@nuxt/webpack-builder') {
|
||||||
addPlugin(resolve(nuxt.options.appDir, 'plugins/preload.server'))
|
addPlugin(resolve(nuxt.options.appDir, 'plugins/preload.server'))
|
||||||
@ -522,6 +541,51 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
|
|
||||||
await nuxt.callHook('modules:done')
|
await nuxt.callHook('modules:done')
|
||||||
|
|
||||||
|
// Add <NuxtIsland>
|
||||||
|
if (nuxt.options.experimental.componentIslands) {
|
||||||
|
addComponent({
|
||||||
|
name: 'NuxtIsland',
|
||||||
|
priority: 10, // built-in that we do not expect the user to override
|
||||||
|
filePath: resolve(nuxt.options.appDir, 'components/nuxt-island'),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!nuxt.options.ssr && nuxt.options.experimental.componentIslands !== 'auto') {
|
||||||
|
nuxt.options.ssr = true
|
||||||
|
nuxt.options.nitro.routeRules ||= {}
|
||||||
|
nuxt.options.nitro.routeRules['/**'] = defu(nuxt.options.nitro.routeRules['/**'], { ssr: false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add prerender payload support
|
||||||
|
if (!nuxt.options.dev && nuxt.options.experimental.payloadExtraction) {
|
||||||
|
addPlugin(resolve(nuxt.options.appDir, 'plugins/payload.client'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add experimental cross-origin prefetch support using Speculation Rules API
|
||||||
|
if (nuxt.options.experimental.crossOriginPrefetch) {
|
||||||
|
addPlugin(resolve(nuxt.options.appDir, 'plugins/cross-origin-prefetch.client'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add experimental page reload support
|
||||||
|
if (nuxt.options.experimental.emitRouteChunkError === 'automatic') {
|
||||||
|
addPlugin(resolve(nuxt.options.appDir, 'plugins/chunk-reload.client'))
|
||||||
|
}
|
||||||
|
// Add experimental session restoration support
|
||||||
|
if (nuxt.options.experimental.restoreState) {
|
||||||
|
addPlugin(resolve(nuxt.options.appDir, 'plugins/restore-state.client'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add experimental automatic view transition api support
|
||||||
|
if (nuxt.options.experimental.viewTransition) {
|
||||||
|
addPlugin(resolve(nuxt.options.appDir, 'plugins/view-transitions.client'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add experimental support for custom types in JSON payload
|
||||||
|
if (nuxt.options.experimental.renderJsonPayloads) {
|
||||||
|
addPlugin(resolve(nuxt.options.appDir, 'plugins/revive-payload.client'))
|
||||||
|
addPlugin(resolve(nuxt.options.appDir, 'plugins/revive-payload.server'))
|
||||||
|
}
|
||||||
|
|
||||||
if (nuxt.options.experimental.appManifest) {
|
if (nuxt.options.experimental.appManifest) {
|
||||||
addRouteMiddleware({
|
addRouteMiddleware({
|
||||||
name: 'manifest-route-rule',
|
name: 'manifest-route-rule',
|
||||||
@ -611,7 +675,7 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
|
|||||||
|
|
||||||
// Temporary until finding better placement for each
|
// Temporary until finding better placement for each
|
||||||
options.appDir = options.alias['#app'] = resolve(distDir, 'app')
|
options.appDir = options.alias['#app'] = resolve(distDir, 'app')
|
||||||
options._majorVersion = 3
|
options._majorVersion = 4
|
||||||
|
|
||||||
// De-duplicate key arrays
|
// De-duplicate key arrays
|
||||||
for (const key in options.app.head || {}) {
|
for (const key in options.app.head || {}) {
|
||||||
@ -762,3 +826,10 @@ function createPortalProperties (sourceValue: any, options: NuxtOptions, paths:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _getDefaultNuxtConfig = () => /* js */
|
||||||
|
`// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
|
export default defineNuxtConfig({
|
||||||
|
devtools: { enabled: true }
|
||||||
|
})
|
||||||
|
`
|
||||||
|
@ -1,10 +1,18 @@
|
|||||||
|
// @ts-check
|
||||||
|
|
||||||
import { defineDriver } from 'unstorage'
|
import { defineDriver } from 'unstorage'
|
||||||
import fsDriver from 'unstorage/drivers/fs-lite'
|
import fsDriver from 'unstorage/drivers/fs-lite'
|
||||||
import lruCache from 'unstorage/drivers/lru-cache'
|
import lruCache from 'unstorage/drivers/lru-cache'
|
||||||
|
|
||||||
const normalizeFsKey = (item: string) => item.replaceAll(':', '_')
|
/**
|
||||||
|
* @param {string} item
|
||||||
|
*/
|
||||||
|
const normalizeFsKey = item => item.replaceAll(':', '_')
|
||||||
|
|
||||||
export default defineDriver((opts: { base: string }) => {
|
/**
|
||||||
|
* @param {{ base: string }} opts
|
||||||
|
*/
|
||||||
|
export default defineDriver((opts) => {
|
||||||
const fs = fsDriver({ base: opts.base })
|
const fs = fsDriver({ base: opts.base })
|
||||||
const lru = lruCache({ max: 1000 })
|
const lru = lruCache({ max: 1000 })
|
||||||
|
|
@ -311,8 +311,6 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
|||||||
payload: (ssrError ? { error: ssrError } : {}) as NuxtPayload,
|
payload: (ssrError ? { error: ssrError } : {}) as NuxtPayload,
|
||||||
_payloadReducers: {},
|
_payloadReducers: {},
|
||||||
modules: new Set(),
|
modules: new Set(),
|
||||||
set _registeredComponents (value) { this.modules = value },
|
|
||||||
get _registeredComponents () { return this.modules },
|
|
||||||
islandContext,
|
islandContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,7 +384,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
|||||||
// Setup head
|
// Setup head
|
||||||
const { styles, scripts } = getRequestDependencies(ssrContext, renderer.rendererContext)
|
const { styles, scripts } = getRequestDependencies(ssrContext, renderer.rendererContext)
|
||||||
// 1.Extracted payload preloading
|
// 1.Extracted payload preloading
|
||||||
if (_PAYLOAD_EXTRACTION && !isRenderingIsland) {
|
if (_PAYLOAD_EXTRACTION && !NO_SCRIPTS && !isRenderingIsland) {
|
||||||
head.push({
|
head.push({
|
||||||
link: [
|
link: [
|
||||||
process.env.NUXT_JSON_PAYLOADS
|
process.env.NUXT_JSON_PAYLOADS
|
||||||
@ -461,23 +459,6 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
|||||||
// remove certain tags for nuxt islands
|
// remove certain tags for nuxt islands
|
||||||
const { headTags, bodyTags, bodyTagsOpen, htmlAttrs, bodyAttrs } = await renderSSRHead(head, renderSSRHeadOptions)
|
const { headTags, bodyTags, bodyTagsOpen, htmlAttrs, bodyAttrs } = await renderSSRHead(head, renderSSRHeadOptions)
|
||||||
|
|
||||||
// Create render context
|
|
||||||
const htmlContext: NuxtRenderHTMLContext = {
|
|
||||||
island: isRenderingIsland,
|
|
||||||
htmlAttrs: htmlAttrs ? [htmlAttrs] : [],
|
|
||||||
head: normalizeChunks([headTags]),
|
|
||||||
bodyAttrs: bodyAttrs ? [bodyAttrs] : [],
|
|
||||||
bodyPrepend: normalizeChunks([bodyTagsOpen, ssrContext.teleports?.body]),
|
|
||||||
body: [
|
|
||||||
componentIslands ? replaceIslandTeleports(ssrContext, _rendered.html) : _rendered.html,
|
|
||||||
APP_TELEPORT_OPEN_TAG + (HAS_APP_TELEPORTS ? joinTags([ssrContext.teleports?.[`#${appTeleportAttrs.id}`]]) : '') + APP_TELEPORT_CLOSE_TAG,
|
|
||||||
],
|
|
||||||
bodyAppend: [bodyTags],
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow hooking into the rendered result
|
|
||||||
await nitroApp.hooks.callHook('render:html', htmlContext, { event })
|
|
||||||
|
|
||||||
// Response for component islands
|
// Response for component islands
|
||||||
if (isRenderingIsland && islandContext) {
|
if (isRenderingIsland && islandContext) {
|
||||||
const islandHead: NuxtIslandResponse['head'] = {
|
const islandHead: NuxtIslandResponse['head'] = {
|
||||||
@ -494,7 +475,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
|||||||
const islandResponse: NuxtIslandResponse = {
|
const islandResponse: NuxtIslandResponse = {
|
||||||
id: islandContext.id,
|
id: islandContext.id,
|
||||||
head: islandHead,
|
head: islandHead,
|
||||||
html: getServerComponentHTML(htmlContext.body),
|
html: getServerComponentHTML(_rendered.html),
|
||||||
components: getClientIslandResponse(ssrContext),
|
components: getClientIslandResponse(ssrContext),
|
||||||
slots: getSlotIslandResponse(ssrContext),
|
slots: getSlotIslandResponse(ssrContext),
|
||||||
}
|
}
|
||||||
@ -517,6 +498,23 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
|||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create render context
|
||||||
|
const htmlContext: NuxtRenderHTMLContext = {
|
||||||
|
island: isRenderingIsland,
|
||||||
|
htmlAttrs: htmlAttrs ? [htmlAttrs] : [],
|
||||||
|
head: normalizeChunks([headTags]),
|
||||||
|
bodyAttrs: bodyAttrs ? [bodyAttrs] : [],
|
||||||
|
bodyPrepend: normalizeChunks([bodyTagsOpen, ssrContext.teleports?.body]),
|
||||||
|
body: [
|
||||||
|
componentIslands ? replaceIslandTeleports(ssrContext, _rendered.html) : _rendered.html,
|
||||||
|
APP_TELEPORT_OPEN_TAG + (HAS_APP_TELEPORTS ? joinTags([ssrContext.teleports?.[`#${appTeleportAttrs.id}`]]) : '') + APP_TELEPORT_CLOSE_TAG,
|
||||||
|
],
|
||||||
|
bodyAppend: [bodyTags],
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow hooking into the rendered result
|
||||||
|
await nitroApp.hooks.callHook('render:html', htmlContext, { event })
|
||||||
|
|
||||||
// Construct HTML response
|
// Construct HTML response
|
||||||
const response = {
|
const response = {
|
||||||
body: renderHTMLDocument(htmlContext),
|
body: renderHTMLDocument(htmlContext),
|
||||||
@ -637,8 +635,8 @@ function splitPayload (ssrContext: NuxtSSRContext) {
|
|||||||
/**
|
/**
|
||||||
* remove the root node from the html body
|
* remove the root node from the html body
|
||||||
*/
|
*/
|
||||||
function getServerComponentHTML (body: string[]): string {
|
function getServerComponentHTML (body: string): string {
|
||||||
const match = body[0].match(ROOT_NODE_REGEX)
|
const match = body.match(ROOT_NODE_REGEX)
|
||||||
return match ? match[1] : body[0]
|
return match ? match[1] : body[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
import { existsSync } from 'node:fs'
|
import { existsSync } from 'node:fs'
|
||||||
import { mkdir, writeFile } from 'node:fs/promises'
|
import { mkdir, writeFile } from 'node:fs/promises'
|
||||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
import { dirname, resolve } from 'pathe'
|
import { resolve } from 'pathe'
|
||||||
import chokidar from 'chokidar'
|
import { watch } from 'chokidar'
|
||||||
import { interopDefault } from 'mlly'
|
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { debounce } from 'perfect-debounce'
|
import { debounce } from 'perfect-debounce'
|
||||||
import { createResolver, defineNuxtModule, logger, tryResolveModule } from '@nuxt/kit'
|
import { createResolver, defineNuxtModule, importModule, logger, tryResolveModule } from '@nuxt/kit'
|
||||||
import {
|
import {
|
||||||
generateTypes,
|
generateTypes,
|
||||||
resolveSchema as resolveUntypedSchema,
|
resolveSchema as resolveUntypedSchema,
|
||||||
} from 'untyped'
|
} from 'untyped'
|
||||||
import type { Schema, SchemaDefinition } from 'untyped'
|
import type { Schema, SchemaDefinition } from 'untyped'
|
||||||
import untypedPlugin from 'untyped/babel-plugin'
|
import untypedPlugin from 'untyped/babel-plugin'
|
||||||
import jiti from 'jiti'
|
import { createJiti } from 'jiti'
|
||||||
|
|
||||||
export default defineNuxtModule({
|
export default defineNuxtModule({
|
||||||
meta: {
|
meta: {
|
||||||
@ -23,11 +22,9 @@ export default defineNuxtModule({
|
|||||||
const resolver = createResolver(import.meta.url)
|
const resolver = createResolver(import.meta.url)
|
||||||
|
|
||||||
// Initialize untyped/jiti loader
|
// Initialize untyped/jiti loader
|
||||||
const _resolveSchema = jiti(dirname(fileURLToPath(import.meta.url)), {
|
const _resolveSchema = createJiti(fileURLToPath(import.meta.url), {
|
||||||
esmResolve: true,
|
|
||||||
interopDefault: true,
|
interopDefault: true,
|
||||||
cache: false,
|
cache: false,
|
||||||
requireCache: false,
|
|
||||||
transformOptions: {
|
transformOptions: {
|
||||||
babel: {
|
babel: {
|
||||||
plugins: [untypedPlugin],
|
plugins: [untypedPlugin],
|
||||||
@ -62,7 +59,7 @@ export default defineNuxtModule({
|
|||||||
if (nuxt.options.experimental.watcher === 'parcel') {
|
if (nuxt.options.experimental.watcher === 'parcel') {
|
||||||
const watcherPath = await tryResolveModule('@parcel/watcher', [nuxt.options.rootDir, ...nuxt.options.modulesDir])
|
const watcherPath = await tryResolveModule('@parcel/watcher', [nuxt.options.rootDir, ...nuxt.options.modulesDir])
|
||||||
if (watcherPath) {
|
if (watcherPath) {
|
||||||
const { subscribe } = await import(pathToFileURL(watcherPath).href).then(interopDefault) as typeof import('@parcel/watcher')
|
const { subscribe } = await importModule<typeof import('@parcel/watcher')>(watcherPath)
|
||||||
for (const layer of nuxt.options._layers) {
|
for (const layer of nuxt.options._layers) {
|
||||||
const subscription = await subscribe(layer.config.rootDir, onChange, {
|
const subscription = await subscribe(layer.config.rootDir, onChange, {
|
||||||
ignore: ['!nuxt.schema.*'],
|
ignore: ['!nuxt.schema.*'],
|
||||||
@ -77,7 +74,7 @@ export default defineNuxtModule({
|
|||||||
const filesToWatch = await Promise.all(nuxt.options._layers.map(layer =>
|
const filesToWatch = await Promise.all(nuxt.options._layers.map(layer =>
|
||||||
resolver.resolve(layer.config.rootDir, 'nuxt.schema.*'),
|
resolver.resolve(layer.config.rootDir, 'nuxt.schema.*'),
|
||||||
))
|
))
|
||||||
const watcher = chokidar.watch(filesToWatch, {
|
const watcher = watch(filesToWatch, {
|
||||||
...nuxt.options.watchers.chokidar,
|
...nuxt.options.watchers.chokidar,
|
||||||
ignoreInitial: true,
|
ignoreInitial: true,
|
||||||
})
|
})
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
export * from './names'
|
export { getNameFromPath, hasSuffix, resolveComponentNameSegments } from './names'
|
||||||
export * from './plugins'
|
export { isJS, isVue } from './plugins'
|
||||||
|
|
||||||
export function uniqueBy<T, K extends keyof T> (arr: T[], key: K) {
|
export function uniqueBy<T, K extends keyof T> (arr: T[], key: K) {
|
||||||
if (arr.length < 2) {
|
if (arr.length < 2) {
|
||||||
|
17
packages/nuxt/src/core/utils/types.ts
Normal file
17
packages/nuxt/src/core/utils/types.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { resolvePackageJSON } from 'pkg-types'
|
||||||
|
import { resolvePath as _resolvePath } from 'mlly'
|
||||||
|
import { dirname } from 'pathe'
|
||||||
|
import { tryUseNuxt } from '@nuxt/kit'
|
||||||
|
|
||||||
|
export async function resolveTypePath (path: string, subpath: string, searchPaths = tryUseNuxt()?.options.modulesDir) {
|
||||||
|
try {
|
||||||
|
const r = await _resolvePath(path, { url: searchPaths, conditions: ['types', 'import', 'require'] })
|
||||||
|
if (subpath) {
|
||||||
|
return r.replace(/(?:\.d)?\.[mc]?[jt]s$/, '')
|
||||||
|
}
|
||||||
|
const rootPath = await resolvePackageJSON(r)
|
||||||
|
return dirname(rootPath)
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ const components = ['NoScript', 'Link', 'Base', 'Title', 'Meta', 'Style', 'Head'
|
|||||||
export default defineNuxtModule<NuxtOptions['unhead']>({
|
export default defineNuxtModule<NuxtOptions['unhead']>({
|
||||||
meta: {
|
meta: {
|
||||||
name: 'meta',
|
name: 'meta',
|
||||||
|
configKey: 'unhead',
|
||||||
},
|
},
|
||||||
async setup (options, nuxt) {
|
async setup (options, nuxt) {
|
||||||
const runtimeDir = resolve(distDir, 'head/runtime')
|
const runtimeDir = resolve(distDir, 'head/runtime')
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
export * from './core/nuxt'
|
export { createNuxt, loadNuxt } from './core/nuxt'
|
||||||
export * from './core/builder'
|
export { build } from './core/builder'
|
||||||
|
@ -3,6 +3,7 @@ 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, addVitePlugin, addWebpackPlugin, 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 type { Nuxt, NuxtApp, NuxtPage } from 'nuxt/schema'
|
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'
|
||||||
@ -11,12 +12,15 @@ import type { EditableTreeNode, Options as TypedRouterOptions } from 'unplugin-v
|
|||||||
import type { NitroRouteConfig } from 'nitro/types'
|
import type { NitroRouteConfig } from 'nitro/types'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { distDir } from '../dirs'
|
import { distDir } from '../dirs'
|
||||||
|
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 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'
|
||||||
|
|
||||||
|
const OPTIONAL_PARAM_RE = /^\/?:.*(?:\?|\(\.\*\)\*)$/
|
||||||
|
|
||||||
export default defineNuxtModule({
|
export default defineNuxtModule({
|
||||||
meta: {
|
meta: {
|
||||||
name: 'pages',
|
name: 'pages',
|
||||||
@ -28,6 +32,15 @@ export default defineNuxtModule({
|
|||||||
layer => resolve(layer.config.srcDir, (layer.config.rootDir === nuxt.options.rootDir ? nuxt.options : layer.config).dir?.pages || 'pages'),
|
layer => resolve(layer.config.srcDir, (layer.config.rootDir === nuxt.options.rootDir ? nuxt.options : layer.config).dir?.pages || 'pages'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
nuxt.options.alias['#vue-router'] = 'vue-router'
|
||||||
|
const routerPath = await resolveTypePath('vue-router', '', nuxt.options.modulesDir) || 'vue-router'
|
||||||
|
nuxt.hook('prepare:types', ({ tsConfig }) => {
|
||||||
|
tsConfig.compilerOptions ||= {}
|
||||||
|
tsConfig.compilerOptions.paths ||= {}
|
||||||
|
tsConfig.compilerOptions.paths['#vue-router'] = [routerPath]
|
||||||
|
delete tsConfig.compilerOptions.paths['#vue-router/*']
|
||||||
|
})
|
||||||
|
|
||||||
async function resolveRouterOptions () {
|
async function resolveRouterOptions () {
|
||||||
const context = {
|
const context = {
|
||||||
files: [] as Array<{ path: string, optional?: boolean }>,
|
files: [] as Array<{ path: string, optional?: boolean }>,
|
||||||
@ -210,6 +223,14 @@ export default defineNuxtModule({
|
|||||||
references.push({ types: useExperimentalTypedPages ? 'vue-router/auto-routes' : 'vue-router' })
|
references.push({ types: useExperimentalTypedPages ? 'vue-router/auto-routes' : 'vue-router' })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Add vue-router route guard imports
|
||||||
|
nuxt.hook('imports:sources', (sources) => {
|
||||||
|
const routerImports = sources.find(s => s.from === '#app/composables/router' && s.imports.includes('onBeforeRouteLeave'))
|
||||||
|
if (routerImports) {
|
||||||
|
routerImports.from = 'vue-router'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Regenerate templates when adding or removing pages
|
// Regenerate templates when adding or removing pages
|
||||||
const updateTemplatePaths = nuxt.options._layers.flatMap((l) => {
|
const updateTemplatePaths = nuxt.options._layers.flatMap((l) => {
|
||||||
const dir = (l.config.rootDir === nuxt.options.rootDir ? nuxt.options : l.config).dir
|
const dir = (l.config.rootDir === nuxt.options.rootDir ? nuxt.options : l.config).dir
|
||||||
@ -259,6 +280,59 @@ export default defineNuxtModule({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Record all pages for use in prerendering
|
||||||
|
const prerenderRoutes = new Set<string>()
|
||||||
|
|
||||||
|
function processPages (pages: NuxtPage[], currentPath = '/') {
|
||||||
|
for (const page of pages) {
|
||||||
|
// Add root of optional dynamic paths and catchalls
|
||||||
|
if (OPTIONAL_PARAM_RE.test(page.path) && !page.children?.length) {
|
||||||
|
prerenderRoutes.add(currentPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip dynamic paths
|
||||||
|
if (page.path.includes(':')) { continue }
|
||||||
|
|
||||||
|
const route = joinURL(currentPath, page.path)
|
||||||
|
prerenderRoutes.add(route)
|
||||||
|
|
||||||
|
if (page.children) {
|
||||||
|
processPages(page.children, route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nuxt.hook('pages:extend', (pages) => {
|
||||||
|
if (nuxt.options.dev) { return }
|
||||||
|
|
||||||
|
prerenderRoutes.clear()
|
||||||
|
processPages(pages)
|
||||||
|
})
|
||||||
|
|
||||||
|
// For static sites with ssr: false with crawl, prerender all routes
|
||||||
|
nuxt.hook('nitro:init', (nitro) => {
|
||||||
|
if (nuxt.options.dev || !nitro.options.static || nuxt.options.router.options.hashMode || !nitro.options.prerender.crawlLinks) { return }
|
||||||
|
|
||||||
|
// Only hint the first route when `ssr: true` and no routes are provided
|
||||||
|
if (nuxt.options.ssr) {
|
||||||
|
nitro.hooks.hook('prerender:routes', (routes) => {
|
||||||
|
if ([...routes].every(r => r.match(/(^\/api|\.\w+)/))) {
|
||||||
|
const [firstPage] = [...prerenderRoutes].sort()
|
||||||
|
routes.add(firstPage || '/')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prerender all non-dynamic page routes when generating `ssr: false` app
|
||||||
|
nuxt.hook('nitro:build:before', (nitro) => {
|
||||||
|
for (const route of nitro.options.prerender.routes || []) {
|
||||||
|
prerenderRoutes.add(route)
|
||||||
|
}
|
||||||
|
nitro.options.prerender.routes = Array.from(prerenderRoutes)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
nuxt.hook('imports:extend', (imports) => {
|
nuxt.hook('imports:extend', (imports) => {
|
||||||
imports.push(
|
imports.push(
|
||||||
{ name: 'definePageMeta', as: 'definePageMeta', from: resolve(runtimeDir, 'composables') },
|
{ name: 'definePageMeta', as: 'definePageMeta', from: resolve(runtimeDir, 'composables') },
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export * from './composables'
|
export { definePageMeta, defineRouteRules } from './composables'
|
||||||
|
export type { PageMeta } from './composables'
|
||||||
|
@ -149,7 +149,7 @@ export async function augmentPages (routes: NuxtPage[], vfs: Record<string, stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (route.children && route.children.length > 0) {
|
if (route.children && route.children.length > 0) {
|
||||||
await augmentPages(route.children, vfs)
|
await augmentPages(route.children, vfs, augmentedPages)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return augmentedPages
|
return augmentedPages
|
||||||
|
Binary file not shown.
@ -59,6 +59,10 @@ describe('resolveApp', () => {
|
|||||||
"mode": "client",
|
"mode": "client",
|
||||||
"src": "<repoRoot>/packages/nuxt/src/app/plugins/revive-payload.client.ts",
|
"src": "<repoRoot>/packages/nuxt/src/app/plugins/revive-payload.client.ts",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"mode": "client",
|
||||||
|
"src": "<repoRoot>/packages/nuxt/src/app/plugins/chunk-reload.client.ts",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"filename": "components.plugin.mjs",
|
"filename": "components.plugin.mjs",
|
||||||
"getContents": [Function],
|
"getContents": [Function],
|
||||||
@ -73,10 +77,6 @@ describe('resolveApp', () => {
|
|||||||
"mode": "all",
|
"mode": "all",
|
||||||
"src": "<repoRoot>/packages/nuxt/src/app/plugins/router.ts",
|
"src": "<repoRoot>/packages/nuxt/src/app/plugins/router.ts",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"mode": "client",
|
|
||||||
"src": "<repoRoot>/packages/nuxt/src/app/plugins/chunk-reload.client.ts",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
"rootComponent": "<repoRoot>/packages/nuxt/src/app/components/nuxt-root.vue",
|
"rootComponent": "<repoRoot>/packages/nuxt/src/app/components/nuxt-root.vue",
|
||||||
"templates": [],
|
"templates": [],
|
||||||
|
@ -67,10 +67,19 @@ const treeshake = async (source: string): Promise<string> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function SFCCompile (name: string, source: string, options: Options, ssr = false): Promise<string> {
|
async function SFCCompile (name: string, source: string, options: Options, ssr = false): Promise<string> {
|
||||||
const result = await (vuePlugin({
|
const plugin = vuePlugin({
|
||||||
compiler: VueCompilerSFC,
|
compiler: VueCompilerSFC,
|
||||||
...options,
|
...options,
|
||||||
}).transform! as Function).call({
|
})
|
||||||
|
// @ts-expect-error Types are not correct as they are too generic
|
||||||
|
plugin.configResolved!({
|
||||||
|
isProduction: options.isProduction,
|
||||||
|
command: 'build',
|
||||||
|
root: process.cwd(),
|
||||||
|
build: { sourcemap: false },
|
||||||
|
define: {},
|
||||||
|
})
|
||||||
|
const result = await (plugin.transform! as Function).call({
|
||||||
parse: (code: string, opts: any = {}) => Parser.parse(code, {
|
parse: (code: string, opts: any = {}) => Parser.parse(code, {
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
ecmaVersion: 'latest',
|
ecmaVersion: 'latest',
|
||||||
@ -84,14 +93,16 @@ async function SFCCompile (name: string, source: string, options: Options, ssr =
|
|||||||
return typeof result === 'string' ? result : result?.code
|
return typeof result === 'string' ? result : result?.code
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateToTest: { name: string, options: Partial<Options & { devServer: { config: { server: any } } }> }[] = [
|
const stateToTest: { index: number, name: string, options: Partial<Options & { devServer: { config: { server: any } } }> }[] = [
|
||||||
{
|
{
|
||||||
|
index: 0,
|
||||||
name: 'prod',
|
name: 'prod',
|
||||||
options: {
|
options: {
|
||||||
isProduction: true,
|
isProduction: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
index: 1,
|
||||||
name: 'dev',
|
name: 'dev',
|
||||||
options: {
|
options: {
|
||||||
isProduction: false,
|
isProduction: false,
|
||||||
@ -107,93 +118,91 @@ const stateToTest: { name: string, options: Partial<Options & { devServer: { con
|
|||||||
|
|
||||||
describe('treeshake client only in ssr', () => {
|
describe('treeshake client only in ssr', () => {
|
||||||
vi.spyOn(process, 'cwd').mockImplementation(() => '')
|
vi.spyOn(process, 'cwd').mockImplementation(() => '')
|
||||||
for (const [index, state] of stateToTest.entries()) {
|
it.each(stateToTest)(`should treeshake ClientOnly correctly in $name`, async (state) => {
|
||||||
it(`should treeshake ClientOnly correctly in ${state.name}`, async () => {
|
// add index to avoid using vite vue plugin cache
|
||||||
// add index to avoid using vite vue plugin cache
|
const clientResult = await SFCCompile(`SomeComponent${state.index}.vue`, WithClientOnly, state.options)
|
||||||
const clientResult = await SFCCompile(`SomeComponent${index}.vue`, WithClientOnly, state.options)
|
|
||||||
|
|
||||||
const ssrResult = await SFCCompile(`SomeComponent${index}.vue`, WithClientOnly, state.options, true)
|
const ssrResult = await SFCCompile(`SomeComponent${state.index}.vue`, WithClientOnly, state.options, true)
|
||||||
|
|
||||||
const treeshaken = await treeshake(ssrResult)
|
const treeshaken = await treeshake(ssrResult)
|
||||||
const [_, scopeId] = clientResult.match(/_pushScopeId\("(.*)"\)/)!
|
const [_, scopeId] = clientResult.match(/_pushScopeId\("(.*)"\)/)!
|
||||||
|
|
||||||
// ensure the id is correctly passed between server and client
|
// ensure the id is correctly passed between server and client
|
||||||
expect(clientResult).toContain(`pushScopeId("${scopeId}")`)
|
expect(clientResult).toContain(`pushScopeId("${scopeId}")`)
|
||||||
expect(treeshaken).toContain(`<div ${scopeId}>`)
|
expect(treeshaken).toContain(`<div ${scopeId}>`)
|
||||||
|
|
||||||
expect(clientResult).toContain('should-be-treeshaken')
|
expect(clientResult).toContain('should-be-treeshaken')
|
||||||
expect(treeshaken).not.toContain('should-be-treeshaken')
|
expect(treeshaken).not.toContain('should-be-treeshaken')
|
||||||
|
|
||||||
expect(treeshaken).not.toContain('import HelloWorld from \'../HelloWorld.vue\'')
|
expect(treeshaken).not.toContain('import HelloWorld from \'../HelloWorld.vue\'')
|
||||||
expect(clientResult).toContain('import HelloWorld from \'../HelloWorld.vue\'')
|
expect(clientResult).toContain('import HelloWorld from \'../HelloWorld.vue\'')
|
||||||
|
|
||||||
expect(treeshaken).not.toContain('import { Treeshaken } from \'somepath\'')
|
expect(treeshaken).not.toContain('import { Treeshaken } from \'somepath\'')
|
||||||
expect(clientResult).toContain('import { Treeshaken } from \'somepath\'')
|
expect(clientResult).toContain('import { Treeshaken } from \'somepath\'')
|
||||||
|
|
||||||
// remove resolved import
|
// remove resolved import
|
||||||
expect(treeshaken).not.toContain('const _component_ResolvedImport =')
|
expect(treeshaken).not.toContain('const _component_ResolvedImport =')
|
||||||
expect(clientResult).toContain('const _component_ResolvedImport =')
|
expect(clientResult).toContain('const _component_ResolvedImport =')
|
||||||
|
|
||||||
// treeshake multi line variable declaration
|
// treeshake multi line variable declaration
|
||||||
expect(clientResult).toContain('const SomeIsland = defineAsyncComponent(async () => {')
|
expect(clientResult).toContain('const SomeIsland = defineAsyncComponent(async () => {')
|
||||||
expect(treeshaken).not.toContain('const SomeIsland = defineAsyncComponent(async () => {')
|
expect(treeshaken).not.toContain('const SomeIsland = defineAsyncComponent(async () => {')
|
||||||
expect(treeshaken).not.toContain('return (await import(\'./../some.island.vue\'))')
|
expect(treeshaken).not.toContain('return (await import(\'./../some.island.vue\'))')
|
||||||
expect(treeshaken).toContain('const NotToBeTreeShaken = defineAsyncComponent(async () => {')
|
expect(treeshaken).toContain('const NotToBeTreeShaken = defineAsyncComponent(async () => {')
|
||||||
|
|
||||||
// treeshake object and array declaration
|
// treeshake object and array declaration
|
||||||
expect(treeshaken).not.toContain('const { ObjectPattern } = await import(\'nuxt.com\')')
|
expect(treeshaken).not.toContain('const { ObjectPattern } = await import(\'nuxt.com\')')
|
||||||
expect(treeshaken).not.toContain('const { ObjectPattern: ObjectPatternDeclaration } = await import(\'nuxt.com\')')
|
expect(treeshaken).not.toContain('const { ObjectPattern: ObjectPatternDeclaration } = await import(\'nuxt.com\')')
|
||||||
expect(treeshaken).toContain('const { ButShouldNotBeTreeShaken } = defineAsyncComponent(async () => {')
|
expect(treeshaken).toContain('const { ButShouldNotBeTreeShaken } = defineAsyncComponent(async () => {')
|
||||||
expect(treeshaken).toContain('const [ { Dont, }, That] = defineAsyncComponent(async () => {')
|
expect(treeshaken).toContain('const [ { Dont, }, That] = defineAsyncComponent(async () => {')
|
||||||
|
|
||||||
// treeshake object that has an assignment pattern
|
// treeshake object that has an assignment pattern
|
||||||
expect(treeshaken).toContain('const { woooooo, } = defineAsyncComponent(async () => {')
|
expect(treeshaken).toContain('const { woooooo, } = defineAsyncComponent(async () => {')
|
||||||
expect(treeshaken).not.toContain('const { Deep, assignment: { Pattern = ofComponent } } = defineAsyncComponent(async () => {')
|
expect(treeshaken).not.toContain('const { Deep, assignment: { Pattern = ofComponent } } = defineAsyncComponent(async () => {')
|
||||||
|
|
||||||
// expect no empty ObjectPattern on treeshaking
|
// expect no empty ObjectPattern on treeshaking
|
||||||
expect(treeshaken).not.toContain('const { } = defineAsyncComponent')
|
expect(treeshaken).not.toContain('const { } = defineAsyncComponent')
|
||||||
expect(treeshaken).not.toContain('import { } from')
|
expect(treeshaken).not.toContain('import { } from')
|
||||||
|
|
||||||
// expect components used in setup to not be removed
|
// expect components used in setup to not be removed
|
||||||
expect(treeshaken).toContain('import DontRemoveThisSinceItIsUsedInSetup from \'./ComponentWithProps.vue\'')
|
expect(treeshaken).toContain('import DontRemoveThisSinceItIsUsedInSetup from \'./ComponentWithProps.vue\'')
|
||||||
|
|
||||||
// expect import of ClientImport to be treeshaken but not Glob since it is also used outside <ClientOnly>
|
// expect import of ClientImport to be treeshaken but not Glob since it is also used outside <ClientOnly>
|
||||||
expect(treeshaken).not.toContain('ClientImport')
|
expect(treeshaken).not.toContain('ClientImport')
|
||||||
expect(treeshaken).toContain('import { Glob } from \'#components\'')
|
expect(treeshaken).toContain('import { Glob } from \'#components\'')
|
||||||
|
|
||||||
// treeshake .client slot
|
// treeshake .client slot
|
||||||
expect(treeshaken).not.toContain('ByeBye')
|
expect(treeshaken).not.toContain('ByeBye')
|
||||||
// don't treeshake variables that has the same name as .client components
|
// don't treeshake variables that has the same name as .client components
|
||||||
expect(treeshaken).toContain('NotDotClientComponent')
|
expect(treeshaken).toContain('NotDotClientComponent')
|
||||||
expect(treeshaken).not.toContain('(DotClientComponent')
|
expect(treeshaken).not.toContain('(DotClientComponent')
|
||||||
|
|
||||||
expect(treeshaken).not.toContain('AutoImportedComponent')
|
expect(treeshaken).not.toContain('AutoImportedComponent')
|
||||||
expect(treeshaken).toContain('AutoImportedNotTreeShakenComponent')
|
expect(treeshaken).toContain('AutoImportedNotTreeShakenComponent')
|
||||||
|
|
||||||
expect(treeshaken).not.toContain('Both')
|
expect(treeshaken).not.toContain('Both')
|
||||||
expect(treeshaken).not.toContain('AreTreeshaken')
|
expect(treeshaken).not.toContain('AreTreeshaken')
|
||||||
|
|
||||||
if (state.options.isProduction === false) {
|
if (state.options.isProduction === false) {
|
||||||
// treeshake at inlined template
|
// treeshake at inlined template
|
||||||
expect(treeshaken).not.toContain('ssrRenderComponent($setup["HelloWorld"]')
|
expect(treeshaken).not.toContain('ssrRenderComponent($setup["HelloWorld"]')
|
||||||
expect(treeshaken).toContain('ssrRenderComponent($setup["Glob"]')
|
expect(treeshaken).toContain('ssrRenderComponent($setup["Glob"]')
|
||||||
} else {
|
} else {
|
||||||
// treeshake unref
|
// treeshake unref
|
||||||
expect(treeshaken).not.toContain('ssrRenderComponent(_unref(HelloWorld')
|
expect(treeshaken).not.toContain('ssrRenderComponent(_unref(HelloWorld')
|
||||||
expect(treeshaken).toContain('ssrRenderComponent(_unref(Glob')
|
expect(treeshaken).toContain('ssrRenderComponent(_unref(Glob')
|
||||||
}
|
}
|
||||||
expect(treeshaken.replace(/data-v-\w{8}/g, 'data-v-one-hash').replace(/scoped=\w{8}/g, 'scoped=one-hash')).toMatchSnapshot()
|
expect(treeshaken.replace(/data-v-\w{8}/g, 'data-v-one-hash').replace(/scoped=\w{8}/g, 'scoped=one-hash')).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
it('should not treeshake reused component #26137', async () => {
|
it('should not treeshake reused component #26137', async () => {
|
||||||
const treeshaken = await treeshake(`import { resolveComponent as _resolveComponent, withCtx as _withCtx, createVNode as _createVNode } from "vue"
|
const treeshaken = await treeshake(`import { resolveComponent as _resolveComponent, withCtx as _withCtx, createVNode as _createVNode } from "vue"
|
||||||
import { ssrRenderComponent as _ssrRenderComponent, ssrRenderAttrs as _ssrRenderAttrs } from "vue/server-renderer"
|
import { ssrRenderComponent as _ssrRenderComponent, ssrRenderAttrs as _ssrRenderAttrs } from "vue/server-renderer"
|
||||||
|
|
||||||
export function ssrRender(_ctx, _push, _parent, _attrs) {
|
export function ssrRender(_ctx, _push, _parent, _attrs) {
|
||||||
const _component_AppIcon = _resolveComponent("AppIcon")
|
const _component_AppIcon = _resolveComponent("AppIcon")
|
||||||
const _component_ClientOnly = _resolveComponent("ClientOnly")
|
const _component_ClientOnly = _resolveComponent("ClientOnly")
|
||||||
|
|
||||||
_push(\`<div\${_ssrRenderAttrs(_attrs)}>\`)
|
_push(\`<div\${_ssrRenderAttrs(_attrs)}>\`)
|
||||||
_push(_ssrRenderComponent(_component_AppIcon, { name: "caret-left" }, null, _parent))
|
_push(_ssrRenderComponent(_component_AppIcon, { name: "caret-left" }, null, _parent))
|
||||||
_push(_ssrRenderComponent(_component_ClientOnly, null, {
|
_push(_ssrRenderComponent(_component_ClientOnly, null, {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
/// <reference types="nitro/types" />
|
/// <reference types="nitro/types" />
|
||||||
|
/// <reference path="dist/app/types/augments.d.ts" />
|
||||||
|
|
||||||
import type { DefineNuxtConfig } from 'nuxt/config'
|
import type { DefineNuxtConfig } from 'nuxt/config'
|
||||||
import type { RuntimeConfig, SchemaDefinition } from 'nuxt/schema'
|
import type { RuntimeConfig, SchemaDefinition } from 'nuxt/schema'
|
||||||
|
2
packages/nuxt/types.d.ts
vendored
2
packages/nuxt/types.d.ts
vendored
@ -1,4 +1,6 @@
|
|||||||
/// <reference types="nitro/types" />
|
/// <reference types="nitro/types" />
|
||||||
|
/// <reference path="dist/app/types/augments.d.ts" />
|
||||||
|
|
||||||
import type { DefineNuxtConfig } from 'nuxt/config'
|
import type { DefineNuxtConfig } from 'nuxt/config'
|
||||||
import type { RuntimeConfig, SchemaDefinition } from 'nuxt/schema'
|
import type { RuntimeConfig, SchemaDefinition } from 'nuxt/schema'
|
||||||
import type { H3Event } from 'h3'
|
import type { H3Event } from 'h3'
|
||||||
|
@ -23,6 +23,8 @@ export default defineBuildConfig({
|
|||||||
externals: [
|
externals: [
|
||||||
// Type imports
|
// Type imports
|
||||||
'#app/components/nuxt-link',
|
'#app/components/nuxt-link',
|
||||||
|
'cssnano',
|
||||||
|
'autoprefixer',
|
||||||
'ofetch',
|
'ofetch',
|
||||||
'vue-router',
|
'vue-router',
|
||||||
'@nuxt/telemetry',
|
'@nuxt/telemetry',
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user