Merge branch 'main' into docs/new-structure

This commit is contained in:
Sébastien Chopin 2023-04-06 11:30:53 +02:00
commit 0cfde6e382
73 changed files with 3658 additions and 3139 deletions

30
.github/workflows/autofix-docs.yml vendored Normal file
View File

@ -0,0 +1,30 @@
name: autofix.ci # needed to securely identify the workflow
on:
pull_request:
paths:
- "docs/**"
- ".github/workflows/docs.yml"
permissions:
contents: read
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: corepack enable
- uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Lint (docs)
run: pnpm lint:docs:fix
- uses: autofix-ci/action@8bc06253bec489732e5f9c52884c7cace15c0160

42
.github/workflows/autofix.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: autofix.ci # needed to securely identify the workflow
on:
pull_request:
paths-ignore:
- "docs/**"
permissions:
contents: read
jobs:
code:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: corepack enable
- uses: actions/setup-node@v3
with:
node-version: 18
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Dedupe dependencies
if: ${{ contains(github.ref_name, 'renovate') }}
run: pnpm dedupe
- name: Build (stub)
run: pnpm build:stub
- name: Lint (code)
run: pnpm lint:fix
- name: Test (unit)
run: pnpm test:unit -u
- name: Update bundle size
run: pnpm vitest run bundle -u
- uses: autofix-ci/action@8bc06253bec489732e5f9c52884c7cace15c0160

View File

@ -88,6 +88,8 @@ jobs:
run: pnpm test:types
lint:
# autofix workflow will be triggered instead for PRs
if: github.event_name == 'push'
runs-on: ubuntu-latest
timeout-minutes: 10
@ -169,9 +171,6 @@ jobs:
- name: Test (unit)
run: pnpm test:unit
env:
TEST_ENV: ${{ matrix.env }}
TEST_BUILDER: ${{ matrix.builder }}
- name: Test (fixtures)
run: pnpm test:fixtures

View File

@ -5,30 +5,24 @@ on:
paths:
- "docs/**"
- ".github/workflows/docs.yml"
# autofix workflow will be triggered instead for PRs
branches:
- main
pull_request:
paths:
- "docs/**"
- ".github/workflows/docs.yml"
branches:
- main
# Remove default permissions of GITHUB_TOKEN for security
# https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs
permissions: {}
jobs:
lint-docs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
node: [14]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: corepack enable
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
node-version: 18
cache: "pnpm"
- name: Install dependencies

View File

@ -1,4 +1,4 @@
name: Docs
name: CI
on:
push:

View File

@ -12,3 +12,5 @@ MD025: false
MD033: false
# Allow non blank lines around list
MD032: false
MD046:
style: fenced

View File

@ -1,5 +1,5 @@
**/node_modules
docs/content/index.md
docs/content/**/*.nuxt.config.md
docs/content/changelog.md
docs/0.index.md
docs/1.getting-started/1.introduction.md
docs/**/*.nuxt.config.md

View File

@ -17,6 +17,10 @@ You can find a reference for auto-imported [composables](/docs/api/composables/u
In the [server directory](/docs/guide/directory-structure/server), we auto import exported functions and variables from `server/utils/`.
::
::alert
You can also auto-import functions exported from custom folders or third-party packages by configuring the [`imports` section](/docs/api/configuration/nuxt-config#imports) of your `nuxt.config` file.
::
## Built-in Auto-imports
### Nuxt Auto-imports
@ -46,7 +50,7 @@ Vue 3 exposes Reactivity APIs like `ref` or `computed`, as well as lifecycle hoo
<!-- TODO: move to separate page with https://github.com/nuxt/nuxt/issues/14723 and add more information -->
When you are using the built-in composition API composables provided by Vue and Nuxt, be aware that many of them rely on being called in the right _context_.
When you are using the built-in Composition API composables provided by Vue and Nuxt, be aware that many of them rely on being called in the right _context_.
During a component lifecycle, Vue tracks the temporary instance of the current component (and similarly, Nuxt tracks a temporary instance of `nuxtApp`) via a global variable, and then unsets it in same tick. This is essential when server rendering, both to avoid cross-request state pollution (leaking a shared reference between two users) and to avoid leakage between different components.

View File

@ -141,7 +141,7 @@ Nuxt 3 can be deployed to several cloud providers with a minimal amount of confi
- :icon{name="logos:microsoft-azure" class="h-5 w-4 inline mb-2"} [Azure](https://nitro.unjs.io/deploy/providers/azure)
- :icon{name="ph:cloud-duotone" class="h-5 w-4 inline mb-2"} [Cleavr](https://nitro.unjs.io/deploy/providers/cleavr)
- :icon{name="logos:cloudflare" class="h-5 w-4 inline mb-2"} [CloudFlare](https://nitro.unjs.io/deploy/providers/cloudflare)
- :icon{name="logos:digital-ocean" class="h-5 w-4 inline mb-2"} [Digital Ocean](https://nitro.unjs.io/deploy/providers/digitalocean)
- :icon{name="logos:digital-ocean" class="h-5 w-4 inline mb-2"} [DigitalOcean](https://nitro.unjs.io/deploy/providers/digitalocean)
- :icon{name="logos:firebase" class="h-5 w-4 inline mb-2"} [Firebase](https://nitro.unjs.io/deploy/providers/firebase)
- :icon{name="logos:heroku-icon" class="h-5 w-4 inline mb-2"} [heroku](https://nitro.unjs.io/deploy/providers/heroku)
- :icon{name="ph:cloud-duotone" class="h-5 w-4 inline mb-2"} [Edgio](https://nitro.unjs.io/deploy/providers/edgio)

View File

@ -19,28 +19,32 @@ Start with one of our starters and themes directly by opening [nuxt.new](https:/
## New Project
<!-- TODO: need to fix upstream in nuxt/nuxt.com -->
<!-- markdownlint-disable-next-line MD001 -->
#### Prerequisites
- **Node.js** - [`v16.10.0`](https://nodejs.org/en/) or newer
- **Text editor** - We recommend [Visual Studio Code]() with the [Volar Extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
- **Text editor** - We recommend [Visual Studio Code](https://code.visualstudio.com/) with the [Volar Extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
- **Terminal** - In order to run Nuxt commands
::alert
::details
:summary[Additional notes for an optimal setup:]
- **Node.js**: Make sure to use an even numbered version (16, 18, etc)
::details
:summary[Additional notes for an optimal setup:]
- **Node.js**: Make sure to use an even numbered version (16, 18, etc)
- **Volar**: Either enable [**Take Over Mode**](https://vuejs.org/guide/typescript/overview.html#volar-takeover-mode) (recommended) or add the [TypeScript Vue Plugin](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin)
- **Volar**: Either enable [**Take Over Mode**](https://vuejs.org/guide/typescript/overview.html#volar-takeover-mode) (recommended) or add the [TypeScript Vue Plugin](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin)
If you have enabled **Take Over Mode** or installed the **TypeScript Vue Plugin (Volar)**, you can disable generating the shim for `*.vue` files in your `nuxt.config.ts` file:
If you have enabled **Take Over Mode** or installed the **TypeScript Vue Plugin (Volar)**, you can disable generating the shim for `*.vue` files in your `nuxt.config.ts` file:
```ts [nuxt.config.ts]
export default defineNuxtConfig({
typescript: {
shim: false
}
})
```
::
```ts [nuxt.config.ts]
export default defineNuxtConfig({
typescript: {
shim: false
}
})
```
::
::
Open a terminal (if you're using [Visual Studio Code](https://code.visualstudio.com/), you can open an [integrated terminal](https://code.visualstudio.com/docs/editor/integrated-terminal)) and use the following command to create a new starter project:
@ -110,4 +114,4 @@ Well done! A browser window should automatically open for <http://localhost:3000
Now that you've created your Nuxt 3 project, you are ready to start building your application.
* Learn about the framework [concepts](/docs/guide/concepts/auto-imports)
- Learn about the framework [concepts](/docs/guide/concepts/auto-imports)

View File

@ -48,7 +48,6 @@ This includes:
You cannot currently define a server-side handler for these errors, but can render an error page (see the next section).
### Errors downloading JS chunks
You might encounter chunk loading errors due to a network connectivity failure or a new deployment (which invalidates your old, hashed JS chunk URLs). Nuxt provides built-in support for handling chunk loading errors by performing a hard reload when a chunk fails to load during route navigation.

View File

@ -57,6 +57,59 @@ Unlike navigation guards in [the vue-router docs](https://router.vuejs.org/guide
We recommend using the helper functions above for performing redirects or stopping navigation. Other possible return values described in [the vue-router docs](https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards) may work but there may be breaking changes in future.
::
## What Order Middleware Runs In
Middleware runs in the following order:
1. Global Middleware
2. Page defined middleware order (if there are multiple middleware declared with the array syntax)
For example, assuming you have the following middleware and component:
```text [middleware/ directory]
middleware/
--| analytics.global.ts
--| setup.global.ts
--| auth.ts
```
```vue [pages/profile.vue]
<script setup>
definePageMeta({
middleware: [
function (to, from) {
// Custom inline middleware
},
'auth',
],
});
</script>
```
You can expect the middleware to be run in the following order:
1. `analytics.global.ts`
2. `setup.global.ts`
3. Custom inline middleware
4. `auth.ts`
### Ordering Global Middleware
By default, global middleware is executed alphabetically based on the filename.
However, there may be times you want to define a specific order. For example, in the last scenario, `setup.global.ts` may need to run before `analytics.global.ts`. In that case, we recommend prefixing global middleware with 'alphabetical' numbering.
```text [middleware/ directory]
middleware/
--| 01.setup.global.ts
--| 02.analytics.global.ts
--| auth.ts
```
::alert{type=info icon=💡}
In case you're new to 'alphabetical' numbering, remember that filenames are sorted as strings, not as numeric values. For example, `10.new.global.ts` would come before `2.new.global.ts`. This is why the example prefixes single digit numbers with `0`.
::
## When Middleware Runs
If your site is server-rendered or generated, middleware for the initial page will be executed both when the page is rendered and then again on the client. This might be needed if your middleware needs a browser environment, such as if you have a generated site, aggressively cache responses, or want to read a value from local storage.

View File

@ -15,6 +15,7 @@ The auto-registered files patterns are:
You don't need to add those local modules to your [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt.config) separately.
::code-group
```ts [modules/hello/index.ts]
// `nuxt/kit` is a helper subpath import you can use when defining local modules
// that means you do not need to add `@nuxt/kit` to your project's dependencies
@ -35,11 +36,13 @@ export default defineNuxtModule({
}
})
```
```ts [modules/hello/runtime/api-route.ts]
export default defineEventHandler(() => {
return { hello: 'world' }
}
```
::
When starting Nuxt, the `hello` module will be registered and the `/api/hello` route will be available.

View File

@ -340,7 +340,6 @@ Nuxt 3 allows programmatic navigation through the `navigateTo()` utility method.
```vue
<script setup>
const router = useRouter();
const name = ref('');
const type = ref(1);

View File

@ -133,7 +133,7 @@ export { }
```
::alert{type=warning}
If you are using WebStorm, you may need to augment `@vue/runtime-core` until [this issue](https://youtrack.jetbrains.com/issue/WEB-59818/VUE-Typescript-WS-PS-does-not-correctly-display-type-of-globally-injected-properties) is resolved.
If you are using WebStorm, you may need to augment `@vue/runtime-core` until [this issue](https://youtrack.jetbrains.com/issue/WEB-59818/VUE-TypeScript-WS-PS-does-not-correctly-display-type-of-globally-injected-properties) is resolved.
::
## Vue Plugins

View File

@ -90,4 +90,3 @@ declare module 'nuxt/schema' {
// It is always important to ensure you import/export something when augmenting a type
export {}
```

View File

@ -58,7 +58,7 @@ All fetch options can be given a `computed` or `ref` value. These will be watche
* `server`: Whether to fetch the data on the server (defaults to `true`).
* `default`: A factory function to set the default value of the data, before the async function resolves - particularly useful with the `lazy: true` option.
* `pick`: Only pick specified keys in this array from the `handler` function result.
* `watch`: watch reactive sources to auto-refresh.
* `watch`: Watch an array of reactive sources and auto-refresh the fetch result when they change. Fetch options and URL are watched by default. You can completely ignore reactive sources by using `watch: false`. Together with `immediate: false`, this allows for a fully-manual `useFetch`.
* `transform`: A function that can be used to alter `handler` function result after resolving.
* `immediate`: When set to `false`, will prevent the request from firing immediately. (defaults to `true`)

View File

@ -9,6 +9,7 @@ The `useHeadSafe` composable is a wrapper around the [`useHead`](/docs/api/compo
## Usage
You can pass all the same values as `useHead`
```ts
useHeadSafe({
script: [

View File

@ -10,11 +10,13 @@ Nuxt provides composables and utilities for first-class server-side-rendering su
`setResponseStatus` can only be called within component setup functions, plugins, and route middleware.
```js
const event = useRequestEvent()
// Set the status code to 404 for a custom 404 page
setResponseStatus(404)
setResponseStatus(event, 404)
// Set the status message as well
setResponseStatus(404, 'Page Not Found')
setResponseStatus(event, 404, 'Page Not Found')
```
::alert{icon=👉}

View File

@ -46,7 +46,7 @@ Hook | Arguments | Description
`app:templatesGenerated` | `app` | Called after templates are compiled into the [virtual file system](https://nuxt.com/docs/guide/directory-structure/nuxt#virtual-file-system) (vfs).
`build:before` | - | Called before Nuxt bundle builder.
`build:done` | - | Called after Nuxt bundle builder is complete.
`build:manifest` | `manifest` | Called during the manifest build by Vite and Webpack. This allows customizing the manifest that Nitro will use to render `<script>` and `<link>` tags in the final HTML.
`build:manifest` | `manifest` | Called during the manifest build by Vite and webpack. This allows customizing the manifest that Nitro will use to render `<script>` and `<link>` tags in the final HTML.
`builder:generateApp` | `options` | Called before generating the app.
`builder:watch` | `event, path` | Called at build time in development when the watcher spots a change to a file or directory in the project.
`pages:extend` | `pages` | Called after pages routes are resolved.

View File

@ -1,6 +1,6 @@
---
title: "nuxi devtools"
description: The devtools command allows you to enable or disable Nuxt Devtools on a per-project basis.
description: The devtools command allows you to enable or disable Nuxt DevTools on a per-project basis.
---
# `nuxi devtools`

View File

@ -424,6 +424,7 @@ export default defineNuxtModule({
:ReadMore{link="/docs/api/advanced/hooks" title="API > Advanced > Hooks"}
::alert{type=info}
**Module cleanup**
If your module opens, handles, or starts a watcher, you should close it when the Nuxt lifecycle is done. The `close` hook is available for this.
@ -440,6 +441,8 @@ export default defineNuxtModule({
})
```
::
#### Augmenting Types
If your module should augment types handled by Nuxt, you can use the `prepare:types` hook to perform this operation.
@ -589,9 +592,9 @@ Consider documenting module usage in the readme file:
Linking to the integration website and documentation is always a good idea.
#### Provide a Stackblitz Demo or Boilerplate
#### Provide a StackBlitz Demo or Boilerplate
It's a good practice to make a minimal reproduction with your module and [StackBlitz](https://nuxt.new/s/v3) that you add to your module readme.
It's a good practice to make a minimal reproduction with your module and [StackBlitz](https://nuxt.new/s/v3) that you add to your module readme.
This not only provides potential users of your module a quick and easy way to experiment with the module but also an easy way for them to build minimal reproductions they can send you when they encounter issues.

View File

@ -22,7 +22,7 @@ export default <RouterConfig> {
{
name: 'home',
path: '/',
component: () => import('~/pages/home.vue')
component: () => import('~/pages/home.vue').then(r => r.default || r)
}
],
}
@ -44,7 +44,7 @@ export default defineNuxtConfig({
pages.push({
name: 'profile',
path: '/profile',
component: () => import('~/pages/profile.vue')
file: '~/extra-pages/profile.vue'
})
// remove routes

View File

@ -134,11 +134,18 @@ git checkout -b my-new-branch
### Set Up Documentation Website in Local Environment
We are using [Docus](https://docus.dev) for documentation.
The Nuxt documentation is currently deployed within [nuxt/nuxt.com](https://github.com/nuxt/nuxt.com) as a layer.
- Run `pnpm build:stub` once in the root directory
- Go into the docs directory: `cd docs`
- Install docs dependencies using `yarn install`
- Run `yarn dev` to start docs in development mode
<!-- - Go into the docs directory: `cd docs` -->
<!-- - Install docs dependencies using `yarn install` -->
<!-- - Run `yarn dev` to start docs in development mode -->
- Before opening a PR, run `pnpm docs:lint:fix` to highlight and resolve any lint issues
::alert
🚧 This repository will be open-sourced shortly. Until then, you will need to open a pull request to see a preview of your changes.
::
::alert
We recommend that you install the [MDC extension](https://marketplace.visualstudio.com/items?itemName=Nuxt.mdc) for VS Code.
::

View File

@ -88,7 +88,6 @@ Nuxt and Nuxt Modules are now build-time-only.
})
```
::alert
If you are a module author, you can check out [more information about module compatibility](/docs/migration/module-authors) and [our module author guide](/docs/guide/going-further/modules).
::

View File

@ -12,8 +12,9 @@
"example": "./scripts/example.sh dev",
"example:build": "./scripts/example.sh build",
"lint": "eslint --ext .vue,.ts,.js,.mjs .",
"lint:docs": "markdownlint ./docs/content/1.docs && case-police 'docs/content/1.docs/**/*.md'",
"lint:docs:fix": "markdownlint ./docs/content/1.docs --fix && case-police 'docs/content/1.docs/**/*.md' --fix",
"lint:fix": "eslint --ext .vue,.ts,.js,.mjs . --fix",
"lint:docs": "markdownlint ./docs && case-police 'docs/**/*.md'",
"lint:docs:fix": "markdownlint ./docs --fix && case-police 'docs/**/*.md' --fix",
"nuxi": "JITI_ESM_RESOLVE=1 nuxi",
"nuxt": "JITI_ESM_RESOLVE=1 nuxi",
"play": "pnpm nuxi dev playground",
@ -35,7 +36,7 @@
"nuxi": "workspace:*",
"nuxt": "workspace:*",
"nuxt3": "workspace:nuxt@*",
"unbuild": "^1.1.2",
"unbuild": "^1.2.0",
"vite": "^4.2.1",
"vue": "3.2.47",
"magic-string": "^0.30.0"
@ -49,15 +50,15 @@
"@nuxt/webpack-builder": "workspace:*",
"@nuxtjs/eslint-config-typescript": "^12.0.0",
"@types/crawler": "^1.2.2",
"@types/node": "^18.15.8",
"@types/node": "^18.15.11",
"@types/rimraf": "^3.0.2",
"@types/semver": "^7.3.13",
"@unocss/reset": "^0.50.6",
"case-police": "^0.5.14",
"changelogen": "^0.5.1",
"changelogen": "^0.5.2",
"crawler": "^1.4.0",
"eslint": "^8.36.0",
"eslint-plugin-jsdoc": "^40.1.0",
"eslint": "^8.37.0",
"eslint-plugin-jsdoc": "^40.1.1",
"execa": "^7.1.1",
"expect-type": "^0.15.0",
"globby": "^13.1.3",
@ -70,14 +71,14 @@
"rimraf": "^4.4.1",
"semver": "^7.3.8",
"std-env": "^3.3.2",
"typescript": "^5.0.2",
"typescript": "^5.0.3",
"ufo": "^1.1.1",
"unbuild": "^1.1.2",
"unbuild": "^1.2.0",
"vite": "^4.2.1",
"vitest": "^0.29.7",
"vitest": "^0.29.8",
"vue-tsc": "^1.2.0"
},
"packageManager": "pnpm@7.30.3",
"packageManager": "pnpm@8.1.1",
"engines": {
"node": "^14.18.0 || ^16.10.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}

View File

@ -1,6 +1,6 @@
{
"name": "@nuxt/kit",
"version": "3.3.2",
"version": "3.3.3",
"repository": "nuxt/nuxt",
"license": "MIT",
"type": "module",
@ -20,7 +20,7 @@
"prepack": "unbuild"
},
"dependencies": {
"@nuxt/schema": "3.3.2",
"@nuxt/schema": "workspace:../schema",
"c12": "^1.2.0",
"consola": "^2.15.3",
"defu": "^6.1.2",
@ -36,8 +36,8 @@
"scule": "^1.0.0",
"semver": "^7.3.8",
"unctx": "^2.1.2",
"unimport": "^3.0.4",
"untyped": "^1.2.2"
"unimport": "^3.0.5",
"untyped": "^1.3.2"
},
"devDependencies": {
"@types/lodash.template": "^4.5.1",

View File

@ -8,13 +8,13 @@ export interface ExtendConfigOptions {
*
* @default true
*/
dev?: boolean
/**
* Install plugin on build
*
* @default true
*/
build?: boolean
dev?: boolean
/**
* Install plugin on build
*
* @default true
*/
build?: boolean
/**
* Install plugin on server side
*
@ -41,7 +41,7 @@ export interface ExtendViteConfigOptions extends ExtendConfigOptions {}
* when applying to both client and server builds.
*/
export function extendWebpackConfig (
fn: ((config: WebpackConfig)=> void),
fn: ((config: WebpackConfig) => void),
options: ExtendWebpackConfigOptions = {}
) {
const nuxt = useNuxt()

View File

@ -1,4 +1,5 @@
import type { Nuxt, NuxtModule } from '@nuxt/schema'
import { isNuxt2 } from '../compatibility'
import { useNuxt } from '../context'
import { resolveModule, requireModule } from '../internal/cjs'
import { importModule } from '../internal/esm'
@ -10,7 +11,12 @@ export async function installModule (moduleToInstall: string | NuxtModule, _inli
const { nuxtModule, inlineOptions } = await normalizeModule(moduleToInstall, _inlineOptions)
// Call module
const res = await nuxtModule(inlineOptions, nuxt) ?? {}
const res = (
isNuxt2()
// @ts-expect-error Nuxt 2 `moduleContainer` is not typed
? await nuxtModule.call(nuxt.moduleContainer, inlineOptions, nuxt)
: await nuxtModule(inlineOptions, nuxt)
) ?? {}
if (res === false /* setup aborted */) {
return
}

View File

@ -1,6 +1,6 @@
{
"name": "nuxi",
"version": "3.3.2",
"version": "3.3.3",
"repository": "nuxt/nuxt",
"license": "MIT",
"type": "module",
@ -18,8 +18,8 @@
"prepack": "unbuild"
},
"devDependencies": {
"@nuxt/kit": "3.3.2",
"@nuxt/schema": "3.3.2",
"@nuxt/kit": "workspace:../kit",
"@nuxt/schema": "workspace:../schema",
"@types/clear": "^0.1.2",
"@types/flat": "^5.0.2",
"@types/mri": "^1.1.1",
@ -35,7 +35,7 @@
"execa": "^7.1.1",
"flat": "^5.0.2",
"giget": "^1.1.2",
"h3": "^1.6.2",
"h3": "^1.6.4",
"jiti": "^1.18.2",
"listhen": "^1.0.4",
"mlly": "^1.2.0",

View File

@ -1,6 +1,6 @@
{
"name": "nuxt",
"version": "3.3.2",
"version": "3.3.3",
"repository": "nuxt/nuxt",
"license": "MIT",
"type": "module",
@ -61,11 +61,11 @@
},
"dependencies": {
"@nuxt/devalue": "^2.0.0",
"@nuxt/kit": "3.3.2",
"@nuxt/schema": "3.3.2",
"@nuxt/kit": "workspace:../kit",
"@nuxt/schema": "workspace:../schema",
"@nuxt/telemetry": "^2.1.10",
"@nuxt/ui-templates": "^1.1.1",
"@nuxt/vite-builder": "3.3.2",
"@nuxt/vite-builder": "workspace:../vite",
"@unhead/ssr": "^1.1.25",
"@unhead/vue": "^1.1.25",
"@vue/reactivity": "^3.2.47",
@ -78,15 +78,15 @@
"estree-walker": "^3.0.3",
"fs-extra": "^11.1.1",
"globby": "^13.1.3",
"h3": "^1.6.2",
"h3": "^1.6.4",
"hash-sum": "^2.0.0",
"hookable": "^5.5.0",
"hookable": "^5.5.3",
"jiti": "^1.18.2",
"knitwork": "^1.0.0",
"magic-string": "^0.30.0",
"mlly": "^1.2.0",
"nitropack": "~2.3.2",
"nuxi": "3.3.2",
"nitropack": "^2.3.2",
"nuxi": "workspace:../nuxi",
"ofetch": "^1.0.1",
"ohash": "^1.0.0",
"pathe": "^1.1.0",
@ -96,9 +96,9 @@
"ufo": "^1.1.1",
"unctx": "^2.1.2",
"unenv": "^1.2.2",
"unimport": "^3.0.4",
"unimport": "^3.0.5",
"unplugin": "^1.3.1",
"untyped": "^1.2.2",
"untyped": "^1.3.2",
"vue": "^3.2.47",
"vue-bundle-renderer": "^1.0.3",
"vue-devtools-stub": "^0.1.0",

View File

@ -265,7 +265,7 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
}
// Resolves `to` value if it's a route location object
// converts `'''` to `null` to prevent the attribute from being added as empty (`href=""`)
// converts `""` to `null` to prevent the attribute from being added as empty (`href=""`)
const href = typeof to.value === 'object' ? router.resolve(to.value)?.href ?? null : to.value || null
// Resolves `target` value

View File

@ -37,6 +37,7 @@ onErrorCaptured((err, target, info) => {
if (process.server || (isNuxtError(err) && (err.fatal || err.unhandled))) {
const p = callWithNuxt(nuxtApp, showError, [err])
onServerPrefetch(() => p)
return false // suppress error from breaking render
}
})

View File

@ -24,7 +24,7 @@ export type KeysOf<T> = Array<
export type KeyOfRes<Transform extends _Transform> = KeysOf<ReturnType<Transform>>
type MultiWatchSources = (WatchSource<unknown> | object)[]
export type MultiWatchSources = (WatchSource<unknown> | object)[]
export interface AsyncDataOptions<
ResT,

View File

@ -4,7 +4,7 @@ import type { Ref } from 'vue'
import { computed, unref, reactive } from 'vue'
import { hash } from 'ohash'
import { useRequestFetch } from './ssr'
import type { AsyncDataOptions, _Transform, KeysOf, AsyncData, PickFrom } from './asyncData'
import type { AsyncDataOptions, _Transform, KeysOf, AsyncData, PickFrom, MultiWatchSources } from './asyncData'
import { useAsyncData } from './asyncData'
export type FetchResult<ReqT extends NitroFetchRequest, M extends AvailableRouterMethod<ReqT>> = TypedInternalResponse<ReqT, unknown, M>
@ -21,9 +21,10 @@ export interface UseFetchOptions<
PickKeys extends KeysOf<DataT> = KeysOf<DataT>,
R extends NitroFetchRequest = string & {},
M extends AvailableRouterMethod<R> = AvailableRouterMethod<R>
> extends AsyncDataOptions<ResT, DataT, PickKeys>, ComputedFetchOptions<R, M> {
> extends Omit<AsyncDataOptions<ResT, DataT, PickKeys>, 'watch'>, ComputedFetchOptions<R, M> {
key?: string
$fetch?: typeof globalThis.$fetch
watch?: MultiWatchSources | false
}
export function useFetch<
@ -59,6 +60,7 @@ export function useFetch<
if (!request) {
throw new Error('[nuxt] [useFetch] request is missing.')
}
const key = _key === autoKey ? '$f' + _key : _key
const _request = computed(() => {
@ -69,6 +71,10 @@ export function useFetch<
return unref(r)
})
if (!opts.baseURL && typeof _request.value === 'string' && _request.value.startsWith('//')) {
throw new Error('[nuxt] [useFetch] the request URL must not start with "//".')
}
const {
server,
lazy,
@ -92,11 +98,7 @@ export function useFetch<
transform,
pick,
immediate,
watch: [
_fetchOptions,
_request,
...(watch || [])
]
watch: watch === false ? [] : [_fetchOptions, _request, ...(watch || [])]
}
let controller: AbortController

View File

@ -17,6 +17,9 @@ export const useRouter = () => {
}
export const useRoute = (): RouteLocationNormalizedLoaded => {
if (process.dev && isProcessingMiddleware()) {
console.warn('[nuxt] Calling `useRoute` within middleware may lead to misleading results. Instead, use the (to, from) arguments passed to the middleware to access the new and old routes.')
}
if (getCurrentInstance()) {
return inject('_route', useNuxtApp()._route)
}
@ -111,7 +114,7 @@ export const navigateTo = (to: RouteLocationRaw | undefined | null, options?: Na
// Let vue-router handle internal redirects within middleware
// to prevent the navigation happening after response is sent
if (isProcessingMiddleware() && !isExternal) {
setResponseStatus(options?.redirectCode || 302)
setResponseStatus(nuxtApp.ssrContext.event, options?.redirectCode || 302)
return to
}
const redirectLocation = isExternal ? toPath : joinURL(useRuntimeConfig().app.baseURL, router.resolve(to).fullPath || '/')

View File

@ -25,7 +25,13 @@ export function useRequestFetch (): typeof global.$fetch {
return event?.$fetch as typeof globalThis.$fetch || globalThis.$fetch
}
export function setResponseStatus (code: number, message?: string) {
export function setResponseStatus (event: H3Event, code?: number, message?: string): void
/** @deprecated Pass `event` as first option. */
export function setResponseStatus (code: number, message?: string): void
export function setResponseStatus (arg1: H3Event | number | undefined, arg2?: number | string, arg3?: string) {
if (process.client) { return }
_setResponseStatus(useRequestEvent(), code, message)
if (arg1 && typeof arg1 !== 'number') {
return _setResponseStatus(arg1, arg2 as number | undefined, arg3)
}
return _setResponseStatus(useRequestEvent(), arg1, arg2 as string | undefined)
}

View File

@ -238,8 +238,9 @@ export function createNuxtApp (options: CreateOptions) {
})
// Log errors captured when running plugins, in the `app:created` and `app:beforeMount` hooks
// as well as when mounting the app and in the `app:mounted` hook
nuxtApp.hook('app:error', (...args) => { console.error('[nuxt] error caught during app initialization', ...args) })
// as well as when mounting the app.
const unreg = nuxtApp.hook('app:error', (...args) => { console.error('[nuxt] error caught during app initialization', ...args) })
nuxtApp.hook('app:mounted', unreg)
}
// Expose runtime config

View File

@ -189,6 +189,15 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
}
}
// Add backward-compatible middleware to respect `x-nuxt-no-ssr` header
if (nuxt.options.experimental.respectNoSSRHeader) {
nitroConfig.handlers = nitroConfig.handlers || []
nitroConfig.handlers.push({
handler: resolve(distDir, 'core/runtime/nitro/no-ssr'),
middleware: true
})
}
// Register nuxt protection patterns
nitroConfig.rollupConfig!.plugins = await nitroConfig.rollupConfig!.plugins || []
nitroConfig.rollupConfig!.plugins = Array.isArray(nitroConfig.rollupConfig!.plugins) ? nitroConfig.rollupConfig!.plugins : [nitroConfig.rollupConfig!.plugins]

View File

@ -20,6 +20,7 @@ import { UnctxTransformPlugin } from './plugins/unctx'
import type { TreeShakeComposablesPluginOptions } from './plugins/tree-shake'
import { TreeShakeComposablesPlugin } from './plugins/tree-shake'
import { DevOnlyPlugin } from './plugins/dev-only'
import { LayerAliasingPlugin } from './plugins/layer-aliasing'
import { addModuleTranspiles } from './modules'
import { initNitro } from './nitro'
import schemaModule from './schema'
@ -80,13 +81,28 @@ async function initNuxt (nuxt: Nuxt) {
addVitePlugin(ImportProtectionPlugin.vite(config))
addWebpackPlugin(ImportProtectionPlugin.webpack(config))
if (nuxt.options.experimental.localLayerAliases) {
// Add layer aliasing support for ~, ~~, @ and @@ aliases
addVitePlugin(LayerAliasingPlugin.vite({
sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client,
// skip top-level layer (user's project) as the aliases will already be correctly resolved
layers: nuxt.options._layers.slice(1)
}))
addWebpackPlugin(LayerAliasingPlugin.webpack({
sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client,
// skip top-level layer (user's project) as the aliases will already be correctly resolved
layers: nuxt.options._layers.slice(1),
transform: true
}))
}
nuxt.hook('modules:done', () => {
// Add unctx transform
addVitePlugin(UnctxTransformPlugin(nuxt).vite({ sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))
addWebpackPlugin(UnctxTransformPlugin(nuxt).webpack({ sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))
// Add composable tree-shaking optimisations
const serverTreeShakeOptions : TreeShakeComposablesPluginOptions = {
const serverTreeShakeOptions: TreeShakeComposablesPluginOptions = {
sourcemap: nuxt.options.sourcemap.server,
composables: nuxt.options.optimization.treeShake.composables.server
}
@ -94,7 +110,7 @@ async function initNuxt (nuxt: Nuxt) {
addVitePlugin(TreeShakeComposablesPlugin.vite(serverTreeShakeOptions), { client: false })
addWebpackPlugin(TreeShakeComposablesPlugin.webpack(serverTreeShakeOptions), { client: false })
}
const clientTreeShakeOptions : TreeShakeComposablesPluginOptions = {
const clientTreeShakeOptions: TreeShakeComposablesPluginOptions = {
sourcemap: nuxt.options.sourcemap.client,
composables: nuxt.options.optimization.treeShake.composables.client
}

View File

@ -0,0 +1,68 @@
import { createUnplugin } from 'unplugin'
import type { NuxtConfigLayer } from 'nuxt/schema'
import { resolveAlias } from '@nuxt/kit'
import { normalize } from 'pathe'
import MagicString from 'magic-string'
interface LayerAliasingOptions {
sourcemap?: boolean
transform?: boolean
layers: NuxtConfigLayer[]
}
const ALIAS_RE = /(?<=['"])[~@]{1,2}(?=\/)/g
export const LayerAliasingPlugin = createUnplugin((options: LayerAliasingOptions) => {
const aliases = Object.fromEntries(options.layers.map(l => [l.config.srcDir || l.cwd, {
'~': l.config?.alias?.['~'] || l.config.srcDir || l.cwd,
'@': l.config?.alias?.['@'] || l.config.srcDir || l.cwd,
'~~': l.config?.alias?.['~~'] || l.config.rootDir || l.cwd,
'@@': l.config?.alias?.['@@'] || l.config.rootDir || l.cwd
}]))
const layers = Object.keys(aliases).sort((a, b) => b.length - a.length)
return {
name: 'nuxt:layer-aliasing',
enforce: 'pre',
vite: {
resolveId: {
order: 'pre',
async handler (id, importer) {
if (!importer) { return }
const layer = layers.find(l => importer.startsWith(l))
if (!layer) { return }
const resolvedId = resolveAlias(id, aliases[layer])
if (resolvedId !== id) {
return await this.resolve(resolvedId, importer, { skipSelf: true })
}
}
}
},
// webpack-only transform
transformInclude: (id) => {
if (!options.transform) { return false }
const _id = normalize(id)
return layers.some(dir => _id.startsWith(dir))
},
transform (code, id) {
if (!options.transform) { return }
const _id = normalize(id)
const layer = layers.find(l => _id.startsWith(l))
if (!layer || !code.match(ALIAS_RE)) { return }
const s = new MagicString(code)
s.replace(ALIAS_RE, r => aliases[layer][r as '~'] || r)
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap ? s.generateMap({ source: id, includeContent: true }) : undefined
}
}
}
}
})

View File

@ -0,0 +1,8 @@
import { defineEventHandler, getRequestHeader } from 'h3'
export default defineEventHandler((event) => {
if (getRequestHeader(event, 'x-nuxt-no-ssr')) {
event.context.nuxt = event.context.nuxt || {}
event.context.nuxt.noSSR = true
}
})

View File

@ -5,7 +5,7 @@ import type { H3Event } from 'h3'
import { appendHeader, getQuery, writeEarlyHints, readBody, createError } from 'h3'
import devalue from '@nuxt/devalue'
import destr from 'destr'
import { joinURL } from 'ufo'
import { joinURL, withoutTrailingSlash } from 'ufo'
import { renderToString as _renderToString } from 'vue/server-renderer'
import { hash } from 'ohash'
@ -213,7 +213,7 @@ export default defineRenderHandler(async (event) => {
runtimeConfig: useRuntimeConfig() as NuxtSSRContext['runtimeConfig'],
noSSR:
!!(process.env.NUXT_NO_SSR) ||
!!(event.node.req.headers['x-nuxt-no-ssr']) ||
event.context.nuxt?.noSSR ||
routeOptions.ssr === false ||
(process.env.prerender ? PRERENDER_NO_SSR_ROUTES.has(url) : false),
error: !!ssrError,
@ -262,7 +262,7 @@ export default defineRenderHandler(async (event) => {
// Hint nitro to prerender payload for this route
appendHeader(event, 'x-nitro-prerender', joinURL(url, '_payload.js'))
// Use same ssr context to generate payload for this route
PAYLOAD_CACHE!.set(url, renderPayloadResponse(ssrContext))
PAYLOAD_CACHE!.set(withoutTrailingSlash(url), renderPayloadResponse(ssrContext))
}
// Render meta

View File

@ -129,7 +129,8 @@ export default defineNuxtModule({
const _types = generateTypes(schema, {
addExport: true,
interfaceName: 'NuxtCustomSchema',
partial: true
partial: true,
allowExtraKeys: false
})
const types =
_types +

View File

@ -130,7 +130,7 @@ export const schemaTemplate: NuxtTemplate<TemplateContext> = {
const modules = moduleInfo.map(meta => [genString(meta.configKey), getImportName(meta.importName)])
return [
"import { NuxtModule } from 'nuxt/schema'",
"import { NuxtModule, RuntimeConfig } from 'nuxt/schema'",
"declare module 'nuxt/schema' {",
' interface NuxtConfig {',
...modules.map(([configKey, importName]) =>
@ -154,7 +154,18 @@ export const schemaTemplate: NuxtTemplate<TemplateContext> = {
allowExtraKeys: false,
indentation: 2
}),
'}'
'}',
`declare module 'vue' {
interface ComponentCustomProperties {
$config: RuntimeConfig
}
}`,
// TODO: remove when webstorm has support for augumenting 'vue' directly
`declare module '@vue/runtime-dom' {
interface ComponentCustomProperties {
$config: RuntimeConfig
}
}`
].join('\n')
}
}
@ -278,7 +289,10 @@ export const publicPathTemplate: NuxtTemplate = {
export const nuxtConfigTemplate = {
filename: 'nuxt.config.mjs',
getContents: (ctx: TemplateContext) => {
return Object.entries(ctx.nuxt.options.app).map(([k, v]) => `export const ${camelCase('app-' + k)} = ${JSON.stringify(v)}`).join('\n\n')
return [
...Object.entries(ctx.nuxt.options.app).map(([k, v]) => `export const ${camelCase('app-' + k)} = ${JSON.stringify(v)}`),
`export const devPagesDir = ${ctx.nuxt.options.dev ? JSON.stringify(ctx.nuxt.options.dir.pages) : 'null'}`
].join('\n\n')
}
}

View File

@ -1,10 +1,12 @@
import { defineComponent } from 'vue'
// @ts-expect-error virtual file
import { devPagesDir } from '#build/nuxt.config.mjs'
export default defineComponent({
name: 'NuxtPage',
setup (_, props) {
if (process.dev) {
console.warn('Create a Vue component in the `pages/` directory to enable `<NuxtPage>`')
console.warn(`Create a Vue component in the \`${devPagesDir}/\` directory to enable \`<NuxtPage>\``)
}
return () => props.slots.default?.()
}

View File

@ -101,7 +101,7 @@ function getRoutePath (tokens: SegmentToken[]): string {
(token.type === SegmentTokenType.optional
? `:${token.value}?`
: token.type === SegmentTokenType.dynamic
? `:${token.value}`
? `:${token.value}()`
: token.type === SegmentTokenType.catchall
? `:${token.value}(.*)*`
: encodePath(token.value))

View File

@ -64,13 +64,13 @@ describe('pages:generateRoutesFromFiles', () => {
output: [
{
name: 'parent-child',
path: '/parent/:child',
path: '/parent/:child()',
file: `${pagesDir}/parent/[child].vue`,
children: []
},
{
name: 'parent-child',
path: '/parent-:child',
path: '/parent-:child()',
file: `${pagesDir}/parent-[child].vue`,
children: []
}
@ -91,7 +91,7 @@ describe('pages:generateRoutesFromFiles', () => {
},
{
name: 'stories-id',
path: '/stories/:id',
path: '/stories/:id()',
file: `${pagesDir}/stories/[id].vue`,
children: []
}
@ -106,7 +106,7 @@ describe('pages:generateRoutesFromFiles', () => {
output: [
{
name: 'stories-id',
path: '/stories/:id',
path: '/stories/:id()',
file: `${pagesDir}/stories/[id].vue`,
children: []
},
@ -167,7 +167,7 @@ describe('pages:generateRoutesFromFiles', () => {
children: [],
name: 'slug',
file: 'pages/[slug].vue',
path: '/:slug'
path: '/:slug()'
},
{
children: [
@ -186,11 +186,11 @@ describe('pages:generateRoutesFromFiles', () => {
children: [],
name: 'bar',
file: 'pages/[bar]/index.vue',
path: '/:bar'
path: '/:bar()'
},
{
name: 'nonopt-slug',
path: '/nonopt/:slug',
path: '/nonopt/:slug()',
file: `${pagesDir}/nonopt/[slug].vue`,
children: []
},
@ -202,7 +202,7 @@ describe('pages:generateRoutesFromFiles', () => {
},
{
name: 'sub-route-slug',
path: '/:sub?/route-:slug',
path: '/:sub?/route-:slug()',
file: `${pagesDir}/[[sub]]/route-[slug].vue`,
children: []
}
@ -243,22 +243,29 @@ describe('pages:generateRoutesFromFiles', () => {
files: [
`${pagesDir}/[a1_1a].vue`,
`${pagesDir}/[b2.2b].vue`,
`${pagesDir}/[b2]_[2b].vue`,
`${pagesDir}/[[c3@3c]].vue`,
`${pagesDir}/[[d4-4d]].vue`
],
output: [
{
name: 'a1_1a',
path: '/:a1_1a',
path: '/:a1_1a()',
file: `${pagesDir}/[a1_1a].vue`,
children: []
},
{
name: 'b2.2b',
path: '/:b2.2b',
path: '/:b2.2b()',
file: `${pagesDir}/[b2.2b].vue`,
children: []
},
{
name: 'b2_2b',
path: '/:b2()_:2b()',
file: `${pagesDir}/[b2]_[2b].vue`,
children: []
},
{
name: 'c33c',
path: '/:c33c?',

View File

@ -1,6 +1,6 @@
{
"name": "@nuxt/schema",
"version": "3.3.2",
"version": "3.3.3",
"repository": "nuxt/nuxt",
"license": "MIT",
"type": "module",
@ -8,7 +8,8 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs"
"import": "./dist/index.mjs",
"require": "./dist/index.mjs"
},
"./package.json": "./package.json"
},
@ -25,7 +26,7 @@
"@unhead/schema": "^1.1.25",
"@vitejs/plugin-vue": "^4.1.0",
"@vitejs/plugin-vue-jsx": "^3.0.1",
"nitropack": "~2.3.2",
"nitropack": "^2.3.2",
"unbuild": "latest",
"vite": "~4.2.1"
},
@ -33,7 +34,7 @@
"c12": "^1.2.0",
"create-require": "^1.1.1",
"defu": "^6.1.2",
"hookable": "^5.5.0",
"hookable": "^5.5.3",
"jiti": "^1.18.2",
"pathe": "^1.1.0",
"pkg-types": "^1.0.2",
@ -41,8 +42,8 @@
"scule": "^1.0.0",
"std-env": "^3.3.2",
"ufo": "^1.1.1",
"unimport": "^3.0.4",
"untyped": "^1.2.2"
"unimport": "^3.0.5",
"untyped": "^1.3.2"
},
"engines": {
"node": "^14.18.0 || ^16.10.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"

View File

@ -303,10 +303,10 @@ export default defineUntypedSchema({
*/
alias: {
$resolve: async (val, get) => ({
'~~': await get('rootDir'),
'@@': await get('rootDir'),
'~': await get('srcDir'),
'@': await get('srcDir'),
'~~': await get('rootDir'),
'@@': await get('rootDir'),
[await get('dir.assets')]: join(await get('srcDir'), await get('dir.assets')),
[await get('dir.public')]: join(await get('srcDir'), await get('dir.public')),
...val

View File

@ -158,6 +158,12 @@ export default defineUntypedSchema({
*
* This can be disabled for most Nuxt sites to reduce the client-side bundle by ~0.5kb.
*/
polyfillVueUseHead: true
polyfillVueUseHead: true,
/** Allow disabling Nuxt SSR responses by setting the `x-nuxt-no-ssr` header. */
respectNoSSRHeader: false,
/** Resolve `~`, `~~`, `@` and `@@` aliases located within layers with respect to their layer source and root directories. */
localLayerAliases: true,
}
})

View File

@ -94,14 +94,16 @@ export interface NuxtOptions extends Omit<ConfigSchema, 'builder'> {
}
export interface ViteConfig extends ViteUserConfig {
/** The path to the entrypoint for the Vite build. */
entry?: string
/**
* Options passed to @vitejs/plugin-vue
* Options passed to @vitejs/plugin-vue.
* @see https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue
*/
vue?: VuePluginOptions
/**
* Options passed to @vitejs/plugin-vue-jsx
* Options passed to @vitejs/plugin-vue-jsx.
* @see https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue-jsx
*/
vueJsx?: VueJsxPluginOptions

View File

@ -1,7 +1,7 @@
import type { TSConfig } from 'pkg-types'
import type { Server as HttpServer } from 'node:http'
import type { Server as HttpsServer } from 'node:https'
import type { InlineConfig as ViteInlineConfig, ViteDevServer } from 'vite'
import type { ViteDevServer } from 'vite'
import type { Manifest } from 'vue-bundle-renderer'
import type { EventHandler } from 'h3'
import type { Import, InlinePreset, Unimport } from 'unimport'
@ -9,7 +9,7 @@ import type { Compiler, Configuration, Stats } from 'webpack'
import type { Nuxt, NuxtApp, ResolvedNuxtTemplate } from './nuxt'
import type { Nitro, NitroConfig } from 'nitropack'
import type { Component, ComponentsOptions } from './components'
import type { NuxtCompatibility, NuxtCompatibilityIssues } from '..'
import type { NuxtCompatibility, NuxtCompatibilityIssues, ViteConfig } from '..'
import type { Schema, SchemaDefinition } from 'untyped'
export type HookResult = Promise<void> | void
@ -282,14 +282,14 @@ export interface NuxtHooks {
* @param viteBuildContext The vite build context object
* @returns Promise
*/
'vite:extend': (viteBuildContext: { nuxt: Nuxt, config: ViteInlineConfig }) => HookResult
'vite:extend': (viteBuildContext: { nuxt: Nuxt, config: ViteConfig }) => HookResult
/**
* Allows to extend Vite default config.
* @param viteInlineConfig The vite inline config object
* @param env Server or client
* @returns Promise
*/
'vite:extendConfig': (viteInlineConfig: ViteInlineConfig, env: { isClient: boolean, isServer: boolean }) => HookResult
'vite:extendConfig': (viteInlineConfig: ViteConfig, env: { isClient: boolean, isServer: boolean }) => HookResult
/**
* Called when the Vite server is created.
* @param viteServer Vite development server

View File

@ -1,6 +1,6 @@
{
"name": "@nuxt/test-utils",
"version": "3.3.2",
"version": "3.3.3",
"repository": "nuxt/nuxt",
"license": "MIT",
"type": "module",
@ -18,8 +18,8 @@
"prepack": "unbuild"
},
"dependencies": {
"@nuxt/kit": "3.3.2",
"@nuxt/schema": "3.3.2",
"@nuxt/kit": "workspace:../kit",
"@nuxt/schema": "workspace:../schema",
"consola": "^2.15.3",
"defu": "^6.1.2",
"execa": "^7.1.1",
@ -29,9 +29,9 @@
"pathe": "^1.1.0"
},
"devDependencies": {
"playwright": "^1.32.0",
"playwright": "^1.32.2",
"unbuild": "latest",
"vitest": "^0.29.7"
"vitest": "^0.29.8"
},
"peerDependencies": {
"vue": "^3.2.47"

View File

@ -1,6 +1,6 @@
{
"name": "@nuxt/vite-builder",
"version": "3.3.2",
"version": "3.3.3",
"repository": "nuxt/nuxt",
"license": "MIT",
"type": "module",
@ -18,28 +18,27 @@
"prepack": "unbuild"
},
"devDependencies": {
"@nuxt/schema": "3.3.2",
"@types/cssnano": "^5.1.0",
"@nuxt/schema": "workspace:../schema",
"unbuild": "latest",
"vue": "3.2.47"
},
"dependencies": {
"@nuxt/kit": "3.3.2",
"@nuxt/kit": "workspace:../kit",
"@rollup/plugin-replace": "^5.0.2",
"@vitejs/plugin-vue": "^4.1.0",
"@vitejs/plugin-vue-jsx": "^3.0.1",
"autoprefixer": "^10.4.14",
"chokidar": "^3.5.3",
"clear": "^0.1.0",
"cssnano": "^5.1.15",
"cssnano": "^6.0.0",
"defu": "^6.1.2",
"esbuild": "^0.17.13",
"esbuild": "^0.17.15",
"escape-string-regexp": "^5.0.0",
"estree-walker": "^3.0.3",
"externality": "^1.0.0",
"fs-extra": "^11.1.1",
"get-port-please": "^3.0.1",
"h3": "^1.6.2",
"h3": "^1.6.4",
"knitwork": "^1.0.0",
"magic-string": "^0.30.0",
"mlly": "^1.2.0",
@ -50,14 +49,13 @@
"postcss": "^8.4.21",
"postcss-import": "^15.1.0",
"postcss-url": "^10.1.3",
"rollup": "^3.20.2",
"rollup-plugin-visualizer": "^5.9.0",
"std-env": "^3.3.2",
"strip-literal": "^1.0.1",
"ufo": "^1.1.1",
"unplugin": "^1.3.1",
"vite": "~4.2.1",
"vite-node": "^0.29.7",
"vite-node": "^0.29.8",
"vite-plugin-checker": "^0.5.6",
"vue-bundle-renderer": "^1.0.3"
},

View File

@ -3,16 +3,16 @@ import { join, resolve } from 'pathe'
import * as vite from 'vite'
import vuePlugin from '@vitejs/plugin-vue'
import viteJsxPlugin from '@vitejs/plugin-vue-jsx'
import type { ServerOptions } from 'vite'
import type { ServerOptions, BuildOptions } from 'vite'
import { logger } from '@nuxt/kit'
import { getPort } from 'get-port-please'
import { joinURL, withoutLeadingSlash } from 'ufo'
import { defu } from 'defu'
import type { OutputOptions } from 'rollup'
import { defineEventHandler } from 'h3'
import type { ViteConfig } from '@nuxt/schema'
import { cacheDirPlugin } from './plugins/cache-dir'
import { chunkErrorPlugin } from './plugins/chunk-error'
import type { ViteBuildContext, ViteOptions } from './vite'
import type { ViteBuildContext } from './vite'
import { devStyleSSRPlugin } from './plugins/dev-ssr-css'
import { runtimePathsPlugin } from './plugins/paths'
import { pureAnnotationsPlugin } from './plugins/pure-annotations'
@ -20,8 +20,7 @@ import { viteNodePlugin } from './vite-node'
import { createViteLogger } from './utils/logger'
export async function buildClient (ctx: ViteBuildContext) {
const clientConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
entry: ctx.entry,
const clientConfig: ViteConfig = vite.mergeConfig(ctx.config, {
base: ctx.nuxt.options.dev
? joinURL(ctx.nuxt.options.app.baseURL.replace(/^\.\//, '/') || '/', ctx.nuxt.options.app.buildAssetsDir)
: './',
@ -62,8 +61,6 @@ export async function buildClient (ctx: ViteBuildContext) {
},
plugins: [
cacheDirPlugin(ctx.nuxt.options.rootDir, 'client'),
vuePlugin(ctx.config.vue),
viteJsxPlugin(ctx.config.vueJsx),
devStyleSSRPlugin({
srcDir: ctx.nuxt.options.srcDir,
buildAssetsURL: joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir)
@ -81,7 +78,7 @@ export async function buildClient (ctx: ViteBuildContext) {
server: {
middlewareMode: true
}
} as ViteOptions)
} satisfies vite.InlineConfig)
clientConfig.customLogger = createViteLogger(clientConfig)
@ -101,7 +98,7 @@ export async function buildClient (ctx: ViteBuildContext) {
output: {
chunkFileNames: ctx.nuxt.options.dev ? undefined : withoutLeadingSlash(join(ctx.nuxt.options.app.buildAssetsDir, '[name].[hash].js')),
entryFileNames: ctx.nuxt.options.dev ? 'entry.js' : withoutLeadingSlash(join(ctx.nuxt.options.app.buildAssetsDir, '[name].[hash].js'))
} as OutputOptions
} satisfies NonNullable<BuildOptions['rollupOptions']>['output']
}) as any
if (clientConfig.server && clientConfig.server.hmr !== false) {
@ -126,6 +123,11 @@ export async function buildClient (ctx: ViteBuildContext) {
await ctx.nuxt.callHook('vite:extendConfig', clientConfig, { isClient: true, isServer: false })
clientConfig.plugins!.unshift(
vuePlugin(clientConfig.vue),
viteJsxPlugin(clientConfig.vueJsx)
)
if (ctx.nuxt.options.dev) {
// Dev
const viteServer = await vite.createServer(clientConfig)

View File

@ -1,10 +1,10 @@
import { requireModule } from '@nuxt/kit'
import type { Nuxt } from '@nuxt/schema'
import type { ViteOptions } from './vite'
import type { InlineConfig as ViteConfig } from 'vite'
import { distDir } from './dirs'
export function resolveCSSOptions (nuxt: Nuxt): ViteOptions['css'] {
const css: ViteOptions['css'] & { postcss: NonNullable<Exclude<NonNullable<ViteOptions['css']>['postcss'], string>> } = {
export function resolveCSSOptions (nuxt: Nuxt): ViteConfig['css'] {
const css: ViteConfig['css'] & { postcss: NonNullable<Exclude<NonNullable<ViteConfig['css']>['postcss'], string>> } = {
postcss: {
plugins: []
}

View File

@ -1,5 +1,5 @@
import { dirname, isAbsolute, join, resolve } from 'pathe'
import type { Plugin } from 'rollup'
import type { Plugin } from 'vite'
const PREFIX = 'virtual:nuxt:'

View File

@ -4,7 +4,8 @@ import vuePlugin from '@vitejs/plugin-vue'
import viteJsxPlugin from '@vitejs/plugin-vue-jsx'
import { logger, resolveModule, resolvePath } from '@nuxt/kit'
import { joinURL, withoutLeadingSlash, withTrailingSlash } from 'ufo'
import type { ViteBuildContext, ViteOptions } from './vite'
import type { ViteConfig } from '@nuxt/schema'
import type { ViteBuildContext } from './vite'
import { createViteLogger } from './utils/logger'
import { cacheDirPlugin } from './plugins/cache-dir'
import { initViteNodeServer } from './vite-node'
@ -17,8 +18,7 @@ export async function buildServer (ctx: ViteBuildContext) {
const _resolve = (id: string) => resolveModule(id, { paths: ctx.nuxt.options.modulesDir })
const helper = ctx.nuxt.options.nitro.imports !== false ? '' : 'globalThis.'
const entry = ctx.nuxt.options.ssr ? ctx.entry : await resolvePath(resolve(ctx.nuxt.options.appDir, 'entry-spa'))
const serverConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
entry,
const serverConfig: ViteConfig = vite.mergeConfig(ctx.config, {
base: ctx.nuxt.options.dev
? joinURL(ctx.nuxt.options.app.baseURL.replace(/^\.\//, '/') || '/', ctx.nuxt.options.app.buildAssetsDir)
: undefined,
@ -111,14 +111,12 @@ export async function buildServer (ctx: ViteBuildContext) {
},
plugins: [
cacheDirPlugin(ctx.nuxt.options.rootDir, 'server'),
vuePlugin(ctx.config.vue),
viteJsxPlugin(ctx.config.vueJsx),
pureAnnotationsPlugin.vite({
sourcemap: ctx.nuxt.options.sourcemap.server,
functions: ['defineComponent', 'defineAsyncComponent', 'defineNuxtLink', 'createClientOnly', 'defineNuxtPlugin', 'defineNuxtRouteMiddleware', 'defineNuxtComponent', 'useRuntimeConfig']
})
]
} as ViteOptions)
} satisfies vite.InlineConfig)
serverConfig.customLogger = createViteLogger(serverConfig)
@ -146,6 +144,11 @@ export async function buildServer (ctx: ViteBuildContext) {
await ctx.nuxt.callHook('vite:extendConfig', serverConfig, { isClient: false, isServer: true })
serverConfig.plugins!.unshift(
vuePlugin(serverConfig.vue),
viteJsxPlugin(serverConfig.vueJsx)
)
const onBuild = () => ctx.nuxt.callHook('vite:compiled')
// Production build

View File

@ -1,10 +1,7 @@
import * as vite from 'vite'
import { join, resolve } from 'pathe'
import type { Nuxt } from '@nuxt/schema'
import type { InlineConfig, SSROptions } from 'vite'
import type { Nuxt, ViteConfig } from '@nuxt/schema'
import { logger, isIgnored, resolvePath, addVitePlugin } from '@nuxt/kit'
import type { Options as VueOptions } from '@vitejs/plugin-vue'
import type { Options as VueJsxOptions } from '@vitejs/plugin-vue-jsx'
import replace from '@rollup/plugin-replace'
import { sanitizeFilePath } from 'mlly'
import { withoutLeadingSlash } from 'ufo'
@ -18,16 +15,9 @@ import { resolveCSSOptions } from './css'
import { composableKeysPlugin } from './plugins/composable-keys'
import { logLevelMap } from './utils/logger'
export interface ViteOptions extends InlineConfig {
vue?: VueOptions
vueJsx?: VueJsxOptions
ssr?: SSROptions
devBundler?: 'vite-node' | 'legacy'
}
export interface ViteBuildContext {
nuxt: Nuxt
config: ViteOptions
config: ViteConfig
entry: string
clientServer?: vite.ViteDevServer
ssrServer?: vite.ViteDevServer
@ -104,7 +94,7 @@ export async function bundle (nuxt: Nuxt) {
]
}
}
} as ViteOptions,
} satisfies ViteConfig,
nuxt.options.vite
)
}

View File

@ -1,6 +1,6 @@
{
"name": "@nuxt/webpack-builder",
"version": "3.3.2",
"version": "3.3.3",
"repository": "nuxt/nuxt",
"license": "MIT",
"type": "module",
@ -19,13 +19,13 @@
"prepack": "unbuild"
},
"dependencies": {
"@babel/core": "^7.21.3",
"@babel/core": "^7.21.4",
"@nuxt/friendly-errors-webpack-plugin": "^2.5.2",
"@nuxt/kit": "3.3.2",
"@nuxt/kit": "workspace:../kit",
"autoprefixer": "^10.4.14",
"css-loader": "^6.7.3",
"css-minimizer-webpack-plugin": "^4.2.2",
"cssnano": "^5.1.15",
"css-minimizer-webpack-plugin": "^5.0.0",
"cssnano": "^6.0.0",
"esbuild-loader": "^3.0.1",
"escape-string-regexp": "^5.0.0",
"estree-walker": "^3.0.3",
@ -43,7 +43,7 @@
"pify": "^6.1.0",
"postcss": "^8.4.21",
"postcss-import": "^15.1.0",
"postcss-loader": "^7.1.0",
"postcss-loader": "^7.2.4",
"postcss-url": "^10.1.3",
"style-resources-loader": "^1.5.0",
"time-fix-plugin": "^2.0.7",
@ -52,7 +52,7 @@
"url-loader": "^4.1.1",
"vue-bundle-renderer": "^1.0.3",
"vue-loader": "^17.0.1",
"webpack": "^5.76.3",
"webpack": "^5.77.0",
"webpack-bundle-analyzer": "^4.8.0",
"webpack-dev-middleware": "^6.0.2",
"webpack-hot-middleware": "^2.25.3",
@ -60,11 +60,10 @@
"webpackbar": "^5.0.2"
},
"devDependencies": {
"@nuxt/schema": "3.3.2",
"@nuxt/schema": "workspace:../schema",
"@types/lodash-es": "^4.17.7",
"@types/pify": "^5.0.1",
"@types/webpack-bundle-analyzer": "^4.6.0",
"@types/webpack-dev-middleware": "^5.3.0",
"@types/webpack-hot-middleware": "^2.25.6",
"@types/webpack-virtual-modules": "^0.1.1",
"unbuild": "latest",

View File

@ -159,7 +159,7 @@ export function baseTranspile (ctx: WebpackConfigContext) {
/\.vue\.js/i, // include SFCs in node_modules
/consola\/src/,
/vue-demi/,
/(^|\/)nuxt\/(dist\/)?app($|\/)/
/(^|\/)nuxt\/(dist\/)?(app|[^/]+\/runtime)($|\/)/
]
for (let pattern of options.build.transpile) {

File diff suppressed because it is too large Load Diff

View File

@ -69,8 +69,10 @@ export async function loadWorkspace (dir: string) {
}
}
const setVersion = (name: string, newVersion: string) => {
const setVersion = (name: string, newVersion: string, opts: { updateDeps?: boolean } = {}) => {
find(name).data.version = newVersion
if (!opts.updateDeps) { return }
for (const pkg of packages) {
pkg.updateDeps((dep) => {
if (dep.name === name) {

View File

@ -26,7 +26,9 @@ async function main () {
for (const pkg of workspace.packages.filter(p => !p.data.private)) {
const newVersion = inc(pkg.data.version, bumpType || 'patch')
workspace.setVersion(pkg.data.name, `${newVersion}-${date}.${commit}`)
workspace.setVersion(pkg.data.name, `${newVersion}-${date}.${commit}`, {
updateDeps: true
})
const newname = pkg.data.name === 'nuxt' ? 'nuxt3' : (pkg.data.name + '-edge')
workspace.rename(pkg.data.name, newname)
}

View File

@ -707,6 +707,10 @@ describe('extends support', () => {
})
describe('middlewares', () => {
it('works with layer aliases', async () => {
const html = await $fetch('/foo')
expect(html).toContain('from layer alias')
})
it('extends foo/middleware/foo', async () => {
const html = await $fetch('/foo')
expect(html).toContain('Middleware | foo: Injected by extended middleware from foo')

View File

@ -6,7 +6,7 @@ import { globby } from 'globby'
import { join } from 'pathe'
import { isWindows } from 'std-env'
describe.skipIf(isWindows)('minimal nuxt application', () => {
describe.skipIf(isWindows || process.env.ECOSYSTEM_CI)('minimal nuxt application', () => {
const rootDir = fileURLToPath(new URL('./fixtures/minimal', import.meta.url))
const publicDir = join(rootDir, '.output/public')
const serverDir = join(rootDir, '.output/server')
@ -26,7 +26,7 @@ describe.skipIf(isWindows)('minimal nuxt application', () => {
it('default client bundle size', async () => {
stats.client = await analyzeSizes('**/*.js', publicDir)
expect(stats.client.totalBytes).toBeLessThan(106650)
expect(roundToKilobytes(stats.client.totalBytes)).toMatchInlineSnapshot('"104k"')
expect(stats.client.files.map(f => f.replace(/\..*\.js/, '.js'))).toMatchInlineSnapshot(`
[
"_nuxt/_plugin-vue_export-helper.js",
@ -40,10 +40,10 @@ describe.skipIf(isWindows)('minimal nuxt application', () => {
it('default server bundle size', async () => {
stats.server = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
expect(stats.server.totalBytes).toBeLessThan(93900)
expect(roundToKilobytes(stats.server.totalBytes)).toMatchInlineSnapshot('"91k"')
const modules = await analyzeSizes('node_modules/**/*', serverDir)
expect(modules.totalBytes).toBeLessThan(2694400)
expect(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"2632k"')
const packages = modules.files
.filter(m => m.endsWith('package.json'))
@ -104,3 +104,7 @@ async function analyzeSizes (pattern: string | string[], rootDir: string) {
}
return { files, totalBytes }
}
function roundToKilobytes (bytes: number) {
return (Math.round(bytes / 1024) + 'k')
}

View File

@ -0,0 +1 @@
export const test = 'from layer alias'

View File

@ -1,4 +1,5 @@
<script setup>
import { test } from '~/alias/test'
definePageMeta({
middleware: 'foo'
})
@ -8,6 +9,7 @@ const foo = useExtendsFoo()
<template>
<div>
<div>{{ test }}</div>
<div>Extended page from foo</div>
<div>Middleware | foo: {{ $route.meta.foo }}</div>
<div>Composable | useExtendsFoo: {{ foo }}</div>

View File

@ -184,6 +184,7 @@ export default defineNuxtConfig({
}
},
experimental: {
respectNoSSRHeader: true,
clientFallback: true,
restoreState: true,
inlineSSRStyles: id => !!id && !id.includes('assets.vue'),

View File

@ -1,10 +1,10 @@
{
"private": true,
"name": "fixture-bridge",
"name": "fixture-basic",
"scripts": {
"build": "nuxi build"
},
"dependencies": {
"nuxt": "workspace:*"
"nuxt": "latest"
}
}

View File

@ -146,6 +146,13 @@ describe('runtimeConfig', () => {
expectTypeOf(runtimeConfig.privateConfig).toEqualTypeOf<string>()
expectTypeOf(runtimeConfig.public.ids).toEqualTypeOf<number[]>()
expectTypeOf(runtimeConfig.unknown).toEqualTypeOf<any>()
const injectedConfig = useNuxtApp().$config
expectTypeOf(injectedConfig.public.testConfig).toEqualTypeOf<number>()
expectTypeOf(injectedConfig.public.needsFallback).toEqualTypeOf<string>()
expectTypeOf(injectedConfig.privateConfig).toEqualTypeOf<string>()
expectTypeOf(injectedConfig.public.ids).toEqualTypeOf<number[]>()
expectTypeOf(injectedConfig.unknown).toEqualTypeOf<any>()
})
it('provides hints on overriding these values', () => {
const val = defineNuxtConfig({

10
test/fixtures/minimal/package.json vendored Normal file
View File

@ -0,0 +1,10 @@
{
"private": true,
"name": "fixture-minimal",
"scripts": {
"build": "nuxi build"
},
"dependencies": {
"nuxt": "latest"
}
}