Merge branch 'main' into docs/kit

This commit is contained in:
Andrey Yolkin 2023-08-04 17:39:46 +03:00 committed by GitHub
commit 76769924ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 2350 additions and 227 deletions

View File

@ -112,7 +112,6 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest]
module: ['bundler', 'node']
base: ['with-base-url', 'without-base-url']
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
@ -135,7 +134,6 @@ jobs:
run: pnpm test:types
env:
MODULE_RESOLUTION: ${{ matrix.module }}
TS_BASE_URL: ${{ matrix.base }}
lint:
# autofix workflow will be triggered instead for PRs

12
.website/.gitignore vendored Executable file
View File

@ -0,0 +1,12 @@
node_modules
*.iml
.idea
*.log*
.nuxt
.vscode
.DS_Store
coverage
dist
sw.*
.env
.output

35
.website/README.md Executable file
View File

@ -0,0 +1,35 @@
# Nuxt Docs Website
This is a temporary directory until we open source the repository for nuxt.com.
The goal is to simplify the contribution in the meantine to the documentation by having the possibility to preview the changes locally.
## Setup
Install dependencies in the root of the `nuxt` folder:
```bash
pnpm i
```
Then stub the dependencies:
```bash
pnpm build:stub
```
## Development
In the root of the `nuxt` folder, run:
```bash
pnpm docs:dev
```
Then open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
Update the documentation within the `docs` folder.
---
For a detailed explanation of how things work, check out [Docus](https://docus.dev).

14
.website/app.config.ts Normal file
View File

@ -0,0 +1,14 @@
export default defineAppConfig({
docus: {
title: 'Nuxt Docs [dev]',
description: 'The best place to start your documentation.',
socials: {
twitter: 'nuxt_js',
github: 'nuxt/nuxt'
},
aside: {
level: 1,
collapsed: false,
},
}
})

20
.website/nuxt.config.ts Executable file
View File

@ -0,0 +1,20 @@
import { createResolver } from 'nuxt/kit'
const { resolve } = createResolver(import.meta.url)
export default defineNuxtConfig({
// https://github.com/nuxt-themes/docus
extends: '@nuxt-themes/docus',
content: {
sources: {
docs: {
driver: 'fs',
prefix: '/',
base: resolve('../docs')
}
}
},
experimental: {
renderJsonPayloads: false
}
})

14
.website/package.json Executable file
View File

@ -0,0 +1,14 @@
{
"name": "docus-starter",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "nuxt dev",
"build": "nuxt build",
"generate": "nuxt generate",
"preview": "nuxt preview"
},
"devDependencies": {
"@nuxt-themes/docus": "1.14.6"
}
}

3
.website/tsconfig.json Executable file
View File

@ -0,0 +1,3 @@
{
"extends": "./.nuxt/tsconfig.json"
}

View File

@ -81,52 +81,61 @@ There are two ways to deploy a Nuxt application to any static hosting services:
### Crawl-based Pre-rendering
Use the [`nuxi generate` command](/docs/api/commands/generate) to build your application. For every page, Nuxt uses a crawler to generate a corresponding HTML and payload files. The built files will be generated in the `.output/public` directory.
Use the [`nuxi generate` command](/docs/api/commands/generate) to build and pre-render your application using the [Nitro](/docs/guide/concepts/server-engine) crawler. This command is similar to `nuxt build` with the `nitro.static` option set to `true`, or running `nuxt build --prerender`.
```bash
npx nuxi generate
```
You can enable crawl-based pre-rendering when using `nuxt build` in the `nuxt.config` file:
That's it! You can now deploy the `.output/public` directory to any static hosting service or preview it locally with `npx serve .output/public`.
```ts [nuxt.config.ts|js]
defineNuxtConfig({
nitro: {
prerender: {
crawlLinks: true
}
}
})
```
Working of the Nitro crawler:
1. Load the HTML of your application's root route (`/`), any non-dynamic pages in your `~/pages` directory, and any other routes in the `nitro.prerender.routes` array.
2. Save the HTML and `payload.json` to the `~/.output/public/` directory to be served statically.
3. Find all anchor tags (`<a href="...">`) in the HTML to navigate to other routes.
4. Repeat steps 1-3 for each anchor tag found until there are no more anchor tags to crawl.
This is important to understand since pages that are not linked to a discoverable page can't be pre-rendered automatically.
::alert{type=info}
Read more about the [`nuxi generate` command](/docs/api/commands/generate#nuxi-generate).
::
### Selective Pre-rendering
You can manually specify routes that [Nitro](/docs/guide/concepts/server-engine) will fetch and pre-render during the build.
You can manually specify routes that [Nitro](/docs/guide/concepts/server-engine) will fetch and pre-render during the build or ignore routes that you don't want to pre-render like `/dynamic` in the `nuxt.config` file:
```ts [nuxt.config.ts|js]
defineNuxtConfig({
nitro: {
prerender: {
routes: ['/user/1', '/user/2']
ignore: ['/dynamic']
}
}
})
```
When using this option with `nuxi build`, static payloads won't be generated by default at build time. For now, selective payload generation is under an experimental flag.
You can combine this with the `crawLinks` option to pre-render a set of routes that the crawler can't discover like your `/sitemap.xml` or `/robots.txt`:
```ts [nuxt.config.ts|js]
defineNuxtConfig({
/* The /dynamic route won't be crawled */
nitro: {
prerender: { crawlLinks: true, ignore: ['/dynamic'] }
},
experimental: {
payloadExtraction: true
prerender: {
crawlLinks: true,
routes: ['/sitemap.xml', '/robots.txt']
}
}
})
```
Setting `nitro.prerender` to `true` is similar to `nitro.prerender.crawlLinks` to `true`.
::alert{type=info}
Read more about [pre-rendering](https://nitro.unjs.io/config#prerender) in the Nitro documentation.
::
### Client-side Only Rendering
If you don't want to pre-render your routes, another way of using static hosting is to set the `ssr` property to `false` in the `nuxt.config` file. The `nuxi generate` command will then output an `.output/public/index.html` entrypoint and JavaScript bundles like a classic client-side Vue.js application.

View File

@ -71,7 +71,7 @@ When a `<NuxtLink>` enters the viewport on the client side, Nuxt will automatica
The `useRoute()` composable can be used in a `<script setup>` block or a `setup()` method of a Vue component to access the current route details.
```vue [pages/posts/[id].vue]
```vue [pages/posts/[id\\].vue]
<script setup lang="ts">
const route = useRoute()
@ -133,7 +133,7 @@ The `validate` property accepts the `route` as an argument. You can return a boo
If you have a more complex use case, then you can use anonymous route middleware instead.
```vue [pages/posts/[id].vue]
```vue [pages/posts/[id\\].vue]
<script setup lang="ts">
definePageMeta({
validate: async (route) => {

View File

@ -318,7 +318,7 @@ To apply dynamic transitions using conditional logic, you can leverage inline [m
::code-group
```html [pages/[id].vue]
```html [pages/[id\\].vue]
<script setup lang="ts">
definePageMeta({
pageTransition: {

View File

@ -111,7 +111,7 @@ If you throw an error created with `createError`:
### Example
```vue [pages/movies/[slug].vue]
```vue [pages/movies/[slug\\].vue]
<script setup lang="ts">
const route = useRoute()
const { data } = await useFetch(`/api/movies/${route.params.slug}`)

View File

@ -90,7 +90,7 @@ export default defineNuxtConfig({
// Homepage pre-rendered at build time
'/': { prerender: true },
// Product page generated on-demand, revalidates in background
'/products/**': { swr: true },
'/products/**': { swr: 3600 },
// Blog post generated on-demand once until next deploy
'/blog/**': { isr: true },
// Admin dashboard renders only on client-side
@ -108,8 +108,8 @@ The different properties you can use are the following:
- `ssr: boolean`{lang=ts} - Disables server-side rendering for sections of your app and make them SPA-only with `ssr: false`
- `cors: boolean`{lang=ts} - Automatically adds cors headers with `cors: true` - you can customize the output by overriding with `headers`
- `headers: object`{lang=ts} - Add specific headers to sections of your site - for example, your assets
- `swr: number`{lang=ts} - Add cache headers to the server response and cache it on the server or reverse proxy for a configurable TTL (time to live). The `node-server` preset of Nitro is able to cache the full response. When the TTL expired, the cached response will be sent while the page will be regenerated in the background.
- `isr: boolean`{lang=ts} - The behavior is the same as `swr` except that we are able to add the response to the CDN cache on platforms that support this (currently Netlify or Vercel)
- `swr: number|boolean`{lang=ts} - Add cache headers to the server response and cache it on the server or reverse proxy for a configurable TTL (time to live). The `node-server` preset of Nitro is able to cache the full response. When the TTL expired, the cached response will be sent while the page will be regenerated in the background. If true is used, a `stale-while-revalidate` header is added without a MaxAge.
- `isr: number|boolean`{lang=ts} - The behavior is the same as `swr` except that we are able to add the response to the CDN cache on platforms that support this (currently Netlify or Vercel). If `true` is used, the content persists until the next deploy inside the CDN.
- `prerender:boolean`{lang=ts} - Prerenders routes at build time and includes them in your build as static assets
- `experimentalNoScripts: boolean`{lang=ts} - Disables rendering of Nuxt scripts and JS resource hints for sections of your site.

View File

@ -67,7 +67,7 @@ The module automatically loads and parses them.
To render content pages, add a [catch-all route](/docs/guide/directory-structure/pages/#catch-all-route) using the `ContentDoc` component:
```vue [pages/[...slug].vue]
```vue [pages/[...slug\\].vue]
<template>
<main>
<ContentDoc />

View File

@ -110,7 +110,7 @@ If you want a parameter to be _optional_, you must enclose it in double square b
Given the example above, you can access group/id within your component via the `$route` object:
```vue [pages/users-[group]/[id].vue]
```vue [pages/users-[group\\]/[id\\].vue]
<template>
<p>{{ $route.params.group }} - {{ $route.params.id }}</p>
</template>
@ -138,7 +138,7 @@ if (route.params.group === 'admins' && !route.params.id) {
If you need a catch-all route, you create it by using a file named like `[...slug].vue`. This will match _all_ routes under that path.
```vue [pages/[...slug].vue]
```vue [pages/[...slug\\].vue]
<template>
<p>{{ $route.params.slug }}</p>
</template>

View File

@ -151,7 +151,7 @@ Server routes can use dynamic parameters within brackets in the file name like `
**Example:**
```ts [server/api/hello/[name].ts]
```ts [server/api/hello/[name\\].ts]
export default defineEventHandler((event) => `Hello, ${event.context.params.name}!`)
```
@ -181,11 +181,11 @@ Catch-all routes are helpful for fallback route handling. For example, creating
**Examples:**
```ts [server/api/foo/[...].ts]
```ts [server/api/foo/[...\\].ts]
export default defineEventHandler(() => `Default foo handler`)
```
```ts [server/api/[...].ts]
```ts [server/api/[...\\].ts]
export default defineEventHandler(() => `Default api handler`)
```
@ -221,7 +221,7 @@ If no errors are thrown, a status code of `200 OK` will be returned. Any uncaugh
To return other error codes, throw an exception with `createError`
```ts [server/api/validation/[id].ts]
```ts [server/api/validation/[id\\].ts]
export default defineEventHandler((event) => {
const id = parseInt(event.context.params.id) as number
if (!Number.isInteger(id)) {
@ -240,7 +240,7 @@ To return other status codes, you can use the `setResponseStatus` utility.
For example, to return `202 Accepted`
```ts [server/api/validation/[id].ts]
```ts [server/api/validation/[id\\].ts]
export default defineEventHandler((event) => {
setResponseStatus(event, 202)
})
@ -284,7 +284,7 @@ export default defineNuxtConfig({
### Using a Nested Router
```ts [server/api/hello/[...slug].ts]
```ts [server/api/hello/[...slug\\].ts]
import { createRouter, defineEventHandler, useBase } from 'h3'
const router = createRouter()

View File

@ -13,7 +13,7 @@ Within the template of a Vue component, you can access the route using `$route`.
In the following example, we call an API via [`useFetch`](/docs/api/composables/use-fetch) using a dynamic page parameter - `slug` - as part of the URL.
```html [~/pages/[slug].vue]
```html [~/pages/[slug\\].vue]
<script setup lang="ts">
const route = useRoute()
const { data: mountain } = await useFetch(`https://api.nuxtjs.dev/mountains/${route.params.slug}`)

View File

@ -0,0 +1,50 @@
---
title: "<NuxtIsland>"
description: "Nuxt provides `<NuxtIsland>` component to render a non-interactive component without any client JS"
---
# `<NuxtIsland>`
Nuxt provide `<NuxtIsland>` to render components only server side.
When rendering an island component, the content of the island component is static, thus no JS is downloaded client-side.
Changing the island component props triggers a refetch of the island component to re-render it again.
::alert{type=warning}
This component is experimental and in order to use it you must enable the `experimental.componentsIsland` option in your `nuxt.config`.
::
::alert{type=info}
Global styles of your application are sent with the response
::
::alert{type=info}
Server only components use `<NuxtIsland>` under the hood
::
## Props
- **name** : Name of the component to render.
- **type**: `string`
- **required**
- **lazy**: Make the component non-blocking.
- **type**: `boolean`
- **default**: `false`
- **props**: Props to send to the component to render.
- **type**: `Record<string, any>`
- **source**: Remote source to call the island to render.
- **type**: `string`
::alert{type=warning}
Remote islands need `experimental.componentsIsland` to be `'local+remote'` in your `nuxt.config`.
::
## `Slots`
Slots can be passed to an island component if declared.
Every slot is interactive since the parent component is the one providing it.
Some slots are reserved to `NuxtIsland` for special cases.
- **fallback**: Specify the content to be rendered before the island loads (if the component is lazy) or if `NuxtIsland` fails to fetch the component.

View File

@ -19,7 +19,7 @@ If you throw an error created with `createError`:
### Example
```vue [pages/movies/[slug].vue]
```vue [pages/movies/[slug\\].vue]
<script setup lang="ts">
const route = useRoute()
const { data } = await useFetch(`/api/movies/${route.params.slug}`)

View File

@ -15,3 +15,7 @@ Option | Default | Description
-------------------------|-----------------|------------------
`rootDir` | `.` | The root directory of the application to generate
`--dotenv` | `.` | Point to another `.env` file to load, **relative** to the root directory.
::alert{type=info}
Read more about [pre-rendering and static hosting](/docs/1.getting-started/10.deployment.md#static-hosting).
::

View File

@ -114,7 +114,7 @@ See [layout migration](/docs/migration/pages-and-layouts).
The validate hook in Nuxt 3 only accepts a single argument, the `route`. Just as in Nuxt 2, you can return a boolean value. If you return false and another match can't be found, this will mean a 404. You can also directly return an object with `statusCode`/`statusMessage` to respond immediately with an error (other matches will not be checked).
```diff [pages/users/[id].vue]
```diff [pages/users/[id\\].vue]
- <script>
- export default {
- async validate({ params }) {
@ -135,7 +135,7 @@ The validate hook in Nuxt 3 only accepts a single argument, the `route`. Just as
This is not supported in Nuxt 3. Instead, you can directly use a watcher to trigger refetching data.
```vue [pages/users/[id].vue]
```vue [pages/users/[id\\].vue]
<script setup lang="ts">
const route = useRoute()
const { data, refresh } = await useFetch('/api/user')

View File

@ -14,6 +14,7 @@
"lint:docs": "markdownlint ./docs && case-police 'docs/**/*.md' *.md",
"lint:docs:fix": "markdownlint ./docs --fix && case-police 'docs/**/*.md' *.md --fix",
"lint:knip": "pnpx knip",
"docs:dev": "nuxi dev .website",
"play": "nuxi dev playground",
"play:build": "nuxi build playground",
"play:preview": "nuxi preview playground",
@ -35,7 +36,7 @@
"@nuxt/webpack-builder": "workspace:*",
"nuxi": "workspace:*",
"nuxt": "workspace:*",
"vite": "4.4.7",
"vite": "4.4.8",
"vue": "3.3.4",
"magic-string": "^0.30.2"
},
@ -61,7 +62,7 @@
"fs-extra": "11.1.1",
"globby": "13.2.2",
"h3": "1.7.1",
"happy-dom": "10.5.2",
"happy-dom": "10.7.0",
"jiti": "1.19.1",
"markdownlint-cli": "^0.33.0",
"nitropack": "2.5.2",
@ -76,7 +77,7 @@
"std-env": "3.3.3",
"typescript": "5.1.6",
"ufo": "1.2.0",
"vite": "4.4.7",
"vite": "4.4.8",
"vitest": "0.33.0",
"vitest-environment-nuxt": "0.10.2",
"vue": "3.3.4",

View File

@ -36,7 +36,7 @@
"semver": "^7.5.4",
"ufo": "^1.2.0",
"unctx": "^2.3.1",
"unimport": "^3.1.0",
"unimport": "^3.1.3",
"untyped": "^1.4.0"
},
"devDependencies": {
@ -46,7 +46,7 @@
"lodash-es": "4.17.21",
"nitropack": "2.5.2",
"unbuild": "latest",
"vite": "4.4.7",
"vite": "4.4.8",
"vitest": "0.33.0",
"webpack": "5.88.2"
},

View File

@ -121,6 +121,8 @@ export async function writeTypes (nuxt: Nuxt) {
module: 'ESNext',
moduleResolution: nuxt.options.experimental?.typescriptBundlerResolution ? 'Bundler' : 'Node',
skipLibCheck: true,
isolatedModules: true,
useDefineForClassFields: true,
strict: nuxt.options.typescript?.strict ?? true,
allowJs: true,
noEmit: true,

View File

@ -32,13 +32,13 @@
"consola": "3.2.3",
"deep-object-diff": "1.1.9",
"defu": "6.1.2",
"destr": "2.0.0",
"destr": "2.0.1",
"execa": "7.2.0",
"flat": "5.0.2",
"giget": "1.1.2",
"h3": "1.7.1",
"jiti": "1.19.1",
"listhen": "1.1.2",
"listhen": "1.2.2",
"mlly": "1.4.0",
"mri": "1.2.0",
"ohash": "1.1.2",

View File

@ -58,15 +58,15 @@
"@nuxt/telemetry": "^2.3.2",
"@nuxt/ui-templates": "^1.3.1",
"@nuxt/vite-builder": "workspace:../vite",
"@unhead/ssr": "^1.1.35",
"@unhead/vue": "^1.1.35",
"@unhead/ssr": "^1.2.2",
"@unhead/vue": "^1.2.2",
"@vue/shared": "^3.3.4",
"acorn": "8.10.0",
"c12": "^1.4.2",
"chokidar": "^3.5.3",
"cookie-es": "^1.0.0",
"defu": "^6.1.2",
"destr": "^2.0.0",
"destr": "^2.0.1",
"devalue": "^4.3.2",
"esbuild": "^0.18.17",
"escape-string-regexp": "^5.0.0",
@ -90,13 +90,13 @@
"pkg-types": "^1.0.3",
"prompts": "^2.4.2",
"scule": "^1.0.0",
"strip-literal": "^1.0.1",
"strip-literal": "^1.3.0",
"ufo": "^1.2.0",
"ultrahtml": "^1.3.0",
"uncrypto": "^0.1.3",
"unctx": "^2.3.1",
"unenv": "^1.5.2",
"unimport": "^3.1.0",
"unenv": "^1.6.1",
"unimport": "^3.1.3",
"unplugin": "^1.4.0",
"unplugin-vue-router": "^0.6.4",
"untyped": "^1.4.0",
@ -112,7 +112,7 @@
"@types/prompts": "2.4.4",
"@vitejs/plugin-vue": "4.2.3",
"unbuild": "latest",
"vite": "4.4.7",
"vite": "4.4.8",
"vitest": "0.33.0"
},
"peerDependencies": {

View File

@ -18,7 +18,7 @@ export default defineComponent({
if (!component) {
throw createError({
statusCode: 404,
statusMessage: `Island component not found: ${JSON.stringify(component)}`
statusMessage: `Island component not found: ${props.context.name}`
})
}

View File

@ -53,8 +53,8 @@ export function useCookie<T = string | null | undefined> (name: string, _opts?:
}
if (opts.watch) {
watch(cookie, (newVal, oldVal) => {
if (watchPaused || isEqual(newVal, oldVal)) { return }
watch(cookie, () => {
if (watchPaused) { return }
callback()
},
{ deep: opts.watch !== 'shallow' })

View File

@ -273,6 +273,12 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
// Init nitro
const nitro = await createNitro(nitroConfig)
// Set prerender-only options
nitro.options._config.storage ||= {}
nitro.options._config.storage['internal:nuxt:prerender'] = { driver: 'memory' }
nitro.options._config.storage['internal:nuxt:prerender:island'] = { driver: 'lruCache', max: 1000 }
nitro.options._config.storage['internal:nuxt:prerender:payload'] = { driver: 'lruCache', max: 1000 }
// Expose nitro to modules and kit
nuxt._nitro = nitro
await nuxt.callHook('nitro:init', nitro)

View File

@ -17,7 +17,7 @@ import { renderToString as _renderToString } from 'vue/server-renderer'
import { hash } from 'ohash'
import { renderSSRHead } from '@unhead/ssr'
import { defineRenderHandler, getRouteRules, useRuntimeConfig } from '#internal/nitro'
import { defineRenderHandler, getRouteRules, useRuntimeConfig, useStorage } from '#internal/nitro'
import { useNitroApp } from '#internal/nitro/app'
import type { Link, Script } from '@unhead/vue'
@ -155,9 +155,18 @@ const getSPARenderer = lazyCachedFunction(async () => {
}
})
const payloadCache = process.env.prerender ? useStorage('internal:nuxt:prerender:payload') : null
const islandCache = process.env.prerender ? useStorage('internal:nuxt:prerender:island') : null
const islandPropCache = process.env.prerender ? useStorage('internal:nuxt:prerender:island-props') : null
async function getIslandContext (event: H3Event): Promise<NuxtIslandContext> {
// TODO: Strict validation for url
const url = event.node.req.url?.substring('/__nuxt_island'.length + 1) || ''
let url = event.node.req.url || ''
if (process.env.prerender && event.node.req.url && await islandPropCache!.hasItem(event.node.req.url)) {
// rehydrate props from cache so we can rerender island if cache does not have it any more
url = await islandPropCache!.getItem(event.node.req.url) as string
}
url = url.substring('/__nuxt_island'.length + 1) || ''
const [componentName, hashId] = url.split('?')[0].split('_')
// TODO: Validate context
@ -175,8 +184,6 @@ async function getIslandContext (event: H3Event): Promise<NuxtIslandContext> {
return ctx
}
const PAYLOAD_CACHE = (process.env.NUXT_PAYLOAD_EXTRACTION && process.env.prerender) ? new Map() : null // TODO: Use LRU cache
const ISLAND_CACHE = (process.env.NUXT_COMPONENT_ISLANDS && process.env.prerender) ? new Map() : null // TODO: Use LRU cache
const PAYLOAD_URL_RE = process.env.NUXT_JSON_PAYLOADS ? /\/_payload(\.[a-zA-Z0-9]+)?.json(\?.*)?$/ : /\/_payload(\.[a-zA-Z0-9]+)?.js(\?.*)?$/
const ROOT_NODE_REGEX = new RegExp(`^<${appRootTag} id="${appRootId}">([\\s\\S]*)</${appRootTag}>$`)
@ -206,8 +213,8 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
? await getIslandContext(event)
: undefined
if (process.env.prerender && islandContext && ISLAND_CACHE!.has(event.node.req.url)) {
return ISLAND_CACHE!.get(event.node.req.url)
if (process.env.prerender && islandContext && event.node.req.url && await islandCache!.hasItem(event.node.req.url)) {
return islandCache!.getItem(event.node.req.url) as Promise<Partial<RenderResponse>>
}
// Request url
@ -218,8 +225,8 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
if (isRenderingPayload) {
url = url.substring(0, url.lastIndexOf('/')) || '/'
event.node.req.url = url
if (process.env.prerender && PAYLOAD_CACHE!.has(url)) {
return PAYLOAD_CACHE!.get(url)
if (process.env.prerender && await payloadCache!.hasItem(url)) {
return payloadCache!.getItem(url) as Promise<Partial<RenderResponse>>
}
}
@ -285,7 +292,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
if (isRenderingPayload) {
const response = renderPayloadResponse(ssrContext)
if (process.env.prerender) {
PAYLOAD_CACHE!.set(url, response)
await payloadCache!.setItem(url, response)
}
return response
}
@ -294,7 +301,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
// Hint nitro to prerender payload for this route
appendResponseHeader(event, 'x-nitro-prerender', joinURL(url, process.env.NUXT_JSON_PAYLOADS ? '_payload.json' : '_payload.js'))
// Use same ssr context to generate payload for this route
PAYLOAD_CACHE!.set(withoutTrailingSlash(url), renderPayloadResponse(ssrContext))
await payloadCache!.setItem(withoutTrailingSlash(url), renderPayloadResponse(ssrContext))
}
if (process.env.NUXT_INLINE_STYLES && !islandContext) {
@ -422,7 +429,8 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
}
} satisfies RenderResponse
if (process.env.prerender) {
ISLAND_CACHE!.set(`/__nuxt_island/${islandContext!.name}_${islandContext!.id}`, response)
await islandCache!.setItem(`/__nuxt_island/${islandContext!.name}_${islandContext!.id}`, response)
await islandPropCache!.setItem(`/__nuxt_island/${islandContext!.name}_${islandContext!.id}`, event.node.req.url!)
}
return response
}

View File

@ -54,6 +54,10 @@ export default defineNuxtModule({
addPlugin({ src: resolve(runtimeDir, 'plugins/vueuse-head-polyfill') })
}
if (nuxt.options.experimental.headCapoPlugin) {
addPlugin({ src: resolve(runtimeDir, 'plugins/capo') })
}
// Add library-specific plugin
addPlugin({ src: resolve(runtimeDir, 'plugins/unhead') })
}

View File

@ -0,0 +1,9 @@
import { CapoPlugin } from '@unhead/vue'
import { defineNuxtPlugin } from '#app/nuxt'
export default defineNuxtPlugin({
name: 'nuxt:head:capo',
setup (nuxtApp) {
nuxtApp.vueApp._context.provides.usehead.use(CapoPlugin({ track: true }))
}
})

View File

@ -30,17 +30,17 @@
"@types/file-loader": "5.0.1",
"@types/pug": "2.0.6",
"@types/sass-loader": "8.0.5",
"@unhead/schema": "1.1.35",
"@unhead/schema": "1.2.2",
"@vitejs/plugin-vue": "4.2.3",
"@vitejs/plugin-vue-jsx": "3.0.1",
"@vue/compiler-core": "3.3.4",
"esbuild-loader": "3.0.1",
"esbuild-loader": "3.1.0",
"h3": "1.7.1",
"ignore": "5.2.4",
"nitropack": "2.5.2",
"unbuild": "latest",
"unctx": "2.3.1",
"vite": "4.4.7",
"vite": "4.4.8",
"vue": "3.3.4",
"vue-bundle-renderer": "2.0.0",
"vue-loader": "17.2.2",
@ -57,7 +57,7 @@
"postcss-import-resolver": "^2.0.0",
"std-env": "^3.3.3",
"ufo": "^1.2.0",
"unimport": "^3.1.0",
"unimport": "^3.1.3",
"untyped": "^1.4.0"
},
"engines": {

View File

@ -19,7 +19,7 @@ export default defineUntypedSchema({
*/
reactivityTransform: false,
// TODO: Remove in v3.6 when nitro has support for mocking traced dependencies
// TODO: Remove in v3.8 when nitro has support for mocking traced dependencies
// https://github.com/unjs/nitro/issues/1118
/**
* Externalize `vue`, `@vue/*` and `vue-router` when building.
@ -207,6 +207,13 @@ export default defineUntypedSchema({
* @see https://github.com/parcel-bundler/watcher
* @type {'chokidar' | 'parcel' | 'chokidar-granular'}
*/
watcher: 'chokidar-granular'
watcher: 'chokidar-granular',
/**
* Add the capo.js head plugin in order to render tags in of the head in a more performant way.
*
* @see https://rviscomi.github.io/capo.js/user/rules/
*/
headCapoPlugin: false
}
})

View File

@ -54,10 +54,10 @@
"postcss-url": "^10.1.3",
"rollup-plugin-visualizer": "^5.9.2",
"std-env": "^3.3.3",
"strip-literal": "^1.0.1",
"strip-literal": "^1.3.0",
"ufo": "^1.2.0",
"unplugin": "^1.4.0",
"vite": "^4.4.7",
"vite": "^4.4.8",
"vite-node": "^0.33.0",
"vite-plugin-checker": "^0.6.1",
"vue-bundle-renderer": "^2.0.0"

View File

@ -26,7 +26,7 @@
"css-minimizer-webpack-plugin": "^5.0.1",
"cssnano": "^6.0.1",
"defu": "^6.1.2",
"esbuild-loader": "^3.0.1",
"esbuild-loader": "^3.1.0",
"escape-string-regexp": "^5.0.0",
"estree-walker": "^3.0.3",
"file-loader": "^6.2.0",

File diff suppressed because it is too large Load Diff

View File

@ -2,3 +2,4 @@ packages:
- "packages/**"
- "playground"
- "test/fixtures/*"
- ".website"

View File

@ -35,7 +35,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot('"64.4k"')
const modules = await analyzeSizes('node_modules/**/*', serverDir)
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"2330k"')
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"2346k"')
const packages = modules.files
.filter(m => m.endsWith('package.json'))
@ -95,7 +95,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot('"370k"')
const modules = await analyzeSizes('node_modules/**/*', serverDir)
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"591k"')
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"608k"')
const packages = modules.files
.filter(m => m.endsWith('package.json'))

View File

@ -28,14 +28,6 @@ export default defineNuxtConfig({
}
},
modules: [
function (_, nuxt) {
// TODO: remove in v3.7
if (process.env.TS_BASE_URL === 'without-base-url') {
nuxt.hook('prepare:types', ({ tsConfig }) => {
delete tsConfig.compilerOptions!.baseUrl
})
}
},
function () {
addTypeTemplate({
filename: 'test.d.ts',

View File

@ -187,7 +187,8 @@ export default defineNuxtConfig({
componentIslands: true,
reactivityTransform: true,
treeshakeClientOnly: true,
payloadExtraction: true
payloadExtraction: true,
headCapoPlugin: true
},
appConfig: {
fromNuxtConfig: true,