mirror of
https://github.com/nuxt/nuxt.git
synced 2025-01-22 11:22:43 +00:00
Merge remote-tracking branch 'origin/main' into feat/oxc
This commit is contained in:
commit
84680265f2
@ -1,4 +1,4 @@
|
|||||||
FROM node:lts@sha256:5c76d05034644fa8ecc9c2aa84e0a83cd981d0ef13af5455b87b9adf5b216561
|
FROM node:lts@sha256:35a5dd72bcac4bce43266408b58a02be6ff0b6098ffa6f5435aeea980a8951d7
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -fy libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdbus-1-3 libdrm2 libxkbcommon0 libatspi2.0-0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2 && \
|
apt-get install -fy libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdbus-1-3 libdrm2 libxkbcommon0 libatspi2.0-0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2 && \
|
||||||
|
3
.github/workflows/autofix.yml
vendored
3
.github/workflows/autofix.yml
vendored
@ -23,6 +23,9 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
|
- name: Check engine ranges, peer dependency ranges and installed versions
|
||||||
|
run: pnpm installed-check --fix
|
||||||
|
|
||||||
- name: Build (stub)
|
- name: Build (stub)
|
||||||
run: pnpm dev:prepare
|
run: pnpm dev:prepare
|
||||||
|
|
||||||
|
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@ -56,6 +56,9 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
|
- name: Check types
|
||||||
|
run: pnpm test:attw
|
||||||
|
|
||||||
- name: Cache dist
|
- name: Cache dist
|
||||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||||
with:
|
with:
|
||||||
|
@ -4,12 +4,14 @@ on:
|
|||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- "**/package.json"
|
- "**/package.json"
|
||||||
|
- "pnpm-lock.yaml"
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- 3.x
|
- 3.x
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- "**/package.json"
|
- "**/package.json"
|
||||||
|
- "pnpm-lock.yaml"
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- 3.x
|
- 3.x
|
||||||
@ -32,5 +34,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Lint monorepo
|
- name: Lint monorepo
|
||||||
run: pnpm sherif -r multiple-dependency-versions
|
run: pnpm sherif -r multiple-dependency-versions
|
||||||
|
|
||||||
|
- name: Check engine ranges, peer dependency ranges and installed versions
|
||||||
|
run: pnpm installed-check
|
@ -25,7 +25,7 @@ Nuxt uses conventions and an opinionated directory structure to automate repetit
|
|||||||
- **Auto-imports:** write Vue composables and components in their respective directories and use them without having to import them with the benefits of tree-shaking and optimized JS bundles.
|
- **Auto-imports:** write Vue composables and components in their respective directories and use them without having to import them with the benefits of tree-shaking and optimized JS bundles.
|
||||||
- **Data-fetching utilities:** Nuxt provides composables to handle SSR-compatible data fetching as well as different strategies.
|
- **Data-fetching utilities:** Nuxt provides composables to handle SSR-compatible data fetching as well as different strategies.
|
||||||
- **Zero-config TypeScript support:** write type-safe code without having to learn TypeScript with our auto-generated types and `tsconfig.json`
|
- **Zero-config TypeScript support:** write type-safe code without having to learn TypeScript with our auto-generated types and `tsconfig.json`
|
||||||
- **Configured build tools:** we use [Vite](https://vitejs.dev) by default to support hot module replacement (HMR) in development and bundling your code for production with best-practices baked-in.
|
- **Configured build tools:** we use [Vite](https://vite.dev) by default to support hot module replacement (HMR) in development and bundling your code for production with best-practices baked-in.
|
||||||
|
|
||||||
Nuxt takes care of these and provides both frontend and backend functionality so you can focus on what matters: **creating your web application**.
|
Nuxt takes care of these and provides both frontend and backend functionality so you can focus on what matters: **creating your web application**.
|
||||||
|
|
||||||
|
@ -73,7 +73,8 @@ export default defineNuxtConfig({
|
|||||||
// resetAsyncDataToUndefined: true,
|
// resetAsyncDataToUndefined: true,
|
||||||
// templateUtils: true,
|
// templateUtils: true,
|
||||||
// relativeWatchPaths: true,
|
// relativeWatchPaths: true,
|
||||||
// normalizeComponentNames: false
|
// normalizeComponentNames: false,
|
||||||
|
// spaLoadingTemplateLocation: 'within',
|
||||||
// defaults: {
|
// defaults: {
|
||||||
// useAsyncData: {
|
// useAsyncData: {
|
||||||
// deep: true
|
// deep: true
|
||||||
@ -237,6 +238,45 @@ export default defineNuxtConfig({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### New DOM Location for SPA Loading Screen
|
||||||
|
|
||||||
|
🚦 **Impact Level**: Minimal
|
||||||
|
|
||||||
|
##### What Changed
|
||||||
|
|
||||||
|
When rendering a client-only page (with `ssr: false`), we optionally render a loading screen (from `app/spa-loading-template.html`), within the Nuxt app root:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div id="__nuxt">
|
||||||
|
<!-- spa loading template -->
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, we default to rendering the template alongside the Nuxt app root:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div id="__nuxt"></div>
|
||||||
|
<!-- spa loading template -->
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Reasons for Change
|
||||||
|
|
||||||
|
This allows the spa loading template to remain in the DOM until the Vue app suspense resolves, preventing a flash of white.
|
||||||
|
|
||||||
|
##### Migration Steps
|
||||||
|
|
||||||
|
If you were targeting the spa loading template with CSS or `document.queryElement` you will need to update your selectors. For this purpose you can use the new `app.spaLoaderTag` and `app.spaLoaderAttrs` configuration options.
|
||||||
|
|
||||||
|
Alternatively, you can revert to the previous behaviour with:
|
||||||
|
|
||||||
|
```ts twoslash [nuxt.config.ts]
|
||||||
|
export default defineNuxtConfig({
|
||||||
|
experimental: {
|
||||||
|
spaLoadingTemplateLocation: 'within',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
#### Scan Page Meta After Resolution
|
#### Scan Page Meta After Resolution
|
||||||
|
|
||||||
🚦 **Impact Level**: Minimal
|
🚦 **Impact Level**: Minimal
|
||||||
|
@ -91,6 +91,9 @@ pnpm dev -o
|
|||||||
|
|
||||||
```bash [bun]
|
```bash [bun]
|
||||||
bun run dev -o
|
bun run dev -o
|
||||||
|
|
||||||
|
# To use the Bun runtime during development
|
||||||
|
# bun --bun run dev -o
|
||||||
```
|
```
|
||||||
::
|
::
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ Name | Config File | How
|
|||||||
---------------------------------------------|---------------------------|-------------------------
|
---------------------------------------------|---------------------------|-------------------------
|
||||||
[Nitro](https://nitro.unjs.io) | ~~`nitro.config.ts`~~ | Use [`nitro`](/docs/api/nuxt-config#nitro) key in `nuxt.config`
|
[Nitro](https://nitro.unjs.io) | ~~`nitro.config.ts`~~ | Use [`nitro`](/docs/api/nuxt-config#nitro) key in `nuxt.config`
|
||||||
[PostCSS](https://postcss.org) | ~~`postcss.config.js`~~ | Use [`postcss`](/docs/api/nuxt-config#postcss) key in `nuxt.config`
|
[PostCSS](https://postcss.org) | ~~`postcss.config.js`~~ | Use [`postcss`](/docs/api/nuxt-config#postcss) key in `nuxt.config`
|
||||||
[Vite](https://vitejs.dev) | ~~`vite.config.ts`~~ | Use [`vite`](/docs/api/nuxt-config#vite) key in `nuxt.config`
|
[Vite](https://vite.dev) | ~~`vite.config.ts`~~ | Use [`vite`](/docs/api/nuxt-config#vite) key in `nuxt.config`
|
||||||
[webpack](https://webpack.js.org) | ~~`webpack.config.ts`~~ | Use [`webpack`](/docs/api/nuxt-config#webpack-1) key in `nuxt.config`
|
[webpack](https://webpack.js.org) | ~~`webpack.config.ts`~~ | Use [`webpack`](/docs/api/nuxt-config#webpack-1) key in `nuxt.config`
|
||||||
|
|
||||||
Here is a list of other common config files:
|
Here is a list of other common config files:
|
||||||
@ -162,8 +162,8 @@ Name | Config File | How To
|
|||||||
---------------------------------------------|-------------------------|--------------------------
|
---------------------------------------------|-------------------------|--------------------------
|
||||||
[TypeScript](https://www.typescriptlang.org) | `tsconfig.json` | [More Info](/docs/guide/concepts/typescript#nuxttsconfigjson)
|
[TypeScript](https://www.typescriptlang.org) | `tsconfig.json` | [More Info](/docs/guide/concepts/typescript#nuxttsconfigjson)
|
||||||
[ESLint](https://eslint.org) | `eslint.config.js` | [More Info](https://eslint.org/docs/latest/use/configure/configuration-files)
|
[ESLint](https://eslint.org) | `eslint.config.js` | [More Info](https://eslint.org/docs/latest/use/configure/configuration-files)
|
||||||
[Prettier](https://prettier.io) | `.prettierrc.json` | [More Info](https://prettier.io/docs/en/configuration.html)
|
[Prettier](https://prettier.io) | `prettier.config.js` | [More Info](https://prettier.io/docs/en/configuration.html)
|
||||||
[Stylelint](https://stylelint.io) | `.stylelintrc.json` | [More Info](https://stylelint.io/user-guide/configure)
|
[Stylelint](https://stylelint.io) | `stylelint.config.js` | [More Info](https://stylelint.io/user-guide/configure)
|
||||||
[TailwindCSS](https://tailwindcss.com) | `tailwind.config.js` | [More Info](https://tailwindcss.nuxtjs.org/tailwind/config)
|
[TailwindCSS](https://tailwindcss.com) | `tailwind.config.js` | [More Info](https://tailwindcss.nuxtjs.org/tailwind/config)
|
||||||
[Vitest](https://vitest.dev) | `vitest.config.ts` | [More Info](https://vitest.dev/config)
|
[Vitest](https://vitest.dev) | `vitest.config.ts` | [More Info](https://vitest.dev/config)
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ For example, referencing an image file in the `public/img/` directory, available
|
|||||||
|
|
||||||
## Assets Directory
|
## Assets Directory
|
||||||
|
|
||||||
Nuxt uses [Vite](https://vitejs.dev/guide/assets.html) (default) or [webpack](https://webpack.js.org/guides/asset-management) to build and bundle your application. The main function of these build tools is to process JavaScript files, but they can be extended through [plugins](https://vitejs.dev/plugins) (for Vite) or [loaders](https://webpack.js.org/loaders) (for webpack) to process other kind of assets, like stylesheets, fonts or SVG. This step transforms the original file mainly for performance or caching purposes (such as stylesheets minification or browser cache invalidation).
|
Nuxt uses [Vite](https://vite.dev/guide/assets.html) (default) or [webpack](https://webpack.js.org/guides/asset-management) to build and bundle your application. The main function of these build tools is to process JavaScript files, but they can be extended through [plugins](https://vite.dev/plugins) (for Vite) or [loaders](https://webpack.js.org/loaders) (for webpack) to process other kind of assets, like stylesheets, fonts or SVG. This step transforms the original file mainly for performance or caching purposes (such as stylesheets minification or browser cache invalidation).
|
||||||
|
|
||||||
By convention, Nuxt uses the [`assets/`](/docs/guide/directory-structure/assets) directory to store these files but there is no auto-scan functionality for this directory, and you can use any other name for it.
|
By convention, Nuxt uses the [`assets/`](/docs/guide/directory-structure/assets) directory to store these files but there is no auto-scan functionality for this directory, and you can use any other name for it.
|
||||||
|
|
||||||
|
@ -204,7 +204,7 @@ export default defineNuxtConfig({
|
|||||||
In both cases, the compiled stylesheets will be inlined in the HTML rendered by Nuxt.
|
In both cases, the compiled stylesheets will be inlined in the HTML rendered by Nuxt.
|
||||||
::
|
::
|
||||||
|
|
||||||
If you need to inject code in pre-processed files, like a [sass partial](https://sass-lang.com/documentation/at-rules/use#partials) with color variables, you can do so with the vite [preprocessors options](https://vitejs.dev/config/shared-options.html#css-preprocessoroptions).
|
If you need to inject code in pre-processed files, like a [sass partial](https://sass-lang.com/documentation/at-rules/use#partials) with color variables, you can do so with the vite [preprocessors options](https://vite.dev/config/shared-options.html#css-preprocessoroptions).
|
||||||
|
|
||||||
Create some partials in your `assets` directory:
|
Create some partials in your `assets` directory:
|
||||||
|
|
||||||
@ -416,7 +416,7 @@ SFC style blocks support preprocessors syntax. Vite come with built-in support f
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
You can refer to the [Vite CSS docs](https://vitejs.dev/guide/features.html#css) and the [@vitejs/plugin-vue docs](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue).
|
You can refer to the [Vite CSS docs](https://vite.dev/guide/features.html#css) and the [@vitejs/plugin-vue docs](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue).
|
||||||
For webpack users, refer to the [vue loader docs](https://vue-loader.vuejs.org).
|
For webpack users, refer to the [vue loader docs](https://vue-loader.vuejs.org).
|
||||||
|
|
||||||
## Using PostCSS
|
## Using PostCSS
|
||||||
|
@ -21,7 +21,7 @@ We chose to build Nuxt on top of Vue for these reasons:
|
|||||||
|
|
||||||
### Single File Components
|
### Single File Components
|
||||||
|
|
||||||
[Vue’s single-file components](https://v3.vuejs.org/guide/single-file-component.html) (SFC or `*.vue` files) encapsulate the markup (`<template>`), logic (`<script>`) and styling (`<style>`) of a Vue component. Nuxt provides a zero-config experience for SFCs with [Hot Module Replacement](https://vitejs.dev/guide/features.html#hot-module-replacement) that offers a seamless developer experience.
|
[Vue’s single-file components](https://vuejs.org/guide/scaling-up/sfc.html) (SFC or `*.vue` files) encapsulate the markup (`<template>`), logic (`<script>`) and styling (`<style>`) of a Vue component. Nuxt provides a zero-config experience for SFCs with [Hot Module Replacement](https://vite.dev/guide/features.html#hot-module-replacement) that offers a seamless developer experience.
|
||||||
|
|
||||||
### Auto-imports
|
### Auto-imports
|
||||||
|
|
||||||
|
@ -451,3 +451,24 @@ In this case, the component name would be `MyComponent`, as far as Vue is concer
|
|||||||
But in order to auto-import it, you would need to use `SomeFolderMyComponent`.
|
But in order to auto-import it, you would need to use `SomeFolderMyComponent`.
|
||||||
|
|
||||||
By setting `experimental.normalizeComponentNames`, these two values match, and Vue will generate a component name that matches the Nuxt pattern for component naming.
|
By setting `experimental.normalizeComponentNames`, these two values match, and Vue will generate a component name that matches the Nuxt pattern for component naming.
|
||||||
|
|
||||||
|
## spaLoadingTemplateLocation
|
||||||
|
|
||||||
|
When rendering a client-only page (with `ssr: false`), we optionally render a loading screen (from `app/spa-loading-template.html`).
|
||||||
|
|
||||||
|
It can be set to `within`, which will render it like this:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div id="__nuxt">
|
||||||
|
<!-- spa loading template -->
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can render the template alongside the Nuxt app root by setting it to `body`:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div id="__nuxt"></div>
|
||||||
|
<!-- spa loading template -->
|
||||||
|
```
|
||||||
|
|
||||||
|
This avoids a white flash when hydrating a client-only page.
|
||||||
|
@ -85,7 +85,7 @@ export default defineNuxtConfig({
|
|||||||
### typescriptBundlerResolution
|
### typescriptBundlerResolution
|
||||||
|
|
||||||
This enables 'Bundler' module resolution mode for TypeScript, which is the recommended setting
|
This enables 'Bundler' module resolution mode for TypeScript, which is the recommended setting
|
||||||
for frameworks like Nuxt and [Vite](https://vitejs.dev/guide/performance.html#reduce-resolve-operations).
|
for frameworks like Nuxt and [Vite](https://vite.dev/guide/performance.html#reduce-resolve-operations).
|
||||||
|
|
||||||
It improves type support when using modern libraries with `exports`.
|
It improves type support when using modern libraries with `exports`.
|
||||||
|
|
||||||
|
@ -104,6 +104,10 @@ If you want to extend a private remote source, you need to add the environment v
|
|||||||
If you want to extend a remote source from a self-hosted GitHub or GitLab instance, you need to supply its URL with the `GIGET_GITHUB_URL=<url>` or `GIGET_GITLAB_URL=<url>` environment variable - or directly configure it with [the `auth` option](https://github.com/unjs/c12#extending-config-layer-from-remote-sources) in your `nuxt.config`.
|
If you want to extend a remote source from a self-hosted GitHub or GitLab instance, you need to supply its URL with the `GIGET_GITHUB_URL=<url>` or `GIGET_GITLAB_URL=<url>` environment variable - or directly configure it with [the `auth` option](https://github.com/unjs/c12#extending-config-layer-from-remote-sources) in your `nuxt.config`.
|
||||||
::
|
::
|
||||||
|
|
||||||
|
::warning
|
||||||
|
Bear in mind that if you are extending a remote source as a layer, you will not be able to access its dependencies outside of Nuxt. For example, if the remote layer depends on an eslint plugin, this will not be usable in your eslint config. That is because these dependencies will be located in a special location (`node_modules/.c12/layer_name/node_modules/`) that is not accessible to your package manager.
|
||||||
|
::
|
||||||
|
|
||||||
::note
|
::note
|
||||||
When using git remote sources, if a layer has npm dependencies and you wish to install them, you can do so by specifying `install: true` in your layer options.
|
When using git remote sources, if a layer has npm dependencies and you wish to install them, you can do so by specifying `install: true` in your layer options.
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ Read more on `unhead` documentation.
|
|||||||
useHeadSafe(input: MaybeComputedRef<HeadSafe>): void
|
useHeadSafe(input: MaybeComputedRef<HeadSafe>): void
|
||||||
```
|
```
|
||||||
|
|
||||||
The whitelist of safe values is:
|
The list of allowed values is:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
export default {
|
export default {
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
---
|
|
||||||
title: "useId"
|
|
||||||
description: Generate an SSR-friendly unique identifier that can be passed to accessibility attributes.
|
|
||||||
links:
|
|
||||||
- label: Source
|
|
||||||
icon: i-simple-icons-github
|
|
||||||
to: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/id.ts
|
|
||||||
size: xs
|
|
||||||
---
|
|
||||||
|
|
||||||
::important
|
|
||||||
This composable is available since [Nuxt v3.10](/blog/v3-10#ssr-safe-accessible-unique-id-creation).
|
|
||||||
::
|
|
||||||
|
|
||||||
`useId` generates an SSR-friendly unique identifier that can be passed to accessibility attributes.
|
|
||||||
|
|
||||||
Call `useId` at the top level of your component to generate a unique string identifier:
|
|
||||||
|
|
||||||
```vue [components/EmailField.vue]
|
|
||||||
<script setup lang="ts">
|
|
||||||
const id = useId()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<label :for="id">Email</label>
|
|
||||||
<input :id="id" name="email" type="email" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
```
|
|
||||||
|
|
||||||
::note
|
|
||||||
`useId` must be used in a component with a single root element, as it uses this root element's attributes to pass the id from server to client.
|
|
||||||
::
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
|
|
||||||
`useId` does not take any parameters.
|
|
||||||
|
|
||||||
## Returns
|
|
||||||
|
|
||||||
`useId` returns a unique string associated with this particular `useId` call in this particular component.
|
|
@ -16,6 +16,10 @@ By default, [`useFetch`](/docs/api/composables/use-fetch) blocks navigation unti
|
|||||||
`useLazyFetch` has the same signature as [`useFetch`](/docs/api/composables/use-fetch).
|
`useLazyFetch` has the same signature as [`useFetch`](/docs/api/composables/use-fetch).
|
||||||
::
|
::
|
||||||
|
|
||||||
|
::note
|
||||||
|
Awaiting `useLazyFetch` in this mode only ensures the call is initialized. On client-side navigation, data may not be immediately available, and you should make sure to handle the pending state in your app.
|
||||||
|
::
|
||||||
|
|
||||||
:read-more{to="/docs/api/composables/use-fetch"}
|
:read-more{to="/docs/api/composables/use-fetch"}
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
@ -17,3 +17,4 @@ The `upgrade` command upgrades Nuxt to the latest version.
|
|||||||
Option | Default | Description
|
Option | Default | Description
|
||||||
-------------------------|-----------------|------------------
|
-------------------------|-----------------|------------------
|
||||||
`--force, -f` | `false` | Removes `node_modules` and lock files before upgrade.
|
`--force, -f` | `false` | Removes `node_modules` and lock files before upgrade.
|
||||||
|
`--channel, -ch` | `"stable"` | Specify a channel to install from ("nightly" or "stable")
|
||||||
|
@ -123,7 +123,7 @@ export interface ExtendViteConfigOptions {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
::read-more{to="https://vitejs.dev/config" target="_blank" color="gray" icon="i-simple-icons-vite"}
|
::read-more{to="https://vite.dev/config" target="_blank" color="gray" icon="i-simple-icons-vite"}
|
||||||
Checkout Vite website for more information about its configuration.
|
Checkout Vite website for more information about its configuration.
|
||||||
::
|
::
|
||||||
|
|
||||||
@ -329,7 +329,7 @@ interface ExtendViteConfigOptions {
|
|||||||
```
|
```
|
||||||
|
|
||||||
::tip
|
::tip
|
||||||
See [Vite website](https://vitejs.dev/guide/api-plugin.html) for more information about Vite plugins. You can also use [this repository](https://github.com/vitejs/awesome-vite#plugins) to find a plugin that suits your needs.
|
See [Vite website](https://vite.dev/guide/api-plugin.html) for more information about Vite plugins. You can also use [this repository](https://github.com/vitejs/awesome-vite#plugins) to find a plugin that suits your needs.
|
||||||
::
|
::
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
@ -12,7 +12,7 @@ Once you've read the [general contribution guide](/docs/community/contribution),
|
|||||||
- `packages/nuxt`: The core of Nuxt, published as [`nuxt`](https://npmjs.com/package/nuxt).
|
- `packages/nuxt`: The core of Nuxt, published as [`nuxt`](https://npmjs.com/package/nuxt).
|
||||||
- `packages/schema`: Cross-version Nuxt typedefs and defaults, published as [`@nuxt/schema`](https://npmjs.com/package/@nuxt/schema).
|
- `packages/schema`: Cross-version Nuxt typedefs and defaults, published as [`@nuxt/schema`](https://npmjs.com/package/@nuxt/schema).
|
||||||
- `packages/test-utils`: Test utilities for Nuxt, published as [`@nuxt/test-utils`](https://npmjs.com/package/@nuxt/test-utils).
|
- `packages/test-utils`: Test utilities for Nuxt, published as [`@nuxt/test-utils`](https://npmjs.com/package/@nuxt/test-utils).
|
||||||
- `packages/vite`: The [Vite](https://vitejs.dev) bundler for Nuxt, published as [`@nuxt/vite-builder`](https://npmjs.com/package/@nuxt/vite-builder).
|
- `packages/vite`: The [Vite](https://vite.dev) bundler for Nuxt, published as [`@nuxt/vite-builder`](https://npmjs.com/package/@nuxt/vite-builder).
|
||||||
- `packages/webpack`: The [webpack](https://webpack.js.org) bundler for Nuxt 3, published as [`@nuxt/webpack-builder`](https://npmjs.com/package/@nuxt/webpack-builder).
|
- `packages/webpack`: The [webpack](https://webpack.js.org) bundler for Nuxt 3, published as [`@nuxt/webpack-builder`](https://npmjs.com/package/@nuxt/webpack-builder).
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
@ -5,7 +5,7 @@ description: 'Learn how to migrate from Nuxt 2 to Nuxt 3 build tooling.'
|
|||||||
|
|
||||||
We use the following build tools by default:
|
We use the following build tools by default:
|
||||||
|
|
||||||
- [Vite](https://vitejs.dev) or [webpack](https://webpack.js.org)
|
- [Vite](https://vite.dev) or [webpack](https://webpack.js.org)
|
||||||
- [Rollup](https://rollupjs.org)
|
- [Rollup](https://rollupjs.org)
|
||||||
- [PostCSS](https://postcss.org)
|
- [PostCSS](https://postcss.org)
|
||||||
- [esbuild](https://esbuild.github.io)
|
- [esbuild](https://esbuild.github.io)
|
||||||
|
26
package.json
26
package.json
@ -30,6 +30,7 @@
|
|||||||
"test:runtime": "vitest -c vitest.nuxt.config.ts",
|
"test:runtime": "vitest -c vitest.nuxt.config.ts",
|
||||||
"test:types": "pnpm --filter './test/fixtures/**' test:types",
|
"test:types": "pnpm --filter './test/fixtures/**' test:types",
|
||||||
"test:unit": "vitest run packages/",
|
"test:unit": "vitest run packages/",
|
||||||
|
"test:attw": "pnpm --filter './packages/**' test:attw",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"typecheck:docs": "DOCS_TYPECHECK=true pnpm nuxi prepare && nuxt-content-twoslash verify --content-dir docs --languages html"
|
"typecheck:docs": "DOCS_TYPECHECK=true pnpm nuxi prepare && nuxt-content-twoslash verify --content-dir docs --languages html"
|
||||||
},
|
},
|
||||||
@ -50,33 +51,35 @@
|
|||||||
"@vue/shared": "3.5.13",
|
"@vue/shared": "3.5.13",
|
||||||
"c12": "2.0.1",
|
"c12": "2.0.1",
|
||||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
"jiti": "2.4.0",
|
"jiti": "2.4.1",
|
||||||
"magic-string": "^0.30.14",
|
"magic-string": "^0.30.14",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
|
||||||
"nuxt": "workspace:*",
|
"nuxt": "workspace:*",
|
||||||
"ohash": "1.1.4",
|
"ohash": "1.1.4",
|
||||||
"postcss": "8.4.49",
|
"postcss": "8.4.49",
|
||||||
"rollup": "4.27.4",
|
"rollup": "4.28.0",
|
||||||
"send": ">=1.1.0",
|
"send": ">=1.1.0",
|
||||||
"typescript": "5.6.3",
|
"typescript": "5.6.3",
|
||||||
"ufo": "1.5.4",
|
"ufo": "1.5.4",
|
||||||
"unbuild": "3.0.0-rc.11",
|
"unbuild": "3.0.0-rc.11",
|
||||||
"unhead": "1.11.13",
|
"unhead": "1.11.13",
|
||||||
"vite": "6.0.1",
|
"unimport": "3.13.4",
|
||||||
|
"vite": "6.0.2",
|
||||||
"vue": "3.5.13"
|
"vue": "3.5.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@arethetypeswrong/cli": "0.17.1",
|
||||||
"@nuxt/eslint-config": "0.7.2",
|
"@nuxt/eslint-config": "0.7.2",
|
||||||
"@nuxt/kit": "workspace:*",
|
"@nuxt/kit": "workspace:*",
|
||||||
"@nuxt/rspack-builder": "workspace:*",
|
"@nuxt/rspack-builder": "workspace:*",
|
||||||
"@nuxt/test-utils": "3.14.4",
|
"@nuxt/test-utils": "3.15.1",
|
||||||
"@nuxt/webpack-builder": "workspace:*",
|
"@nuxt/webpack-builder": "workspace:*",
|
||||||
"@testing-library/vue": "8.1.0",
|
"@testing-library/vue": "8.1.0",
|
||||||
"@types/node": "22.10.1",
|
"@types/node": "22.10.1",
|
||||||
"@types/semver": "7.5.8",
|
"@types/semver": "7.5.8",
|
||||||
"@unhead/schema": "1.11.13",
|
"@unhead/schema": "1.11.13",
|
||||||
"@unhead/vue": "1.11.13",
|
"@unhead/vue": "1.11.13",
|
||||||
"@vitest/coverage-v8": "2.1.6",
|
"@vitest/coverage-v8": "2.1.8",
|
||||||
"@vue/test-utils": "2.4.6",
|
"@vue/test-utils": "2.4.6",
|
||||||
"autoprefixer": "10.4.20",
|
"autoprefixer": "10.4.20",
|
||||||
"case-police": "0.7.2",
|
"case-police": "0.7.2",
|
||||||
@ -85,14 +88,15 @@
|
|||||||
"cssnano": "7.0.6",
|
"cssnano": "7.0.6",
|
||||||
"destr": "2.0.3",
|
"destr": "2.0.3",
|
||||||
"devalue": "5.1.1",
|
"devalue": "5.1.1",
|
||||||
"eslint": "9.15.0",
|
"eslint": "9.16.0",
|
||||||
"eslint-plugin-no-only-tests": "3.3.0",
|
"eslint-plugin-no-only-tests": "3.3.0",
|
||||||
"eslint-plugin-perfectionist": "4.1.2",
|
"eslint-plugin-perfectionist": "4.1.2",
|
||||||
"eslint-typegen": "0.3.2",
|
"eslint-typegen": "0.3.2",
|
||||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
"happy-dom": "15.11.7",
|
"happy-dom": "15.11.7",
|
||||||
"jiti": "2.4.0",
|
"installed-check": "9.3.0",
|
||||||
"knip": "5.38.2",
|
"jiti": "2.4.1",
|
||||||
|
"knip": "5.39.1",
|
||||||
"markdownlint-cli": "0.43.0",
|
"markdownlint-cli": "0.43.0",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
|
||||||
"nuxi": "3.16.0",
|
"nuxi": "3.16.0",
|
||||||
@ -109,14 +113,14 @@
|
|||||||
"tinyglobby": "0.2.10",
|
"tinyglobby": "0.2.10",
|
||||||
"typescript": "5.6.3",
|
"typescript": "5.6.3",
|
||||||
"ufo": "1.5.4",
|
"ufo": "1.5.4",
|
||||||
"vitest": "2.1.6",
|
"vitest": "2.1.8",
|
||||||
"vitest-environment-nuxt": "1.0.1",
|
"vitest-environment-nuxt": "1.0.1",
|
||||||
"vue": "3.5.13",
|
"vue": "3.5.13",
|
||||||
"vue-tsc": "2.1.10"
|
"vue-tsc": "2.1.10"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.14.2",
|
"packageManager": "pnpm@9.14.4",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^16.10.0 || >=18.0.0"
|
"node": "^20.9.0 || ^22.0.0 || >=23.0.0"
|
||||||
},
|
},
|
||||||
"version": ""
|
"version": ""
|
||||||
}
|
}
|
||||||
|
3
packages/kit/.attw.json
Normal file
3
packages/kit/.attw.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ignoreRules": ["cjs-resolves-to-esm"]
|
||||||
|
}
|
@ -23,7 +23,8 @@
|
|||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepack": "unbuild"
|
"prepack": "unbuild",
|
||||||
|
"test:attw": "attw --pack"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/schema": "workspace:*",
|
"@nuxt/schema": "workspace:*",
|
||||||
@ -34,7 +35,7 @@
|
|||||||
"errx": "^0.1.0",
|
"errx": "^0.1.0",
|
||||||
"globby": "^14.0.2",
|
"globby": "^14.0.2",
|
||||||
"ignore": "^6.0.2",
|
"ignore": "^6.0.2",
|
||||||
"jiti": "^2.4.0",
|
"jiti": "^2.4.1",
|
||||||
"klona": "^2.0.6",
|
"klona": "^2.0.6",
|
||||||
"mlly": "^1.7.3",
|
"mlly": "^1.7.3",
|
||||||
"ohash": "^1.1.4",
|
"ohash": "^1.1.4",
|
||||||
@ -48,15 +49,15 @@
|
|||||||
"untyped": "^1.5.1"
|
"untyped": "^1.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rspack/core": "1.1.4",
|
"@rspack/core": "1.1.5",
|
||||||
"@types/semver": "7.5.8",
|
"@types/semver": "7.5.8",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
|
||||||
"unbuild": "3.0.0-rc.11",
|
"unbuild": "3.0.0-rc.11",
|
||||||
"vite": "6.0.1",
|
"vite": "6.0.2",
|
||||||
"vitest": "2.1.6",
|
"vitest": "2.1.8",
|
||||||
"webpack": "5.96.1"
|
"webpack": "5.96.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.10.0"
|
"node": "^18.12.0 || ^20.0.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { existsSync, promises as fsp, lstatSync } from 'node:fs'
|
import { existsSync, promises as fsp, lstatSync } from 'node:fs'
|
||||||
import { pathToFileURL } from 'node:url'
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||||
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 { createJiti } from 'jiti'
|
||||||
import { resolve as resolveModule } from 'mlly'
|
import { parseNodeModulePath, resolve as resolveModule } from 'mlly'
|
||||||
import { isRelative } from 'ufo'
|
import { isRelative } from 'ufo'
|
||||||
import { useNuxt } from '../context'
|
import { useNuxt } from '../context'
|
||||||
import { resolveAlias, resolvePath } from '../resolve'
|
import { resolveAlias, resolvePath } from '../resolve'
|
||||||
@ -17,7 +17,7 @@ export async function installModule<
|
|||||||
T extends string | NuxtModule,
|
T extends string | NuxtModule,
|
||||||
Config extends Extract<NonNullable<NuxtConfig['modules']>[number], [T, any]>,
|
Config extends Extract<NonNullable<NuxtConfig['modules']>[number], [T, any]>,
|
||||||
> (moduleToInstall: T, inlineOptions?: [Config] extends [never] ? any : Config[1], nuxt: Nuxt = useNuxt()) {
|
> (moduleToInstall: T, inlineOptions?: [Config] extends [never] ? any : Config[1], nuxt: Nuxt = useNuxt()) {
|
||||||
const { nuxtModule, buildTimeModuleMeta } = await loadNuxtModuleInstance(moduleToInstall, nuxt)
|
const { nuxtModule, buildTimeModuleMeta, resolvedModulePath } = await loadNuxtModuleInstance(moduleToInstall, nuxt)
|
||||||
|
|
||||||
const localLayerModuleDirs = new Set<string>()
|
const localLayerModuleDirs = new Set<string>()
|
||||||
for (const l of nuxt.options._layers) {
|
for (const l of nuxt.options._layers) {
|
||||||
@ -33,9 +33,12 @@ export async function installModule<
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof moduleToInstall === 'string') {
|
const modulePath = resolvedModulePath || moduleToInstall
|
||||||
nuxt.options.build.transpile.push(normalizeModuleTranspilePath(moduleToInstall))
|
if (typeof modulePath === 'string') {
|
||||||
const directory = getDirectory(moduleToInstall)
|
const parsed = parseNodeModulePath(modulePath)
|
||||||
|
const moduleRoot = parsed.dir ? parsed.dir + parsed.name : modulePath
|
||||||
|
nuxt.options.build.transpile.push(normalizeModuleTranspilePath(moduleRoot))
|
||||||
|
const directory = parsed.dir ? moduleRoot : getDirectory(modulePath)
|
||||||
if (directory !== moduleToInstall && !localLayerModuleDirs.has(directory)) {
|
if (directory !== moduleToInstall && !localLayerModuleDirs.has(directory)) {
|
||||||
nuxt.options.modulesDir.push(resolve(directory, 'node_modules'))
|
nuxt.options.modulesDir.push(resolve(directory, 'node_modules'))
|
||||||
}
|
}
|
||||||
@ -74,6 +77,7 @@ export const normalizeModuleTranspilePath = (p: string) => {
|
|||||||
|
|
||||||
export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, nuxt: Nuxt = useNuxt()) {
|
export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, nuxt: Nuxt = useNuxt()) {
|
||||||
let buildTimeModuleMeta: ModuleMeta = {}
|
let buildTimeModuleMeta: ModuleMeta = {}
|
||||||
|
let resolvedModulePath: string | undefined
|
||||||
|
|
||||||
const jiti = createJiti(nuxt.options.rootDir, { alias: nuxt.options.alias })
|
const jiti = createJiti(nuxt.options.rootDir, { alias: nuxt.options.alias })
|
||||||
|
|
||||||
@ -98,6 +102,7 @@ export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, n
|
|||||||
: await resolveModule(path, { url: pathToFileURL(parentURL.replace(/\/node_modules\/?$/, '')), extensions: nuxt.options.extensions })
|
: await resolveModule(path, { url: pathToFileURL(parentURL.replace(/\/node_modules\/?$/, '')), extensions: nuxt.options.extensions })
|
||||||
|
|
||||||
nuxtModule = await jiti.import(src, { default: true }) as NuxtModule
|
nuxtModule = await jiti.import(src, { default: true }) as NuxtModule
|
||||||
|
resolvedModulePath = fileURLToPath(new URL(src))
|
||||||
|
|
||||||
// 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 = new URL('module.json', src)
|
const moduleMetadataPath = new URL('module.json', src)
|
||||||
@ -118,10 +123,15 @@ export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, n
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Throw error if module could not be found
|
||||||
|
if (typeof nuxtModule === 'string') {
|
||||||
|
throw new TypeError(`Could not load \`${nuxtModule}\`. Is it installed?`)
|
||||||
|
}
|
||||||
|
|
||||||
// Throw error if input is not a function
|
// Throw error if input is not a function
|
||||||
if (typeof nuxtModule !== 'function') {
|
if (typeof nuxtModule !== 'function') {
|
||||||
throw new TypeError('Nuxt module should be a function: ' + nuxtModule)
|
throw new TypeError('Nuxt module should be a function: ' + nuxtModule)
|
||||||
}
|
}
|
||||||
|
|
||||||
return { nuxtModule, buildTimeModuleMeta } as { nuxtModule: NuxtModule<any>, buildTimeModuleMeta: ModuleMeta }
|
return { nuxtModule, buildTimeModuleMeta, resolvedModulePath } as { nuxtModule: NuxtModule<any>, buildTimeModuleMeta: ModuleMeta, resolvedModulePath?: string }
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,7 @@ export function addTemplate<T> (_template: NuxtTemplate<T> | string) {
|
|||||||
const template = normalizeTemplate(_template)
|
const template = normalizeTemplate(_template)
|
||||||
|
|
||||||
// Remove any existing template with the same destination path
|
// Remove any existing template with the same destination path
|
||||||
nuxt.options.build.templates = nuxt.options.build.templates
|
nuxt.options.build.templates = nuxt.options.build.templates.filter(p => normalizeTemplate(p).dst !== template.dst)
|
||||||
.filter(p => normalizeTemplate(p).dst !== template.dst)
|
|
||||||
|
|
||||||
// Add to templates array
|
// Add to templates array
|
||||||
nuxt.options.build.templates.push(template)
|
nuxt.options.build.templates.push(template)
|
||||||
@ -68,7 +67,7 @@ export function addTypeTemplate<T> (_template: NuxtTypeTemplate<T>) {
|
|||||||
/**
|
/**
|
||||||
* Normalize a nuxt template object
|
* Normalize a nuxt template object
|
||||||
*/
|
*/
|
||||||
export function normalizeTemplate<T> (template: NuxtTemplate<T> | string): ResolvedNuxtTemplate<T> {
|
export function normalizeTemplate<T> (template: NuxtTemplate<T> | string, buildDir?: string): ResolvedNuxtTemplate<T> {
|
||||||
if (!template) {
|
if (!template) {
|
||||||
throw new Error('Invalid template: ' + JSON.stringify(template))
|
throw new Error('Invalid template: ' + JSON.stringify(template))
|
||||||
}
|
}
|
||||||
@ -87,17 +86,16 @@ export function normalizeTemplate<T> (template: NuxtTemplate<T> | string): Resol
|
|||||||
}
|
}
|
||||||
if (!template.filename) {
|
if (!template.filename) {
|
||||||
const srcPath = parse(template.src)
|
const srcPath = parse(template.src)
|
||||||
template.filename = (template as any).fileName ||
|
template.filename = (template as any).fileName || `${basename(srcPath.dir)}.${srcPath.name}.${hash(template.src)}${srcPath.ext}`
|
||||||
`${basename(srcPath.dir)}.${srcPath.name}.${hash(template.src)}${srcPath.ext}`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!template.src && !template.getContents) {
|
if (!template.src && !template.getContents) {
|
||||||
throw new Error('Invalid template. Either getContents or src options should be provided: ' + JSON.stringify(template))
|
throw new Error('Invalid template. Either `getContents` or `src` should be provided: ' + JSON.stringify(template))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!template.filename) {
|
if (!template.filename) {
|
||||||
throw new Error('Invalid template. Either filename should be provided: ' + JSON.stringify(template))
|
throw new Error('Invalid template. `filename` must be provided: ' + JSON.stringify(template))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always write declaration files
|
// Always write declaration files
|
||||||
@ -107,8 +105,7 @@ export function normalizeTemplate<T> (template: NuxtTemplate<T> | string): Resol
|
|||||||
|
|
||||||
// Resolve dst
|
// Resolve dst
|
||||||
if (!template.dst) {
|
if (!template.dst) {
|
||||||
const nuxt = useNuxt()
|
template.dst = resolve(buildDir ?? useNuxt().options.buildDir, template.filename)
|
||||||
template.dst = resolve(nuxt.options.buildDir, template.filename)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return template as ResolvedNuxtTemplate<T>
|
return template as ResolvedNuxtTemplate<T>
|
||||||
|
3
packages/nuxt/.attw.json
Normal file
3
packages/nuxt/.attw.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ignoreRules": ["cjs-resolves-to-esm", "false-esm"]
|
||||||
|
}
|
2
packages/nuxt/app.d.ts
vendored
2
packages/nuxt/app.d.ts
vendored
@ -1 +1 @@
|
|||||||
export * from './dist/app/index.js'
|
export * from './dist/app/index'
|
||||||
|
@ -43,6 +43,14 @@
|
|||||||
"#app": {
|
"#app": {
|
||||||
"types": "./dist/app/index.d.ts",
|
"types": "./dist/app/index.d.ts",
|
||||||
"import": "./dist/app/index.js"
|
"import": "./dist/app/index.js"
|
||||||
|
},
|
||||||
|
"#app/defaults": {
|
||||||
|
"types": "./dist/app/defaults.d.ts",
|
||||||
|
"import": "./dist/app/defaults.js"
|
||||||
|
},
|
||||||
|
"#app/nuxt": {
|
||||||
|
"types": "./dist/app/nuxt.d.ts",
|
||||||
|
"import": "./dist/app/nuxt.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
@ -56,11 +64,12 @@
|
|||||||
"schema.*"
|
"schema.*"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepack": "unbuild"
|
"prepack": "unbuild",
|
||||||
|
"test:attw": "attw --pack"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/devalue": "^2.0.2",
|
"@nuxt/devalue": "^2.0.2",
|
||||||
"@nuxt/devtools": "^1.6.1",
|
"@nuxt/devtools": "^1.6.2",
|
||||||
"@nuxt/kit": "workspace:*",
|
"@nuxt/kit": "workspace:*",
|
||||||
"@nuxt/schema": "workspace:*",
|
"@nuxt/schema": "workspace:*",
|
||||||
"@nuxt/telemetry": "^2.6.0",
|
"@nuxt/telemetry": "^2.6.0",
|
||||||
@ -86,7 +95,7 @@
|
|||||||
"hookable": "^5.5.3",
|
"hookable": "^5.5.3",
|
||||||
"ignore": "^6.0.2",
|
"ignore": "^6.0.2",
|
||||||
"impound": "^0.2.0",
|
"impound": "^0.2.0",
|
||||||
"jiti": "^2.4.0",
|
"jiti": "^2.4.1",
|
||||||
"klona": "^2.0.6",
|
"klona": "^2.0.6",
|
||||||
"knitwork": "^1.1.0",
|
"knitwork": "^1.1.0",
|
||||||
"magic-string": "^0.30.14",
|
"magic-string": "^0.30.14",
|
||||||
@ -94,7 +103,7 @@
|
|||||||
"nanotar": "^0.1.1",
|
"nanotar": "^0.1.1",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
|
||||||
"nuxi": "^3.16.0",
|
"nuxi": "^3.16.0",
|
||||||
"nypm": "^0.4.0",
|
"nypm": "^0.4.1",
|
||||||
"ofetch": "^1.4.1",
|
"ofetch": "^1.4.1",
|
||||||
"ohash": "^1.1.4",
|
"ohash": "^1.1.4",
|
||||||
"oxc-parser": "^0.38.0",
|
"oxc-parser": "^0.38.0",
|
||||||
@ -115,7 +124,7 @@
|
|||||||
"unenv": "^1.10.0",
|
"unenv": "^1.10.0",
|
||||||
"unhead": "^1.11.13",
|
"unhead": "^1.11.13",
|
||||||
"unimport": "^3.13.4",
|
"unimport": "^3.13.4",
|
||||||
"unplugin": "^1.16.0",
|
"unplugin": "^2.0.0",
|
||||||
"unplugin-vue-router": "^0.10.8",
|
"unplugin-vue-router": "^0.10.8",
|
||||||
"unstorage": "^1.13.1",
|
"unstorage": "^1.13.1",
|
||||||
"untyped": "^1.5.1",
|
"untyped": "^1.5.1",
|
||||||
@ -130,12 +139,12 @@
|
|||||||
"@vitejs/plugin-vue": "5.2.1",
|
"@vitejs/plugin-vue": "5.2.1",
|
||||||
"@vue/compiler-sfc": "3.5.13",
|
"@vue/compiler-sfc": "3.5.13",
|
||||||
"unbuild": "3.0.0-rc.11",
|
"unbuild": "3.0.0-rc.11",
|
||||||
"vite": "6.0.1",
|
"vite": "6.0.2",
|
||||||
"vitest": "2.1.6"
|
"vitest": "2.1.8"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@parcel/watcher": "^2.1.0",
|
"@parcel/watcher": "^2.1.0",
|
||||||
"@types/node": "^14.18.0 || >=16.10.0"
|
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@parcel/watcher": {
|
"@parcel/watcher": {
|
||||||
@ -146,6 +155,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.10.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { defineComponent, h } from 'vue'
|
import { defineComponent, h } from 'vue'
|
||||||
import type { Politeness } from '#app/composables/route-announcer'
|
import type { Politeness } from 'nuxt/app'
|
||||||
import { useRouteAnnouncer } from '#app/composables/route-announcer'
|
import { useRouteAnnouncer } from '#app/composables/route-announcer'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { Component, InjectionKey } from 'vue'
|
import type { Component, InjectionKey } from 'vue'
|
||||||
import { Teleport, defineComponent, h, inject, provide } from 'vue'
|
import { Teleport, defineComponent, h, inject, provide, useId } from 'vue'
|
||||||
import { useNuxtApp } from '../nuxt'
|
import { useNuxtApp } from '../nuxt'
|
||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
import { paths } from '#build/components-chunk'
|
import { paths } from '#build/components-chunk'
|
||||||
@ -20,10 +20,6 @@ export default defineComponent({
|
|||||||
name: 'NuxtTeleportIslandComponent',
|
name: 'NuxtTeleportIslandComponent',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
props: {
|
props: {
|
||||||
to: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
nuxtClient: {
|
nuxtClient: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
@ -31,11 +27,12 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
setup (props, { slots }) {
|
setup (props, { slots }) {
|
||||||
const nuxtApp = useNuxtApp()
|
const nuxtApp = useNuxtApp()
|
||||||
|
const to = useId()
|
||||||
|
|
||||||
// if there's already a teleport parent, we don't need to teleport or to render the wrapped component client side
|
// if there's already a teleport parent, we don't need to teleport or to render the wrapped component client side
|
||||||
if (!nuxtApp.ssrContext?.islandContext || !props.nuxtClient || inject(NuxtTeleportIslandSymbol, false)) { return () => slots.default?.() }
|
if (!nuxtApp.ssrContext?.islandContext || !props.nuxtClient || inject(NuxtTeleportIslandSymbol, false)) { return () => slots.default?.() }
|
||||||
|
|
||||||
provide(NuxtTeleportIslandSymbol, props.to)
|
provide(NuxtTeleportIslandSymbol, to)
|
||||||
const islandContext = nuxtApp.ssrContext!.islandContext!
|
const islandContext = nuxtApp.ssrContext!.islandContext!
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@ -43,7 +40,7 @@ export default defineComponent({
|
|||||||
const slotType = slot.type as ExtendedComponent
|
const slotType = slot.type as ExtendedComponent
|
||||||
const name = (slotType.__name || slotType.name) as string
|
const name = (slotType.__name || slotType.name) as string
|
||||||
|
|
||||||
islandContext.components[props.to] = {
|
islandContext.components[to] = {
|
||||||
chunk: import.meta.dev ? nuxtApp.$config.app.buildAssetsDir + paths[name] : paths[name],
|
chunk: import.meta.dev ? nuxtApp.$config.app.buildAssetsDir + paths[name] : paths[name],
|
||||||
props: slot.props || {},
|
props: slot.props || {},
|
||||||
}
|
}
|
||||||
@ -51,8 +48,8 @@ export default defineComponent({
|
|||||||
return [h('div', {
|
return [h('div', {
|
||||||
'style': 'display: contents;',
|
'style': 'display: contents;',
|
||||||
'data-island-uid': '',
|
'data-island-uid': '',
|
||||||
'data-island-component': props.to,
|
'data-island-component': to,
|
||||||
}, []), h(Teleport, { to: props.to }, slot)]
|
}, []), h(Teleport, { to }, slot)]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
import { useId as _useId } from 'vue'
|
import { useId as _useId } from 'vue'
|
||||||
|
|
||||||
|
/** @deprecated Use `useId` from `vue` */
|
||||||
export const useId = _useId
|
export const useId = _useId
|
||||||
|
@ -38,4 +38,5 @@ export { useRequestURL } from './url'
|
|||||||
export { usePreviewMode } from './preview'
|
export { usePreviewMode } from './preview'
|
||||||
export { useId } from './id'
|
export { useId } from './id'
|
||||||
export { useRouteAnnouncer } from './route-announcer'
|
export { useRouteAnnouncer } from './route-announcer'
|
||||||
|
export type { Politeness } from './route-announcer'
|
||||||
export { useRuntimeHook } from './runtime-hook'
|
export { useRuntimeHook } from './runtime-hook'
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import type { MatcherExport, RouteMatcher } from 'radix3'
|
import type { MatcherExport, RouteMatcher } from 'radix3'
|
||||||
import { createMatcherFromExport, createRouter as createRadixRouter, toRouteMatcher } from 'radix3'
|
import { createMatcherFromExport, createRouter as createRadixRouter, toRouteMatcher } from 'radix3'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { useRuntimeConfig } from '../nuxt'
|
import { useNuxtApp, useRuntimeConfig } from '../nuxt'
|
||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
import { appManifest as isAppManifestEnabled } from '#build/nuxt.config.mjs'
|
import { appManifest as isAppManifestEnabled } from '#build/nuxt.config.mjs'
|
||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
@ -24,9 +24,14 @@ function fetchManifest () {
|
|||||||
if (!isAppManifestEnabled) {
|
if (!isAppManifestEnabled) {
|
||||||
throw new Error('[nuxt] app manifest should be enabled with `experimental.appManifest`')
|
throw new Error('[nuxt] app manifest should be enabled with `experimental.appManifest`')
|
||||||
}
|
}
|
||||||
|
if (import.meta.server) {
|
||||||
|
// @ts-expect-error virtual file
|
||||||
|
manifest = import('#app-manifest')
|
||||||
|
} else {
|
||||||
manifest = $fetch<NuxtAppManifest>(buildAssetsURL(`builds/meta/${useRuntimeConfig().app.buildId}.json`), {
|
manifest = $fetch<NuxtAppManifest>(buildAssetsURL(`builds/meta/${useRuntimeConfig().app.buildId}.json`), {
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
})
|
})
|
||||||
|
}
|
||||||
manifest.then((m) => {
|
manifest.then((m) => {
|
||||||
matcher = createMatcherFromExport(m.matcher)
|
matcher = createMatcherFromExport(m.matcher)
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
@ -40,12 +45,16 @@ export function getAppManifest (): Promise<NuxtAppManifest> {
|
|||||||
if (!isAppManifestEnabled) {
|
if (!isAppManifestEnabled) {
|
||||||
throw new Error('[nuxt] app manifest should be enabled with `experimental.appManifest`')
|
throw new Error('[nuxt] app manifest should be enabled with `experimental.appManifest`')
|
||||||
}
|
}
|
||||||
|
if (import.meta.server) {
|
||||||
|
useNuxtApp().ssrContext!._preloadManifest = true
|
||||||
|
}
|
||||||
return manifest || fetchManifest()
|
return manifest || fetchManifest()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 3.7.4 */
|
/** @since 3.7.4 */
|
||||||
export async function getRouteRules (url: string) {
|
export async function getRouteRules (url: string) {
|
||||||
if (import.meta.server) {
|
if (import.meta.server) {
|
||||||
|
useNuxtApp().ssrContext!._preloadManifest = true
|
||||||
const _routeRulesMatcher = toRouteMatcher(
|
const _routeRulesMatcher = toRouteMatcher(
|
||||||
createRadixRouter({ routes: useRuntimeConfig().nitro!.routeRules }),
|
createRadixRouter({ routes: useRuntimeConfig().nitro!.routeRules }),
|
||||||
)
|
)
|
||||||
|
@ -85,15 +85,18 @@ async function _importPayload (payloadURL: string) {
|
|||||||
}
|
}
|
||||||
/** @since 3.0.0 */
|
/** @since 3.0.0 */
|
||||||
export async function isPrerendered (url = useRoute().path) {
|
export async function isPrerendered (url = useRoute().path) {
|
||||||
|
const nuxtApp = useNuxtApp()
|
||||||
// Note: Alternative for server is checking x-nitro-prerender header
|
// Note: Alternative for server is checking x-nitro-prerender header
|
||||||
if (!appManifest) { return !!useNuxtApp().payload.prerenderedAt }
|
if (!appManifest) { return !!nuxtApp.payload.prerenderedAt }
|
||||||
url = withoutTrailingSlash(url)
|
url = withoutTrailingSlash(url)
|
||||||
const manifest = await getAppManifest()
|
const manifest = await getAppManifest()
|
||||||
if (manifest.prerendered.includes(url)) {
|
if (manifest.prerendered.includes(url)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
return nuxtApp.runWithContext(async () => {
|
||||||
const rules = await getRouteRules(url)
|
const rules = await getRouteRules(url)
|
||||||
return !!rules.prerender && !rules.redirect
|
return !!rules.prerender && !rules.redirect
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let payloadCache: NuxtPayload | null = null
|
let payloadCache: NuxtPayload | null = null
|
||||||
|
@ -2,6 +2,7 @@ import type { H3Event } from 'h3'
|
|||||||
import { setResponseStatus as _setResponseStatus, appendHeader, getRequestHeader, getRequestHeaders, getResponseHeader, removeResponseHeader, setResponseHeader } from 'h3'
|
import { setResponseStatus as _setResponseStatus, appendHeader, getRequestHeader, getRequestHeaders, getResponseHeader, removeResponseHeader, setResponseHeader } from 'h3'
|
||||||
import { computed, getCurrentInstance, ref } from 'vue'
|
import { computed, getCurrentInstance, ref } from 'vue'
|
||||||
import { useServerHead } from '@unhead/vue'
|
import { useServerHead } from '@unhead/vue'
|
||||||
|
import type { H3Event$Fetch } from 'nitro/types'
|
||||||
|
|
||||||
import type { NuxtApp } from '../nuxt'
|
import type { NuxtApp } from '../nuxt'
|
||||||
import { useNuxtApp } from '../nuxt'
|
import { useNuxtApp } from '../nuxt'
|
||||||
@ -39,11 +40,11 @@ export function useRequestHeader (header: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @since 3.2.0 */
|
/** @since 3.2.0 */
|
||||||
export function useRequestFetch (): typeof global.$fetch {
|
export function useRequestFetch (): H3Event$Fetch | typeof global.$fetch {
|
||||||
if (import.meta.client) {
|
if (import.meta.client) {
|
||||||
return globalThis.$fetch
|
return globalThis.$fetch
|
||||||
}
|
}
|
||||||
return useRequestEvent()?.$fetch as typeof globalThis.$fetch || globalThis.$fetch
|
return useRequestEvent()?.$fetch || globalThis.$fetch
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 3.0.0 */
|
/** @since 3.0.0 */
|
||||||
|
@ -17,7 +17,7 @@ import plugins from '#build/plugins'
|
|||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
import RootComponent from '#build/root-component.mjs'
|
import RootComponent from '#build/root-component.mjs'
|
||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
import { appId, multiApp, vueAppRootContainer } from '#build/nuxt.config.mjs'
|
import { appId, appSpaLoaderAttrs, multiApp, spaLoadingTemplateOutside, vueAppRootContainer } from '#build/nuxt.config.mjs'
|
||||||
|
|
||||||
let entry: (ssrContext?: CreateOptions['ssrContext']) => Promise<App<Element>>
|
let entry: (ssrContext?: CreateOptions['ssrContext']) => Promise<App<Element>>
|
||||||
|
|
||||||
@ -72,6 +72,13 @@ if (import.meta.client) {
|
|||||||
if (vueApp.config.errorHandler === handleVueError) { vueApp.config.errorHandler = undefined }
|
if (vueApp.config.errorHandler === handleVueError) { vueApp.config.errorHandler = undefined }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (spaLoadingTemplateOutside && !isSSR && appSpaLoaderAttrs.id) {
|
||||||
|
// Remove spa loader if present
|
||||||
|
nuxt.hook('app:suspense:resolve', () => {
|
||||||
|
document.getElementById(appSpaLoaderAttrs.id)?.remove()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await applyPlugins(nuxt, plugins)
|
await applyPlugins(nuxt, plugins)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -2,7 +2,7 @@ export { applyPlugin, applyPlugins, callWithNuxt, createNuxtApp, defineAppConfig
|
|||||||
export type { CreateOptions, NuxtApp, NuxtPayload, NuxtPluginIndicator, NuxtSSRContext, ObjectPlugin, Plugin, PluginEnvContext, PluginMeta, ResolvedPluginMeta, RuntimeNuxtHooks } from './nuxt'
|
export type { CreateOptions, NuxtApp, NuxtPayload, NuxtPluginIndicator, NuxtSSRContext, ObjectPlugin, Plugin, PluginEnvContext, PluginMeta, ResolvedPluginMeta, RuntimeNuxtHooks } from './nuxt'
|
||||||
|
|
||||||
export { defineNuxtComponent, useAsyncData, useLazyAsyncData, useNuxtData, refreshNuxtData, clearNuxtData, useHydration, callOnce, useState, clearNuxtState, clearError, createError, isNuxtError, showError, useError, useFetch, useLazyFetch, useCookie, refreshCookie, onPrehydrate, prerenderRoutes, useRequestHeaders, useRequestEvent, useRequestFetch, setResponseStatus, useResponseHeader, onNuxtReady, abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter, preloadComponents, prefetchComponents, preloadRouteComponents, isPrerendered, loadPayload, preloadPayload, definePayloadReducer, definePayloadReviver, getAppManifest, getRouteRules, reloadNuxtApp, useRequestURL, usePreviewMode, useId, useRouteAnnouncer, useHead, useSeoMeta, useServerSeoMeta, useRuntimeHook } from './composables/index'
|
export { defineNuxtComponent, useAsyncData, useLazyAsyncData, useNuxtData, refreshNuxtData, clearNuxtData, useHydration, callOnce, useState, clearNuxtState, clearError, createError, isNuxtError, showError, useError, useFetch, useLazyFetch, useCookie, refreshCookie, onPrehydrate, prerenderRoutes, useRequestHeaders, useRequestEvent, useRequestFetch, setResponseStatus, useResponseHeader, onNuxtReady, abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter, preloadComponents, prefetchComponents, preloadRouteComponents, isPrerendered, loadPayload, preloadPayload, definePayloadReducer, definePayloadReviver, getAppManifest, getRouteRules, reloadNuxtApp, useRequestURL, usePreviewMode, useId, useRouteAnnouncer, useHead, useSeoMeta, useServerSeoMeta, useRuntimeHook } from './composables/index'
|
||||||
export type { AddRouteMiddlewareOptions, AsyncData, AsyncDataOptions, AsyncDataRequestStatus, CookieOptions, CookieRef, FetchResult, NuxtAppManifest, NuxtAppManifestMeta, NuxtError, ReloadNuxtAppOptions, RouteMiddleware, UseFetchOptions } from './composables/index'
|
export type { AddRouteMiddlewareOptions, AsyncData, AsyncDataOptions, AsyncDataRequestStatus, CookieOptions, CookieRef, FetchResult, NuxtAppManifest, NuxtAppManifestMeta, NuxtError, Politeness, ReloadNuxtAppOptions, RouteMiddleware, UseFetchOptions } from './composables/index'
|
||||||
|
|
||||||
export { defineNuxtLink } from './components/index'
|
export { defineNuxtLink } from './components/index'
|
||||||
export type { NuxtLinkOptions, NuxtLinkProps } from './components/index'
|
export type { NuxtLinkOptions, NuxtLinkProps } from './components/index'
|
||||||
|
@ -11,6 +11,8 @@ import type { RenderResponse } from 'nitro/types'
|
|||||||
import type { LogObject } from 'consola'
|
import type { LogObject } from 'consola'
|
||||||
import type { MergeHead, VueHeadClient } from '@unhead/vue'
|
import type { MergeHead, VueHeadClient } from '@unhead/vue'
|
||||||
|
|
||||||
|
import type { NuxtAppLiterals } from 'nuxt/app'
|
||||||
|
|
||||||
import type { NuxtIslandContext } from '../app/types'
|
import type { NuxtIslandContext } from '../app/types'
|
||||||
import type { RouteMiddleware } from '../app/composables/router'
|
import type { RouteMiddleware } from '../app/composables/router'
|
||||||
import type { NuxtError } from '../app/composables/error'
|
import type { NuxtError } from '../app/composables/error'
|
||||||
@ -22,8 +24,6 @@ import type { RouteAnnouncer } from '../app/composables/route-announcer'
|
|||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
import { appId, chunkErrorEvent, multiApp } from '#build/nuxt.config.mjs'
|
import { appId, chunkErrorEvent, multiApp } from '#build/nuxt.config.mjs'
|
||||||
|
|
||||||
import type { NuxtAppLiterals } from '#app'
|
|
||||||
|
|
||||||
function getNuxtAppCtx (id = appId || 'nuxt-app') {
|
function getNuxtAppCtx (id = appId || 'nuxt-app') {
|
||||||
return getContext<NuxtApp>(id, {
|
return getContext<NuxtApp>(id, {
|
||||||
asyncContext: !!__NUXT_ASYNC_CONTEXT__ && import.meta.server,
|
asyncContext: !!__NUXT_ASYNC_CONTEXT__ && import.meta.server,
|
||||||
@ -81,6 +81,8 @@ export interface NuxtSSRContext extends SSRContext {
|
|||||||
get<T = unknown> (key: string): Promise<T> | undefined
|
get<T = unknown> (key: string): Promise<T> | undefined
|
||||||
set<T> (key: string, value: Promise<T>): Promise<void>
|
set<T> (key: string, value: Promise<T>): Promise<void>
|
||||||
}
|
}
|
||||||
|
/** @internal */
|
||||||
|
_preloadManifest?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NuxtPayload {
|
export interface NuxtPayload {
|
||||||
@ -114,11 +116,6 @@ interface _NuxtApp {
|
|||||||
* The id of the Nuxt application.
|
* The id of the Nuxt application.
|
||||||
* @internal */
|
* @internal */
|
||||||
_id: string
|
_id: string
|
||||||
/**
|
|
||||||
* The next id that can be used for generating unique ids via `useId`.
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
_genId?: number
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_scope: EffectScope
|
_scope: EffectScope
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
import { defineNuxtPlugin } from '../nuxt'
|
||||||
|
|
||||||
|
export default defineNuxtPlugin({
|
||||||
|
name: 'nuxt:browser-devtools-timing',
|
||||||
|
enforce: 'pre',
|
||||||
|
setup (nuxtApp) {
|
||||||
|
nuxtApp.hooks.beforeEach((event) => {
|
||||||
|
// @ts-expect-error __startTime is not a public API
|
||||||
|
event.__startTime = performance.now()
|
||||||
|
})
|
||||||
|
|
||||||
|
// After each
|
||||||
|
nuxtApp.hooks.afterEach((event) => {
|
||||||
|
performance.measure(event.name, {
|
||||||
|
// @ts-expect-error __startTime is not a public API
|
||||||
|
start: event.__startTime,
|
||||||
|
detail: {
|
||||||
|
devtools: {
|
||||||
|
dataType: 'track-entry',
|
||||||
|
track: 'nuxt',
|
||||||
|
color: 'tertiary-dark',
|
||||||
|
} satisfies ExtensionTrackEntryPayload,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
type DevToolsColor =
|
||||||
|
'primary' | 'primary-light' | 'primary-dark' |
|
||||||
|
'secondary' | 'secondary-light' | 'secondary-dark' |
|
||||||
|
'tertiary' | 'tertiary-light' | 'tertiary-dark' |
|
||||||
|
'error'
|
||||||
|
|
||||||
|
interface ExtensionTrackEntryPayload {
|
||||||
|
dataType?: 'track-entry' // Defaults to "track-entry"
|
||||||
|
color?: DevToolsColor // Defaults to "primary"
|
||||||
|
track: string // Required: Name of the custom track
|
||||||
|
trackGroup?: string // Optional: Group for organizing tracks
|
||||||
|
properties?: [string, string][] // Key-value pairs for detailed view
|
||||||
|
tooltipText?: string // Short description for tooltip
|
||||||
|
}
|
2
packages/nuxt/src/app/types/augments.d.ts
vendored
2
packages/nuxt/src/app/types/augments.d.ts
vendored
@ -1,5 +1,5 @@
|
|||||||
import type { UseHeadInput } from '@unhead/vue'
|
import type { UseHeadInput } from '@unhead/vue'
|
||||||
import type { NuxtApp, useNuxtApp } from '../nuxt'
|
import type { NuxtApp, useNuxtApp } from '../nuxt.js'
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace NodeJS {
|
namespace NodeJS {
|
||||||
|
@ -28,7 +28,7 @@ export type getComponentsT = (mode?: 'client' | 'server' | 'all') => Component[]
|
|||||||
|
|
||||||
export default defineNuxtModule<ComponentsOptions>({
|
export default defineNuxtModule<ComponentsOptions>({
|
||||||
meta: {
|
meta: {
|
||||||
name: 'components',
|
name: 'nuxt:components',
|
||||||
configKey: 'components',
|
configKey: 'components',
|
||||||
},
|
},
|
||||||
defaults: {
|
defaults: {
|
||||||
|
@ -6,7 +6,6 @@ import { parseURL } from 'ufo'
|
|||||||
import { createUnplugin } from 'unplugin'
|
import { createUnplugin } from 'unplugin'
|
||||||
import MagicString from 'magic-string'
|
import MagicString from 'magic-string'
|
||||||
import { ELEMENT_NODE, parse, walk } from 'ultrahtml'
|
import { ELEMENT_NODE, parse, walk } from 'ultrahtml'
|
||||||
import { hash } from 'ohash'
|
|
||||||
import { resolvePath } from '@nuxt/kit'
|
import { resolvePath } from '@nuxt/kit'
|
||||||
import defu from 'defu'
|
import defu from 'defu'
|
||||||
import { isVue } from '../../core/utils'
|
import { isVue } from '../../core/utils'
|
||||||
@ -113,8 +112,6 @@ export const IslandsTransformPlugin = (options: ServerOnlyComponentTransformPlug
|
|||||||
|
|
||||||
const { loc, attributes } = node
|
const { loc, attributes } = node
|
||||||
const attributeValue = attributes[':nuxt-client'] || attributes['nuxt-client'] || 'true'
|
const attributeValue = attributes[':nuxt-client'] || attributes['nuxt-client'] || 'true'
|
||||||
|
|
||||||
const uid = hash(id + node.loc[0].start + node.loc[0].end)
|
|
||||||
const wrapperAttributes = extractAttributes(attributes, ['v-if', 'v-else-if', 'v-else'])
|
const wrapperAttributes = extractAttributes(attributes, ['v-if', 'v-else-if', 'v-else'])
|
||||||
|
|
||||||
let startTag = code.slice(startingIndex + loc[0].start, startingIndex + loc[0].end).replace(NUXTCLIENT_ATTR_RE, '')
|
let startTag = code.slice(startingIndex + loc[0].start, startingIndex + loc[0].end).replace(NUXTCLIENT_ATTR_RE, '')
|
||||||
@ -122,7 +119,7 @@ export const IslandsTransformPlugin = (options: ServerOnlyComponentTransformPlug
|
|||||||
startTag = startTag.replaceAll(EXTRACTED_ATTRS_RE, '')
|
startTag = startTag.replaceAll(EXTRACTED_ATTRS_RE, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
s.appendLeft(startingIndex + loc[0].start, `<NuxtTeleportIslandComponent${attributeToString(wrapperAttributes)} to="${node.name}-${uid}" :nuxt-client="${attributeValue}">`)
|
s.appendLeft(startingIndex + loc[0].start, `<NuxtTeleportIslandComponent${attributeToString(wrapperAttributes)} :nuxt-client="${attributeValue}">`)
|
||||||
s.overwrite(startingIndex + loc[0].start, startingIndex + loc[0].end, startTag)
|
s.overwrite(startingIndex + loc[0].start, startingIndex + loc[0].end, startTag)
|
||||||
s.appendRight(startingIndex + loc[1].end, '</NuxtTeleportIslandComponent>')
|
s.appendRight(startingIndex + loc[1].end, '</NuxtTeleportIslandComponent>')
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { defineAsyncComponent, defineComponent, h } from 'vue'
|
import { defineAsyncComponent, defineComponent, h } from 'vue'
|
||||||
import type { AsyncComponentLoader } from 'vue'
|
import type { AsyncComponentLoader } from 'vue'
|
||||||
import ClientOnly from '#app/components/client-only'
|
import ClientOnly from '#app/components/client-only'
|
||||||
|
import { useNuxtApp } from '#app/nuxt'
|
||||||
|
|
||||||
/* @__NO_SIDE_EFFECTS__ */
|
/* @__NO_SIDE_EFFECTS__ */
|
||||||
export const createClientPage = (loader: AsyncComponentLoader) => {
|
export const createClientPage = (loader: AsyncComponentLoader) => {
|
||||||
@ -15,11 +16,15 @@ export const createClientPage = (loader: AsyncComponentLoader) => {
|
|||||||
return defineComponent({
|
return defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
setup (_, { attrs }) {
|
setup (_, { attrs }) {
|
||||||
|
const nuxtApp = useNuxtApp()
|
||||||
|
if (import.meta.server || nuxtApp.isHydrating) {
|
||||||
return () => h('div', [
|
return () => h('div', [
|
||||||
h(ClientOnly, undefined, {
|
h(ClientOnly, undefined, {
|
||||||
default: () => h(page, attrs),
|
default: () => h(page, attrs),
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
|
}
|
||||||
|
return () => h(page, attrs)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,12 @@ import { defu } from 'defu'
|
|||||||
import { findPath, logger, normalizePlugin, normalizeTemplate, resolveAlias, resolveFiles, resolvePath } from '@nuxt/kit'
|
import { findPath, logger, normalizePlugin, normalizeTemplate, resolveAlias, resolveFiles, resolvePath } from '@nuxt/kit'
|
||||||
import type { Nuxt, NuxtApp, NuxtPlugin, NuxtTemplate, ResolvedNuxtTemplate } from 'nuxt/schema'
|
import type { Nuxt, NuxtApp, NuxtPlugin, NuxtTemplate, ResolvedNuxtTemplate } from 'nuxt/schema'
|
||||||
|
|
||||||
|
import type { PluginMeta } from 'nuxt/app'
|
||||||
|
|
||||||
import * as defaultTemplates from './templates'
|
import * as defaultTemplates from './templates'
|
||||||
import { getNameFromPath, hasSuffix, uniqueBy } from './utils'
|
import { getNameFromPath, hasSuffix, uniqueBy } from './utils'
|
||||||
import { extractMetadata, orderMap } from './plugins/plugin-metadata'
|
import { extractMetadata, orderMap } from './plugins/plugin-metadata'
|
||||||
|
|
||||||
import type { PluginMeta } from '#app'
|
|
||||||
|
|
||||||
export function createApp (nuxt: Nuxt, options: Partial<NuxtApp> = {}): NuxtApp {
|
export function createApp (nuxt: Nuxt, options: Partial<NuxtApp> = {}): NuxtApp {
|
||||||
return defu(options, {
|
return defu(options, {
|
||||||
dir: nuxt.options.srcDir,
|
dir: nuxt.options.srcDir,
|
||||||
@ -37,7 +37,7 @@ export async function generateApp (nuxt: Nuxt, app: NuxtApp, options: { filter?:
|
|||||||
await nuxt.callHook('app:templates', app)
|
await nuxt.callHook('app:templates', app)
|
||||||
|
|
||||||
// Normalize templates
|
// Normalize templates
|
||||||
app.templates = app.templates.map(tmpl => normalizeTemplate(tmpl))
|
app.templates = app.templates.map(tmpl => normalizeTemplate(tmpl, nuxt.options.buildDir))
|
||||||
|
|
||||||
// compile plugins first as they are needed within the nuxt.vfs
|
// compile plugins first as they are needed within the nuxt.vfs
|
||||||
// in order to annotate templated plugins
|
// in order to annotate templated plugins
|
||||||
|
@ -273,7 +273,18 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
|
|
||||||
nuxt.options.alias['#app-manifest'] = join(tempDir, `meta/${buildId}.json`)
|
nuxt.options.alias['#app-manifest'] = join(tempDir, `meta/${buildId}.json`)
|
||||||
|
|
||||||
|
// write stub manifest before build so external import of #app-manifest can be resolved
|
||||||
|
if (!nuxt.options.dev) {
|
||||||
|
nuxt.hook('build:before', async () => {
|
||||||
|
await fsp.mkdir(join(tempDir, 'meta'), { recursive: true })
|
||||||
|
await fsp.writeFile(join(tempDir, `meta/${buildId}.json`), JSON.stringify({}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
nuxt.hook('nitro:config', (config) => {
|
nuxt.hook('nitro:config', (config) => {
|
||||||
|
config.alias ||= {}
|
||||||
|
config.alias['#app-manifest'] = join(tempDir, `meta/${buildId}.json`)
|
||||||
|
|
||||||
const rules = config.routeRules
|
const rules = config.routeRules
|
||||||
for (const rule in rules) {
|
for (const rule in rules) {
|
||||||
if (!(rules[rule] as any).appMiddleware) { continue }
|
if (!(rules[rule] as any).appMiddleware) { continue }
|
||||||
@ -349,6 +360,11 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add stub alias to allow vite to resolve import
|
||||||
|
if (!nuxt.options.experimental.appManifest) {
|
||||||
|
nuxt.options.alias['#app-manifest'] = 'unenv/runtime/mock/proxy'
|
||||||
|
}
|
||||||
|
|
||||||
// Add fallback server for `ssr: false`
|
// Add fallback server for `ssr: false`
|
||||||
const FORWARD_SLASH_RE = /\//g
|
const FORWARD_SLASH_RE = /\//g
|
||||||
if (!nuxt.options.ssr) {
|
if (!nuxt.options.ssr) {
|
||||||
|
@ -548,6 +548,12 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
addPlugin(resolve(nuxt.options.appDir, 'plugins/debug'))
|
addPlugin(resolve(nuxt.options.appDir, 'plugins/debug'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add experimental Chrome devtools timings support
|
||||||
|
// https://developer.chrome.com/docs/devtools/performance/extension
|
||||||
|
if (nuxt.options.experimental.browserDevtoolsTiming) {
|
||||||
|
addPlugin(resolve(nuxt.options.appDir, 'plugins/browser-devtools-timing.client'))
|
||||||
|
}
|
||||||
|
|
||||||
for (const [key, options] of modulesToInstall) {
|
for (const [key, options] of modulesToInstall) {
|
||||||
await installModule(key, options)
|
await installModule(key, options)
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,10 @@ import { createUnplugin } from 'unplugin'
|
|||||||
import MagicString from 'magic-string'
|
import MagicString from 'magic-string'
|
||||||
import { normalize } from 'pathe'
|
import { normalize } from 'pathe'
|
||||||
import { logger } from '@nuxt/kit'
|
import { logger } from '@nuxt/kit'
|
||||||
import { parseAndWalk } from 'oxc-walker'
|
|
||||||
|
|
||||||
import type { ObjectPlugin, PluginMeta } from '#app'
|
import type { ObjectPlugin, PluginMeta } from 'nuxt/app'
|
||||||
|
|
||||||
|
import { parseAndWalk } from 'oxc-walker'
|
||||||
|
|
||||||
const internalOrderMap = {
|
const internalOrderMap = {
|
||||||
// -50: pre-all (nuxt)
|
// -50: pre-all (nuxt)
|
||||||
|
@ -3,7 +3,7 @@ import type { NitroErrorHandler } from 'nitro/types'
|
|||||||
import type { H3Error, H3Event } from 'h3'
|
import type { H3Error, H3Event } from 'h3'
|
||||||
import { getRequestHeader, getRequestHeaders, send, setResponseHeader, setResponseStatus } from 'h3'
|
import { getRequestHeader, getRequestHeaders, send, setResponseHeader, setResponseStatus } from 'h3'
|
||||||
import { useNitroApp, useRuntimeConfig } from 'nitro/runtime'
|
import { useNitroApp, useRuntimeConfig } from 'nitro/runtime'
|
||||||
import type { NuxtPayload } from '#app'
|
import type { NuxtPayload } from 'nuxt/app'
|
||||||
|
|
||||||
export default <NitroErrorHandler> async function errorhandler (error: H3Error, event) {
|
export default <NitroErrorHandler> async function errorhandler (error: H3Error, event) {
|
||||||
// Parse and normalize error
|
// Parse and normalize error
|
||||||
|
@ -22,15 +22,15 @@ import type { Link, Script, Style } from '@unhead/vue'
|
|||||||
import { createServerHead, resolveUnrefHeadInput } from '@unhead/vue'
|
import { createServerHead, resolveUnrefHeadInput } from '@unhead/vue'
|
||||||
|
|
||||||
import { defineRenderHandler, getRouteRules, useNitroApp, useRuntimeConfig, useStorage } from 'nitro/runtime'
|
import { defineRenderHandler, getRouteRules, useNitroApp, useRuntimeConfig, useStorage } from 'nitro/runtime'
|
||||||
|
import type { NuxtPayload, NuxtSSRContext } from 'nuxt/app'
|
||||||
|
|
||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
import unheadPlugins from '#internal/unhead-plugins.mjs'
|
import unheadPlugins from '#internal/unhead-plugins.mjs'
|
||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
import { renderSSRHeadOptions } from '#internal/unhead.config.mjs'
|
import { renderSSRHeadOptions } from '#internal/unhead.config.mjs'
|
||||||
|
|
||||||
import type { NuxtPayload, NuxtSSRContext } from '#app'
|
|
||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
import { appHead, appId, appRootAttrs, appRootTag, appTeleportAttrs, appTeleportTag, componentIslands, multiApp } from '#internal/nuxt.config.mjs'
|
import { appHead, appId, appRootAttrs, appRootTag, appSpaLoaderAttrs, appSpaLoaderTag, appTeleportAttrs, appTeleportTag, componentIslands, appManifest as isAppManifestEnabled, multiApp, spaLoadingTemplateOutside } from '#internal/nuxt.config.mjs'
|
||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
import { buildAssetsURL, publicAssetsURL } from '#internal/nuxt/paths'
|
import { buildAssetsURL, publicAssetsURL } from '#internal/nuxt/paths'
|
||||||
|
|
||||||
@ -144,7 +144,17 @@ const getSPARenderer = lazyCachedFunction(async () => {
|
|||||||
|
|
||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
const spaTemplate = await import('#spa-template').then(r => r.template).catch(() => '')
|
const spaTemplate = await import('#spa-template').then(r => r.template).catch(() => '')
|
||||||
.then(r => APP_ROOT_OPEN_TAG + r + APP_ROOT_CLOSE_TAG)
|
.then((r) => {
|
||||||
|
if (spaLoadingTemplateOutside) {
|
||||||
|
const APP_SPA_LOADER_OPEN_TAG = `<${appSpaLoaderTag}${propsToString(appSpaLoaderAttrs)}>`
|
||||||
|
const APP_SPA_LOADER_CLOSE_TAG = `</${appSpaLoaderTag}>`
|
||||||
|
const appTemplate = APP_ROOT_OPEN_TAG + APP_ROOT_CLOSE_TAG
|
||||||
|
const loaderTemplate = r ? APP_SPA_LOADER_OPEN_TAG + r + APP_SPA_LOADER_CLOSE_TAG : ''
|
||||||
|
return appTemplate + loaderTemplate
|
||||||
|
} else {
|
||||||
|
return APP_ROOT_OPEN_TAG + r + APP_ROOT_CLOSE_TAG
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
manifest,
|
manifest,
|
||||||
@ -379,7 +389,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. Preload payloads and app manifest
|
||||||
if (_PAYLOAD_EXTRACTION && !NO_SCRIPTS && !isRenderingIsland) {
|
if (_PAYLOAD_EXTRACTION && !NO_SCRIPTS && !isRenderingIsland) {
|
||||||
head.push({
|
head.push({
|
||||||
link: [
|
link: [
|
||||||
@ -389,7 +399,13 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
|||||||
],
|
],
|
||||||
}, headEntryOptions)
|
}, headEntryOptions)
|
||||||
}
|
}
|
||||||
|
if (isAppManifestEnabled && ssrContext._preloadManifest) {
|
||||||
|
head.push({
|
||||||
|
link: [
|
||||||
|
{ rel: 'preload', as: 'fetch', fetchpriority: 'low', crossorigin: 'anonymous', href: buildAssetsURL(`builds/meta/${ssrContext.runtimeConfig.app.buildId}.json`) },
|
||||||
|
],
|
||||||
|
}, { ...headEntryOptions, tagPriority: 'low' })
|
||||||
|
}
|
||||||
// 2. Styles
|
// 2. Styles
|
||||||
if (inlinedStyles.length) {
|
if (inlinedStyles.length) {
|
||||||
head.push({ style: inlinedStyles })
|
head.push({ style: inlinedStyles })
|
||||||
|
@ -16,7 +16,7 @@ import { createJiti } from 'jiti'
|
|||||||
|
|
||||||
export default defineNuxtModule({
|
export default defineNuxtModule({
|
||||||
meta: {
|
meta: {
|
||||||
name: 'nuxt-config-schema',
|
name: 'nuxt:nuxt-config-schema',
|
||||||
},
|
},
|
||||||
async setup (_, nuxt) {
|
async setup (_, nuxt) {
|
||||||
const resolver = createResolver(import.meta.url)
|
const resolver = createResolver(import.meta.url)
|
||||||
|
@ -177,7 +177,6 @@ export { }
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const adHocModules = ['router', 'pages', 'imports', 'meta', 'components', 'nuxt-config-schema']
|
|
||||||
const IMPORT_NAME_RE = /\.\w+$/
|
const IMPORT_NAME_RE = /\.\w+$/
|
||||||
const GIT_RE = /^git\+/
|
const GIT_RE = /^git\+/
|
||||||
export const schemaTemplate: NuxtTemplate = {
|
export const schemaTemplate: NuxtTemplate = {
|
||||||
@ -187,7 +186,7 @@ export const schemaTemplate: NuxtTemplate = {
|
|||||||
const getImportName = (name: string) => (name[0] === '.' ? './' + join(relativeRoot, name) : name).replace(IMPORT_NAME_RE, '')
|
const getImportName = (name: string) => (name[0] === '.' ? './' + join(relativeRoot, name) : name).replace(IMPORT_NAME_RE, '')
|
||||||
|
|
||||||
const modules = nuxt.options._installedModules
|
const modules = nuxt.options._installedModules
|
||||||
.filter(m => m.meta && m.meta.configKey && m.meta.name && !adHocModules.includes(m.meta.name))
|
.filter(m => m.meta && m.meta.configKey && m.meta.name && !m.meta.name.startsWith('nuxt:') && m.meta.name !== 'nuxt-config-schema')
|
||||||
.map(m => [genString(m.meta.configKey), getImportName(m.entryPath || m.meta.name), m] as const)
|
.map(m => [genString(m.meta.configKey), getImportName(m.entryPath || m.meta.name), m] as const)
|
||||||
|
|
||||||
const privateRuntimeConfig = Object.create(null)
|
const privateRuntimeConfig = Object.create(null)
|
||||||
@ -285,7 +284,7 @@ export const layoutTemplate: NuxtTemplate = {
|
|||||||
filename: 'layouts.mjs',
|
filename: 'layouts.mjs',
|
||||||
getContents ({ app }) {
|
getContents ({ app }) {
|
||||||
const layoutsObject = genObjectFromRawEntries(Object.values(app.layouts).map(({ name, file }) => {
|
const layoutsObject = genObjectFromRawEntries(Object.values(app.layouts).map(({ name, file }) => {
|
||||||
return [name, `defineAsyncComponent(${genDynamicImport(file)})`]
|
return [name, `defineAsyncComponent(${genDynamicImport(file, { interopDefault: true })})`]
|
||||||
}))
|
}))
|
||||||
return [
|
return [
|
||||||
`import { defineAsyncComponent } from 'vue'`,
|
`import { defineAsyncComponent } from 'vue'`,
|
||||||
@ -525,6 +524,7 @@ export const nuxtConfigTemplate: NuxtTemplate = {
|
|||||||
`export const multiApp = ${!!ctx.nuxt.options.future.multiApp}`,
|
`export const multiApp = ${!!ctx.nuxt.options.future.multiApp}`,
|
||||||
`export const chunkErrorEvent = ${ctx.nuxt.options.experimental.emitRouteChunkError ? ctx.nuxt.options.builder === '@nuxt/vite-builder' ? '"vite:preloadError"' : '"nuxt:preloadError"' : 'false'}`,
|
`export const chunkErrorEvent = ${ctx.nuxt.options.experimental.emitRouteChunkError ? ctx.nuxt.options.builder === '@nuxt/vite-builder' ? '"vite:preloadError"' : '"nuxt:preloadError"' : 'false'}`,
|
||||||
`export const crawlLinks = ${!!((ctx.nuxt as any)._nitro as Nitro).options.prerender.crawlLinks}`,
|
`export const crawlLinks = ${!!((ctx.nuxt as any)._nitro as Nitro).options.prerender.crawlLinks}`,
|
||||||
|
`export const spaLoadingTemplateOutside = ${ctx.nuxt.options.experimental.spaLoadingTemplateLocation === 'body'}`,
|
||||||
].join('\n\n')
|
].join('\n\n')
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,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: 'nuxt:meta',
|
||||||
configKey: 'unhead',
|
configKey: 'unhead',
|
||||||
},
|
},
|
||||||
async setup (options, nuxt) {
|
async setup (options, nuxt) {
|
||||||
|
@ -13,7 +13,7 @@ import { defaultPresets } from './presets'
|
|||||||
|
|
||||||
export default defineNuxtModule<Partial<ImportsOptions>>({
|
export default defineNuxtModule<Partial<ImportsOptions>>({
|
||||||
meta: {
|
meta: {
|
||||||
name: 'imports',
|
name: 'nuxt:imports',
|
||||||
configKey: 'imports',
|
configKey: 'imports',
|
||||||
},
|
},
|
||||||
defaults: nuxt => ({
|
defaults: nuxt => ({
|
||||||
@ -41,13 +41,19 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
|
|||||||
// Filter disabled sources
|
// Filter disabled sources
|
||||||
// options.sources = options.sources.filter(source => source.disabled !== true)
|
// options.sources = options.sources.filter(source => source.disabled !== true)
|
||||||
|
|
||||||
|
const { addons: inlineAddons, ...rest } = options
|
||||||
|
|
||||||
|
const [addons, addonsOptions] = Array.isArray(inlineAddons) ? [inlineAddons] : [[], inlineAddons]
|
||||||
|
|
||||||
// Create a context to share state between module internals
|
// Create a context to share state between module internals
|
||||||
const ctx = createUnimport({
|
const ctx = createUnimport({
|
||||||
injectAtEnd: true,
|
injectAtEnd: true,
|
||||||
...options,
|
...rest,
|
||||||
addons: {
|
addons: {
|
||||||
|
addons,
|
||||||
vueTemplate: options.autoImport,
|
vueTemplate: options.autoImport,
|
||||||
...options.addons,
|
vueDirectives: options.autoImport === false ? undefined : true,
|
||||||
|
...addonsOptions,
|
||||||
},
|
},
|
||||||
presets,
|
presets,
|
||||||
})
|
})
|
||||||
|
@ -23,7 +23,8 @@ const OPTIONAL_PARAM_RE = /^\/?:.*(?:\?|\(\.\*\)\*)$/
|
|||||||
|
|
||||||
export default defineNuxtModule({
|
export default defineNuxtModule({
|
||||||
meta: {
|
meta: {
|
||||||
name: 'pages',
|
name: 'nuxt:pages',
|
||||||
|
configKey: 'pages',
|
||||||
},
|
},
|
||||||
async setup (_options, nuxt) {
|
async setup (_options, nuxt) {
|
||||||
const useExperimentalTypedPages = nuxt.options.experimental.typedPages
|
const useExperimentalTypedPages = nuxt.options.experimental.typedPages
|
||||||
@ -455,6 +456,8 @@ export default defineNuxtModule({
|
|||||||
addBuildPlugin(PageMetaPlugin({
|
addBuildPlugin(PageMetaPlugin({
|
||||||
dev: nuxt.options.dev,
|
dev: nuxt.options.dev,
|
||||||
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
||||||
|
isPage,
|
||||||
|
routesPath: resolve(nuxt.options.buildDir, 'routes.mjs'),
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -499,13 +502,13 @@ export default defineNuxtModule({
|
|||||||
addTemplate({
|
addTemplate({
|
||||||
filename: 'routes.mjs',
|
filename: 'routes.mjs',
|
||||||
getContents ({ app }) {
|
getContents ({ app }) {
|
||||||
if (!app.pages) { return 'export default []' }
|
if (!app.pages) { return ROUTES_HMR_CODE + 'export default []' }
|
||||||
const { routes, imports } = normalizeRoutes(app.pages, new Set(), {
|
const { routes, imports } = normalizeRoutes(app.pages, new Set(), {
|
||||||
serverComponentRuntime,
|
serverComponentRuntime,
|
||||||
clientComponentRuntime,
|
clientComponentRuntime,
|
||||||
overrideMeta: !!nuxt.options.experimental.scanPageMeta,
|
overrideMeta: !!nuxt.options.experimental.scanPageMeta,
|
||||||
})
|
})
|
||||||
return [...imports, `export default ${routes}`].join('\n')
|
return ROUTES_HMR_CODE + [...imports, `export default ${routes}`].join('\n')
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -610,3 +613,26 @@ export default defineNuxtModule({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const ROUTES_HMR_CODE = /* js */`
|
||||||
|
if (import.meta.hot) {
|
||||||
|
import.meta.hot.accept((mod) => {
|
||||||
|
const router = import.meta.hot.data.router
|
||||||
|
if (!router) {
|
||||||
|
import.meta.hot.invalidate('[nuxt] Cannot replace routes because there is no active router. Reloading.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router.clearRoutes()
|
||||||
|
for (const route of mod.default || mod) {
|
||||||
|
router.addRoute(route)
|
||||||
|
}
|
||||||
|
router.replace('')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleHotUpdate(_router) {
|
||||||
|
if (import.meta.hot) {
|
||||||
|
import.meta.hot.data.router = _router
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
@ -11,6 +11,8 @@ import { parseAndWalk, walk } from 'oxc-walker'
|
|||||||
interface PageMetaPluginOptions {
|
interface PageMetaPluginOptions {
|
||||||
dev?: boolean
|
dev?: boolean
|
||||||
sourcemap?: boolean
|
sourcemap?: boolean
|
||||||
|
isPage?: (file: string) => boolean
|
||||||
|
routesPath?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const HAS_MACRO_RE = /\bdefinePageMeta\s*\(\s*/
|
const HAS_MACRO_RE = /\bdefinePageMeta\s*\(\s*/
|
||||||
@ -20,6 +22,11 @@ const __nuxt_page_meta = null
|
|||||||
export default __nuxt_page_meta
|
export default __nuxt_page_meta
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const CODE_DEV_EMPTY = `
|
||||||
|
const __nuxt_page_meta = {}
|
||||||
|
export default __nuxt_page_meta
|
||||||
|
`
|
||||||
|
|
||||||
const CODE_HMR = `
|
const CODE_HMR = `
|
||||||
// Vite
|
// Vite
|
||||||
if (import.meta.hot) {
|
if (import.meta.hot) {
|
||||||
@ -87,11 +94,11 @@ export const PageMetaPlugin = (options: PageMetaPluginOptions = {}) => createUnp
|
|||||||
|
|
||||||
if (!hasMacro && !code.includes('export { default }') && !code.includes('__nuxt_page_meta')) {
|
if (!hasMacro && !code.includes('export { default }') && !code.includes('__nuxt_page_meta')) {
|
||||||
if (!code) {
|
if (!code) {
|
||||||
s.append(CODE_EMPTY + (options.dev ? CODE_HMR : ''))
|
s.append(options.dev ? (CODE_DEV_EMPTY + CODE_HMR) : CODE_EMPTY)
|
||||||
const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href))
|
const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href))
|
||||||
logger.error(`The file \`${pathname}\` is not a valid page as it has no content.`)
|
logger.error(`The file \`${pathname}\` is not a valid page as it has no content.`)
|
||||||
} else {
|
} else {
|
||||||
s.overwrite(0, code.length, CODE_EMPTY + (options.dev ? CODE_HMR : ''))
|
s.overwrite(0, code.length, options.dev ? (CODE_DEV_EMPTY + CODE_HMR) : CODE_EMPTY)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result()
|
return result()
|
||||||
@ -145,19 +152,23 @@ export const PageMetaPlugin = (options: PageMetaPluginOptions = {}) => createUnp
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!s.hasChanged() && !code.includes('__nuxt_page_meta')) {
|
if (!s.hasChanged() && !code.includes('__nuxt_page_meta')) {
|
||||||
s.overwrite(0, code.length, CODE_EMPTY + (options.dev ? CODE_HMR : ''))
|
s.overwrite(0, code.length, options.dev ? (CODE_DEV_EMPTY + CODE_HMR) : CODE_EMPTY)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result()
|
return result()
|
||||||
},
|
},
|
||||||
vite: {
|
vite: {
|
||||||
handleHotUpdate: {
|
handleHotUpdate: {
|
||||||
order: 'pre',
|
order: 'post',
|
||||||
handler: ({ modules }) => {
|
handler: ({ file, modules, server }) => {
|
||||||
// Remove macro file from modules list to prevent HMR overrides
|
if (options.isPage?.(file)) {
|
||||||
const index = modules.findIndex(i => i.id?.includes('?macro=true'))
|
const macroModule = server.moduleGraph.getModuleById(file + '?macro=true')
|
||||||
if (index !== -1) {
|
const routesModule = server.moduleGraph.getModuleById('virtual:nuxt:' + options.routesPath)
|
||||||
modules.splice(index, 1)
|
return [
|
||||||
|
...modules,
|
||||||
|
...macroModule ? [macroModule] : [],
|
||||||
|
...routesModule ? [routesModule] : [],
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -3,8 +3,8 @@ import { getCurrentInstance } from 'vue'
|
|||||||
import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteRecordRaw, RouteRecordRedirectOption } from 'vue-router'
|
import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteRecordRaw, RouteRecordRedirectOption } from 'vue-router'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import type { NitroRouteConfig } from 'nitro/types'
|
import type { NitroRouteConfig } from 'nitro/types'
|
||||||
|
import type { NuxtError } from 'nuxt/app'
|
||||||
import { useNuxtApp } from '#app/nuxt'
|
import { useNuxtApp } from '#app/nuxt'
|
||||||
import type { NuxtError } from '#app'
|
|
||||||
|
|
||||||
export interface PageMeta {
|
export interface PageMeta {
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
|
@ -5,10 +5,11 @@ import { START_LOCATION, createMemoryHistory, createRouter, createWebHashHistory
|
|||||||
import { createError } from 'h3'
|
import { createError } from 'h3'
|
||||||
import { isEqual, withoutBase } from 'ufo'
|
import { isEqual, withoutBase } from 'ufo'
|
||||||
|
|
||||||
|
import type { Plugin, RouteMiddleware } from 'nuxt/app'
|
||||||
import type { PageMeta } from '../composables'
|
import type { PageMeta } from '../composables'
|
||||||
|
|
||||||
import { toArray } from '../utils'
|
import { toArray } from '../utils'
|
||||||
import type { Plugin, RouteMiddleware } from '#app'
|
|
||||||
import { getRouteRules } from '#app/composables/manifest'
|
import { getRouteRules } from '#app/composables/manifest'
|
||||||
import { defineNuxtPlugin, useRuntimeConfig } from '#app/nuxt'
|
import { defineNuxtPlugin, useRuntimeConfig } from '#app/nuxt'
|
||||||
import { clearError, showError, useError } from '#app/composables/error'
|
import { clearError, showError, useError } from '#app/composables/error'
|
||||||
@ -17,7 +18,7 @@ import { navigateTo } from '#app/composables/router'
|
|||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
import { appManifest as isAppManifestEnabled } from '#build/nuxt.config.mjs'
|
import { appManifest as isAppManifestEnabled } from '#build/nuxt.config.mjs'
|
||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
import _routes from '#build/routes'
|
import _routes, { handleHotUpdate } from '#build/routes'
|
||||||
import routerOptions from '#build/router.options'
|
import routerOptions from '#build/router.options'
|
||||||
// @ts-expect-error virtual file
|
// @ts-expect-error virtual file
|
||||||
import { globalMiddleware, namedMiddleware } from '#build/middleware'
|
import { globalMiddleware, namedMiddleware } from '#build/middleware'
|
||||||
@ -87,6 +88,8 @@ const plugin: Plugin<{ router: Router }> = defineNuxtPlugin({
|
|||||||
routes,
|
routes,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
handleHotUpdate(router)
|
||||||
|
|
||||||
if (import.meta.client && 'scrollRestoration' in window.history) {
|
if (import.meta.client && 'scrollRestoration' in window.history) {
|
||||||
window.history.scrollRestoration = 'auto'
|
window.history.scrollRestoration = 'auto'
|
||||||
}
|
}
|
||||||
|
@ -271,7 +271,7 @@ withDefaults(defineProps<{ things?: any[]; somethingElse?: string }>(), {
|
|||||||
"<template>
|
"<template>
|
||||||
<div>
|
<div>
|
||||||
<HelloWorld />
|
<HelloWorld />
|
||||||
<NuxtTeleportIslandComponent to="HelloWorld-CyH3UXLuYA" :nuxt-client="true"><HelloWorld /></NuxtTeleportIslandComponent>
|
<NuxtTeleportIslandComponent :nuxt-client="true"><HelloWorld /></NuxtTeleportIslandComponent>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -305,7 +305,7 @@ withDefaults(defineProps<{ things?: any[]; somethingElse?: string }>(), {
|
|||||||
"<template>
|
"<template>
|
||||||
<div>
|
<div>
|
||||||
<HelloWorld />
|
<HelloWorld />
|
||||||
<NuxtTeleportIslandComponent to="HelloWorld-eo0XycWCUV" :nuxt-client="nuxtClient"><HelloWorld /></NuxtTeleportIslandComponent>
|
<NuxtTeleportIslandComponent :nuxt-client="nuxtClient"><HelloWorld /></NuxtTeleportIslandComponent>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -376,7 +376,7 @@ withDefaults(defineProps<{ things?: any[]; somethingElse?: string }>(), {
|
|||||||
import NuxtTeleportSsrSlot from '#app/components/nuxt-teleport-island-slot'</script><template>
|
import NuxtTeleportSsrSlot from '#app/components/nuxt-teleport-island-slot'</script><template>
|
||||||
<div>
|
<div>
|
||||||
<HelloWorld />
|
<HelloWorld />
|
||||||
<NuxtTeleportIslandComponent to="HelloWorld-CyH3UXLuYA" :nuxt-client="true"><HelloWorld /></NuxtTeleportIslandComponent>
|
<NuxtTeleportIslandComponent :nuxt-client="true"><HelloWorld /></NuxtTeleportIslandComponent>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -402,9 +402,9 @@ withDefaults(defineProps<{ things?: any[]; somethingElse?: string }>(), {
|
|||||||
import NuxtTeleportIslandComponent from '#app/components/nuxt-teleport-island-component'
|
import NuxtTeleportIslandComponent from '#app/components/nuxt-teleport-island-component'
|
||||||
import NuxtTeleportSsrSlot from '#app/components/nuxt-teleport-island-slot'</script><template>
|
import NuxtTeleportSsrSlot from '#app/components/nuxt-teleport-island-slot'</script><template>
|
||||||
<div>
|
<div>
|
||||||
<NuxtTeleportIslandComponent v-if="false" to="HelloWorld-D9uaHyzL7X" :nuxt-client="true"><HelloWorld /></NuxtTeleportIslandComponent>
|
<NuxtTeleportIslandComponent v-if="false" :nuxt-client="true"><HelloWorld /></NuxtTeleportIslandComponent>
|
||||||
<NuxtTeleportIslandComponent v-else-if="true" to="HelloWorld-o4RZMtArnE" :nuxt-client="true"><HelloWorld /></NuxtTeleportIslandComponent>
|
<NuxtTeleportIslandComponent v-else-if="true" :nuxt-client="true"><HelloWorld /></NuxtTeleportIslandComponent>
|
||||||
<NuxtTeleportIslandComponent v-else to="HelloWorld-m1IbXHdd8O" :nuxt-client="true"><HelloWorld /></NuxtTeleportIslandComponent>
|
<NuxtTeleportIslandComponent v-else :nuxt-client="true"><HelloWorld /></NuxtTeleportIslandComponent>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
"
|
"
|
||||||
|
3
packages/rspack/.attw.json
Normal file
3
packages/rspack/.attw.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ignoreRules": ["cjs-resolves-to-esm"]
|
||||||
|
}
|
@ -26,12 +26,13 @@
|
|||||||
"builder.mjs"
|
"builder.mjs"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepack": "unbuild"
|
"prepack": "unbuild",
|
||||||
|
"test:attw": "attw --pack"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/friendly-errors-webpack-plugin": "^2.6.0",
|
"@nuxt/friendly-errors-webpack-plugin": "^2.6.0",
|
||||||
"@nuxt/kit": "workspace:*",
|
"@nuxt/kit": "workspace:*",
|
||||||
"@rspack/core": "^1.1.4",
|
"@rspack/core": "^1.1.5",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"css-loader": "^7.1.2",
|
"css-loader": "^7.1.2",
|
||||||
"css-minimizer-webpack-plugin": "^7.0.0",
|
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||||
@ -43,11 +44,11 @@
|
|||||||
"fork-ts-checker-webpack-plugin": "^9.0.2",
|
"fork-ts-checker-webpack-plugin": "^9.0.2",
|
||||||
"globby": "^14.0.2",
|
"globby": "^14.0.2",
|
||||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
"jiti": "^2.4.0",
|
"jiti": "^2.4.1",
|
||||||
"knitwork": "^1.1.0",
|
"knitwork": "^1.1.0",
|
||||||
"lodash-es": "4.17.21",
|
"lodash-es": "4.17.21",
|
||||||
"magic-string": "^0.30.14",
|
"magic-string": "^0.30.14",
|
||||||
"memfs": "^4.14.0",
|
"memfs": "^4.14.1",
|
||||||
"ohash": "^1.1.4",
|
"ohash": "^1.1.4",
|
||||||
"pathe": "^1.1.2",
|
"pathe": "^1.1.2",
|
||||||
"pify": "^6.1.0",
|
"pify": "^6.1.0",
|
||||||
@ -61,7 +62,7 @@
|
|||||||
"time-fix-plugin": "^2.0.7",
|
"time-fix-plugin": "^2.0.7",
|
||||||
"ufo": "^1.5.4",
|
"ufo": "^1.5.4",
|
||||||
"unenv": "^1.10.0",
|
"unenv": "^1.10.0",
|
||||||
"unplugin": "^1.16.0",
|
"unplugin": "^2.0.0",
|
||||||
"url-loader": "^4.1.1",
|
"url-loader": "^4.1.1",
|
||||||
"vue-bundle-renderer": "^2.1.1",
|
"vue-bundle-renderer": "^2.1.1",
|
||||||
"vue-loader": "^17.4.2",
|
"vue-loader": "^17.4.2",
|
||||||
@ -76,7 +77,7 @@
|
|||||||
"@types/pify": "5.0.4",
|
"@types/pify": "5.0.4",
|
||||||
"@types/webpack-bundle-analyzer": "4.7.0",
|
"@types/webpack-bundle-analyzer": "4.7.0",
|
||||||
"@types/webpack-hot-middleware": "2.25.9",
|
"@types/webpack-hot-middleware": "2.25.9",
|
||||||
"rollup": "4.27.4",
|
"rollup": "4.28.0",
|
||||||
"unbuild": "3.0.0-rc.11",
|
"unbuild": "3.0.0-rc.11",
|
||||||
"vue": "3.5.13"
|
"vue": "3.5.13"
|
||||||
},
|
},
|
||||||
@ -84,6 +85,6 @@
|
|||||||
"vue": "^3.3.4"
|
"vue": "^3.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.10.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
packages/schema/.attw.json
Normal file
5
packages/schema/.attw.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"ignoreRules": [
|
||||||
|
"cjs-resolves-to-esm"
|
||||||
|
]
|
||||||
|
}
|
@ -22,7 +22,7 @@ export default defineBuildConfig({
|
|||||||
],
|
],
|
||||||
externals: [
|
externals: [
|
||||||
// Type imports
|
// Type imports
|
||||||
'#app/components/nuxt-link',
|
'nuxt/app',
|
||||||
'cssnano',
|
'cssnano',
|
||||||
'autoprefixer',
|
'autoprefixer',
|
||||||
'ofetch',
|
'ofetch',
|
||||||
|
2
packages/schema/builder-env.d.ts
vendored
2
packages/schema/builder-env.d.ts
vendored
@ -1 +1 @@
|
|||||||
export * from './dist/env'
|
export * from './dist/builder-env'
|
||||||
|
@ -28,10 +28,12 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
"schema",
|
"schema",
|
||||||
|
"builder-env.d.ts",
|
||||||
"env.d.ts"
|
"env.d.ts"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepack": "unbuild"
|
"prepack": "unbuild",
|
||||||
|
"test:attw": "attw --pack"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/file-loader": "5.0.4",
|
"@types/file-loader": "5.0.4",
|
||||||
@ -50,7 +52,7 @@
|
|||||||
"ofetch": "1.4.1",
|
"ofetch": "1.4.1",
|
||||||
"unbuild": "3.0.0-rc.11",
|
"unbuild": "3.0.0-rc.11",
|
||||||
"unctx": "2.3.1",
|
"unctx": "2.3.1",
|
||||||
"vite": "6.0.1",
|
"vite": "6.0.2",
|
||||||
"vue": "3.5.13",
|
"vue": "3.5.13",
|
||||||
"vue-bundle-renderer": "2.1.1",
|
"vue-bundle-renderer": "2.1.1",
|
||||||
"vue-loader": "17.4.2",
|
"vue-loader": "17.4.2",
|
||||||
@ -74,6 +76,6 @@
|
|||||||
"untyped": "^1.5.1"
|
"untyped": "^1.5.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.10.0"
|
"node": "^18.12.0 || ^20.0.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,7 +235,7 @@ export default defineUntypedSchema({
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Customize Nuxt root element tag.
|
* Customize Nuxt Teleport element tag.
|
||||||
*/
|
*/
|
||||||
teleportTag: {
|
teleportTag: {
|
||||||
$resolve: val => val || 'div',
|
$resolve: val => val || 'div',
|
||||||
@ -262,6 +262,21 @@ export default defineUntypedSchema({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customize Nuxt SpaLoader element tag.
|
||||||
|
*/
|
||||||
|
spaLoaderTag: {
|
||||||
|
$resolve: val => val || 'div',
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customize Nuxt Nuxt SpaLoader element attributes.
|
||||||
|
* @type {typeof import('@unhead/schema').HtmlAttributes}
|
||||||
|
*/
|
||||||
|
spaLoaderAttrs: {
|
||||||
|
id: '__nuxt-loader',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,7 +24,16 @@ export default defineUntypedSchema({
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to generate sourcemaps.
|
* Configures whether and how sourcemaps are generated for server and/or client bundles.
|
||||||
|
*
|
||||||
|
* If set to a single boolean, that value applies to both server and client.
|
||||||
|
* Additionally, the `'hidden'` option is also available for both server and client.
|
||||||
|
*
|
||||||
|
* Available options for both client and server:
|
||||||
|
* - `true`: Generates sourcemaps and includes source references in the final bundle.
|
||||||
|
* - `false`: Does not generate any sourcemaps.
|
||||||
|
* - `'hidden'`: Generates sourcemaps but does not include references in the final bundle.
|
||||||
|
*
|
||||||
* @type {boolean | { server?: boolean | 'hidden', client?: boolean | 'hidden' }}
|
* @type {boolean | { server?: boolean | 'hidden', client?: boolean | 'hidden' }}
|
||||||
*/
|
*/
|
||||||
sourcemap: {
|
sourcemap: {
|
||||||
|
@ -360,7 +360,7 @@ export default defineUntypedSchema({
|
|||||||
* `app/` directory.
|
* `app/` directory.
|
||||||
*/
|
*/
|
||||||
defaults: {
|
defaults: {
|
||||||
/** @type {typeof import('#app/components/nuxt-link')['NuxtLinkOptions']} */
|
/** @type {typeof import('nuxt/app')['NuxtLinkOptions']} */
|
||||||
nuxtLink: {
|
nuxtLink: {
|
||||||
componentName: 'NuxtLink',
|
componentName: 'NuxtLink',
|
||||||
prefetch: true,
|
prefetch: true,
|
||||||
@ -417,5 +417,25 @@ export default defineUntypedSchema({
|
|||||||
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion === 4)
|
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion === 4)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep showing the spa-loading-template until suspense:resolve
|
||||||
|
* @see [Nuxt Issues #24770](https://github.com/nuxt/nuxt/issues/21721)
|
||||||
|
* @type {'body' | 'within'}
|
||||||
|
*/
|
||||||
|
spaLoadingTemplateLocation: {
|
||||||
|
$resolve: async (val, get) => {
|
||||||
|
return val ?? (((await get('future') as Record<string, unknown>).compatibilityVersion === 4) ? 'body' : 'within')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable timings for Nuxt application hooks in the performance panel of Chromium-based browsers.
|
||||||
|
*
|
||||||
|
* @see [the Chrome DevTools extensibility API](https://developer.chrome.com/docs/devtools/performance/extension#tracks)
|
||||||
|
*/
|
||||||
|
browserDevtoolsTiming: {
|
||||||
|
$resolve: async (val, get) => val ?? await get('dev'),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -8,7 +8,7 @@ export default defineUntypedSchema({
|
|||||||
/**
|
/**
|
||||||
* Configuration that will be passed directly to Vite.
|
* Configuration that will be passed directly to Vite.
|
||||||
*
|
*
|
||||||
* @see [Vite configuration docs](https://vitejs.dev/config) for more information.
|
* @see [Vite configuration docs](https://vite.dev/config) for more information.
|
||||||
* Please note that not all vite options are supported in Nuxt.
|
* Please note that not all vite options are supported in Nuxt.
|
||||||
* @type {typeof import('../src/types/config').ViteConfig & { $client?: typeof import('../src/types/config').ViteConfig, $server?: typeof import('../src/types/config').ViteConfig }}
|
* @type {typeof import('../src/types/config').ViteConfig & { $client?: typeof import('../src/types/config').ViteConfig, $server?: typeof import('../src/types/config').ViteConfig }}
|
||||||
*/
|
*/
|
||||||
|
@ -119,10 +119,10 @@ export interface ImportGlobEagerFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ViteImportMeta {
|
export interface ViteImportMeta {
|
||||||
/** Vite client HMR API - see https://vitejs.dev/guide/api-hmr.html */
|
/** Vite client HMR API - see https://vite.dev/guide/api-hmr.html */
|
||||||
readonly hot?: ViteHot
|
readonly hot?: ViteHot
|
||||||
|
|
||||||
/** vite glob import utility - https://vitejs.dev/guide/features.html#glob-import */
|
/** vite glob import utility - https://vite.dev/guide/features.html#glob-import */
|
||||||
glob: ImportGlobFunction
|
glob: ImportGlobFunction
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import type { RouterHistory, RouterOptions as _RouterOptions } from 'vue-router'
|
import type { RouterHistory, RouterOptions as _RouterOptions } from 'vue-router'
|
||||||
|
|
||||||
export type RouterOptions = Partial<Omit<_RouterOptions, 'history' | 'routes'>> & {
|
export type RouterOptions = Partial<Omit<_RouterOptions, 'history' | 'routes'>> & {
|
||||||
history?: (baseURL?: string) => RouterHistory
|
history?: (baseURL?: string) => RouterHistory | null | undefined
|
||||||
routes?: (_routes: _RouterOptions['routes']) => _RouterOptions['routes'] | Promise<_RouterOptions['routes']>
|
routes?: (_routes: _RouterOptions['routes']) => _RouterOptions['routes'] | Promise<_RouterOptions['routes']>
|
||||||
hashMode?: boolean
|
hashMode?: boolean
|
||||||
scrollBehaviorType?: 'smooth' | 'auto'
|
scrollBehaviorType?: 'smooth' | 'auto'
|
||||||
|
@ -17,11 +17,11 @@
|
|||||||
"prerender": "pnpm build && jiti ./lib/prerender"
|
"prerender": "pnpm build && jiti ./lib/prerender"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@unocss/reset": "0.64.1",
|
"@unocss/reset": "0.65.0",
|
||||||
"beasties": "0.1.0",
|
"beasties": "0.1.0",
|
||||||
"html-validate": "8.26.0",
|
"html-validate": "8.27.0",
|
||||||
"htmlnano": "2.1.1",
|
"htmlnano": "2.1.1",
|
||||||
"jiti": "2.4.0",
|
"jiti": "2.4.1",
|
||||||
"knitwork": "1.1.0",
|
"knitwork": "1.1.0",
|
||||||
"pathe": "1.1.2",
|
"pathe": "1.1.2",
|
||||||
"prettier": "3.4.1",
|
"prettier": "3.4.1",
|
||||||
@ -29,7 +29,10 @@
|
|||||||
"svgo": "3.3.2",
|
"svgo": "3.3.2",
|
||||||
"tinyexec": "0.3.1",
|
"tinyexec": "0.3.1",
|
||||||
"tinyglobby": "0.2.10",
|
"tinyglobby": "0.2.10",
|
||||||
"unocss": "0.64.1",
|
"unocss": "0.65.0",
|
||||||
"vite": "6.0.1"
|
"vite": "6.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
packages/vite/.attw.json
Normal file
3
packages/vite/.attw.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ignoreRules": ["cjs-resolves-to-esm"]
|
||||||
|
}
|
@ -21,12 +21,13 @@
|
|||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepack": "unbuild"
|
"prepack": "unbuild",
|
||||||
|
"test:attw": "attw --pack"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/schema": "workspace:*",
|
"@nuxt/schema": "workspace:*",
|
||||||
"@types/clear": "0.1.4",
|
"@types/clear": "0.1.4",
|
||||||
"rollup": "4.27.4",
|
"rollup": "4.28.0",
|
||||||
"unbuild": "3.0.0-rc.11",
|
"unbuild": "3.0.0-rc.11",
|
||||||
"vue": "3.5.13"
|
"vue": "3.5.13"
|
||||||
},
|
},
|
||||||
@ -45,7 +46,7 @@
|
|||||||
"externality": "^1.0.2",
|
"externality": "^1.0.2",
|
||||||
"get-port-please": "^3.1.2",
|
"get-port-please": "^3.1.2",
|
||||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
"jiti": "^2.4.0",
|
"jiti": "^2.4.1",
|
||||||
"knitwork": "^1.1.0",
|
"knitwork": "^1.1.0",
|
||||||
"magic-string": "^0.30.14",
|
"magic-string": "^0.30.14",
|
||||||
"mlly": "^1.7.3",
|
"mlly": "^1.7.3",
|
||||||
@ -56,9 +57,9 @@
|
|||||||
"std-env": "^3.8.0",
|
"std-env": "^3.8.0",
|
||||||
"ufo": "^1.5.4",
|
"ufo": "^1.5.4",
|
||||||
"unenv": "^1.10.0",
|
"unenv": "^1.10.0",
|
||||||
"unplugin": "^1.16.0",
|
"unplugin": "^2.0.0",
|
||||||
"vite": "^6.0.1",
|
"vite": "^6.0.2",
|
||||||
"vite-node": "^2.1.6",
|
"vite-node": "^2.1.8",
|
||||||
"vite-plugin-checker": "^0.8.0",
|
"vite-plugin-checker": "^0.8.0",
|
||||||
"vue-bundle-renderer": "^2.1.1"
|
"vue-bundle-renderer": "^2.1.1"
|
||||||
},
|
},
|
||||||
@ -66,6 +67,6 @@
|
|||||||
"vue": "^3.3.4"
|
"vue": "^3.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.10.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,7 @@ export async function buildServer (ctx: ViteBuildContext) {
|
|||||||
'nitro/runtime',
|
'nitro/runtime',
|
||||||
'#internal/nuxt/paths',
|
'#internal/nuxt/paths',
|
||||||
'#internal/nuxt/app-config',
|
'#internal/nuxt/app-config',
|
||||||
|
'#app-manifest',
|
||||||
'#shared',
|
'#shared',
|
||||||
new RegExp('^' + escapeStringRegexp(withTrailingSlash(resolve(ctx.nuxt.options.rootDir, ctx.nuxt.options.dir.shared)))),
|
new RegExp('^' + escapeStringRegexp(withTrailingSlash(resolve(ctx.nuxt.options.rootDir, ctx.nuxt.options.dir.shared)))),
|
||||||
],
|
],
|
||||||
|
@ -39,33 +39,21 @@ export function viteNodePlugin (ctx: ViteBuildContext): VitePlugin {
|
|||||||
name: 'nuxt:vite-node-server',
|
name: 'nuxt:vite-node-server',
|
||||||
enforce: 'post',
|
enforce: 'post',
|
||||||
configureServer (server) {
|
configureServer (server) {
|
||||||
function invalidateVirtualModules () {
|
server.middlewares.use('/__nuxt_vite_node__', toNodeListener(createViteNodeApp(ctx, invalidates)))
|
||||||
for (const [id, mod] of server.moduleGraph.idToModuleMap) {
|
|
||||||
if (id.startsWith('virtual:') || id.startsWith('\0virtual:')) {
|
// invalidate changed virtual modules when templates are regenerated
|
||||||
|
ctx.nuxt.hook('app:templatesGenerated', (_app, changedTemplates) => {
|
||||||
|
for (const template of changedTemplates) {
|
||||||
|
const mods = server.moduleGraph.getModulesByFile(`virtual:nuxt:${template.dst}`)
|
||||||
|
|
||||||
|
for (const mod of mods || []) {
|
||||||
markInvalidate(mod)
|
markInvalidate(mod)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.nuxt.apps.default) {
|
|
||||||
for (const template of ctx.nuxt.apps.default.templates) {
|
|
||||||
markInvalidates(server.moduleGraph.getModulesByFile(template.dst!))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
server.middlewares.use('/__nuxt_vite_node__', toNodeListener(createViteNodeApp(ctx, invalidates)))
|
|
||||||
|
|
||||||
// Invalidate all virtual modules when templates are regenerated
|
|
||||||
ctx.nuxt.hook('app:templatesGenerated', () => {
|
|
||||||
invalidateVirtualModules()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
server.watcher.on('all', (event, file) => {
|
server.watcher.on('all', (event, file) => {
|
||||||
markInvalidates(server.moduleGraph.getModulesByFile(normalize(file)))
|
markInvalidates(server.moduleGraph.getModulesByFile(normalize(file)))
|
||||||
// Invalidate all virtual modules when a file is added or removed
|
|
||||||
if (event === 'add' || event === 'unlink') {
|
|
||||||
invalidateVirtualModules()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -210,10 +210,11 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
|
|||||||
|
|
||||||
nuxt.hook('vite:serverCreated', (server: vite.ViteDevServer, env) => {
|
nuxt.hook('vite:serverCreated', (server: vite.ViteDevServer, env) => {
|
||||||
// Invalidate virtual modules when templates are re-generated
|
// Invalidate virtual modules when templates are re-generated
|
||||||
ctx.nuxt.hook('app:templatesGenerated', () => {
|
ctx.nuxt.hook('app:templatesGenerated', (_app, changedTemplates) => {
|
||||||
for (const [id, mod] of server.moduleGraph.idToModuleMap) {
|
for (const template of changedTemplates) {
|
||||||
if (id.startsWith('virtual:') || id.startsWith('\0virtual:')) {
|
for (const mod of server.moduleGraph.getModulesByFile(`virtual:nuxt:${template.dst}`) || []) {
|
||||||
server.moduleGraph.invalidateModule(mod)
|
server.moduleGraph.invalidateModule(mod)
|
||||||
|
server.reloadModule(mod)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
3
packages/webpack/.attw.json
Normal file
3
packages/webpack/.attw.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ignoreRules": ["cjs-resolves-to-esm"]
|
||||||
|
}
|
@ -26,7 +26,8 @@
|
|||||||
"builder.mjs"
|
"builder.mjs"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepack": "unbuild"
|
"prepack": "unbuild",
|
||||||
|
"test:attw": "attw --pack"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/friendly-errors-webpack-plugin": "^2.6.0",
|
"@nuxt/friendly-errors-webpack-plugin": "^2.6.0",
|
||||||
@ -42,11 +43,11 @@
|
|||||||
"fork-ts-checker-webpack-plugin": "^9.0.2",
|
"fork-ts-checker-webpack-plugin": "^9.0.2",
|
||||||
"globby": "^14.0.2",
|
"globby": "^14.0.2",
|
||||||
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
|
||||||
"jiti": "^2.4.0",
|
"jiti": "^2.4.1",
|
||||||
"knitwork": "^1.1.0",
|
"knitwork": "^1.1.0",
|
||||||
"lodash-es": "4.17.21",
|
"lodash-es": "4.17.21",
|
||||||
"magic-string": "^0.30.14",
|
"magic-string": "^0.30.14",
|
||||||
"memfs": "^4.14.0",
|
"memfs": "^4.14.1",
|
||||||
"mini-css-extract-plugin": "^2.9.2",
|
"mini-css-extract-plugin": "^2.9.2",
|
||||||
"ohash": "^1.1.4",
|
"ohash": "^1.1.4",
|
||||||
"pathe": "^1.1.2",
|
"pathe": "^1.1.2",
|
||||||
@ -61,7 +62,7 @@
|
|||||||
"time-fix-plugin": "^2.0.7",
|
"time-fix-plugin": "^2.0.7",
|
||||||
"ufo": "^1.5.4",
|
"ufo": "^1.5.4",
|
||||||
"unenv": "^1.10.0",
|
"unenv": "^1.10.0",
|
||||||
"unplugin": "^1.16.0",
|
"unplugin": "^2.0.0",
|
||||||
"url-loader": "^4.1.1",
|
"url-loader": "^4.1.1",
|
||||||
"vue-bundle-renderer": "^2.1.1",
|
"vue-bundle-renderer": "^2.1.1",
|
||||||
"vue-loader": "^17.4.2",
|
"vue-loader": "^17.4.2",
|
||||||
@ -73,12 +74,12 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/schema": "workspace:*",
|
"@nuxt/schema": "workspace:*",
|
||||||
"@rspack/core": "1.1.4",
|
"@rspack/core": "1.1.5",
|
||||||
"@types/lodash-es": "4.17.12",
|
"@types/lodash-es": "4.17.12",
|
||||||
"@types/pify": "5.0.4",
|
"@types/pify": "5.0.4",
|
||||||
"@types/webpack-bundle-analyzer": "4.7.0",
|
"@types/webpack-bundle-analyzer": "4.7.0",
|
||||||
"@types/webpack-hot-middleware": "2.25.9",
|
"@types/webpack-hot-middleware": "2.25.9",
|
||||||
"rollup": "4.27.4",
|
"rollup": "4.28.0",
|
||||||
"unbuild": "3.0.0-rc.11",
|
"unbuild": "3.0.0-rc.11",
|
||||||
"vue": "3.5.13"
|
"vue": "3.5.13"
|
||||||
},
|
},
|
||||||
@ -86,6 +87,6 @@
|
|||||||
"vue": "^3.3.4"
|
"vue": "^3.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.18.0 || >=16.10.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ function serverStandalone (ctx: WebpackConfigContext) {
|
|||||||
resolve(ctx.nuxt.options.rootDir, ctx.nuxt.options.dir.shared),
|
resolve(ctx.nuxt.options.rootDir, ctx.nuxt.options.dir.shared),
|
||||||
]
|
]
|
||||||
if (!ctx.nuxt.options.dev) {
|
if (!ctx.nuxt.options.dev) {
|
||||||
external.push('#internal/nuxt/paths', '#internal/nuxt/app-config')
|
external.push('#internal/nuxt/paths', '#internal/nuxt/app-config', '#app-manifest')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Array.isArray(ctx.config.externals)) { return }
|
if (!Array.isArray(ctx.config.externals)) { return }
|
||||||
|
@ -8,5 +8,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nuxt": "workspace:*"
|
"nuxt": "workspace:*"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2379
pnpm-lock.yaml
2379
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -28,11 +28,22 @@
|
|||||||
"ignoreDeps": [
|
"ignoreDeps": [
|
||||||
"nitro",
|
"nitro",
|
||||||
"h3",
|
"h3",
|
||||||
"typescript",
|
|
||||||
"nuxt",
|
"nuxt",
|
||||||
"nuxt3",
|
"nuxt3",
|
||||||
"@nuxt/kit"
|
"@nuxt/kit"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "typescript",
|
||||||
|
"matchPackageNames": [
|
||||||
|
"typescript"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "unimport",
|
||||||
|
"matchPackageNames": [
|
||||||
|
"unimport"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1960,12 +1960,12 @@ describe('server components/islands', () => {
|
|||||||
await page.waitForLoadState('networkidle')
|
await page.waitForLoadState('networkidle')
|
||||||
await page.getByText('Go to page without lazy server component').click()
|
await page.getByText('Go to page without lazy server component').click()
|
||||||
|
|
||||||
const text = (await page.innerText('pre')).replaceAll(/ data-island-uid="([^"]*)"/g, '').replace(/data-island-component="([^"]*)"/g, (_, content) => `data-island-component="${content.split('-')[0]}"`)
|
const text = (await page.innerText('pre')).replaceAll(/ data-island-uid="([^"]*)"/g, '').replace(/data-island-component="([^"]*)"/g, 'data-island-component')
|
||||||
|
|
||||||
if (isWebpack) {
|
if (isWebpack) {
|
||||||
expect(text).toMatchInlineSnapshot('" End page <pre></pre><section id="fallback"><div> This is a .server (20ms) async component that was very long ... <div id="async-server-component-count">42</div><div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><!--[--><div style="display: contents;" data-island-slot="default"><!--teleport start--><!--teleport end--></div><!--]--></div></section><section id="no-fallback"><div> This is a .server (20ms) async component that was very long ... <div id="async-server-component-count">42</div><div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><!--[--><div style="display: contents;" data-island-slot="default"><!--teleport start--><!--teleport end--></div><!--]--></div></section><div> ServerWithClient.server.vue : <p>count: 0</p> This component should not be preloaded <div><!--[--><div>a</div><div>b</div><div>c</div><!--]--></div> This is not interactive <div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><div class="interactive-component-wrapper" style="border:solid 1px red;"> The component below is not a slot but declared as interactive <div class="sugar-counter" nuxt-client=""> Sugar Counter 12 x 1 = 12 <button> Inc </button></div></div></div>"')
|
expect(text).toMatchInlineSnapshot('" End page <pre></pre><section id="fallback"><div> This is a .server (20ms) async component that was very long ... <div id="async-server-component-count">42</div><div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><!--[--><div style="display: contents;" data-island-slot="default"><!--teleport start--><!--teleport end--></div><!--]--></div></section><section id="no-fallback"><div> This is a .server (20ms) async component that was very long ... <div id="async-server-component-count">42</div><div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><!--[--><div style="display: contents;" data-island-slot="default"><!--teleport start--><!--teleport end--></div><!--]--></div></section><div> ServerWithClient.server.vue : <p>count: 0</p> This component should not be preloaded <div><!--[--><div>a</div><div>b</div><div>c</div><!--]--></div> This is not interactive <div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><div class="interactive-component-wrapper" style="border:solid 1px red;"> The component below is not a slot but declared as interactive <div class="sugar-counter" nuxt-client=""> Sugar Counter 12 x 1 = 12 <button> Inc </button></div></div></div>"')
|
||||||
} else {
|
} else {
|
||||||
expect(text).toMatchInlineSnapshot('" End page <pre></pre><section id="fallback"><div> This is a .server (20ms) async component that was very long ... <div id="async-server-component-count">42</div><div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><!--[--><div style="display: contents;" data-island-slot="default"><!--teleport start--><!--teleport end--></div><!--]--></div></section><section id="no-fallback"><div> This is a .server (20ms) async component that was very long ... <div id="async-server-component-count">42</div><div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><!--[--><div style="display: contents;" data-island-slot="default"><!--teleport start--><!--teleport end--></div><!--]--></div></section><div> ServerWithClient.server.vue : <p>count: 0</p> This component should not be preloaded <div><!--[--><div>a</div><div>b</div><div>c</div><!--]--></div> This is not interactive <div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><div class="interactive-component-wrapper" style="border:solid 1px red;"> The component below is not a slot but declared as interactive <!--[--><div style="display: contents;" data-island-component="Counter"></div><!--teleport start--><!--teleport end--><!--]--></div></div>"')
|
expect(text).toMatchInlineSnapshot('" End page <pre></pre><section id="fallback"><div> This is a .server (20ms) async component that was very long ... <div id="async-server-component-count">42</div><div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><!--[--><div style="display: contents;" data-island-slot="default"><!--teleport start--><!--teleport end--></div><!--]--></div></section><section id="no-fallback"><div> This is a .server (20ms) async component that was very long ... <div id="async-server-component-count">42</div><div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><!--[--><div style="display: contents;" data-island-slot="default"><!--teleport start--><!--teleport end--></div><!--]--></div></section><div> ServerWithClient.server.vue : <p>count: 0</p> This component should not be preloaded <div><!--[--><div>a</div><div>b</div><div>c</div><!--]--></div> This is not interactive <div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><div class="interactive-component-wrapper" style="border:solid 1px red;"> The component below is not a slot but declared as interactive <!--[--><div style="display: contents;" data-island-component></div><!--teleport start--><!--teleport end--><!--]--></div></div>"')
|
||||||
}
|
}
|
||||||
expect(text).toContain('async component that was very long')
|
expect(text).toContain('async component that was very long')
|
||||||
|
|
||||||
@ -2316,7 +2316,7 @@ describe('component islands', () => {
|
|||||||
const { components } = result
|
const { components } = result
|
||||||
result.components = {}
|
result.components = {}
|
||||||
result.slots = {}
|
result.slots = {}
|
||||||
result.html = result.html.replace(/ data-island-component="([^"]*)"/g, (_, content) => ` data-island-component="${content.split('-')[0]}"`)
|
result.html = result.html.replace(/data-island-component="([^"]*)"/g, 'data-island-component')
|
||||||
|
|
||||||
const teleportsEntries = Object.entries(components || {})
|
const teleportsEntries = Object.entries(components || {})
|
||||||
|
|
||||||
@ -2327,12 +2327,11 @@ describe('component islands', () => {
|
|||||||
"link": [],
|
"link": [],
|
||||||
"style": [],
|
"style": [],
|
||||||
},
|
},
|
||||||
"html": "<div data-island-uid> ServerWithClient.server.vue : <p>count: 0</p> This component should not be preloaded <div><!--[--><div>a</div><div>b</div><div>c</div><!--]--></div> This is not interactive <div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><div class="interactive-component-wrapper" style="border:solid 1px red;"> The component below is not a slot but declared as interactive <!--[--><div style="display: contents;" data-island-uid data-island-component="Counter"></div><!--teleport start--><!--teleport end--><!--]--></div></div>",
|
"html": "<div data-island-uid> ServerWithClient.server.vue : <p>count: 0</p> This component should not be preloaded <div><!--[--><div>a</div><div>b</div><div>c</div><!--]--></div> This is not interactive <div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><div class="interactive-component-wrapper" style="border:solid 1px red;"> The component below is not a slot but declared as interactive <!--[--><div style="display: contents;" data-island-uid data-island-component></div><!--teleport start--><!--teleport end--><!--]--></div></div>",
|
||||||
"slots": {},
|
"slots": {},
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
expect(teleportsEntries).toHaveLength(1)
|
expect(teleportsEntries).toHaveLength(1)
|
||||||
expect(teleportsEntries[0]![0].startsWith('Counter-')).toBeTruthy()
|
|
||||||
expect(teleportsEntries[0]![1].props).toMatchInlineSnapshot(`
|
expect(teleportsEntries[0]![1].props).toMatchInlineSnapshot(`
|
||||||
{
|
{
|
||||||
"multiplier": 1,
|
"multiplier": 1,
|
||||||
|
@ -37,7 +37,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
|
|||||||
const serverDir = join(rootDir, '.output/server')
|
const serverDir = join(rootDir, '.output/server')
|
||||||
|
|
||||||
const serverStats = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
|
const serverStats = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
|
||||||
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"208k"`)
|
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"209k"`)
|
||||||
|
|
||||||
const modules = await analyzeSizes(['node_modules/**/*'], serverDir)
|
const modules = await analyzeSizes(['node_modules/**/*'], serverDir)
|
||||||
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"1396k"`)
|
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"1396k"`)
|
||||||
@ -78,7 +78,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
|
|||||||
const serverDir = join(rootDir, '.output-inline/server')
|
const serverDir = join(rootDir, '.output-inline/server')
|
||||||
|
|
||||||
const serverStats = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
|
const serverStats = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
|
||||||
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"559k"`)
|
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"560k"`)
|
||||||
|
|
||||||
const modules = await analyzeSizes(['node_modules/**/*'], serverDir)
|
const modules = await analyzeSizes(['node_modules/**/*'], serverDir)
|
||||||
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"94.4k"`)
|
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"94.4k"`)
|
||||||
|
3
test/fixtures/basic-types/package.json
vendored
3
test/fixtures/basic-types/package.json
vendored
@ -14,5 +14,8 @@
|
|||||||
"vitest": "1.6.0",
|
"vitest": "1.6.0",
|
||||||
"vue": "latest",
|
"vue": "latest",
|
||||||
"vue-router": "latest"
|
"vue-router": "latest"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
17
test/fixtures/basic-types/types.ts
vendored
17
test/fixtures/basic-types/types.ts
vendored
@ -34,6 +34,23 @@ describe('API routes', () => {
|
|||||||
expectTypeOf($fetch<TestResponse>('/test')).toEqualTypeOf<Promise<TestResponse>>()
|
expectTypeOf($fetch<TestResponse>('/test')).toEqualTypeOf<Promise<TestResponse>>()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('works with useRequestFetch', () => {
|
||||||
|
const $fetch = useRequestFetch()
|
||||||
|
expectTypeOf($fetch('/api/hello')).toEqualTypeOf<Promise<string>>()
|
||||||
|
// registered in extends
|
||||||
|
expectTypeOf($fetch('/api/foo')).toEqualTypeOf<Promise<string>>()
|
||||||
|
// registered in module
|
||||||
|
expectTypeOf($fetch('/auto-registered-module')).toEqualTypeOf<Promise<string>>()
|
||||||
|
expectTypeOf($fetch('/api/hey')).toEqualTypeOf<Promise<{ foo: string, baz: string }>>()
|
||||||
|
expectTypeOf($fetch('/api/hey', { method: 'get' })).toEqualTypeOf<Promise<{ foo: string, baz: string }>>()
|
||||||
|
expectTypeOf($fetch('/api/hey', { method: 'post' })).toEqualTypeOf<Promise<{ method: 'post' }>>()
|
||||||
|
// @ts-expect-error not a valid method
|
||||||
|
expectTypeOf($fetch('/api/hey', { method: 'patch ' })).toEqualTypeOf<Promise<{ foo: string, baz: string }>>()
|
||||||
|
expectTypeOf($fetch('/api/union')).toEqualTypeOf<Promise<{ type: 'a', foo: string } | { type: 'b', baz: string }>>()
|
||||||
|
expectTypeOf($fetch('/api/other')).toEqualTypeOf<Promise<unknown>>()
|
||||||
|
expectTypeOf($fetch<TestResponse>('/test')).toEqualTypeOf<Promise<TestResponse>>()
|
||||||
|
})
|
||||||
|
|
||||||
it('works with useAsyncData', () => {
|
it('works with useAsyncData', () => {
|
||||||
expectTypeOf(useAsyncData('api-hello', () => $fetch('/api/hello')).data).toEqualTypeOf<Ref<string | DefaultAsyncDataValue>>()
|
expectTypeOf(useAsyncData('api-hello', () => $fetch('/api/hello')).data).toEqualTypeOf<Ref<string | DefaultAsyncDataValue>>()
|
||||||
expectTypeOf(useAsyncData('api-hey', () => $fetch('/api/hey')).data).toEqualTypeOf<Ref<{ foo: string, baz: string } | DefaultAsyncDataValue>>()
|
expectTypeOf(useAsyncData('api-hey', () => $fetch('/api/hey')).data).toEqualTypeOf<Ref<{ foo: string, baz: string } | DefaultAsyncDataValue>>()
|
||||||
|
3
test/fixtures/basic/package.json
vendored
3
test/fixtures/basic/package.json
vendored
@ -15,5 +15,8 @@
|
|||||||
"ufo": "latest",
|
"ufo": "latest",
|
||||||
"unplugin": "latest",
|
"unplugin": "latest",
|
||||||
"vue": "latest"
|
"vue": "latest"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,8 @@ const hmrId = ref(0)
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<pre id="hmr-id">
|
<div>
|
||||||
HMR ID: {{ hmrId }}
|
HMR ID:
|
||||||
</pre>
|
<span data-testid="hmr-id">{{ hmrId }}</span>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
10
test/fixtures/hmr/nuxt.config.ts
vendored
Normal file
10
test/fixtures/hmr/nuxt.config.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export default defineNuxtConfig({
|
||||||
|
builder: process.env.TEST_BUILDER as 'webpack' | 'rspack' | 'vite' ?? 'vite',
|
||||||
|
experimental: {
|
||||||
|
asyncContext: process.env.TEST_CONTEXT === 'async',
|
||||||
|
appManifest: process.env.TEST_MANIFEST !== 'manifest-off',
|
||||||
|
renderJsonPayloads: process.env.TEST_PAYLOAD !== 'js',
|
||||||
|
inlineRouteRules: true,
|
||||||
|
},
|
||||||
|
compatibilityDate: '2024-06-28',
|
||||||
|
})
|
13
test/fixtures/hmr/package.json
vendored
Normal file
13
test/fixtures/hmr/package.json
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"name": "fixture-hmr",
|
||||||
|
"scripts": {
|
||||||
|
"build": "nuxi build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"nuxt": "workspace:*"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
|
}
|
||||||
|
}
|
21
test/fixtures/hmr/pages/index.vue
vendored
Normal file
21
test/fixtures/hmr/pages/index.vue
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
some: 'stuff',
|
||||||
|
})
|
||||||
|
const count = ref(1)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Title>HMR fixture</Title>
|
||||||
|
<h1>Home page</h1>
|
||||||
|
<div>
|
||||||
|
Count:
|
||||||
|
<span data-testid="count">{{ count }}</span>
|
||||||
|
</div>
|
||||||
|
<button @click="count++">
|
||||||
|
Increment
|
||||||
|
</button>
|
||||||
|
<pre>{{ $route.meta }}</pre>
|
||||||
|
</div>
|
||||||
|
</template>
|
11
test/fixtures/hmr/pages/page-meta.vue
vendored
Normal file
11
test/fixtures/hmr/pages/page-meta.vue
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
some: 'stuff',
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<pre data-testid="meta">{{ $route.meta }}</pre>
|
||||||
|
</div>
|
||||||
|
</template>
|
13
test/fixtures/hmr/pages/route-rules.vue
vendored
Normal file
13
test/fixtures/hmr/pages/route-rules.vue
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineRouteRules({
|
||||||
|
headers: {
|
||||||
|
'x-extend': 'added in routeRules',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
Route rules defined inline
|
||||||
|
</div>
|
||||||
|
</template>
|
7
test/fixtures/hmr/pages/routes/index.vue
vendored
Normal file
7
test/fixtures/hmr/pages/routes/index.vue
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<NuxtLink to="/routes/non-existent">
|
||||||
|
To non-existent link
|
||||||
|
</NuxtLink>
|
||||||
|
</div>
|
||||||
|
</template>
|
3
test/fixtures/hmr/tsconfig.json
vendored
Normal file
3
test/fixtures/hmr/tsconfig.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "./.nuxt/tsconfig.json"
|
||||||
|
}
|
3
test/fixtures/minimal-types/package.json
vendored
3
test/fixtures/minimal-types/package.json
vendored
@ -7,5 +7,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nuxt": "workspace:*"
|
"nuxt": "workspace:*"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
test/fixtures/minimal/package.json
vendored
3
test/fixtures/minimal/package.json
vendored
@ -6,5 +6,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nuxt": "workspace:*"
|
"nuxt": "workspace:*"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
test/fixtures/runtime-compiler/package.json
vendored
3
test/fixtures/runtime-compiler/package.json
vendored
@ -6,5 +6,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nuxt": "workspace:*"
|
"nuxt": "workspace:*"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user