mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 08:33:53 +00:00
Merge remote-tracking branch 'origin/main' into patch-21
This commit is contained in:
commit
31779d2371
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -85,7 +85,7 @@ jobs:
|
||||
run: pnpm install
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0
|
||||
uses: github/codeql-action/init@883d8588e56d1753a8a58c1c86e88976f0c23449 # v3.26.3
|
||||
with:
|
||||
languages: javascript
|
||||
queries: +security-and-quality
|
||||
@ -97,7 +97,7 @@ jobs:
|
||||
path: packages
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0
|
||||
uses: github/codeql-action/analyze@883d8588e56d1753a8a58c1c86e88976f0c23449 # v3.26.3
|
||||
with:
|
||||
category: "/language:javascript"
|
||||
|
||||
|
17
.github/workflows/reproduire-sur-stackblitz.yml
vendored
Normal file
17
.github/workflows/reproduire-sur-stackblitz.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
name: reproduire-sur-stackblitz
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
opened
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
reproduire-sur-stackblitz:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: huang-julien/reproduire-sur-stackblitz@v1.0.0
|
||||
with:
|
||||
reproduction-heading: '### Reproduction'
|
2
.github/workflows/scorecards.yml
vendored
2
.github/workflows/scorecards.yml
vendored
@ -68,7 +68,7 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0
|
||||
uses: github/codeql-action/upload-sarif@883d8588e56d1753a8a58c1c86e88976f0c23449 # v3.26.3
|
||||
if: github.repository == 'nuxt/nuxt' && success()
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
@ -22,13 +22,13 @@ In order to allow you to manage your other testing dependencies, `@nuxt/test-uti
|
||||
- you can choose between `vitest`, `cucumber`, `jest` and `playwright` for end-to-end test runners
|
||||
- `playwright-core` is only required if you wish to use the built-in browser testing utilities (and are not using `@playwright/test` as your test runner)
|
||||
|
||||
::code-group
|
||||
```bash [yarn]
|
||||
yarn add --dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
|
||||
```
|
||||
::package-managers
|
||||
```bash [npm]
|
||||
npm i --save-dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
|
||||
```
|
||||
```bash [yarn]
|
||||
yarn add --dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
|
||||
```
|
||||
```bash [pnpm]
|
||||
pnpm add -D @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
|
||||
```
|
||||
@ -421,13 +421,13 @@ If you prefer to use `@vue/test-utils` on its own for unit testing in Nuxt, and
|
||||
|
||||
1. Install the needed dependencies
|
||||
|
||||
::code-group
|
||||
```bash [yarn]
|
||||
yarn add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue
|
||||
```
|
||||
::package-managers
|
||||
```bash [npm]
|
||||
npm i --save-dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue
|
||||
```
|
||||
```bash [yarn]
|
||||
yarn add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue
|
||||
```
|
||||
```bash [pnpm]
|
||||
pnpm add -D vitest @vue/test-utils happy-dom @vitejs/plugin-vue
|
||||
```
|
||||
@ -487,13 +487,13 @@ If you prefer to use `@vue/test-utils` on its own for unit testing in Nuxt, and
|
||||
|
||||
6. Run vitest command
|
||||
|
||||
::code-group
|
||||
```bash [yarn]
|
||||
yarn test
|
||||
```
|
||||
::package-managers
|
||||
```bash [npm]
|
||||
npm run test
|
||||
```
|
||||
```bash [yarn]
|
||||
yarn test
|
||||
```
|
||||
```bash [pnpm]
|
||||
pnpm run test
|
||||
```
|
||||
@ -658,13 +658,13 @@ const page = await createPage('/page')
|
||||
|
||||
We also provide first-class support for testing Nuxt within [the Playwright test runner](https://playwright.dev/docs/intro).
|
||||
|
||||
::code-group
|
||||
```bash [yarn]
|
||||
yarn add --dev @playwright/test @nuxt/test-utils
|
||||
```
|
||||
::package-managers
|
||||
```bash [npm]
|
||||
npm i --save-dev @playwright/test @nuxt/test-utils
|
||||
```
|
||||
```bash [yarn]
|
||||
yarn add --dev @playwright/test @nuxt/test-utils
|
||||
```
|
||||
```bash [pnpm]
|
||||
pnpm add -D @playwright/test @nuxt/test-utils
|
||||
```
|
||||
|
@ -11,7 +11,7 @@ navigation.icon: i-ph-arrow-circle-up-duotone
|
||||
|
||||
To upgrade Nuxt to the [latest release](https://github.com/nuxt/nuxt/releases), use the `nuxi upgrade` command.
|
||||
|
||||
::code-group
|
||||
::package-managers
|
||||
|
||||
```bash [npm]
|
||||
npx nuxi upgrade
|
||||
|
@ -35,7 +35,7 @@ Or follow the steps below to set up a new Nuxt project on your computer.
|
||||
|
||||
Open a terminal (if you're using [Visual Studio Code](https://code.visualstudio.com), you can open an [integrated terminal](https://code.visualstudio.com/docs/editor/integrated-terminal)) and use the following command to create a new starter project:
|
||||
|
||||
::code-group
|
||||
::package-managers
|
||||
|
||||
```bash [npm]
|
||||
npx nuxi@latest init <project-name>
|
||||
@ -75,16 +75,16 @@ cd <project-name>
|
||||
|
||||
Now you'll be able to start your Nuxt app in development mode:
|
||||
|
||||
::code-group
|
||||
|
||||
```bash [yarn]
|
||||
yarn dev --open
|
||||
```
|
||||
::package-managers
|
||||
|
||||
```bash [npm]
|
||||
npm run dev -- -o
|
||||
```
|
||||
|
||||
```bash [yarn]
|
||||
yarn dev --open
|
||||
```
|
||||
|
||||
```bash [pnpm]
|
||||
pnpm dev -o
|
||||
```
|
||||
|
@ -77,7 +77,7 @@ h1 {
|
||||
|
||||
You can also reference stylesheets that are distributed through npm. Let's use the popular `animate.css` library as an example.
|
||||
|
||||
::code-group
|
||||
::package-managers
|
||||
|
||||
```bash [npm]
|
||||
npm install animate.css
|
||||
|
@ -14,7 +14,7 @@ Use the [`nuxi generate` command](/docs/api/commands/generate) to build and pre-
|
||||
|
||||
This will build your site, stand up a nuxt instance, and, by default, prerender the root page `/` along with any of your site's pages it links to, any of your site's pages they link to, and so on.
|
||||
|
||||
::code-group
|
||||
::package-managers
|
||||
|
||||
```bash [npm]
|
||||
npx nuxi generate
|
||||
|
@ -203,7 +203,7 @@ You can explore open source examples deployed on some of the platform mentioned
|
||||
target: _blank
|
||||
ui.icon.base: text-black dark:text-white
|
||||
---
|
||||
An editable website with universal rendering based on CloudFlare KV.
|
||||
An editable website with universal rendering based on Cloudflare KV.
|
||||
::
|
||||
::
|
||||
|
||||
|
@ -9,16 +9,16 @@ 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:
|
||||
|
||||
::code-group
|
||||
|
||||
```bash [yarn]
|
||||
yarn add --dev vue-tsc typescript
|
||||
```
|
||||
::package-managers
|
||||
|
||||
```bash [npm]
|
||||
npm install --save-dev vue-tsc typescript
|
||||
```
|
||||
|
||||
```bash [yarn]
|
||||
yarn add --dev vue-tsc typescript
|
||||
```
|
||||
|
||||
```bash [pnpm]
|
||||
pnpm add -D vue-tsc typescript
|
||||
```
|
||||
|
@ -246,13 +246,13 @@ If you want to use Vue plugins, like [vue-gtag](https://github.com/MatteoGabriel
|
||||
|
||||
First, install the Vue plugin dependency:
|
||||
|
||||
::code-group
|
||||
```bash [yarn]
|
||||
yarn add --dev vue-gtag-next
|
||||
```
|
||||
::package-managers
|
||||
```bash [npm]
|
||||
npm install --save-dev vue-gtag-next
|
||||
```
|
||||
```bash [yarn]
|
||||
yarn add --dev vue-gtag-next
|
||||
```
|
||||
```bash [pnpm]
|
||||
pnpm add -D vue-gtag-next
|
||||
```
|
||||
|
@ -13,7 +13,7 @@ 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):
|
||||
|
||||
::code-group
|
||||
::package-managers
|
||||
|
||||
```bash [npm]
|
||||
npx nuxi init -t module my-module
|
||||
@ -228,9 +228,9 @@ Learn more about asset injection in [the recipes section](#recipes).
|
||||
|
||||
::warning
|
||||
Published modules cannot leverage auto-imports for assets within their runtime directory. Instead, they have to import them explicitly from `#imports` or alike.
|
||||
|
||||
:br :br
|
||||
Indeed, auto-imports are not enabled for files within `node_modules` (the location where a published module will eventually live) for performance reasons.
|
||||
|
||||
:br :br
|
||||
If you are using the module starter, auto-imports will not be enabled in your playground either.
|
||||
::
|
||||
|
||||
@ -638,7 +638,7 @@ Testing helps ensuring your module works as expected given various setup. Find i
|
||||
|
||||
::tip
|
||||
We're still discussing and exploring how to ease unit and integration testing on Nuxt Modules.
|
||||
|
||||
:br :br
|
||||
[Check out this RFC to join the conversation](https://github.com/nuxt/nuxt/discussions/18399).
|
||||
::
|
||||
|
||||
|
@ -8,7 +8,7 @@ While Nuxt modules offer extensive functionality, sometimes a specific Vite plug
|
||||
|
||||
First, we need to install the Vite plugin, for our example, we'll use `@rollup/plugin-yaml`:
|
||||
|
||||
::code-group
|
||||
::package-managers
|
||||
|
||||
```bash [npm]
|
||||
npm install @rollup/plugin-yaml
|
||||
|
@ -124,6 +124,7 @@ When not using `external`, `<NuxtLink>` supports all Vue Router's [`RouterLink`
|
||||
- `noRel`: If set to `true`, no `rel` attribute will be added to the link
|
||||
- `external`: Forces the link to be rendered as an `a` tag instead of a Vue Router `RouterLink`.
|
||||
- `prefetch`: When enabled will prefetch middleware, layouts and payloads (when using [payloadExtraction](/docs/api/nuxt-config#crossoriginprefetch)) of links in the viewport. Used by the experimental [crossOriginPrefetch](/docs/api/nuxt-config#crossoriginprefetch) config.
|
||||
- `prefetchOn`: Allows custom control of when to prefetch links. Possible options are `interaction` and `visibility` (default). You can also pass an object for full control, for example: `{ interaction: true, visibility: true }`. This prop is only used when `prefetch` is enabled (default) and `noPrefetch` is not set.
|
||||
- `noPrefetch`: Disables prefetching.
|
||||
- `prefetchedClass`: A class to apply to links that have been prefetched.
|
||||
|
||||
@ -185,8 +186,13 @@ interface NuxtLinkOptions {
|
||||
externalRelAttribute?: string;
|
||||
activeClass?: string;
|
||||
exactActiveClass?: string;
|
||||
prefetchedClass?: string;
|
||||
trailingSlash?: 'append' | 'remove'
|
||||
prefetch?: boolean
|
||||
prefetchedClass?: string
|
||||
prefetchOn?: Partial<{
|
||||
visibility: boolean
|
||||
interaction: boolean
|
||||
}>
|
||||
}
|
||||
function defineNuxtLink(options: NuxtLinkOptions): Component {}
|
||||
```
|
||||
@ -195,7 +201,9 @@ function defineNuxtLink(options: NuxtLinkOptions): Component {}
|
||||
- `externalRelAttribute`: A default `rel` attribute value applied on external links. Defaults to `"noopener noreferrer"`. Set it to `""` to disable
|
||||
- `activeClass`: A default class to apply on active links. Works the same as [Vue Router's `linkActiveClass` option](https://router.vuejs.org/api/interfaces/RouterOptions.html#Properties-linkActiveClass). Defaults to Vue Router's default (`"router-link-active"`)
|
||||
- `exactActiveClass`: A default class to apply on exact active links. Works the same as [Vue Router's `linkExactActiveClass` option](https://router.vuejs.org/api/interfaces/RouterOptions.html#Properties-linkExactActiveClass). Defaults to Vue Router's default (`"router-link-exact-active"`)
|
||||
- `prefetchedClass`: A default class to apply to links that have been prefetched.
|
||||
- `trailingSlash`: An option to either add or remove trailing slashes in the `href`. If unset or not matching the valid values `append` or `remove`, it will be ignored.
|
||||
- `prefetch`: Whether or not to prefetch links by default.
|
||||
- `prefetchOn`: Granular control of which prefetch strategies to apply by default.
|
||||
- `prefetchedClass`: A default class to apply to links that have been prefetched.
|
||||
|
||||
:link-example{to="/docs/examples/routing/pages"}
|
||||
|
@ -72,7 +72,7 @@ const { data: posts } = await useAsyncData(
|
||||
- `getCachedData`: Provide a function which returns cached data. A _null_ or _undefined_ return value will trigger a fetch. By default, this is: `key => nuxt.isHydrating ? nuxt.payload.data[key] : nuxt.static.data[key]`, which only caches data when `payloadExtraction` is enabled.
|
||||
- `pick`: only pick specified keys in this array from the `handler` function result
|
||||
- `watch`: watch reactive sources to auto-refresh
|
||||
- `deep`: return data in a deep ref object (it is `true` by default). It can be set to `false` to return data in a shallow ref object, which can improve performance if your data does not need to be deeply reactive.
|
||||
- `deep`: return data in a deep ref object. It is `false` by default to return data in a shallow ref object for performance.
|
||||
- `dedupe`: avoid fetching same key more than once at a time (defaults to `cancel`). Possible options:
|
||||
- `cancel` - cancels existing requests when a new one is made
|
||||
- `defer` - does not make new requests at all if there is a pending request
|
||||
|
@ -108,7 +108,7 @@ All fetch options can be given a `computed` or `ref` value. These will be watche
|
||||
- `getCachedData`: Provide a function which returns cached data. A _null_ or _undefined_ return value will trigger a fetch. By default, this is: `key => nuxt.isHydrating ? nuxt.payload.data[key] : nuxt.static.data[key]`, which only caches data when `payloadExtraction` is enabled.
|
||||
- `pick`: only pick specified keys in this array from the `handler` function result
|
||||
- `watch`: watch an array of reactive sources and auto-refresh the fetch result when they change. Fetch options and URL are watched by default. You can completely ignore reactive sources by using `watch: false`. Together with `immediate: false`, this allows for a fully-manual `useFetch`. (You can [see an example here](/docs/getting-started/data-fetching#watch) of using `watch`.)
|
||||
- `deep`: return data in a deep ref object (it is `true` by default). It can be set to `false` to return data in a shallow ref object, which can improve performance if your data does not need to be deeply reactive.
|
||||
- `deep`: return data in a deep ref object. It is `false` by default to return data in a shallow ref object for performance.
|
||||
- `dedupe`: avoid fetching same key more than once at a time (defaults to `cancel`). Possible options:
|
||||
- `cancel` - cancels existing requests when a new one is made
|
||||
- `defer` - does not make new requests at all if there is a pending request
|
||||
|
@ -4,7 +4,7 @@ description: 'Access runtime config variables with the useRuntimeConfig composab
|
||||
links:
|
||||
- label: Source
|
||||
icon: i-simple-icons-github
|
||||
to: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/asyncData.ts
|
||||
to: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/nuxt.ts
|
||||
size: xs
|
||||
---
|
||||
|
||||
|
@ -28,16 +28,15 @@ Make sure your dev server (`nuxt dev`) isn't running, remove any package lock fi
|
||||
|
||||
Then, reinstall your dependencies:
|
||||
|
||||
::code-group
|
||||
|
||||
```bash [yarn]
|
||||
yarn install
|
||||
```
|
||||
::package-managers
|
||||
|
||||
```bash [npm]
|
||||
npm install
|
||||
```
|
||||
|
||||
```bash [yarn]
|
||||
yarn install
|
||||
```
|
||||
::
|
||||
|
||||
::note
|
||||
@ -48,16 +47,16 @@ Once the installation is complete, make sure both development and production bui
|
||||
|
||||
Install `@nuxt/bridge` and `nuxi` as development dependencies:
|
||||
|
||||
::code-group
|
||||
|
||||
```bash [Yarn]
|
||||
yarn add --dev @nuxt/bridge nuxi
|
||||
```
|
||||
::package-managers
|
||||
|
||||
```bash [npm]
|
||||
npm install -D @nuxt/bridge nuxi
|
||||
```
|
||||
|
||||
```bash [yarn]
|
||||
yarn add --dev @nuxt/bridge nuxi
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
### Update `nuxt.config`
|
||||
|
@ -27,16 +27,16 @@ You will also need to update your scripts within your `package.json` to reflect
|
||||
|
||||
Install `nuxi` as a development dependency:
|
||||
|
||||
::code-group
|
||||
|
||||
```bash [yarn]
|
||||
yarn add --dev nuxi
|
||||
```
|
||||
::package-managers
|
||||
|
||||
```bash [npm]
|
||||
npm install -D nuxi
|
||||
```
|
||||
|
||||
```bash [yarn]
|
||||
yarn add --dev nuxi
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
### Nuxi
|
||||
|
28
package.json
28
package.json
@ -39,17 +39,17 @@
|
||||
"@nuxt/ui-templates": "workspace:*",
|
||||
"@nuxt/vite-builder": "workspace:*",
|
||||
"@nuxt/webpack-builder": "workspace:*",
|
||||
"@types/node": "20.14.15",
|
||||
"@types/node": "20.16.1",
|
||||
"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.11",
|
||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||
"nuxt": "workspace:*",
|
||||
"rollup": "^4.20.0",
|
||||
"rollup": "^4.21.0",
|
||||
"typescript": "5.5.4",
|
||||
"unbuild": "3.0.0-rc.7",
|
||||
"vite": "5.4.0",
|
||||
"vite": "5.4.1",
|
||||
"vue": "3.5.0-beta.1",
|
||||
"@vue/compiler-core": "3.5.0-beta.1",
|
||||
"@vue/compiler-dom": "3.5.0-beta.1",
|
||||
@ -59,30 +59,30 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "9.9.0",
|
||||
"@nuxt/eslint-config": "0.5.0",
|
||||
"@nuxt/eslint-config": "0.5.1",
|
||||
"@nuxt/kit": "workspace:*",
|
||||
"@nuxt/test-utils": "3.14.1",
|
||||
"@nuxt/webpack-builder": "workspace:*",
|
||||
"@testing-library/vue": "8.1.0",
|
||||
"@types/eslint__js": "8.42.3",
|
||||
"@types/node": "20.14.15",
|
||||
"@types/node": "20.16.1",
|
||||
"@types/semver": "7.5.8",
|
||||
"@unhead/schema": "1.9.16",
|
||||
"@vitejs/plugin-vue": "5.1.2",
|
||||
"@vitest/coverage-v8": "2.0.5",
|
||||
"@vue/test-utils": "2.4.6",
|
||||
"autoprefixer": "10.4.20",
|
||||
"case-police": "0.6.1",
|
||||
"case-police": "0.7.0",
|
||||
"changelogen": "0.5.5",
|
||||
"consola": "3.2.3",
|
||||
"cssnano": "7.0.5",
|
||||
"destr": "2.0.3",
|
||||
"devalue": "5.0.0",
|
||||
"eslint": "9.9.0",
|
||||
"eslint-plugin-no-only-tests": "3.1.0",
|
||||
"eslint-plugin-perfectionist": "3.1.3",
|
||||
"eslint-typegen": "0.3.0",
|
||||
"execa": "9.3.0",
|
||||
"eslint-plugin-no-only-tests": "3.3.0",
|
||||
"eslint-plugin-perfectionist": "3.2.0",
|
||||
"eslint-typegen": "0.3.1",
|
||||
"execa": "9.3.1",
|
||||
"globby": "14.0.2",
|
||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||
"happy-dom": "14.12.3",
|
||||
@ -94,19 +94,19 @@
|
||||
"nuxt-content-twoslash": "0.1.1",
|
||||
"ofetch": "1.3.4",
|
||||
"pathe": "1.1.2",
|
||||
"playwright-core": "1.46.0",
|
||||
"playwright-core": "1.46.1",
|
||||
"rimraf": "6.0.1",
|
||||
"semver": "7.6.3",
|
||||
"std-env": "3.7.0",
|
||||
"typescript": "5.5.4",
|
||||
"ufo": "1.5.4",
|
||||
"vitest": "2.0.5",
|
||||
"vitest-environment-nuxt": "1.0.0",
|
||||
"vue": "3.4.37",
|
||||
"vitest-environment-nuxt": "1.0.1",
|
||||
"vue": "3.4.38",
|
||||
"vue-router": "4.4.3",
|
||||
"vue-tsc": "2.0.29"
|
||||
},
|
||||
"packageManager": "pnpm@9.7.0",
|
||||
"packageManager": "pnpm@9.7.1",
|
||||
"engines": {
|
||||
"node": "^16.10.0 || >=18.0.0"
|
||||
},
|
||||
|
@ -52,7 +52,7 @@
|
||||
"@types/semver": "7.5.8",
|
||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28665895.e727afda",
|
||||
"unbuild": "3.0.0-rc.7",
|
||||
"vite": "5.4.0",
|
||||
"vite": "5.4.1",
|
||||
"vitest": "2.0.5",
|
||||
"webpack": "5.93.0"
|
||||
},
|
||||
|
@ -68,7 +68,7 @@
|
||||
"@unhead/dom": "^1.9.16",
|
||||
"@unhead/ssr": "^1.9.16",
|
||||
"@unhead/vue": "^1.9.16",
|
||||
"@vue/shared": "^3.4.37",
|
||||
"@vue/shared": "^3.4.38",
|
||||
"acorn": "8.12.1",
|
||||
"c12": "^2.0.0-beta.1",
|
||||
"chokidar": "^3.6.0",
|
||||
@ -79,7 +79,7 @@
|
||||
"destr": "^2.0.3",
|
||||
"devalue": "^5.0.0",
|
||||
"errx": "^0.1.0",
|
||||
"esbuild": "^0.23.0",
|
||||
"esbuild": "^0.23.1",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"estree-walker": "^3.0.3",
|
||||
"globby": "^14.0.2",
|
||||
@ -110,11 +110,11 @@
|
||||
"unctx": "^2.3.1",
|
||||
"unenv": "^1.10.0",
|
||||
"unimport": "^3.10.0",
|
||||
"unplugin": "^1.12.1",
|
||||
"unplugin-vue-router": "^0.10.3",
|
||||
"unplugin": "^1.12.2",
|
||||
"unplugin-vue-router": "^0.10.7",
|
||||
"unstorage": "^1.10.2",
|
||||
"untyped": "^1.4.2",
|
||||
"vue": "^3.4.37",
|
||||
"vue": "^3.4.38",
|
||||
"vue-bundle-renderer": "^2.1.0",
|
||||
"vue-devtools-stub": "^0.1.0",
|
||||
"vue-router": "^4.4.3"
|
||||
@ -125,9 +125,9 @@
|
||||
"@parcel/watcher": "2.4.1",
|
||||
"@types/estree": "1.0.5",
|
||||
"@vitejs/plugin-vue": "5.1.2",
|
||||
"@vue/compiler-sfc": "3.4.37",
|
||||
"@vue/compiler-sfc": "3.4.38",
|
||||
"unbuild": "3.0.0-rc.7",
|
||||
"vite": "5.4.0",
|
||||
"vite": "5.4.1",
|
||||
"vitest": "2.0.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@ -3,6 +3,7 @@ import type { ComponentInternalInstance, ComponentOptions, InjectionKey } from '
|
||||
import { isPromise } from '@vue/shared'
|
||||
import { useNuxtApp } from '../nuxt'
|
||||
import { getFragmentHTML } from './utils'
|
||||
import ServerPlaceholder from './server-placeholder'
|
||||
|
||||
export const clientOnlySymbol: InjectionKey<boolean> = Symbol.for('nuxt:client-only')
|
||||
|
||||
@ -36,6 +37,9 @@ const cache = new WeakMap()
|
||||
|
||||
/* @__NO_SIDE_EFFECTS__ */
|
||||
export function createClientOnly<T extends ComponentOptions> (component: T) {
|
||||
if (import.meta.server) {
|
||||
return ServerPlaceholder
|
||||
}
|
||||
if (cache.has(component)) {
|
||||
return cache.get(component)
|
||||
}
|
||||
@ -45,15 +49,14 @@ export function createClientOnly<T extends ComponentOptions> (component: T) {
|
||||
if (clone.render) {
|
||||
// override the component render (non script setup component) or dev mode
|
||||
clone.render = (ctx: any, cache: any, $props: any, $setup: any, $data: any, $options: any) => {
|
||||
// import.meta.client for server-side treeshakking
|
||||
if (import.meta.client && ($setup.mounted$ ?? ctx.mounted$)) {
|
||||
if ($setup.mounted$ ?? ctx.mounted$) {
|
||||
const res = component.render?.bind(ctx)(ctx, cache, $props, $setup, $data, $options)
|
||||
return (res.children === null || typeof res.children === 'string')
|
||||
? cloneVNode(res)
|
||||
: h(res)
|
||||
} else {
|
||||
const fragment = getFragmentHTML(ctx._.vnode.el ?? null) ?? ['<div></div>']
|
||||
return import.meta.client ? createStaticVNode(fragment.join(''), fragment.length) : h('div', ctx.$attrs ?? ctx._.attrs)
|
||||
return createStaticVNode(fragment.join(''), fragment.length)
|
||||
}
|
||||
}
|
||||
} else if (clone.template) {
|
||||
@ -66,10 +69,10 @@ export function createClientOnly<T extends ComponentOptions> (component: T) {
|
||||
|
||||
clone.setup = (props, ctx) => {
|
||||
const nuxtApp = useNuxtApp()
|
||||
const mounted$ = ref(import.meta.client && nuxtApp.isHydrating === false)
|
||||
const mounted$ = ref(nuxtApp.isHydrating === false)
|
||||
const instance = getCurrentInstance()!
|
||||
|
||||
if (import.meta.server || nuxtApp.isHydrating) {
|
||||
if (nuxtApp.isHydrating) {
|
||||
const attrs = { ...instance.attrs }
|
||||
// remove existing directives during hydration
|
||||
const directives = extractDirectives(instance)
|
||||
@ -97,14 +100,14 @@ export function createClientOnly<T extends ComponentOptions> (component: T) {
|
||||
return setupState
|
||||
}
|
||||
return (...args: any[]) => {
|
||||
if (import.meta.client && (mounted$.value || !nuxtApp.isHydrating)) {
|
||||
if (mounted$.value || !nuxtApp.isHydrating) {
|
||||
const res = setupState(...args)
|
||||
return (res.children === null || typeof res.children === 'string')
|
||||
? cloneVNode(res)
|
||||
: h(res)
|
||||
} else {
|
||||
const fragment = getFragmentHTML(instance?.vnode.el ?? null) ?? ['<div></div>']
|
||||
return import.meta.client ? createStaticVNode(fragment.join(''), fragment.length) : h('div', ctx.attrs)
|
||||
return createStaticVNode(fragment.join(''), fragment.length)
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -115,9 +118,7 @@ export function createClientOnly<T extends ComponentOptions> (component: T) {
|
||||
return h(setupState(...args), ctx.attrs)
|
||||
}
|
||||
const fragment = getFragmentHTML(instance?.vnode.el ?? null) ?? ['<div></div>']
|
||||
return import.meta.client
|
||||
? createStaticVNode(fragment.join(''), fragment.length) :
|
||||
h('div', ctx.attrs)
|
||||
return createStaticVNode(fragment.join(''), fragment.length)
|
||||
}
|
||||
}
|
||||
return Object.assign(setupState, { mounted$ })
|
||||
|
@ -59,6 +59,13 @@ export interface NuxtLinkProps extends Omit<RouterLinkProps, 'to'> {
|
||||
* When enabled will prefetch middleware, layouts and payloads of links in the viewport.
|
||||
*/
|
||||
prefetch?: boolean
|
||||
/**
|
||||
* Allows controlling when to prefetch links. By default, prefetch is triggered only on visibility.
|
||||
*/
|
||||
prefetchOn?: 'visibility' | 'interaction' | Partial<{
|
||||
visibility: boolean
|
||||
interaction: boolean
|
||||
}>
|
||||
/**
|
||||
* Escape hatch to disable `prefetch` attribute.
|
||||
*/
|
||||
@ -71,7 +78,7 @@ export interface NuxtLinkProps extends Omit<RouterLinkProps, 'to'> {
|
||||
*/
|
||||
export interface NuxtLinkOptions extends
|
||||
Partial<Pick<RouterLinkProps, 'activeClass' | 'exactActiveClass'>>,
|
||||
Partial<Pick<NuxtLinkProps, 'prefetchedClass'>> {
|
||||
Partial<Pick<NuxtLinkProps, 'prefetch' | 'prefetchedClass'>> {
|
||||
/**
|
||||
* The name of the component.
|
||||
* @default "NuxtLink"
|
||||
@ -86,6 +93,11 @@ export interface NuxtLinkOptions extends
|
||||
* If unset or not matching the valid values `append` or `remove`, it will be ignored.
|
||||
*/
|
||||
trailingSlash?: 'append' | 'remove'
|
||||
|
||||
/**
|
||||
* Allows controlling default setting for when to prefetch links. By default, prefetch is triggered only on visibility.
|
||||
*/
|
||||
prefetchOn?: Exclude<NuxtLinkProps['prefetchOn'], string>
|
||||
}
|
||||
|
||||
/* @__NO_SIDE_EFFECTS__ */
|
||||
@ -239,6 +251,14 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
||||
default: undefined,
|
||||
required: false,
|
||||
},
|
||||
prefetchOn: {
|
||||
type: [String, Object] as PropType<NuxtLinkProps['prefetchOn']>,
|
||||
default: options.prefetchOn || {
|
||||
visibility: true,
|
||||
interaction: false,
|
||||
} satisfies NuxtLinkProps['prefetchOn'],
|
||||
required: false,
|
||||
},
|
||||
noPrefetch: {
|
||||
type: Boolean as PropType<NuxtLinkProps['noPrefetch']>,
|
||||
default: undefined,
|
||||
@ -299,10 +319,27 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
||||
const el = import.meta.server ? undefined : ref<HTMLElement | null>(null)
|
||||
const elRef = import.meta.server ? undefined : (ref: any) => { el!.value = props.custom ? ref?.$el?.nextElementSibling : ref?.$el }
|
||||
|
||||
function shouldPrefetch (mode: 'visibility' | 'interaction') {
|
||||
return !prefetched.value && (typeof props.prefetchOn === 'string' ? props.prefetchOn === mode : (props.prefetchOn?.[mode] ?? options.prefetchOn?.[mode])) && (props.prefetch ?? options.prefetch) !== false && props.noPrefetch !== true && props.target !== '_blank' && !isSlowConnection()
|
||||
}
|
||||
|
||||
async function prefetch (nuxtApp = useNuxtApp()) {
|
||||
if (prefetched.value) { return }
|
||||
|
||||
prefetched.value = true
|
||||
|
||||
const path = typeof to.value === 'string'
|
||||
? to.value
|
||||
: isExternal.value ? resolveRouteObject(to.value) : router.resolve(to.value).fullPath
|
||||
await Promise.all([
|
||||
nuxtApp.hooks.callHook('link:prefetch', path).catch(() => {}),
|
||||
!isExternal.value && !hasTarget.value && preloadRouteComponents(to.value as string, router).catch(() => {}),
|
||||
])
|
||||
}
|
||||
|
||||
if (import.meta.client) {
|
||||
checkPropConflicts(props, 'prefetch', 'noPrefetch')
|
||||
const shouldPrefetch = props.prefetch !== false && props.noPrefetch !== true && props.target !== '_blank' && !isSlowConnection()
|
||||
if (shouldPrefetch) {
|
||||
if (shouldPrefetch('visibility')) {
|
||||
const nuxtApp = useNuxtApp()
|
||||
let idleId: number
|
||||
let unobserve: (() => void) | null = null
|
||||
@ -314,15 +351,7 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
||||
unobserve = observer!.observe(el.value as HTMLElement, async () => {
|
||||
unobserve?.()
|
||||
unobserve = null
|
||||
|
||||
const path = typeof to.value === 'string'
|
||||
? to.value
|
||||
: isExternal.value ? resolveRouteObject(to.value) : router.resolve(to.value).fullPath
|
||||
await Promise.all([
|
||||
nuxtApp.hooks.callHook('link:prefetch', path).catch(() => {}),
|
||||
!isExternal.value && !hasTarget.value && preloadRouteComponents(to.value as string, router).catch(() => {}),
|
||||
])
|
||||
prefetched.value = true
|
||||
await prefetch(nuxtApp)
|
||||
})
|
||||
}
|
||||
})
|
||||
@ -355,6 +384,8 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
||||
replace: props.replace,
|
||||
ariaCurrentValue: props.ariaCurrentValue,
|
||||
custom: props.custom,
|
||||
onPointerenter: shouldPrefetch('interaction') ? prefetch.bind(null, undefined) : undefined,
|
||||
onFocus: shouldPrefetch('interaction') ? prefetch.bind(null, undefined) : undefined,
|
||||
}
|
||||
|
||||
// `custom` API cannot support fallthrough attributes as the slot
|
||||
|
@ -84,7 +84,7 @@ export interface AsyncDataOptions<
|
||||
*/
|
||||
immediate?: boolean
|
||||
/**
|
||||
* Return data in a deep ref object (it is true by default). It can be set to false to return data in a shallow ref object, which can improve performance if your data does not need to be deeply reactive.
|
||||
* Return data in a deep ref object (it is false by default). It can be set to false to return data in a shallow ref object, which can improve performance if your data does not need to be deeply reactive.
|
||||
*/
|
||||
deep?: boolean
|
||||
/**
|
||||
@ -350,7 +350,7 @@ export function useAsyncData<
|
||||
if (import.meta.client) {
|
||||
// Setup hook callbacks once per instance
|
||||
const instance = getCurrentInstance()
|
||||
if (import.meta.dev && !nuxtApp.isHydrating && (!instance || instance?.isMounted)) {
|
||||
if (import.meta.dev && !nuxtApp.isHydrating && !nuxtApp._processingMiddleware /* internal flag */ && (!instance || instance?.isMounted)) {
|
||||
// @ts-expect-error private property
|
||||
console.warn(`[nuxt] [${options._functionName || 'useAsyncData'}] Component is already mounted, please use $fetch instead. See https://nuxt.com/docs/getting-started/data-fetching`)
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import { useRoute } from './router'
|
||||
import { getAppManifest, getRouteRules } from './manifest'
|
||||
|
||||
// @ts-expect-error virtual import
|
||||
import { appManifest, payloadExtraction, renderJsonPayloads } from '#build/nuxt.config.mjs'
|
||||
import { appId, appManifest, multiApp, payloadExtraction, renderJsonPayloads } from '#build/nuxt.config.mjs'
|
||||
|
||||
interface LoadPayloadOptions {
|
||||
fresh?: boolean
|
||||
@ -107,7 +107,7 @@ export async function getNuxtClientPayload () {
|
||||
return payloadCache
|
||||
}
|
||||
|
||||
const el = document.getElementById('__NUXT_DATA__')
|
||||
const el = multiApp ? document.querySelector(`[data-nuxt-data="${appId}"]`) as HTMLElement : document.getElementById('__NUXT_DATA__')
|
||||
if (!el) {
|
||||
return {} as Partial<NuxtPayload>
|
||||
}
|
||||
@ -119,7 +119,7 @@ export async function getNuxtClientPayload () {
|
||||
payloadCache = {
|
||||
...inlineData,
|
||||
...externalData,
|
||||
...window.__NUXT__,
|
||||
...(multiApp ? window.__NUXT__?.[appId] : window.__NUXT__),
|
||||
}
|
||||
|
||||
if (payloadCache!.config?.public) {
|
||||
|
@ -15,7 +15,7 @@ import plugins from '#build/plugins'
|
||||
// @ts-expect-error virtual file
|
||||
import RootComponent from '#build/root-component.mjs'
|
||||
// @ts-expect-error virtual file
|
||||
import { vueAppRootContainer } from '#build/nuxt.config.mjs'
|
||||
import { appId, multiApp, vueAppRootContainer } from '#build/nuxt.config.mjs'
|
||||
|
||||
let entry: (ssrContext?: CreateOptions['ssrContext']) => Promise<App<Element>>
|
||||
|
||||
@ -50,9 +50,10 @@ if (import.meta.client) {
|
||||
|
||||
entry = async function initApp () {
|
||||
if (vueAppPromise) { return vueAppPromise }
|
||||
|
||||
const isSSR = Boolean(
|
||||
window.__NUXT__?.serverRendered ||
|
||||
document.getElementById('__NUXT_DATA__')?.dataset.ssr === 'true',
|
||||
(multiApp ? window.__NUXT__?.[appId] : window.__NUXT__)?.serverRendered ??
|
||||
(multiApp ? document.querySelector(`[data-nuxt-data="${appId}"]`) as HTMLElement : document.getElementById('__NUXT_DATA__'))?.dataset.ssr === 'true',
|
||||
)
|
||||
const vueApp = isSSR ? createSSRApp(RootComponent) : createApp(RootComponent)
|
||||
|
||||
|
@ -21,7 +21,7 @@ import type { RouteAnnouncer } from '../app/composables/route-announcer'
|
||||
import type { ViewTransition } from './plugins/view-transitions.client'
|
||||
|
||||
// @ts-expect-error virtual file
|
||||
import { appId } from '#build/nuxt.config.mjs'
|
||||
import { appId, multiApp } from '#build/nuxt.config.mjs'
|
||||
|
||||
import type { NuxtAppLiterals } from '#app'
|
||||
|
||||
@ -310,19 +310,22 @@ export function createNuxtApp (options: CreateOptions) {
|
||||
nuxtApp.payload.serverRendered = true
|
||||
}
|
||||
|
||||
if (import.meta.client) {
|
||||
const __NUXT__ = multiApp ? window.__NUXT__?.[nuxtApp._id] : window.__NUXT__
|
||||
// TODO: remove/refactor in https://github.com/nuxt/nuxt/issues/25336
|
||||
if (import.meta.client && window.__NUXT__) {
|
||||
for (const key in window.__NUXT__) {
|
||||
if (__NUXT__) {
|
||||
for (const key in __NUXT__) {
|
||||
switch (key) {
|
||||
case 'data':
|
||||
case 'state':
|
||||
case '_errors':
|
||||
// Preserve reactivity for non-rich payload support
|
||||
Object.assign(nuxtApp.payload[key], window.__NUXT__[key])
|
||||
Object.assign(nuxtApp.payload[key], __NUXT__[key])
|
||||
break
|
||||
|
||||
default:
|
||||
nuxtApp.payload[key] = window.__NUXT__[key]
|
||||
nuxtApp.payload[key] = __NUXT__[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,8 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
const content = document.getElementById('__NUXT_LOGS__')?.textContent
|
||||
const nuxtLogsElement = document.querySelector(`[data-nuxt-logs="${nuxtApp._name}"]`)
|
||||
const content = nuxtLogsElement?.textContent
|
||||
const logs = content ? parse(content, { ...devRevivers, ...nuxtApp._payloadRevivers }) as LogObject[] : []
|
||||
await nuxtApp.hooks.callHook('dev:ssr-logs', logs)
|
||||
}
|
||||
|
22
packages/nuxt/src/app/types/augments.d.ts
vendored
22
packages/nuxt/src/app/types/augments.d.ts
vendored
@ -26,7 +26,7 @@ declare global {
|
||||
}
|
||||
|
||||
interface Window {
|
||||
__NUXT__?: Record<string, any>
|
||||
__NUXT__?: Record<string, any> | Record<string, Record<string, any>>
|
||||
useNuxtApp?: typeof useNuxtApp
|
||||
}
|
||||
}
|
||||
@ -51,23 +51,3 @@ declare module 'vue' {
|
||||
head?(nuxtApp: NuxtApp): UseHeadInput
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface App<HostElement> {
|
||||
$nuxt: NuxtApp
|
||||
}
|
||||
interface ComponentCustomProperties {
|
||||
$nuxt: NuxtApp
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@vue/runtime-dom' {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface App<HostElement> {
|
||||
$nuxt: NuxtApp
|
||||
}
|
||||
interface ComponentCustomProperties {
|
||||
$nuxt: NuxtApp
|
||||
}
|
||||
}
|
||||
|
@ -128,14 +128,6 @@ interface _GlobalComponents {
|
||||
${componentTypes.map(([pascalName, type]) => ` 'LazyEvent${pascalName}': ${type} & DefineComponent<{hydrate?: Array<keyof HTMLElementEventMap>}>`).join('\n')}
|
||||
}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents extends _GlobalComponents { }
|
||||
}
|
||||
|
||||
declare module '@vue/runtime-dom' {
|
||||
export interface GlobalComponents extends _GlobalComponents { }
|
||||
}
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents extends _GlobalComponents { }
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ import type { NitroApp } from 'nitro/types'
|
||||
|
||||
// @ts-expect-error virtual file
|
||||
import { rootDir } from '#internal/dev-server-logs-options'
|
||||
// @ts-expect-error virtual file
|
||||
import { appId } from '#internal/nuxt.config.mjs'
|
||||
|
||||
const devReducers: Record<string, (data: any) => any> = {
|
||||
VNode: data => isVNode(data) ? { type: data.type, props: data.props } : undefined,
|
||||
@ -75,7 +77,7 @@ export default (nitroApp: NitroApp) => {
|
||||
const ctx = asyncContext.tryUse()
|
||||
if (!ctx) { return }
|
||||
try {
|
||||
htmlContext.bodyAppend.unshift(`<script type="application/json" id="__NUXT_LOGS__">${stringify(ctx.logs, { ...devReducers, ...ctx.event.context._payloadReducers })}</script>`)
|
||||
htmlContext.bodyAppend.unshift(`<script type="application/json" data-nuxt-logs="${appId}">${stringify(ctx.logs, { ...devReducers, ...ctx.event.context._payloadReducers })}</script>`)
|
||||
} catch (e) {
|
||||
const shortError = e instanceof Error && 'toString' in e ? ` Received \`${e.toString()}\`.` : ''
|
||||
console.warn(`[nuxt] Failed to stringify dev server logs.${shortError} You can define your own reducer/reviver for rich types following the instructions in https://nuxt.com/docs/api/composables/use-nuxt-app#payload.`)
|
||||
|
@ -31,7 +31,7 @@ import { renderSSRHeadOptions } from '#internal/unhead.config.mjs'
|
||||
|
||||
import type { NuxtPayload, NuxtSSRContext } from '#app'
|
||||
// @ts-expect-error virtual file
|
||||
import { appHead, appRootAttrs, appRootTag, appTeleportAttrs, appTeleportTag, componentIslands } from '#internal/nuxt.config.mjs'
|
||||
import { appHead, appId, appRootAttrs, appRootTag, appTeleportAttrs, appTeleportTag, componentIslands, multiApp } from '#internal/nuxt.config.mjs'
|
||||
// @ts-expect-error virtual file
|
||||
import { buildAssetsURL, publicAssetsURL } from '#internal/nuxt/paths'
|
||||
|
||||
@ -427,10 +427,10 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
||||
head.push({
|
||||
script: _PAYLOAD_EXTRACTION
|
||||
? process.env.NUXT_JSON_PAYLOADS
|
||||
? renderPayloadJsonScript({ id: '__NUXT_DATA__', ssrContext, data: splitPayload(ssrContext).initial, src: payloadURL })
|
||||
? renderPayloadJsonScript({ ssrContext, data: splitPayload(ssrContext).initial, src: payloadURL })
|
||||
: renderPayloadScript({ ssrContext, data: splitPayload(ssrContext).initial, src: payloadURL })
|
||||
: process.env.NUXT_JSON_PAYLOADS
|
||||
? renderPayloadJsonScript({ id: '__NUXT_DATA__', ssrContext, data: ssrContext.payload })
|
||||
? renderPayloadJsonScript({ ssrContext, data: ssrContext.payload })
|
||||
: renderPayloadScript({ ssrContext, data: ssrContext.payload }),
|
||||
}, {
|
||||
...headEntryOptions,
|
||||
@ -586,21 +586,27 @@ function renderPayloadResponse (ssrContext: NuxtSSRContext) {
|
||||
} satisfies RenderResponse
|
||||
}
|
||||
|
||||
function renderPayloadJsonScript (opts: { id: string, ssrContext: NuxtSSRContext, data?: any, src?: string }): Script[] {
|
||||
function renderPayloadJsonScript (opts: { ssrContext: NuxtSSRContext, data?: any, src?: string }): Script[] {
|
||||
const contents = opts.data ? stringify(opts.data, opts.ssrContext._payloadReducers) : ''
|
||||
const payload: Script = {
|
||||
'type': 'application/json',
|
||||
'id': opts.id,
|
||||
'innerHTML': contents,
|
||||
'data-nuxt-data': appId,
|
||||
'data-ssr': !(process.env.NUXT_NO_SSR || opts.ssrContext.noSSR),
|
||||
}
|
||||
if (!multiApp) {
|
||||
payload.id = '__NUXT_DATA__'
|
||||
}
|
||||
if (opts.src) {
|
||||
payload['data-src'] = opts.src
|
||||
}
|
||||
const config = uneval(opts.ssrContext.config)
|
||||
return [
|
||||
payload,
|
||||
{
|
||||
innerHTML: `window.__NUXT__={};window.__NUXT__.config=${uneval(opts.ssrContext.config)}`,
|
||||
innerHTML: multiApp
|
||||
? `window.__NUXT__=window.__NUXT__||{};window.__NUXT__[${JSON.stringify(appId)}]={config:${config}}`
|
||||
: `window.__NUXT__={};window.__NUXT__.config=${config}`,
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -608,17 +614,22 @@ function renderPayloadJsonScript (opts: { id: string, ssrContext: NuxtSSRContext
|
||||
function renderPayloadScript (opts: { ssrContext: NuxtSSRContext, data?: any, src?: string }): Script[] {
|
||||
opts.data.config = opts.ssrContext.config
|
||||
const _PAYLOAD_EXTRACTION = import.meta.prerender && process.env.NUXT_PAYLOAD_EXTRACTION && !opts.ssrContext.noSSR
|
||||
const nuxtData = devalue(opts.data)
|
||||
if (_PAYLOAD_EXTRACTION) {
|
||||
const singleAppPayload = `import p from "${opts.src}";window.__NUXT__={...p,...(${nuxtData})}`
|
||||
const multiAppPayload = `import p from "${opts.src}";window.__NUXT__=window.__NUXT__||{};window.__NUXT__[${JSON.stringify(appId)}]={...p,...(${nuxtData})}`
|
||||
return [
|
||||
{
|
||||
type: 'module',
|
||||
innerHTML: `import p from "${opts.src}";window.__NUXT__={...p,...(${devalue(opts.data)})}`,
|
||||
innerHTML: multiApp ? multiAppPayload : singleAppPayload,
|
||||
},
|
||||
]
|
||||
}
|
||||
const singleAppPayload = `window.__NUXT__=${nuxtData}`
|
||||
const multiAppPayload = `window.__NUXT__=window.__NUXT__||{};window.__NUXT__[${JSON.stringify(appId)}]=${nuxtData}`
|
||||
return [
|
||||
{
|
||||
innerHTML: `window.__NUXT__=${devalue(opts.data)}`,
|
||||
innerHTML: multiApp ? multiAppPayload : singleAppPayload,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -96,6 +96,8 @@ export const serverPluginTemplate: NuxtTemplate = {
|
||||
},
|
||||
}
|
||||
|
||||
const TS_RE = /\.[cm]?tsx?$/
|
||||
|
||||
export const pluginsDeclaration: NuxtTemplate = {
|
||||
filename: 'types/plugins.d.ts',
|
||||
getContents: async ({ nuxt, app }) => {
|
||||
@ -124,9 +126,14 @@ export const pluginsDeclaration: NuxtTemplate = {
|
||||
}
|
||||
|
||||
if (exists(pluginPath)) {
|
||||
if (TS_RE.test(pluginPath)) {
|
||||
tsImports.push(relativePath.replace(EXTENSION_RE, ''))
|
||||
continue
|
||||
}
|
||||
tsImports.push(relativePath)
|
||||
}
|
||||
|
||||
// No declaration found that TypeScript can use
|
||||
}
|
||||
|
||||
return `// Generated by Nuxt'
|
||||
@ -147,14 +154,6 @@ declare module '#app' {
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties extends NuxtAppInjections { }
|
||||
}
|
||||
|
||||
declare module '@vue/runtime-dom' {
|
||||
interface ComponentCustomProperties extends NuxtAppInjections { }
|
||||
}
|
||||
|
||||
declare module 'vue' {
|
||||
interface ComponentCustomProperties extends NuxtAppInjections { }
|
||||
}
|
||||
@ -350,9 +349,16 @@ declare module 'nitropack/types' {
|
||||
|
||||
export const clientConfigTemplate: NuxtTemplate = {
|
||||
filename: 'nitro.client.mjs',
|
||||
getContents: () => `
|
||||
export const useRuntimeConfig = () => window?.__NUXT__?.config || window?.useNuxtApp?.().payload?.config || {}
|
||||
`,
|
||||
getContents: ({ nuxt }) => {
|
||||
const appId = JSON.stringify(nuxt.options.appId)
|
||||
return [
|
||||
'export const useRuntimeConfig = () => ',
|
||||
(!nuxt.options.future.multiApp
|
||||
? 'window?.__NUXT__?.config || window?.useNuxtApp?.().payload?.config'
|
||||
: `window?.__NUXT__?.[${appId}]?.config || window?.useNuxtApp?.(${appId}).payload?.config`)
|
||||
|| {},
|
||||
].join('\n')
|
||||
},
|
||||
}
|
||||
|
||||
export const appConfigDeclarationTemplate: NuxtTemplate = {
|
||||
@ -497,6 +503,7 @@ export const nuxtConfigTemplate: NuxtTemplate = {
|
||||
`export const viewTransition = ${ctx.nuxt.options.experimental.viewTransition}`,
|
||||
`export const appId = ${JSON.stringify(ctx.nuxt.options.appId)}`,
|
||||
`export const outdatedBuildInterval = ${ctx.nuxt.options.experimental.checkOutdatedBuildInterval}`,
|
||||
`export const multiApp = ${!!ctx.nuxt.options.future.multiApp}`,
|
||||
].join('\n\n')
|
||||
},
|
||||
}
|
||||
|
@ -41,9 +41,9 @@
|
||||
"@types/sass-loader": "8.0.9",
|
||||
"@unhead/schema": "1.9.16",
|
||||
"@vitejs/plugin-vue": "5.1.2",
|
||||
"@vitejs/plugin-vue-jsx": "4.0.0",
|
||||
"@vue/compiler-core": "3.4.37",
|
||||
"@vue/compiler-sfc": "3.4.37",
|
||||
"@vitejs/plugin-vue-jsx": "4.0.1",
|
||||
"@vue/compiler-core": "3.4.38",
|
||||
"@vue/compiler-sfc": "3.4.38",
|
||||
"@vue/language-core": "2.0.29",
|
||||
"c12": "2.0.0-beta.1",
|
||||
"esbuild-loader": "4.2.2",
|
||||
@ -54,13 +54,13 @@
|
||||
"unbuild": "3.0.0-rc.7",
|
||||
"unctx": "2.3.1",
|
||||
"unenv": "1.10.0",
|
||||
"vite": "5.4.0",
|
||||
"vue": "3.4.37",
|
||||
"vite": "5.4.1",
|
||||
"vue": "3.4.38",
|
||||
"vue-bundle-renderer": "2.1.0",
|
||||
"vue-loader": "17.4.2",
|
||||
"vue-router": "4.4.3",
|
||||
"webpack": "5.93.0",
|
||||
"webpack-dev-middleware": "7.3.0"
|
||||
"webpack-dev-middleware": "7.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"compatx": "^0.1.8",
|
||||
|
@ -175,7 +175,29 @@ export default defineUntypedSchema({
|
||||
* ```
|
||||
*/
|
||||
buildDir: {
|
||||
$resolve: async (val: string | undefined, get): Promise<string> => resolve(await get('rootDir') as string, val || '.nuxt'),
|
||||
$resolve: async (val: string | undefined, get): Promise<string> => {
|
||||
const rootDir = await get('rootDir') as string
|
||||
|
||||
if (val) {
|
||||
return resolve(rootDir, val)
|
||||
}
|
||||
|
||||
const defaultBuildDir = resolve(rootDir, '.nuxt')
|
||||
|
||||
const isDev = await get('dev') as boolean
|
||||
if (isDev) {
|
||||
return defaultBuildDir
|
||||
}
|
||||
|
||||
// TODO: nuxi CLI should ensure .nuxt dir exists
|
||||
if (!existsSync(defaultBuildDir)) {
|
||||
// This is to ensure that types continue to work for CI builds
|
||||
return defaultBuildDir
|
||||
}
|
||||
|
||||
// TODO: handle build caching + using buildId in directory
|
||||
return resolve(rootDir, 'node_modules/.cache/nuxt/builds', 'production')
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -353,6 +353,10 @@ export default defineUntypedSchema({
|
||||
/** @type {typeof import('#app/components/nuxt-link')['NuxtLinkOptions']} */
|
||||
nuxtLink: {
|
||||
componentName: 'NuxtLink',
|
||||
prefetch: true,
|
||||
prefetchOn: {
|
||||
visibility: true,
|
||||
},
|
||||
},
|
||||
/**
|
||||
* Options that apply to `useAsyncData` (and also therefore `useFetch`)
|
||||
|
@ -45,7 +45,6 @@ export default defineUntypedSchema({
|
||||
'vue',
|
||||
'@vue/runtime-core',
|
||||
'@vue/compiler-sfc',
|
||||
'@vue/runtime-dom',
|
||||
'vue-router',
|
||||
'vue-router/auto-routes',
|
||||
'unplugin-vue-router/client',
|
||||
|
@ -19,9 +19,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/html-minifier": "4.0.5",
|
||||
"@unocss/reset": "0.62.1",
|
||||
"@unocss/reset": "0.62.2",
|
||||
"critters": "0.0.24",
|
||||
"execa": "9.3.0",
|
||||
"execa": "9.3.1",
|
||||
"globby": "14.0.2",
|
||||
"html-minifier": "4.0.0",
|
||||
"html-validate": "8.21.0",
|
||||
@ -30,7 +30,7 @@
|
||||
"pathe": "1.1.2",
|
||||
"prettier": "3.3.3",
|
||||
"scule": "1.3.0",
|
||||
"unocss": "0.62.1",
|
||||
"vite": "5.4.0"
|
||||
"unocss": "0.62.2",
|
||||
"vite": "5.4.1"
|
||||
}
|
||||
}
|
||||
|
@ -27,21 +27,21 @@
|
||||
"@nuxt/schema": "workspace:*",
|
||||
"@types/clear": "0.1.4",
|
||||
"@types/estree": "1.0.5",
|
||||
"rollup": "4.20.0",
|
||||
"rollup": "4.21.0",
|
||||
"unbuild": "3.0.0-rc.7",
|
||||
"vue": "3.4.37"
|
||||
"vue": "3.4.38"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxt/kit": "workspace:*",
|
||||
"@rollup/plugin-replace": "^5.0.7",
|
||||
"@vitejs/plugin-vue": "^5.1.2",
|
||||
"@vitejs/plugin-vue-jsx": "^4.0.0",
|
||||
"@vitejs/plugin-vue-jsx": "^4.0.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"clear": "^0.1.0",
|
||||
"consola": "^3.2.3",
|
||||
"cssnano": "^7.0.5",
|
||||
"defu": "^6.1.4",
|
||||
"esbuild": "^0.23.0",
|
||||
"esbuild": "^0.23.1",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"estree-walker": "^3.0.3",
|
||||
"externality": "^1.0.2",
|
||||
@ -61,8 +61,8 @@
|
||||
"strip-literal": "^2.1.0",
|
||||
"ufo": "^1.5.4",
|
||||
"unenv": "^1.10.0",
|
||||
"unplugin": "^1.12.1",
|
||||
"vite": "^5.4.0",
|
||||
"unplugin": "^1.12.2",
|
||||
"vite": "^5.4.1",
|
||||
"vite-node": "^2.0.5",
|
||||
"vite-plugin-checker": "^0.7.2",
|
||||
"vue-bundle-renderer": "^2.1.0"
|
||||
|
@ -45,7 +45,7 @@
|
||||
"lodash-es": "4.17.21",
|
||||
"magic-string": "^0.30.11",
|
||||
"memfs": "^4.11.1",
|
||||
"mini-css-extract-plugin": "^2.9.0",
|
||||
"mini-css-extract-plugin": "^2.9.1",
|
||||
"mlly": "^1.7.1",
|
||||
"ohash": "^1.1.3",
|
||||
"pathe": "^1.1.2",
|
||||
@ -60,13 +60,13 @@
|
||||
"time-fix-plugin": "^2.0.7",
|
||||
"ufo": "^1.5.4",
|
||||
"unenv": "^1.10.0",
|
||||
"unplugin": "^1.12.1",
|
||||
"unplugin": "^1.12.2",
|
||||
"url-loader": "^4.1.1",
|
||||
"vue-bundle-renderer": "^2.1.0",
|
||||
"vue-loader": "^17.4.2",
|
||||
"webpack": "^5.93.0",
|
||||
"webpack-bundle-analyzer": "^4.10.2",
|
||||
"webpack-dev-middleware": "^7.3.0",
|
||||
"webpack-dev-middleware": "^7.4.0",
|
||||
"webpack-hot-middleware": "^2.26.1",
|
||||
"webpack-virtual-modules": "^0.6.2",
|
||||
"webpackbar": "^6.0.1"
|
||||
@ -78,9 +78,9 @@
|
||||
"@types/pify": "5.0.4",
|
||||
"@types/webpack-bundle-analyzer": "4.7.0",
|
||||
"@types/webpack-hot-middleware": "2.25.9",
|
||||
"rollup": "4.20.0",
|
||||
"rollup": "4.21.0",
|
||||
"unbuild": "3.0.0-rc.7",
|
||||
"vue": "3.4.37"
|
||||
"vue": "3.4.38"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.3.4"
|
||||
|
1677
pnpm-lock.yaml
1677
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
1
test/fixtures/basic-types/nuxt.config.ts
vendored
1
test/fixtures/basic-types/nuxt.config.ts
vendored
@ -9,7 +9,6 @@ export default defineNuxtConfig({
|
||||
future: {
|
||||
typescriptBundlerResolution: process.env.MODULE_RESOLUTION === 'bundler',
|
||||
},
|
||||
buildDir: process.env.NITRO_BUILD_DIR,
|
||||
builder: process.env.TEST_BUILDER as 'webpack' | 'vite' ?? 'vite',
|
||||
theme: './extends/bar',
|
||||
extends: [
|
||||
|
2
test/fixtures/basic/nuxt.config.ts
vendored
2
test/fixtures/basic/nuxt.config.ts
vendored
@ -31,7 +31,6 @@ export default defineNuxtConfig({
|
||||
include: ['keepalive-in-config', 'not-keepalive-in-nuxtpage'],
|
||||
},
|
||||
},
|
||||
buildDir: process.env.NITRO_BUILD_DIR,
|
||||
builder: process.env.TEST_BUILDER as 'webpack' | 'vite' ?? 'vite',
|
||||
appId: 'nuxt-app-basic',
|
||||
build: {
|
||||
@ -68,7 +67,6 @@ export default defineNuxtConfig({
|
||||
'/hydration/spa-redirection/**': { ssr: false },
|
||||
'/no-scripts': { experimentalNoScripts: true },
|
||||
},
|
||||
output: { dir: process.env.NITRO_OUTPUT_DIR },
|
||||
prerender: {
|
||||
routes: [
|
||||
'/random/a',
|
||||
|
@ -23,8 +23,6 @@ if (process.env.TEST_ENV !== 'built' && !isWindows) {
|
||||
setupTimeout: (isWindows ? 360 : 120) * 1000,
|
||||
nuxtConfig: {
|
||||
builder: isWebpack ? 'webpack' : 'vite',
|
||||
buildDir: process.env.NITRO_BUILD_DIR,
|
||||
nitro: { output: { dir: process.env.NITRO_OUTPUT_DIR } },
|
||||
},
|
||||
})
|
||||
|
||||
|
95
test/nuxt/components.test.ts
Normal file
95
test/nuxt/components.test.ts
Normal file
@ -0,0 +1,95 @@
|
||||
/// <reference path="../fixtures/basic/.nuxt/nuxt.d.ts" />
|
||||
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { mountSuspended } from '@nuxt/test-utils/runtime'
|
||||
|
||||
import { nuxtLinkDefaults } from '#build/nuxt.config.mjs'
|
||||
|
||||
describe('nuxt-link:prefetch', () => {
|
||||
it('should prefetch on visibility by default', async () => {
|
||||
const component = defineNuxtLink(nuxtLinkDefaults)
|
||||
|
||||
const { observer } = useMockObserver()
|
||||
|
||||
const nuxtApp = useNuxtApp()
|
||||
nuxtApp.hooks.callHook = vi.fn(() => Promise.resolve())
|
||||
|
||||
await mountSuspended(component, { props: { to: '/to' } })
|
||||
|
||||
expect(nuxtApp.hooks.callHook).not.toHaveBeenCalled()
|
||||
|
||||
await observer.trigger()
|
||||
expect(nuxtApp.hooks.callHook).toHaveBeenCalledTimes(1)
|
||||
|
||||
await observer.trigger()
|
||||
expect(nuxtApp.hooks.callHook).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should prefetch with custom string `prefetchOn`', async () => {
|
||||
const component = defineNuxtLink(nuxtLinkDefaults)
|
||||
const nuxtApp = useNuxtApp()
|
||||
nuxtApp.hooks.callHook = vi.fn(() => Promise.resolve())
|
||||
|
||||
const { observer } = useMockObserver()
|
||||
const wrapper = await mountSuspended(component, { props: { to: '/to', prefetchOn: 'interaction' } })
|
||||
|
||||
await observer.trigger()
|
||||
expect(nuxtApp.hooks.callHook).not.toHaveBeenCalled()
|
||||
|
||||
await wrapper.find('a').trigger('focus')
|
||||
expect(nuxtApp.hooks.callHook).toHaveBeenCalledTimes(1)
|
||||
|
||||
await wrapper.find('a').trigger('focus')
|
||||
expect(nuxtApp.hooks.callHook).toHaveBeenCalledTimes(1)
|
||||
|
||||
await wrapper.find('a').trigger('pointerenter')
|
||||
expect(nuxtApp.hooks.callHook).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should prefetch with custom object `prefetchOn`', async () => {
|
||||
const component = defineNuxtLink(nuxtLinkDefaults)
|
||||
const nuxtApp = useNuxtApp()
|
||||
nuxtApp.hooks.callHook = vi.fn(() => Promise.resolve())
|
||||
|
||||
const { observer } = useMockObserver()
|
||||
await mountSuspended(component, { props: { to: '/to', prefetchOn: { interaction: true } } })
|
||||
|
||||
await observer.trigger()
|
||||
expect(nuxtApp.hooks.callHook).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should prefetch with custom object `prefetchOn` overriding default', async () => {
|
||||
const component = defineNuxtLink(nuxtLinkDefaults)
|
||||
const nuxtApp = useNuxtApp()
|
||||
nuxtApp.hooks.callHook = vi.fn(() => Promise.resolve())
|
||||
|
||||
const { observer } = useMockObserver()
|
||||
await mountSuspended(component, { props: { to: '/to', prefetchOn: { interaction: true, visibility: false } } })
|
||||
|
||||
await observer.trigger()
|
||||
expect(nuxtApp.hooks.callHook).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
function useMockObserver () {
|
||||
let callback: (entries: Array<{ target: HTMLElement, isIntersecting: boolean }>) => unknown
|
||||
let el: HTMLElement
|
||||
const mockObserver = class IntersectionObserver {
|
||||
el: HTMLElement
|
||||
constructor (_callback?: (entries: Array<{ target: HTMLElement, isIntersecting: boolean }>) => unknown) {
|
||||
callback ||= _callback
|
||||
}
|
||||
|
||||
observe = (_el: HTMLElement) => { el = _el }
|
||||
|
||||
trigger = () => callback?.([{ target: el, isIntersecting: true }])
|
||||
unobserve = () => {}
|
||||
disconnect = () => {}
|
||||
}
|
||||
|
||||
window.IntersectionObserver = mockObserver as any
|
||||
|
||||
const observer = new mockObserver()
|
||||
|
||||
return { observer }
|
||||
}
|
@ -14,8 +14,6 @@ await setup({
|
||||
setupTimeout: (isWindows ? 360 : 120) * 1000,
|
||||
nuxtConfig: {
|
||||
builder: isWebpack ? 'webpack' : 'vite',
|
||||
buildDir: process.env.NITRO_BUILD_DIR,
|
||||
nitro: { output: { dir: process.env.NITRO_OUTPUT_DIR } },
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -114,7 +114,9 @@ export function parseData (html: string) {
|
||||
attrs: {},
|
||||
}
|
||||
}
|
||||
const { script, attrs = '' } = html.match(/<script type="application\/json" id="__NUXT_DATA__"(?<attrs>[^>]+)>(?<script>.*?)<\/script>/)?.groups || {}
|
||||
|
||||
const regexp = /<script type="application\/json" data-nuxt-data="[^"]+"(?<attrs>[^>]+)>(?<script>.*?)<\/script>/
|
||||
const { script, attrs = '' } = html.match(regexp)?.groups || {}
|
||||
const _attrs: Record<string, string> = {}
|
||||
for (const attr of attrs.matchAll(/( |^)(?<key>[\w-]+)="(?<value>[^"]+)"/g)) {
|
||||
_attrs[attr!.groups!.key!] = attr!.groups!.value!
|
||||
|
Loading…
Reference in New Issue
Block a user