mirror of
https://github.com/nuxt/nuxt.git
synced 2025-02-16 13:48:13 +00:00
Merge remote-tracking branch 'origin/main' into feat/import-nuxt-package
This commit is contained in:
commit
e240d7a9dd
3
.github/workflows/autofix-docs.yml
vendored
3
.github/workflows/autofix-docs.yml
vendored
@ -5,6 +5,9 @@ on:
|
||||
paths:
|
||||
- "docs/**"
|
||||
- ".github/workflows/docs.yml"
|
||||
push:
|
||||
branches:
|
||||
- "renovate/**"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
2
.github/workflows/nuxt2-edge.yml
vendored
2
.github/workflows/nuxt2-edge.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
run: git fetch --depth=1 origin "+refs/tags/*:refs/tags/*"
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 14
|
||||
node-version: 16
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
- name: install
|
||||
run: yarn --check-files --frozen-lockfile --non-interactive
|
||||
|
@ -84,6 +84,13 @@ Whether to launch a server to respond to requests in the test suite.
|
||||
* Type: `boolean`
|
||||
* Default: `true`
|
||||
|
||||
#### `port`
|
||||
|
||||
If provided, set the launched test server port to the value.
|
||||
|
||||
* Type: `number | undefined`
|
||||
* Default: `undefined`
|
||||
|
||||
#### `build`
|
||||
|
||||
Whether to run a separate build step.
|
||||
|
@ -131,5 +131,5 @@ Name | Config File | How To
|
||||
| [ESLint](https://eslint.org) | `.eslintrc.js` | [More Info](https://eslint.org/docs/latest/user-guide/configuring/configuration-files)
|
||||
| [Prettier](https://prettier.io) | `.prettierrc.json` | [More Info](https://prettier.io/docs/en/configuration.html)
|
||||
| [Stylelint](https://stylelint.io) | `.stylelintrc.json` | [More Info](https://stylelint.io/user-guide/configure)
|
||||
| [TailwindCSS](https://tailwindcss.com) | `tailwind.config.js` | [More Info](https://tailwindcss.nuxt.dev/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/)
|
||||
|
@ -314,13 +314,13 @@ Be very careful before proxying headers to an external API and just include head
|
||||
If you want to pass on/proxy cookies in the other direction, from an internal request back to the client, you will need to handle this yourself.
|
||||
|
||||
```ts [composables/fetch.ts]
|
||||
import { appendHeader, H3Event } from 'h3'
|
||||
import { appendResponseHeader, H3Event } from 'h3'
|
||||
|
||||
export const fetchWithCookie = async (event: H3Event, url: string) => {
|
||||
const res = await $fetch.raw(url)
|
||||
const cookies = (res.headers.get('set-cookie') || '').split(',')
|
||||
for (const cookie of cookies) {
|
||||
appendHeader(event, 'set-cookie', cookie)
|
||||
appendResponseHeader(event, 'set-cookie', cookie)
|
||||
}
|
||||
return res._data
|
||||
}
|
||||
|
@ -67,6 +67,10 @@ When you are ready to remove the error page, you can call the `clearError` helpe
|
||||
Make sure to check before using anything dependent on Nuxt plugins, such as `$route` or `useRouter`, as if a plugin threw an error, then it won't be re-run until you clear the error.
|
||||
::
|
||||
|
||||
::alert{type="warning"}
|
||||
If you are running on Node 16 and you set any cookies when rendering your error page, they will [overwrite cookies previously set](https://github.com/nuxt/nuxt/pull/20585). We recommend using a newer version of Node as Node 16 will reach end-of-life in September 2023.
|
||||
::
|
||||
|
||||
### Example
|
||||
|
||||
```vue [error.vue]
|
||||
|
@ -114,7 +114,7 @@ const { data, pending, error, refresh } = await useFetch('/api/auth/login', {
|
||||
},
|
||||
onResponse({ request, response, options }) {
|
||||
// Process the response data
|
||||
return response._data
|
||||
localStorage.setItem('token', response._data.token)
|
||||
},
|
||||
onResponseError({ request, response, options }) {
|
||||
// Handle the response errors
|
||||
|
@ -15,12 +15,12 @@ export function useCustomFetch<T> (url: string, options: UseFetchOptions<T> = {}
|
||||
? { Authorization: `Bearer ${userAuth.value}` }
|
||||
: {},
|
||||
|
||||
onResponse (__ctx) {
|
||||
// return new myBusinessResponse(response._data)
|
||||
onResponse (_ctx) {
|
||||
// _ctx.response._data = new myBusinessResponse(_ctx.response._data)
|
||||
},
|
||||
|
||||
onResponseError (__ctx) {
|
||||
// return new myBusinessError(error)
|
||||
onResponseError (_ctx) {
|
||||
// throw new myBusinessError()
|
||||
}
|
||||
}
|
||||
|
||||
|
12
package.json
12
package.json
@ -36,7 +36,7 @@
|
||||
"nuxt": "workspace:*",
|
||||
"nuxt3": "workspace:nuxt@*",
|
||||
"unbuild": "^1.2.1",
|
||||
"vite": "^4.3.3",
|
||||
"vite": "^4.3.4",
|
||||
"vue": "3.2.47",
|
||||
"magic-string": "^0.30.0"
|
||||
},
|
||||
@ -46,7 +46,7 @@
|
||||
"@nuxt/webpack-builder": "workspace:*",
|
||||
"@nuxtjs/eslint-config-typescript": "^12.0.0",
|
||||
"@types/crawler": "^1.2.2",
|
||||
"@types/node": "^18.16.2",
|
||||
"@types/node": "^18.16.3",
|
||||
"@types/semver": "^7.3.13",
|
||||
"case-police": "^0.5.14",
|
||||
"changelogen": "^0.5.3",
|
||||
@ -73,13 +73,13 @@
|
||||
"typescript": "^5.0.4",
|
||||
"ufo": "^1.1.1",
|
||||
"unbuild": "^1.2.1",
|
||||
"vite": "^4.3.3",
|
||||
"vite": "^4.3.4",
|
||||
"vitest": "^0.30.1",
|
||||
"vue": "3.2.47",
|
||||
"vue-eslint-parser": "^9.1.1",
|
||||
"vue-tsc": "^1.6.1"
|
||||
"vue-eslint-parser": "^9.2.0",
|
||||
"vue-tsc": "^1.6.3"
|
||||
},
|
||||
"packageManager": "pnpm@8.3.1",
|
||||
"packageManager": "pnpm@8.4.0",
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.10.0"
|
||||
}
|
||||
|
@ -32,7 +32,7 @@
|
||||
"lodash.template": "^4.5.0",
|
||||
"mlly": "^1.2.0",
|
||||
"pathe": "^1.1.0",
|
||||
"pkg-types": "^1.0.2",
|
||||
"pkg-types": "^1.0.3",
|
||||
"scule": "^1.0.0",
|
||||
"semver": "^7.5.0",
|
||||
"unctx": "^2.3.0",
|
||||
@ -45,7 +45,7 @@
|
||||
"@types/semver": "^7.3.13",
|
||||
"nitropack": "^2.3.3",
|
||||
"unbuild": "latest",
|
||||
"vite": "^4.3.3",
|
||||
"vite": "^4.3.4",
|
||||
"vitest": "^0.30.1",
|
||||
"webpack": "^5.81.0"
|
||||
},
|
||||
|
@ -106,9 +106,11 @@ export function extendViteConfig (
|
||||
/**
|
||||
* Append webpack plugin to the config.
|
||||
*/
|
||||
export function addWebpackPlugin (plugin: WebpackPluginInstance | WebpackPluginInstance[], options?: ExtendWebpackConfigOptions) {
|
||||
const method: 'push' | 'unshift' = options?.prepend ? 'unshift' : 'push'
|
||||
export function addWebpackPlugin (pluginOrGetter: WebpackPluginInstance | WebpackPluginInstance[] | (() => WebpackPluginInstance | WebpackPluginInstance[]), options?: ExtendWebpackConfigOptions) {
|
||||
extendWebpackConfig((config) => {
|
||||
const method: 'push' | 'unshift' = options?.prepend ? 'unshift' : 'push'
|
||||
const plugin = typeof pluginOrGetter === 'function' ? pluginOrGetter() : pluginOrGetter
|
||||
|
||||
config.plugins = config.plugins || []
|
||||
if (Array.isArray(plugin)) {
|
||||
config.plugins[method](...plugin)
|
||||
@ -121,9 +123,11 @@ export function addWebpackPlugin (plugin: WebpackPluginInstance | WebpackPluginI
|
||||
/**
|
||||
* Append Vite plugin to the config.
|
||||
*/
|
||||
export function addVitePlugin (plugin: VitePlugin | VitePlugin[], options?: ExtendViteConfigOptions) {
|
||||
const method: 'push' | 'unshift' = options?.prepend ? 'unshift' : 'push'
|
||||
export function addVitePlugin (pluginOrGetter: VitePlugin | VitePlugin[] | (() => VitePlugin | VitePlugin[]), options?: ExtendViteConfigOptions) {
|
||||
extendViteConfig((config) => {
|
||||
const method: 'push' | 'unshift' = options?.prepend ? 'unshift' : 'push'
|
||||
const plugin = typeof pluginOrGetter === 'function' ? pluginOrGetter() : pluginOrGetter
|
||||
|
||||
config.plugins = config.plugins || []
|
||||
if (Array.isArray(plugin)) {
|
||||
config.plugins[method](...plugin)
|
||||
|
@ -44,7 +44,7 @@
|
||||
"ohash": "^1.1.2",
|
||||
"pathe": "^1.1.0",
|
||||
"perfect-debounce": "^0.1.3",
|
||||
"pkg-types": "^1.0.2",
|
||||
"pkg-types": "^1.0.3",
|
||||
"scule": "^1.0.0",
|
||||
"semver": "^7.5.0",
|
||||
"ufo": "^1.1.1",
|
||||
|
@ -2,7 +2,7 @@ import { promises as fsp } from 'node:fs'
|
||||
import { join, resolve } from 'pathe'
|
||||
import { createApp, eventHandler, lazyEventHandler, toNodeListener } from 'h3'
|
||||
import { listen } from 'listhen'
|
||||
import { writeTypes } from '../utils/prepare'
|
||||
import type { NuxtAnalyzeMeta } from '@nuxt/schema'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { clearDir } from '../utils/fs'
|
||||
import { overrideEnv } from '../utils/env'
|
||||
@ -11,62 +11,99 @@ import { defineNuxtCommand } from './index'
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'analyze',
|
||||
usage: 'npx nuxi analyze [--log-level] [rootDir]',
|
||||
usage: 'npx nuxi analyze [--log-level] [--name] [--no-serve] [rootDir]',
|
||||
description: 'Build nuxt and analyze production bundle (experimental)'
|
||||
},
|
||||
async invoke (args) {
|
||||
overrideEnv('production')
|
||||
|
||||
const name = args.name || 'default'
|
||||
const slug = name.trim().replace(/[^a-z0-9_-]/gi, '_')
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
const statsDir = join(rootDir, '.nuxt/stats')
|
||||
|
||||
let analyzeDir = join(rootDir, '.nuxt/analyze', slug)
|
||||
let buildDir = join(analyzeDir, '.nuxt')
|
||||
let outDir = join(analyzeDir, '.output')
|
||||
|
||||
const startTime = Date.now()
|
||||
|
||||
const { loadNuxt, buildNuxt } = await loadKit(rootDir)
|
||||
|
||||
const nuxt = await loadNuxt({
|
||||
rootDir,
|
||||
overrides: {
|
||||
build: { analyze: true },
|
||||
build: {
|
||||
analyze: true
|
||||
},
|
||||
analyzeDir,
|
||||
buildDir,
|
||||
nitro: {
|
||||
output: {
|
||||
dir: outDir
|
||||
}
|
||||
},
|
||||
logLevel: args['log-level']
|
||||
}
|
||||
})
|
||||
|
||||
await clearDir(nuxt.options.buildDir)
|
||||
await writeTypes(nuxt)
|
||||
analyzeDir = nuxt.options.analyzeDir
|
||||
buildDir = nuxt.options.buildDir
|
||||
outDir = nuxt.options.nitro.output?.dir || outDir
|
||||
|
||||
await clearDir(analyzeDir)
|
||||
await buildNuxt(nuxt)
|
||||
|
||||
const app = createApp()
|
||||
const endTime = Date.now()
|
||||
|
||||
const serveFile = (filePath: string) => lazyEventHandler(async () => {
|
||||
const contents = await fsp.readFile(filePath, 'utf-8')
|
||||
return eventHandler((event) => { event.node.res.end(contents) })
|
||||
})
|
||||
const meta: NuxtAnalyzeMeta = {
|
||||
name,
|
||||
slug,
|
||||
startTime,
|
||||
endTime,
|
||||
analyzeDir,
|
||||
buildDir,
|
||||
outDir
|
||||
}
|
||||
|
||||
await nuxt.callHook('build:analyze:done', meta)
|
||||
await fsp.writeFile(join(analyzeDir, 'meta.json'), JSON.stringify(meta, null, 2), 'utf-8')
|
||||
|
||||
console.info('Analyze results are available at: `' + analyzeDir + '`')
|
||||
console.warn('Do not deploy analyze results! Use `nuxi build` before deploying.')
|
||||
|
||||
console.info('Starting stats server...')
|
||||
if (args.serve !== false && !process.env.CI) {
|
||||
const app = createApp()
|
||||
|
||||
app.use('/client', serveFile(join(statsDir, 'client.html')))
|
||||
app.use('/nitro', serveFile(join(statsDir, 'nitro.html')))
|
||||
app.use(eventHandler(() => `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Nuxt Bundle Stats (experimental)</title>
|
||||
</head>
|
||||
<h1>Nuxt Bundle Stats (experimental)</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/nitro">Nitro server bundle stats</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/client">Client bundle stats</a>
|
||||
</li>
|
||||
</ul>
|
||||
</html>
|
||||
`))
|
||||
const serveFile = (filePath: string) => lazyEventHandler(async () => {
|
||||
const contents = await fsp.readFile(filePath, 'utf-8')
|
||||
return eventHandler((event) => { event.node.res.end(contents) })
|
||||
})
|
||||
|
||||
await listen(toNodeListener(app))
|
||||
console.info('Starting stats server...')
|
||||
|
||||
return 'wait' as const
|
||||
app.use('/client', serveFile(join(analyzeDir, 'client.html')))
|
||||
app.use('/nitro', serveFile(join(analyzeDir, 'nitro.html')))
|
||||
app.use(eventHandler(() => `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Nuxt Bundle Stats (experimental)</title>
|
||||
</head>
|
||||
<h1>Nuxt Bundle Stats (experimental)</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/nitro">Nitro server bundle stats</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/client">Client bundle stats</a>
|
||||
</li>
|
||||
</ul>
|
||||
</html>
|
||||
`))
|
||||
|
||||
await listen(toNodeListener(app))
|
||||
|
||||
return 'wait' as const
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -2,7 +2,7 @@ import { relative, resolve } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
import { writeTypes } from '../utils/prepare'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { clearDir } from '../utils/fs'
|
||||
import { clearBuildDir } from '../utils/fs'
|
||||
import { overrideEnv } from '../utils/env'
|
||||
import { showVersions } from '../utils/banner'
|
||||
import { defineNuxtCommand } from './index'
|
||||
@ -36,7 +36,7 @@ export default defineNuxtCommand({
|
||||
// Use ? for backward compatibility for Nuxt <= RC.10
|
||||
const nitro = useNitro?.()
|
||||
|
||||
await clearDir(nuxt.options.buildDir)
|
||||
await clearBuildDir(nuxt.options.buildDir)
|
||||
|
||||
await writeTypes(nuxt)
|
||||
|
||||
|
@ -12,7 +12,8 @@ import { writeTypes } from '../utils/prepare'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { importModule } from '../utils/esm'
|
||||
import { overrideEnv } from '../utils/env'
|
||||
import { cleanupNuxtDirs, loadNuxtManifest, writeNuxtManifest } from '../utils/nuxt'
|
||||
import { loadNuxtManifest, writeNuxtManifest } from '../utils/nuxt'
|
||||
import { clearBuildDir } from '../utils/fs'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
@ -110,7 +111,7 @@ export default defineNuxtCommand({
|
||||
const previousManifest = await loadNuxtManifest(currentNuxt.options.buildDir)
|
||||
const newManifest = await writeNuxtManifest(currentNuxt)
|
||||
if (previousManifest && newManifest && previousManifest._hash !== newManifest._hash) {
|
||||
await cleanupNuxtDirs(currentNuxt.options.rootDir)
|
||||
await clearBuildDir(currentNuxt.options.buildDir)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { relative, resolve } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
import { clearDir } from '../utils/fs'
|
||||
import { clearBuildDir } from '../utils/fs'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { writeTypes } from '../utils/prepare'
|
||||
import { defineNuxtCommand } from './index'
|
||||
@ -23,7 +23,7 @@ export default defineNuxtCommand({
|
||||
logLevel: args['log-level']
|
||||
}
|
||||
})
|
||||
await clearDir(nuxt.options.buildDir)
|
||||
await clearBuildDir(nuxt.options.buildDir)
|
||||
|
||||
await buildNuxt(nuxt)
|
||||
await writeTypes(nuxt)
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { promises as fsp } from 'node:fs'
|
||||
import { dirname } from 'pathe'
|
||||
import { existsSync, promises as fsp } from 'node:fs'
|
||||
import { dirname, join } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
|
||||
// Check if a file exists
|
||||
@ -12,11 +12,24 @@ export async function exists (path: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function clearDir (path: string) {
|
||||
await fsp.rm(path, { recursive: true, force: true })
|
||||
export async function clearDir (path: string, exclude?: string[]) {
|
||||
if (!exclude) {
|
||||
await fsp.rm(path, { recursive: true, force: true })
|
||||
} else if (existsSync(path)) {
|
||||
const files = await fsp.readdir(path)
|
||||
await Promise.all(files.map(async (name) => {
|
||||
if (!exclude.includes(name)) {
|
||||
await fsp.rm(join(path, name), { recursive: true, force: true })
|
||||
}
|
||||
}))
|
||||
}
|
||||
await fsp.mkdir(path, { recursive: true })
|
||||
}
|
||||
|
||||
export function clearBuildDir (path: string) {
|
||||
return clearDir(path, ['cache', 'analyze'])
|
||||
}
|
||||
|
||||
export async function rmRecursive (paths: string[]) {
|
||||
await Promise.all(paths.filter(p => typeof p === 'string').map(async (path) => {
|
||||
consola.debug('Removing recursive path', path)
|
||||
|
3
packages/nuxt/config.d.ts
vendored
3
packages/nuxt/config.d.ts
vendored
@ -1,4 +1,5 @@
|
||||
import type { NuxtConfig } from 'nuxt/schema'
|
||||
import type { DefineConfig, InputConfig, UserInputConfig, ConfigLayerMeta } from 'c12'
|
||||
export { NuxtConfig } from 'nuxt/schema'
|
||||
|
||||
export declare function defineNuxtConfig(config: NuxtConfig): NuxtConfig
|
||||
export declare const defineNuxtConfig: DefineConfig<NuxtConfig, ConfigLayerMeta>
|
||||
|
@ -52,7 +52,7 @@
|
||||
"prepack": "unbuild"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxt/devalue": "^2.0.0",
|
||||
"@nuxt/devalue": "^2.0.2",
|
||||
"@nuxt/kit": "workspace:../kit",
|
||||
"@nuxt/schema": "workspace:../schema",
|
||||
"@nuxt/telemetry": "^2.2.0",
|
||||
@ -61,6 +61,7 @@
|
||||
"@unhead/ssr": "^1.1.26",
|
||||
"@unhead/vue": "^1.1.26",
|
||||
"@vue/shared": "^3.2.47",
|
||||
"c12": "^1.4.1",
|
||||
"chokidar": "^3.5.3",
|
||||
"cookie-es": "^0.5.0",
|
||||
"defu": "^6.1.2",
|
||||
@ -107,7 +108,7 @@
|
||||
"@vitejs/plugin-vue": "^4.2.1",
|
||||
"acorn": "^8.8.2",
|
||||
"unbuild": "latest",
|
||||
"vite": "^4.3.3",
|
||||
"vite": "^4.3.4",
|
||||
"vitest": "^0.30.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@ -2,7 +2,7 @@ import type { RendererNode } from 'vue'
|
||||
import { computed, createStaticVNode, defineComponent, getCurrentInstance, h, ref, watch } from 'vue'
|
||||
import { debounce } from 'perfect-debounce'
|
||||
import { hash } from 'ohash'
|
||||
import { appendHeader } from 'h3'
|
||||
import { appendResponseHeader } from 'h3'
|
||||
import { useHead } from '@unhead/vue'
|
||||
|
||||
// eslint-disable-next-line import/no-restricted-paths
|
||||
@ -42,7 +42,7 @@ export default defineComponent({
|
||||
const url = `/__nuxt_island/${props.name}:${hashId.value}`
|
||||
if (process.server && process.env.prerender) {
|
||||
// Hint to Nitro to prerender the island component
|
||||
appendHeader(event, 'x-nitro-prerender', url)
|
||||
appendResponseHeader(event, 'x-nitro-prerender', url)
|
||||
}
|
||||
// TODO: Validate response
|
||||
return $fetch<NuxtIslandResponse>(url, {
|
||||
|
@ -212,7 +212,7 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
|
||||
onNuxtReady(() => {
|
||||
idleId = requestIdleCallback(() => {
|
||||
if (el?.value?.tagName) {
|
||||
unobserve = observer!.observe(el.value, async () => {
|
||||
unobserve = observer!.observe(el.value as Element, async () => {
|
||||
unobserve?.()
|
||||
unobserve = null
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
<script setup>
|
||||
import { defineAsyncComponent, onErrorCaptured, onServerPrefetch, provide } from 'vue'
|
||||
import { callWithNuxt, useNuxtApp } from '#app/nuxt'
|
||||
import { useNuxtApp } from '#app/nuxt'
|
||||
import { isNuxtError, showError, useError } from '#app/composables/error'
|
||||
import { useRoute } from '#app/composables/router'
|
||||
import AppComponent from '#build/app-component.mjs'
|
||||
@ -40,7 +40,7 @@ const error = useError()
|
||||
onErrorCaptured((err, target, info) => {
|
||||
nuxtApp.hooks.callHook('vue:error', err, target, info).catch(hookError => console.error('[nuxt] Error in `vue:error` hook', hookError))
|
||||
if (process.server || (isNuxtError(err) && (err.fatal || err.unhandled))) {
|
||||
const p = callWithNuxt(nuxtApp, showError, [err])
|
||||
const p = nuxtApp.runWithContext(() => showError(err))
|
||||
onServerPrefetch(() => p)
|
||||
return false // suppress error from breaking render
|
||||
}
|
||||
|
@ -1,22 +1,16 @@
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { h } from 'vue'
|
||||
import type { Component } from 'vue'
|
||||
// eslint-disable-next-line
|
||||
import { isString, isPromise, isArray } from '@vue/shared'
|
||||
|
||||
const Fragment = defineComponent({
|
||||
name: 'FragmentWrapper',
|
||||
setup (_props, { slots }) {
|
||||
return () => slots.default?.()
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Internal utility
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
export const _wrapIf = (component: Component, props: any, slots: any) => {
|
||||
return { default: () => props ? h(component, props === true ? {} : props, slots) : h(Fragment, {}, slots) }
|
||||
props = props === true ? {} : props
|
||||
return { default: () => props ? h(component, props, slots) : slots.default?.() }
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
|
@ -2,7 +2,7 @@ import { getCurrentInstance, reactive, toRefs } from 'vue'
|
||||
import type { DefineComponent, defineComponent } from 'vue'
|
||||
import { useHead } from '@unhead/vue'
|
||||
import type { NuxtApp } from '../nuxt'
|
||||
import { callWithNuxt, useNuxtApp } from '../nuxt'
|
||||
import { useNuxtApp } from '../nuxt'
|
||||
import { useAsyncData } from './asyncData'
|
||||
import { useRoute } from './router'
|
||||
import { createError } from './error'
|
||||
@ -10,12 +10,12 @@ import { createError } from './error'
|
||||
export const NuxtComponentIndicator = '__nuxt_component'
|
||||
|
||||
async function runLegacyAsyncData (res: Record<string, any> | Promise<Record<string, any>>, fn: (nuxtApp: NuxtApp) => Promise<Record<string, any>>) {
|
||||
const nuxt = useNuxtApp()
|
||||
const nuxtApp = useNuxtApp()
|
||||
const route = useRoute()
|
||||
const vm = getCurrentInstance()!
|
||||
const { fetchKey } = vm.proxy!.$options
|
||||
const key = typeof fetchKey === 'function' ? fetchKey(() => '') : fetchKey || route.fullPath
|
||||
const { data, error } = await useAsyncData(`options:asyncdata:${key}`, () => callWithNuxt(nuxt, fn, [nuxt]))
|
||||
const { data, error } = await useAsyncData(`options:asyncdata:${key}`, () => nuxtApp.runWithContext(() => fn(nuxtApp)))
|
||||
if (error.value) {
|
||||
throw createError(error.value)
|
||||
}
|
||||
@ -43,7 +43,7 @@ export const defineNuxtComponent: typeof defineComponent =
|
||||
...options,
|
||||
setup (props, ctx) {
|
||||
const nuxtApp = useNuxtApp()
|
||||
const res = setup ? Promise.resolve(callWithNuxt(nuxtApp, setup, [props, ctx])).then(r => r || {}) : {}
|
||||
const res = setup ? Promise.resolve(nuxtApp.runWithContext(() => setup(props, ctx))).then(r => r || {}) : {}
|
||||
|
||||
const promises: Promise<any>[] = []
|
||||
if (options.asyncData) {
|
||||
|
@ -2,7 +2,7 @@ import type { Ref } from 'vue'
|
||||
import { ref, watch } from 'vue'
|
||||
import type { CookieParseOptions, CookieSerializeOptions } from 'cookie-es'
|
||||
import { parse, serialize } from 'cookie-es'
|
||||
import { appendHeader } from 'h3'
|
||||
import { appendResponseHeader } from 'h3'
|
||||
import type { H3Event } from 'h3'
|
||||
import destr from 'destr'
|
||||
import { isEqual } from 'ohash'
|
||||
@ -48,11 +48,10 @@ export function useCookie<T = string | null | undefined> (name: string, _opts?:
|
||||
}
|
||||
}
|
||||
const unhook = nuxtApp.hooks.hookOnce('app:rendered', writeFinalCookieValue)
|
||||
const writeAndUnhook = () => {
|
||||
nuxtApp.hooks.hookOnce('app:error', () => {
|
||||
unhook() // don't write cookie subsequently when app:rendered is called
|
||||
return writeFinalCookieValue()
|
||||
}
|
||||
nuxtApp.hooks.hookOnce('app:error', writeAndUnhook)
|
||||
})
|
||||
}
|
||||
|
||||
return cookie as CookieRef<T>
|
||||
@ -82,6 +81,6 @@ function writeClientCookie (name: string, value: any, opts: CookieSerializeOptio
|
||||
function writeServerCookie (event: H3Event, name: string, value: any, opts: CookieSerializeOptions = {}) {
|
||||
if (event) {
|
||||
// TODO: Try to smart join with existing Set-Cookie headers
|
||||
appendHeader(event, 'Set-Cookie', serializeCookie(name, value, opts))
|
||||
appendResponseHeader(event, 'Set-Cookie', serializeCookie(name, value, opts))
|
||||
}
|
||||
}
|
||||
|
@ -13,8 +13,10 @@ export const showError = (_err: string | Error | Partial<NuxtError>) => {
|
||||
|
||||
try {
|
||||
const nuxtApp = useNuxtApp()
|
||||
nuxtApp.callHook('app:error', err)
|
||||
const error = useError()
|
||||
if (process.client) {
|
||||
nuxtApp.hooks.callHook('app:error', err)
|
||||
}
|
||||
error.value = error.value || err
|
||||
} catch {
|
||||
throw err
|
||||
|
@ -76,6 +76,8 @@ interface _NuxtApp {
|
||||
hook: _NuxtApp['hooks']['hook']
|
||||
callHook: _NuxtApp['hooks']['callHook']
|
||||
|
||||
runWithContext: <T extends () => any>(fn: T) => ReturnType<T> | Promise<Awaited<ReturnType<T>>>
|
||||
|
||||
[key: string]: unknown
|
||||
|
||||
/** @internal */
|
||||
@ -193,6 +195,7 @@ export function createNuxtApp (options: CreateOptions) {
|
||||
static: {
|
||||
data: {}
|
||||
},
|
||||
runWithContext: (fn: any) => callWithNuxt(nuxtApp, fn),
|
||||
isHydrating: process.client,
|
||||
deferHydration () {
|
||||
if (!nuxtApp.isHydrating) { return () => {} }
|
||||
@ -224,7 +227,7 @@ export function createNuxtApp (options: CreateOptions) {
|
||||
if (process.server) {
|
||||
async function contextCaller (hooks: HookCallback[], args: any[]) {
|
||||
for (const hook of hooks) {
|
||||
await nuxtAppCtx.callAsync(nuxtApp, () => hook(...args))
|
||||
await nuxtApp.runWithContext(() => hook(...args))
|
||||
}
|
||||
}
|
||||
// Patch callHook to preserve NuxtApp context on server
|
||||
@ -288,7 +291,7 @@ export function createNuxtApp (options: CreateOptions) {
|
||||
|
||||
export async function applyPlugin (nuxtApp: NuxtApp, plugin: Plugin) {
|
||||
if (typeof plugin !== 'function') { return }
|
||||
const { provide } = await callWithNuxt(nuxtApp, plugin, [nuxtApp]) || {}
|
||||
const { provide } = await nuxtApp.runWithContext(() => plugin(nuxtApp)) || {}
|
||||
if (provide && typeof provide === 'object') {
|
||||
for (const key in provide) {
|
||||
nuxtApp.provide(key, provide[key])
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { reactive, ref, shallowReactive, shallowRef } from 'vue'
|
||||
import { definePayloadReviver, getNuxtClientPayload } from '#app/composables/payload'
|
||||
import { createError } from '#app/composables/error'
|
||||
import { callWithNuxt, defineNuxtPlugin } from '#app/nuxt'
|
||||
import { defineNuxtPlugin } from '#app/nuxt'
|
||||
|
||||
const revivers = {
|
||||
NuxtError: (data: any) => createError(data),
|
||||
@ -20,7 +20,7 @@ export default defineNuxtPlugin({
|
||||
for (const reviver in revivers) {
|
||||
definePayloadReviver(reviver, revivers[reviver as keyof typeof revivers])
|
||||
}
|
||||
Object.assign(nuxtApp.payload, await callWithNuxt(nuxtApp, getNuxtClientPayload, []))
|
||||
Object.assign(nuxtApp.payload, await nuxtApp.runWithContext(getNuxtClientPayload))
|
||||
// For backwards compatibility - TODO: remove later
|
||||
window.__NUXT__ = nuxtApp.payload
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { h, isReadonly, reactive } from 'vue'
|
||||
import { isEqual, joinURL, parseQuery, parseURL, stringifyParsedURL, stringifyQuery, withoutBase } from 'ufo'
|
||||
import { createError } from 'h3'
|
||||
import { callWithNuxt, defineNuxtPlugin, useRuntimeConfig } from '../nuxt'
|
||||
import { defineNuxtPlugin, useRuntimeConfig } from '../nuxt'
|
||||
import { clearError, showError } from '../composables/error'
|
||||
import { navigateTo } from '../composables/router'
|
||||
import { useState } from '../composables/state'
|
||||
@ -142,7 +142,7 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>({
|
||||
window.history[replace ? 'replaceState' : 'pushState']({}, '', joinURL(baseURL, to.fullPath))
|
||||
if (!nuxtApp.isHydrating) {
|
||||
// Clear any existing errors
|
||||
await callWithNuxt(nuxtApp, clearError)
|
||||
await nuxtApp.runWithContext(clearError)
|
||||
}
|
||||
}
|
||||
// Run afterEach hooks
|
||||
@ -238,7 +238,7 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>({
|
||||
const middlewareEntries = new Set<RouteGuard>([...globalMiddleware, ...nuxtApp._middleware.global])
|
||||
|
||||
for (const middleware of middlewareEntries) {
|
||||
const result = await callWithNuxt(nuxtApp, middleware, [to, from])
|
||||
const result = await nuxtApp.runWithContext(() => middleware(to, from))
|
||||
if (process.server) {
|
||||
if (result === false || result instanceof Error) {
|
||||
const error = result || createError({
|
||||
@ -246,7 +246,7 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>({
|
||||
statusMessage: `Page Not Found: ${initialURL}`
|
||||
})
|
||||
delete nuxtApp._processingMiddleware
|
||||
return callWithNuxt(nuxtApp, showError, [error])
|
||||
return nuxtApp.runWithContext(() => showError(error))
|
||||
}
|
||||
}
|
||||
if (result || result === false) { return result }
|
||||
@ -257,7 +257,7 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>({
|
||||
|
||||
await router.replace(initialURL)
|
||||
if (!isEqual(route.fullPath, initialURL)) {
|
||||
await callWithNuxt(nuxtApp, navigateTo, [route.fullPath])
|
||||
await nuxtApp.runWithContext(() => navigateTo(route.fullPath))
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -113,14 +113,14 @@ export default defineNuxtModule<ComponentsOptions>({
|
||||
})
|
||||
|
||||
// components.d.ts
|
||||
addTemplate({ ...componentsTypeTemplate, options: { getComponents } })
|
||||
addTemplate({ ...componentsTypeTemplate })
|
||||
// components.plugin.mjs
|
||||
addPluginTemplate({ ...componentsPluginTemplate, options: { getComponents } } as any)
|
||||
addPluginTemplate({ ...componentsPluginTemplate } as any)
|
||||
// component-names.mjs
|
||||
addTemplate({ ...componentNamesTemplate, options: { getComponents, mode: 'all' } })
|
||||
addTemplate({ ...componentNamesTemplate, options: { mode: 'all' } })
|
||||
// components.islands.mjs
|
||||
if (nuxt.options.experimental.componentIslands) {
|
||||
addTemplate({ ...componentsIslandsTemplate, filename: 'components.islands.mjs', options: { getComponents } })
|
||||
addTemplate({ ...componentsIslandsTemplate, filename: 'components.islands.mjs' })
|
||||
} else {
|
||||
addTemplate({ filename: 'components.islands.mjs', getContents: () => 'export default {}' })
|
||||
}
|
||||
@ -158,7 +158,7 @@ export default defineNuxtModule<ComponentsOptions>({
|
||||
})
|
||||
|
||||
// Scan components and add to plugin
|
||||
nuxt.hook('app:templates', async () => {
|
||||
nuxt.hook('app:templates', async (app) => {
|
||||
const newComponents = await scanComponents(componentDirs, nuxt.options.srcDir!)
|
||||
await nuxt.callHook('components:extend', newComponents)
|
||||
// add server placeholder for .client components server side. issue: #7085
|
||||
@ -173,6 +173,7 @@ export default defineNuxtModule<ComponentsOptions>({
|
||||
}
|
||||
}
|
||||
context.components = newComponents
|
||||
app.components = newComponents
|
||||
})
|
||||
|
||||
nuxt.hook('prepare:types', ({ references, tsConfig }) => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Fragment, computed, createStaticVNode, createVNode, defineComponent, h, ref, watch } from 'vue'
|
||||
import { debounce } from 'perfect-debounce'
|
||||
import { hash } from 'ohash'
|
||||
import { appendHeader } from 'h3'
|
||||
import { appendResponseHeader } from 'h3'
|
||||
|
||||
import { useHead } from '@unhead/vue'
|
||||
import type { NuxtIslandResponse } from '../../core/runtime/nitro/renderer'
|
||||
@ -51,7 +51,7 @@ const NuxtServerComponent = defineComponent({
|
||||
const url = `/__nuxt_island/${props.name}:${hashId.value}`
|
||||
if (process.server && process.env.prerender) {
|
||||
// Hint to Nitro to prerender the island component
|
||||
appendHeader(event, 'x-nitro-prerender', url)
|
||||
appendResponseHeader(event, 'x-nitro-prerender', url)
|
||||
}
|
||||
// TODO: Validate response
|
||||
return $fetch<NuxtIslandResponse>(url, {
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { isAbsolute, relative } from 'pathe'
|
||||
import { genDynamicImport } from 'knitwork'
|
||||
import type { Component, Nuxt, NuxtPluginTemplate, NuxtTemplate } from 'nuxt/schema'
|
||||
import type { Component, Nuxt, NuxtApp, NuxtPluginTemplate, NuxtTemplate } from 'nuxt/schema'
|
||||
|
||||
export interface ComponentsTemplateContext {
|
||||
app: NuxtApp
|
||||
nuxt: Nuxt
|
||||
options: {
|
||||
getComponents: (mode?: 'client' | 'server' | 'all') => Component[]
|
||||
@ -34,8 +35,8 @@ export default defineNuxtPlugin({
|
||||
|
||||
export const componentsPluginTemplate: NuxtPluginTemplate<ComponentsTemplateContext> = {
|
||||
filename: 'components.plugin.mjs',
|
||||
getContents ({ options }) {
|
||||
const globalComponents = options.getComponents().filter(c => c.global)
|
||||
getContents ({ app }) {
|
||||
const globalComponents = app.components.filter(c => c.global)
|
||||
if (!globalComponents.length) { return emptyComponentsPlugin }
|
||||
|
||||
return `import { defineNuxtPlugin } from '#app/nuxt'
|
||||
@ -59,15 +60,15 @@ export default defineNuxtPlugin({
|
||||
|
||||
export const componentNamesTemplate: NuxtPluginTemplate<ComponentsTemplateContext> = {
|
||||
filename: 'component-names.mjs',
|
||||
getContents ({ options }) {
|
||||
return `export const componentNames = ${JSON.stringify(options.getComponents().filter(c => !c.island).map(c => c.pascalName))}`
|
||||
getContents ({ app }) {
|
||||
return `export const componentNames = ${JSON.stringify(app.components.filter(c => !c.island).map(c => c.pascalName))}`
|
||||
}
|
||||
}
|
||||
|
||||
export const componentsIslandsTemplate: NuxtTemplate<ComponentsTemplateContext> = {
|
||||
// components.islands.mjs'
|
||||
getContents ({ options }) {
|
||||
const components = options.getComponents()
|
||||
getContents ({ app }) {
|
||||
const components = app.components
|
||||
const islands = components.filter(component =>
|
||||
component.island ||
|
||||
// .server components without a corresponding .client component will need to be rendered as an island
|
||||
@ -85,9 +86,9 @@ export const componentsIslandsTemplate: NuxtTemplate<ComponentsTemplateContext>
|
||||
|
||||
export const componentsTypeTemplate: NuxtTemplate<ComponentsTemplateContext> = {
|
||||
filename: 'components.d.ts',
|
||||
getContents: ({ options, nuxt }) => {
|
||||
getContents: ({ app, nuxt }) => {
|
||||
const buildDir = nuxt.options.buildDir
|
||||
const componentTypes = options.getComponents().filter(c => !c.island).map(c => [
|
||||
const componentTypes = app.components.filter(c => !c.island).map(c => [
|
||||
c.pascalName,
|
||||
`typeof ${genDynamicImport(isAbsolute(c.filePath)
|
||||
? relative(buildDir, c.filePath).replace(/(?<=\w)\.(?!vue)\w+$/g, '')
|
||||
|
@ -21,15 +21,19 @@ export function createTransformPlugin (nuxt: Nuxt, getComponents: getComponentsT
|
||||
function getComponentsImports (): Import[] {
|
||||
const components = getComponents(mode)
|
||||
return components.flatMap((c): Import[] => {
|
||||
const withMode = (mode: string | undefined) => mode
|
||||
? `${c.filePath}${c.filePath.includes('?') ? '&' : '?'}nuxt_component=${mode}`
|
||||
: c.filePath
|
||||
|
||||
return [
|
||||
{
|
||||
as: c.pascalName,
|
||||
from: c.filePath + (c.mode === 'client' ? '?component=client' : ''),
|
||||
from: withMode(c.mode === 'client' ? 'client' : undefined),
|
||||
name: 'default'
|
||||
},
|
||||
{
|
||||
as: 'Lazy' + c.pascalName,
|
||||
from: c.filePath + '?component=' + [c.mode === 'client' ? 'client' : '', 'async'].filter(Boolean).join(','),
|
||||
from: withMode([c.mode === 'client' ? 'client' : '', 'async'].filter(Boolean).join(',')),
|
||||
name: 'default'
|
||||
}
|
||||
]
|
||||
@ -43,10 +47,10 @@ export function createTransformPlugin (nuxt: Nuxt, getComponents: getComponentsT
|
||||
},
|
||||
async transform (code, id) {
|
||||
// Virtual component wrapper
|
||||
if (id.includes('?component')) {
|
||||
if (id.match(/[?&]nuxt_component=/)) {
|
||||
const { search } = parseURL(id)
|
||||
const query = parseQuery(search)
|
||||
const mode = query.component
|
||||
const mode = query.nuxt_component
|
||||
const bare = id.replace(/\?.*/, '')
|
||||
if (mode === 'async') {
|
||||
return [
|
||||
|
@ -12,6 +12,7 @@ export function createApp (nuxt: Nuxt, options: Partial<NuxtApp> = {}): NuxtApp
|
||||
dir: nuxt.options.srcDir,
|
||||
extensions: nuxt.options.extensions,
|
||||
plugins: [],
|
||||
components: [],
|
||||
templates: []
|
||||
} as unknown as NuxtApp) as NuxtApp
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
||||
analyze: nuxt.options.build.analyze && {
|
||||
template: 'treemap',
|
||||
projectRoot: nuxt.options.rootDir,
|
||||
filename: join(nuxt.options.rootDir, '.nuxt/stats', '{name}.html')
|
||||
filename: join(nuxt.options.analyzeDir, '{name}.html')
|
||||
},
|
||||
scanDirs: nuxt.options._layers.map(layer => (layer.config.serverDir || layer.config.srcDir) && resolve(layer.cwd, layer.config.serverDir || resolve(layer.config.srcDir, 'server'))).filter(Boolean),
|
||||
renderer: resolve(distDir, 'core/runtime/nitro/renderer'),
|
||||
@ -130,7 +130,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
||||
],
|
||||
traceInclude: [
|
||||
// force include files used in generated code from the runtime-compiler
|
||||
...(nuxt.options.experimental.runtimeVueCompiler && !nuxt.options.experimental.externalVue)
|
||||
...(nuxt.options.vue.runtimeCompiler && !nuxt.options.experimental.externalVue)
|
||||
? [
|
||||
...nuxt.options.modulesDir.reduce<string[]>((targets, path) => {
|
||||
const serverRendererPath = resolve(path, 'vue/server-renderer/index.js')
|
||||
@ -150,7 +150,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
||||
vue: await resolvePath(`vue/dist/vue.cjs${nuxt.options.dev ? '' : '.prod'}.js`)
|
||||
},
|
||||
// Vue 3 mocks
|
||||
...nuxt.options.experimental.runtimeVueCompiler || nuxt.options.experimental.externalVue
|
||||
...nuxt.options.vue.runtimeCompiler || nuxt.options.experimental.externalVue
|
||||
? {}
|
||||
: {
|
||||
'estree-walker': 'unenv/runtime/mock/proxy',
|
||||
@ -253,7 +253,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
||||
})
|
||||
|
||||
// Enable runtime compiler client side
|
||||
if (nuxt.options.experimental.runtimeVueCompiler) {
|
||||
if (nuxt.options.vue.runtimeCompiler) {
|
||||
nuxt.hook('vite:extendConfig', (config, { isClient }) => {
|
||||
if (isClient) {
|
||||
if (Array.isArray(config.resolve!.alias)) {
|
||||
|
@ -78,17 +78,17 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
exclude: [join(nuxt.options.rootDir, 'index.html')],
|
||||
patterns: vueAppPatterns(nuxt)
|
||||
}
|
||||
addVitePlugin(ImportProtectionPlugin.vite(config))
|
||||
addWebpackPlugin(ImportProtectionPlugin.webpack(config))
|
||||
addVitePlugin(() => ImportProtectionPlugin.vite(config))
|
||||
addWebpackPlugin(() => ImportProtectionPlugin.webpack(config))
|
||||
|
||||
if (nuxt.options.experimental.localLayerAliases) {
|
||||
// Add layer aliasing support for ~, ~~, @ and @@ aliases
|
||||
addVitePlugin(LayerAliasingPlugin.vite({
|
||||
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({
|
||||
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),
|
||||
@ -102,8 +102,8 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client,
|
||||
transformerOptions: nuxt.options.optimization.asyncTransforms
|
||||
}
|
||||
addVitePlugin(UnctxTransformPlugin.vite(options))
|
||||
addWebpackPlugin(UnctxTransformPlugin.webpack(options))
|
||||
addVitePlugin(() => UnctxTransformPlugin.vite(options))
|
||||
addWebpackPlugin(() => UnctxTransformPlugin.webpack(options))
|
||||
|
||||
// Add composable tree-shaking optimisations
|
||||
const serverTreeShakeOptions: TreeShakeComposablesPluginOptions = {
|
||||
@ -111,23 +111,23 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
composables: nuxt.options.optimization.treeShake.composables.server
|
||||
}
|
||||
if (Object.keys(serverTreeShakeOptions.composables).length) {
|
||||
addVitePlugin(TreeShakeComposablesPlugin.vite(serverTreeShakeOptions), { client: false })
|
||||
addWebpackPlugin(TreeShakeComposablesPlugin.webpack(serverTreeShakeOptions), { client: false })
|
||||
addVitePlugin(() => TreeShakeComposablesPlugin.vite(serverTreeShakeOptions), { client: false })
|
||||
addWebpackPlugin(() => TreeShakeComposablesPlugin.webpack(serverTreeShakeOptions), { client: false })
|
||||
}
|
||||
const clientTreeShakeOptions: TreeShakeComposablesPluginOptions = {
|
||||
sourcemap: nuxt.options.sourcemap.client,
|
||||
composables: nuxt.options.optimization.treeShake.composables.client
|
||||
}
|
||||
if (Object.keys(clientTreeShakeOptions.composables).length) {
|
||||
addVitePlugin(TreeShakeComposablesPlugin.vite(clientTreeShakeOptions), { server: false })
|
||||
addWebpackPlugin(TreeShakeComposablesPlugin.webpack(clientTreeShakeOptions), { server: false })
|
||||
addVitePlugin(() => TreeShakeComposablesPlugin.vite(clientTreeShakeOptions), { server: false })
|
||||
addWebpackPlugin(() => TreeShakeComposablesPlugin.webpack(clientTreeShakeOptions), { server: false })
|
||||
}
|
||||
})
|
||||
|
||||
if (!nuxt.options.dev) {
|
||||
// DevOnly component tree-shaking - build time only
|
||||
addVitePlugin(DevOnlyPlugin.vite({ sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))
|
||||
addWebpackPlugin(DevOnlyPlugin.webpack({ sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))
|
||||
addVitePlugin(() => DevOnlyPlugin.vite({ sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))
|
||||
addWebpackPlugin(() => DevOnlyPlugin.webpack({ sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))
|
||||
}
|
||||
|
||||
// TODO: [Experimental] Avoid emitting assets when flag is enabled
|
||||
@ -358,6 +358,17 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
|
||||
// Temporary until finding better placement for each
|
||||
options.appDir = options.alias['#app'] = resolve(distDir, 'app')
|
||||
options._majorVersion = 3
|
||||
|
||||
// Nuxt DevTools is currently opt-in
|
||||
if (options.devtools === true || (options.devtools && options.devtools.enabled !== false)) {
|
||||
if (await import('./features').then(r => r.ensurePackageInstalled(options.rootDir, '@nuxt/devtools', options.modulesDir))) {
|
||||
options._modules.push('@nuxt/devtools')
|
||||
} else {
|
||||
logger.warn('Failed to install `@nuxt/devtools`, please install it manually, or disable `devtools` in `nuxt.config`')
|
||||
}
|
||||
}
|
||||
|
||||
// Add core modules
|
||||
options._modules.push(pagesModule, metaModule, componentsModule)
|
||||
options._modules.push([importsModule, {
|
||||
transform: {
|
||||
@ -376,15 +387,6 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
|
||||
options._modules.push('@nuxt/telemetry')
|
||||
}
|
||||
|
||||
// Nuxt DevTools is currently opt-in
|
||||
if (options.devtools === true || (options.devtools && options.devtools.enabled !== false)) {
|
||||
if (await import('./features').then(r => r.ensurePackageInstalled(options.rootDir, '@nuxt/devtools', options.modulesDir))) {
|
||||
options._modules.push('@nuxt/devtools')
|
||||
} else {
|
||||
logger.warn('Failed to install `@nuxt/devtools`, please install it manually, or disable `devtools` in `nuxt.config`')
|
||||
}
|
||||
}
|
||||
|
||||
const nuxt = createNuxt(options)
|
||||
|
||||
if (nuxt.options.debug) {
|
||||
|
@ -2,7 +2,7 @@ import { createRenderer, renderResourceHeaders } from 'vue-bundle-renderer/runti
|
||||
import type { RenderResponse } from 'nitropack'
|
||||
import type { Manifest } from 'vite'
|
||||
import type { H3Event } from 'h3'
|
||||
import { appendHeader, createError, getQuery, readBody, writeEarlyHints } from 'h3'
|
||||
import { appendResponseHeader, createError, getQuery, readBody, writeEarlyHints } from 'h3'
|
||||
import devalue from '@nuxt/devalue'
|
||||
import { stringify, uneval } from 'devalue'
|
||||
import destr from 'destr'
|
||||
@ -199,7 +199,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
||||
let url = ssrError?.url as string || islandContext?.url || event.node.req.url!
|
||||
|
||||
// Whether we are rendering payload route
|
||||
const isRenderingPayload = PAYLOAD_URL_RE.test(url)
|
||||
const isRenderingPayload = PAYLOAD_URL_RE.test(url) && !islandContext
|
||||
if (isRenderingPayload) {
|
||||
url = url.substring(0, url.lastIndexOf('/')) || '/'
|
||||
event.node.req.url = url
|
||||
@ -229,7 +229,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
||||
}
|
||||
|
||||
// Whether we are prerendering route
|
||||
const _PAYLOAD_EXTRACTION = process.env.prerender && process.env.NUXT_PAYLOAD_EXTRACTION && !ssrContext.noSSR
|
||||
const _PAYLOAD_EXTRACTION = process.env.prerender && process.env.NUXT_PAYLOAD_EXTRACTION && !ssrContext.noSSR && !islandContext
|
||||
const payloadURL = _PAYLOAD_EXTRACTION ? joinURL(useRuntimeConfig().app.baseURL, url, process.env.NUXT_JSON_PAYLOADS ? '_payload.json' : '_payload.js') : undefined
|
||||
if (process.env.prerender) {
|
||||
ssrContext.payload.prerenderedAt = Date.now()
|
||||
@ -275,7 +275,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
||||
|
||||
if (_PAYLOAD_EXTRACTION) {
|
||||
// Hint nitro to prerender payload for this route
|
||||
appendHeader(event, 'x-nitro-prerender', joinURL(url, process.env.NUXT_JSON_PAYLOADS ? '_payload.json' : '_payload.js'))
|
||||
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))
|
||||
}
|
||||
|
@ -81,8 +81,8 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
|
||||
nuxt.options.alias['#imports'] = join(nuxt.options.buildDir, 'imports')
|
||||
|
||||
// Transform to inject imports in production mode
|
||||
addVitePlugin(TransformPlugin.vite({ ctx, options, sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))
|
||||
addWebpackPlugin(TransformPlugin.webpack({ ctx, options, sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))
|
||||
addVitePlugin(() => TransformPlugin.vite({ ctx, options, sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))
|
||||
addWebpackPlugin(() => TransformPlugin.webpack({ ctx, options, sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))
|
||||
|
||||
const priorities = nuxt.options._layers.map((layer, i) => [layer.config.srcDir, -i] as const).sort(([a], [b]) => b.length - a.length)
|
||||
|
||||
|
@ -151,14 +151,11 @@ export default defineNuxtModule({
|
||||
// Extract macros from pages
|
||||
const pageMetaOptions: PageMetaPluginOptions = {
|
||||
dev: nuxt.options.dev,
|
||||
sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client,
|
||||
dirs: nuxt.options._layers.map(
|
||||
layer => resolve(layer.config.srcDir, layer.config.dir?.pages || 'pages')
|
||||
)
|
||||
sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client
|
||||
}
|
||||
nuxt.hook('modules:done', () => {
|
||||
addVitePlugin(PageMetaPlugin.vite(pageMetaOptions))
|
||||
addWebpackPlugin(PageMetaPlugin.webpack(pageMetaOptions))
|
||||
addVitePlugin(() => PageMetaPlugin.vite(pageMetaOptions))
|
||||
addWebpackPlugin(() => PageMetaPlugin.webpack(pageMetaOptions))
|
||||
})
|
||||
|
||||
// Add prefetching support for middleware & layouts
|
||||
|
@ -10,7 +10,6 @@ import MagicString from 'magic-string'
|
||||
import { isAbsolute, normalize } from 'pathe'
|
||||
|
||||
export interface PageMetaPluginOptions {
|
||||
dirs: Array<string | RegExp>
|
||||
dev?: boolean
|
||||
sourcemap?: boolean
|
||||
}
|
||||
@ -42,11 +41,7 @@ export const PageMetaPlugin = createUnplugin((options: PageMetaPluginOptions) =>
|
||||
const query = parseMacroQuery(id)
|
||||
id = normalize(id)
|
||||
|
||||
const isPagesDir = options.dirs.some(dir => typeof dir === 'string' ? id.startsWith(dir) : dir.test(id))
|
||||
if (!isPagesDir && !query.macro) { return false }
|
||||
|
||||
const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href))
|
||||
return /\.(m?[jt]sx?|vue)/.test(pathname)
|
||||
return !!query.macro
|
||||
},
|
||||
transform (code, id) {
|
||||
const query = parseMacroQuery(id)
|
||||
@ -66,26 +61,6 @@ export const PageMetaPlugin = createUnplugin((options: PageMetaPluginOptions) =>
|
||||
|
||||
const hasMacro = code.match(/\bdefinePageMeta\s*\(\s*/)
|
||||
|
||||
// Remove any references to the macro from our pages
|
||||
if (!query.macro) {
|
||||
if (hasMacro) {
|
||||
walk(this.parse(code, {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 'latest'
|
||||
}) as Node, {
|
||||
enter (_node) {
|
||||
if (_node.type !== 'CallExpression' || (_node as CallExpression).callee.type !== 'Identifier') { return }
|
||||
const node = _node as CallExpression & { start: number, end: number }
|
||||
const name = 'name' in node.callee && node.callee.name
|
||||
if (name === 'definePageMeta') {
|
||||
s.overwrite(node.start, node.end, 'false && {}')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return result()
|
||||
}
|
||||
|
||||
const imports = findStaticImports(code)
|
||||
|
||||
// [vite] Re-export any script imports
|
||||
|
@ -1,5 +1,7 @@
|
||||
import type { KeepAliveProps, TransitionProps, UnwrapRef } from 'vue'
|
||||
import { getCurrentInstance } from 'vue'
|
||||
import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteRecordRedirectOption } from 'vue-router'
|
||||
import { useRoute } from 'vue-router'
|
||||
import type { NuxtError } from '#app'
|
||||
|
||||
export interface PageMeta {
|
||||
@ -51,6 +53,14 @@ const warnRuntimeUsage = (method: string) =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const definePageMeta = (meta: PageMeta): void => {
|
||||
if (process.dev) {
|
||||
const component = getCurrentInstance()?.type
|
||||
try {
|
||||
const isRouteComponent = component && useRoute().matched.some(p => Object.values(p.components || {}).includes(component))
|
||||
if (isRouteComponent) {
|
||||
// don't warn if it's being used in a route component
|
||||
return
|
||||
}
|
||||
} catch {}
|
||||
warnRuntimeUsage('definePageMeta')
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import { createError } from 'h3'
|
||||
import { withoutBase } from 'ufo'
|
||||
|
||||
import type { PageMeta, Plugin, RouteMiddleware } from '../../../app/index'
|
||||
import { callWithNuxt, defineNuxtPlugin, useRuntimeConfig } from '#app/nuxt'
|
||||
import { defineNuxtPlugin, useRuntimeConfig } from '#app/nuxt'
|
||||
import { clearError, showError, useError } from '#app/composables/error'
|
||||
import { useState } from '#app/composables/state'
|
||||
import { navigateTo } from '#app/composables/router'
|
||||
@ -113,7 +113,7 @@ export default defineNuxtPlugin({
|
||||
await router.isReady()
|
||||
} catch (error: any) {
|
||||
// We'll catch 404s here
|
||||
await callWithNuxt(nuxtApp, showError, [error])
|
||||
await nuxtApp.runWithContext(() => showError(error))
|
||||
}
|
||||
|
||||
const initialLayout = useState('_layout')
|
||||
@ -148,14 +148,14 @@ export default defineNuxtPlugin({
|
||||
throw new Error(`Unknown route middleware: '${entry}'.`)
|
||||
}
|
||||
|
||||
const result = await callWithNuxt(nuxtApp, middleware, [to, from])
|
||||
const result = await nuxtApp.runWithContext(() => middleware(to, from))
|
||||
if (process.server || (!nuxtApp.payload.serverRendered && nuxtApp.isHydrating)) {
|
||||
if (result === false || result instanceof Error) {
|
||||
const error = result || createError({
|
||||
statusCode: 404,
|
||||
statusMessage: `Page Not Found: ${initialURL}`
|
||||
})
|
||||
await callWithNuxt(nuxtApp, showError, [error])
|
||||
await nuxtApp.runWithContext(() => showError(error))
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -170,19 +170,19 @@ export default defineNuxtPlugin({
|
||||
|
||||
if (process.client && !nuxtApp.isHydrating && error.value) {
|
||||
// Clear any existing errors
|
||||
await callWithNuxt(nuxtApp, clearError)
|
||||
await nuxtApp.runWithContext(clearError)
|
||||
}
|
||||
if (process.server && failure?.type === 4 /* ErrorTypes.NAVIGATION_ABORTED */) {
|
||||
return
|
||||
}
|
||||
if (to.matched.length === 0) {
|
||||
await callWithNuxt(nuxtApp, showError, [createError({
|
||||
await nuxtApp.runWithContext(() => showError(createError({
|
||||
statusCode: 404,
|
||||
fatal: false,
|
||||
statusMessage: `Page not found: ${to.fullPath}`
|
||||
})])
|
||||
})))
|
||||
} else if (process.server && to.redirectedFrom) {
|
||||
await callWithNuxt(nuxtApp, navigateTo, [to.fullPath || '/'])
|
||||
await nuxtApp.runWithContext(() => navigateTo(to.fullPath || '/'))
|
||||
}
|
||||
})
|
||||
|
||||
@ -195,7 +195,7 @@ export default defineNuxtPlugin({
|
||||
})
|
||||
} catch (error: any) {
|
||||
// We'll catch middleware errors or deliberate exceptions here
|
||||
await callWithNuxt(nuxtApp, showError, [error])
|
||||
await nuxtApp.runWithContext(() => showError(error))
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createError, showError } from '#app/composables/error'
|
||||
import { callWithNuxt, useNuxtApp } from '#app/nuxt'
|
||||
import { useNuxtApp } from '#app/nuxt'
|
||||
import { defineNuxtRouteMiddleware, useRouter } from '#app/composables/router'
|
||||
|
||||
export default defineNuxtRouteMiddleware(async (to) => {
|
||||
@ -24,7 +24,7 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
||||
if (final === to) {
|
||||
const unsub = router.afterEach(async () => {
|
||||
unsub()
|
||||
await callWithNuxt(nuxtApp, showError, [error])
|
||||
await nuxtApp.runWithContext(() => showError(error))
|
||||
// We pretend to have navigated to the invalid route so
|
||||
// that the user can return to the previous page with
|
||||
// the back button.
|
||||
|
@ -30,7 +30,7 @@
|
||||
"nitropack": "^2.3.3",
|
||||
"unbuild": "latest",
|
||||
"unctx": "^2.3.0",
|
||||
"vite": "~4.3.3",
|
||||
"vite": "~4.3.4",
|
||||
"vue": "3.2.47",
|
||||
"vue-bundle-renderer": "^1.0.3",
|
||||
"vue-router": "^4.1.6",
|
||||
@ -40,7 +40,7 @@
|
||||
"defu": "^6.1.2",
|
||||
"hookable": "^5.5.3",
|
||||
"pathe": "^1.1.0",
|
||||
"pkg-types": "^1.0.2",
|
||||
"pkg-types": "^1.0.3",
|
||||
"postcss-import-resolver": "^2.0.0",
|
||||
"std-env": "^3.3.2",
|
||||
"ufo": "^1.1.1",
|
||||
|
@ -13,6 +13,13 @@ export default defineUntypedSchema({
|
||||
* @type {typeof import('@vue/compiler-core').CompilerOptions}
|
||||
*/
|
||||
compilerOptions: {},
|
||||
|
||||
/**
|
||||
* Include Vue compiler in runtime bundle.
|
||||
*/
|
||||
runtimeCompiler: {
|
||||
$resolve: async (val, get) => val ?? await get('experimental.runtimeVueCompiler') ?? false,
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -119,10 +119,11 @@ export default defineUntypedSchema({
|
||||
return val ?? false
|
||||
}
|
||||
const rootDir = await get('rootDir')
|
||||
const analyzeDir = await get('analyzeDir')
|
||||
return {
|
||||
template: 'treemap',
|
||||
projectRoot: rootDir,
|
||||
filename: join(rootDir, '.nuxt/stats', '{name}.html')
|
||||
filename: join(analyzeDir, '{name}.html')
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -170,7 +171,7 @@ export default defineUntypedSchema({
|
||||
$resolve: async (val, get) => defu(val || {},
|
||||
await get('dev') ? {} : {
|
||||
vue: ['onBeforeMount', 'onMounted', 'onBeforeUpdate', 'onRenderTracked', 'onRenderTriggered', 'onActivated', 'onDeactivated', 'onBeforeUnmount'],
|
||||
'#app': ['definePayloadReviver']
|
||||
'#app': ['definePayloadReviver', 'definePageMeta']
|
||||
}
|
||||
)
|
||||
},
|
||||
@ -178,7 +179,7 @@ export default defineUntypedSchema({
|
||||
$resolve: async (val, get) => defu(val || {},
|
||||
await get('dev') ? {} : {
|
||||
vue: ['onServerPrefetch', 'onRenderTracked', 'onRenderTriggered'],
|
||||
'#app': ['definePayloadReducer']
|
||||
'#app': ['definePayloadReducer', 'definePageMeta']
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -117,20 +117,20 @@ export default defineUntypedSchema({
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to set the modules directories for path resolving (for example, webpack's
|
||||
* `resolveLoading`, `nodeExternals` and `postcss`).
|
||||
*
|
||||
* The configuration path is relative to `options.rootDir` (default is current working directory).
|
||||
*
|
||||
* Setting this field may be necessary if your project is organized as a yarn workspace-styled mono-repository.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* export default {
|
||||
* modulesDir: ['../../node_modules']
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
* Used to set the modules directories for path resolving (for example, webpack's
|
||||
* `resolveLoading`, `nodeExternals` and `postcss`).
|
||||
*
|
||||
* The configuration path is relative to `options.rootDir` (default is current working directory).
|
||||
*
|
||||
* Setting this field may be necessary if your project is organized as a yarn workspace-styled mono-repository.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* export default {
|
||||
* modulesDir: ['../../node_modules']
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
modulesDir: {
|
||||
$default: ['node_modules'],
|
||||
$resolve: async (val, get) => [
|
||||
@ -139,6 +139,17 @@ export default defineUntypedSchema({
|
||||
]
|
||||
},
|
||||
|
||||
/**
|
||||
* The directory where Nuxt will store the generated files when running `nuxt analyze`.
|
||||
*
|
||||
* If a relative path is specified, it will be relative to your `rootDir`.
|
||||
*/
|
||||
analyzeDir: {
|
||||
$resolve: async (val, get) => val
|
||||
? resolve(await get('rootDir'), val)
|
||||
: resolve(await get('buildDir'), 'analyze')
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether Nuxt is running in development mode.
|
||||
*
|
||||
@ -346,6 +357,7 @@ export default defineUntypedSchema({
|
||||
'**/*.d.ts', // ignore type declarations
|
||||
'.output',
|
||||
'.git',
|
||||
await get('analyzeDir'),
|
||||
await get('ignorePrefix') && `**/${await get('ignorePrefix')}*.*`
|
||||
].concat(val).filter(Boolean)
|
||||
},
|
||||
|
@ -23,12 +23,6 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
externalVue: true,
|
||||
|
||||
// TODO: move to `vue.runtimeCompiler` in v3.5
|
||||
/**
|
||||
* Include Vue compiler in runtime bundle.
|
||||
*/
|
||||
runtimeVueCompiler: false,
|
||||
|
||||
/**
|
||||
* Tree shakes contents of client-only components from server bundle.
|
||||
* @see https://github.com/nuxt/framework/pull/5750
|
||||
|
@ -22,10 +22,11 @@ export default defineUntypedSchema({
|
||||
return val ?? false
|
||||
}
|
||||
const rootDir = await get('rootDir')
|
||||
const analyzeDir = await get('analyzeDir')
|
||||
return {
|
||||
template: 'treemap',
|
||||
projectRoot: rootDir,
|
||||
filename: join(rootDir, '.nuxt/stats', '{name}.html')
|
||||
filename: join(analyzeDir, '{name}.html')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -47,6 +47,16 @@ export interface GenerateAppOptions {
|
||||
filter?: (template: ResolvedNuxtTemplate<any>) => boolean
|
||||
}
|
||||
|
||||
export interface NuxtAnalyzeMeta {
|
||||
name: string
|
||||
slug: string
|
||||
startTime: number
|
||||
endTime: number
|
||||
analyzeDir: string
|
||||
buildDir: string
|
||||
outDir: string
|
||||
}
|
||||
|
||||
/**
|
||||
* The listeners to Nuxt build time events
|
||||
*/
|
||||
@ -131,6 +141,13 @@ export interface NuxtHooks {
|
||||
*/
|
||||
'build:manifest': (manifest: Manifest) => HookResult
|
||||
|
||||
/**
|
||||
* Called when `nuxt analyze` is finished
|
||||
* @param meta the analyze meta object, mutations will be saved to `meta.json`
|
||||
* @returns Promise
|
||||
*/
|
||||
'build:analyze:done': (meta: NuxtAnalyzeMeta) => HookResult
|
||||
|
||||
/**
|
||||
* Called before generating the app.
|
||||
* @param options GenerateAppOptions object
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { Hookable } from 'hookable'
|
||||
import type { Ignore } from 'ignore'
|
||||
import type { NuxtHooks, NuxtLayout, NuxtMiddleware } from './hooks'
|
||||
import type { Component } from './components'
|
||||
import type { NuxtOptions } from './config'
|
||||
|
||||
export interface Nuxt {
|
||||
@ -58,6 +59,7 @@ export interface NuxtApp {
|
||||
dir: string
|
||||
extensions: string[]
|
||||
plugins: NuxtPlugin[]
|
||||
components: Component[]
|
||||
layouts: Record<string, NuxtLayout>
|
||||
middleware: NuxtMiddleware[]
|
||||
templates: NuxtTemplate[]
|
||||
|
@ -13,7 +13,7 @@ const kit: typeof _kit = _kit.default || _kit
|
||||
export async function startServer () {
|
||||
const ctx = useTestContext()
|
||||
await stopServer()
|
||||
const port = await getRandomPort()
|
||||
const port = ctx.options.port || await getRandomPort()
|
||||
ctx.url = 'http://127.0.0.1:' + port
|
||||
if (ctx.options.dev) {
|
||||
const nuxiCLI = await kit.resolvePath('nuxi/cli')
|
||||
|
@ -23,6 +23,7 @@ export interface TestOptions {
|
||||
launch?: LaunchOptions
|
||||
}
|
||||
server: boolean
|
||||
port?: number
|
||||
}
|
||||
|
||||
export interface TestContext {
|
||||
|
@ -30,7 +30,7 @@
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"clear": "^0.1.0",
|
||||
"cssnano": "^6.0.0",
|
||||
"cssnano": "^6.0.1",
|
||||
"defu": "^6.1.2",
|
||||
"esbuild": "^0.17.18",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
@ -45,7 +45,7 @@
|
||||
"ohash": "^1.1.2",
|
||||
"pathe": "^1.1.0",
|
||||
"perfect-debounce": "^0.1.3",
|
||||
"pkg-types": "^1.0.2",
|
||||
"pkg-types": "^1.0.3",
|
||||
"postcss": "^8.4.23",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-url": "^10.1.3",
|
||||
@ -54,9 +54,9 @@
|
||||
"strip-literal": "^1.0.1",
|
||||
"ufo": "^1.1.1",
|
||||
"unplugin": "^1.3.1",
|
||||
"vite": "~4.3.3",
|
||||
"vite": "~4.3.4",
|
||||
"vite-node": "^0.30.1",
|
||||
"vite-plugin-checker": "^0.5.6",
|
||||
"vite-plugin-checker": "^0.6.0",
|
||||
"vue-bundle-renderer": "^1.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@ -24,7 +24,7 @@
|
||||
"autoprefixer": "^10.4.14",
|
||||
"css-loader": "^6.7.3",
|
||||
"css-minimizer-webpack-plugin": "^5.0.0",
|
||||
"cssnano": "^6.0.0",
|
||||
"cssnano": "^6.0.1",
|
||||
"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.23",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-loader": "^7.2.4",
|
||||
"postcss-loader": "^7.3.0",
|
||||
"postcss-url": "^10.1.3",
|
||||
"std-env": "^3.3.2",
|
||||
"time-fix-plugin": "^2.0.7",
|
||||
|
454
pnpm-lock.yaml
454
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -13,10 +13,10 @@
|
||||
"main"
|
||||
],
|
||||
"ignoreDeps": [
|
||||
"markdownlint-cli",
|
||||
"nuxt",
|
||||
"nuxt3",
|
||||
"@nuxt/kit",
|
||||
"parse5"
|
||||
"@nuxt/kit"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -113,9 +113,15 @@ describe('pages', () => {
|
||||
expect(headers.get('location')).toEqual('/')
|
||||
})
|
||||
|
||||
it('includes page metadata from pages added in pages:extend hook', async () => {
|
||||
const res = await fetch('/page-extend')
|
||||
expect(res.headers.get('x-extend')).toEqual('added in pages:extend')
|
||||
})
|
||||
|
||||
it('validates routes', async () => {
|
||||
const { status } = await fetch('/forbidden')
|
||||
const { status, headers } = await fetch('/forbidden')
|
||||
expect(status).toEqual(404)
|
||||
expect(headers.get('Set-Cookie')).toBe('set-in-plugin=true; Path=/')
|
||||
|
||||
const page = await createPage('/navigate-to-forbidden')
|
||||
await page.waitForLoadState('networkidle')
|
||||
@ -135,8 +141,11 @@ describe('pages', () => {
|
||||
expect(status).toEqual(500)
|
||||
})
|
||||
|
||||
it('render 404', async () => {
|
||||
const html = await $fetch('/not-found')
|
||||
it('render catchall page', async () => {
|
||||
const res = await fetch('/not-found')
|
||||
expect(res.status).toEqual(200)
|
||||
|
||||
const html = await res.text()
|
||||
|
||||
// Snapshot
|
||||
// expect(html).toMatchInlineSnapshot()
|
||||
@ -578,7 +587,9 @@ describe('errors', () => {
|
||||
|
||||
it('should render a HTML error page', async () => {
|
||||
const res = await fetch('/error')
|
||||
expect(res.headers.get('Set-Cookie')).toBe('some-error=was%20set; Path=/')
|
||||
expect(res.headers.get('Set-Cookie')).toBe('set-in-plugin=true; Path=/')
|
||||
// TODO: enable when we update test to node v16
|
||||
// expect(res.headers.get('Set-Cookie')).toBe('set-in-plugin=true; Path=/, some-error=was%20set; Path=/')
|
||||
expect(await res.text()).toContain('This is a custom error')
|
||||
})
|
||||
|
||||
|
@ -34,7 +34,7 @@ describe.skipIf(isWindows || process.env.TEST_BUILDER === 'webpack' || process.e
|
||||
|
||||
it('default client bundle size', async () => {
|
||||
stats.client = await analyzeSizes('**/*.js', publicDir)
|
||||
expect(roundToKilobytes(stats.client.totalBytes)).toMatchInlineSnapshot('"94.0k"')
|
||||
expect(roundToKilobytes(stats.client.totalBytes)).toMatchInlineSnapshot('"94.1k"')
|
||||
expect(stats.client.files.map(f => f.replace(/\..*\.js/, '.js'))).toMatchInlineSnapshot(`
|
||||
[
|
||||
"_nuxt/entry.js",
|
||||
@ -45,7 +45,7 @@ describe.skipIf(isWindows || process.env.TEST_BUILDER === 'webpack' || process.e
|
||||
|
||||
it('default server bundle size', async () => {
|
||||
stats.server = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
|
||||
expect(roundToKilobytes(stats.server.totalBytes)).toMatchInlineSnapshot('"66.6k"')
|
||||
expect(roundToKilobytes(stats.server.totalBytes)).toMatchInlineSnapshot('"66.7k"')
|
||||
|
||||
const modules = await analyzeSizes('node_modules/**/*', serverDir)
|
||||
expect(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"2654k"')
|
||||
|
19
test/fixtures/basic/modules/page-extend.ts
vendored
Normal file
19
test/fixtures/basic/modules/page-extend.ts
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
import { createResolver, defineNuxtModule, useNuxt } from 'nuxt/kit'
|
||||
|
||||
export default defineNuxtModule({
|
||||
meta: {
|
||||
name: 'page-extend'
|
||||
},
|
||||
setup () {
|
||||
const nuxt = useNuxt()
|
||||
const resolver = createResolver(import.meta.url)
|
||||
|
||||
nuxt.hook('pages:extend', (pages) => {
|
||||
pages.push({
|
||||
name: 'page-extend',
|
||||
path: '/page-extend',
|
||||
file: resolver.resolve('./runtime/page.vue')
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
17
test/fixtures/basic/modules/runtime/page.vue
vendored
Normal file
17
test/fixtures/basic/modules/runtime/page.vue
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
import { setResponseHeader } from 'h3'
|
||||
|
||||
definePageMeta({
|
||||
value: 'added in pages:extend'
|
||||
})
|
||||
|
||||
if (process.server) {
|
||||
setResponseHeader(useRequestEvent(), 'x-extend', useRoute().meta.value as string)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
added in pages:extend
|
||||
</div>
|
||||
</template>
|
4
test/fixtures/basic/nuxt.config.ts
vendored
4
test/fixtures/basic/nuxt.config.ts
vendored
@ -106,8 +106,8 @@ export default defineNuxtConfig({
|
||||
if (id === 'virtual.css') { return ':root { --virtual: red }' }
|
||||
}
|
||||
}))
|
||||
addVitePlugin(plugin.vite())
|
||||
addWebpackPlugin(plugin.webpack())
|
||||
addVitePlugin(() => plugin.vite())
|
||||
addWebpackPlugin(() => plugin.webpack())
|
||||
},
|
||||
function (_options, nuxt) {
|
||||
const routesToDuplicate = ['/async-parent', '/fixed-keyed-child-parent', '/keyed-child-parent', '/with-layout', '/with-layout2']
|
||||
|
3
test/fixtures/basic/plugins/cookie.ts
vendored
Normal file
3
test/fixtures/basic/plugins/cookie.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
export default defineNuxtPlugin(() => {
|
||||
useCookie('set-in-plugin').value = 'true'
|
||||
})
|
6
test/fixtures/basic/types.ts
vendored
6
test/fixtures/basic/types.ts
vendored
@ -96,7 +96,7 @@ describe('middleware', () => {
|
||||
addRouteMiddleware('example', (to, from) => {
|
||||
expectTypeOf(to).toEqualTypeOf<RouteLocationNormalizedLoaded>()
|
||||
expectTypeOf(from).toEqualTypeOf<RouteLocationNormalizedLoaded>()
|
||||
expectTypeOf(navigateTo).toEqualTypeOf <(to: RouteLocationRaw | null | undefined, options ?: NavigateToOptions) => RouteLocationRaw | void | false | Promise<void | NavigationFailure | false>>()
|
||||
expectTypeOf(navigateTo).toEqualTypeOf<(to: RouteLocationRaw | null | undefined, options?: NavigateToOptions) => RouteLocationRaw | void | false | Promise<void | NavigationFailure | false>>()
|
||||
navigateTo('/')
|
||||
abortNavigation()
|
||||
abortNavigation('error string')
|
||||
@ -315,4 +315,8 @@ describe('composables inference', () => {
|
||||
const bob = callWithNuxt({} as any, () => true)
|
||||
expectTypeOf<typeof bob>().toEqualTypeOf<boolean | Promise<boolean>>()
|
||||
})
|
||||
it('runWithContext', () => {
|
||||
const bob = useNuxtApp().runWithContext(() => true)
|
||||
expectTypeOf<typeof bob>().toEqualTypeOf<boolean | Promise<boolean>>()
|
||||
})
|
||||
})
|
||||
|
5
test/fixtures/minimal/app.vue
vendored
5
test/fixtures/minimal/app.vue
vendored
@ -1,6 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { componentNames } from '#components'
|
||||
console.log(componentNames)
|
||||
// @ts-expect-error this is not usable outside a pages directory
|
||||
definePageMeta({
|
||||
// this should be fully tree-shaken out
|
||||
title: 'jet common fruit chose bright planning exercise herself position wealth stiff known prepare listen leader eleven found boat dollar eye come author won thought pony biggest feel organized die vast class ask cost ball wrong chicken origin model little properly dangerous dull corner jar mighty solution pilot city locate guide gradually affect curve about snake single silly against fireplace money another involved origin sport where thin stop question go stretch although arrow rush mixture fallen power pay fifteen layers play slightly heavy built needed sing sentence diagram quarter yesterday list faster been having construction curious shoe'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -1,8 +1,10 @@
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
export default defineNuxtConfig({
|
||||
experimental: {
|
||||
runtimeVueCompiler: true,
|
||||
externalVue: false
|
||||
},
|
||||
vue: {
|
||||
runtimeCompiler: true
|
||||
},
|
||||
builder: process.env.TEST_BUILDER as 'webpack' | 'vite' ?? 'vite'
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user