mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 08:33:53 +00:00
Merge branch 'main' into patch-21
This commit is contained in:
commit
6fa4dd2807
@ -125,6 +125,10 @@ When you are ready to remove the error page, you can call the [`clearError`](/do
|
||||
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.
|
||||
::
|
||||
|
||||
::note
|
||||
Rendering an error page is an entirely separate page load, meaning any registered middleware will run again. You can use [`useError`](#useerror) in middleware to check if an error is being handled.
|
||||
::
|
||||
|
||||
::note
|
||||
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 reached end-of-life in September 2023.
|
||||
::
|
||||
|
@ -134,6 +134,10 @@ export default defineNuxtRouteMiddleware(to => {
|
||||
})
|
||||
```
|
||||
|
||||
::note
|
||||
Rendering an error page is an entirely separate page load, meaning any registered middleware will run again. You can use [`useError`](/docs/getting-started/error-handling#useerror) in middleware to check if an error is being handled.
|
||||
::
|
||||
|
||||
## Adding Middleware Dynamically
|
||||
|
||||
It is possible to add global or named route middleware manually using the [`addRouteMiddleware()`](/docs/api/utils/add-route-middleware) helper function, such as from within a plugin.
|
||||
|
@ -98,7 +98,7 @@ Hook | Arguments | Description
|
||||
`render:html` | `html, { event }` | Called before constructing the HTML. | [html](https://github.com/nuxt/nuxt/blob/71ef8bd3ff207fd51c2ca18d5a8c7140476780c7/packages/nuxt/src/core/runtime/nitro/renderer.ts#L15), [event](https://github.com/unjs/h3/blob/f6ceb5581043dc4d8b6eab91e9be4531e0c30f8e/src/types.ts#L38)
|
||||
`render:island` | `islandResponse, { event, islandContext }` | Called before constructing the island HTML. | [islandResponse](https://github.com/nuxt/nuxt/blob/e50cabfed1984c341af0d0c056a325a8aec26980/packages/nuxt/src/core/runtime/nitro/renderer.ts#L28), [event](https://github.com/unjs/h3/blob/f6ceb5581043dc4d8b6eab91e9be4531e0c30f8e/src/types.ts#L38), [islandContext](https://github.com/nuxt/nuxt/blob/e50cabfed1984c341af0d0c056a325a8aec26980/packages/nuxt/src/core/runtime/nitro/renderer.ts#L38)
|
||||
`close` | - | Called when Nitro is closed. | -
|
||||
`error` | `error, { event? }` | Called when an error occurs. | [error](https://github.com/unjs/nitro/blob/main/src/runtime/types.ts#L24), [event](https://github.com/unjs/h3/blob/f6ceb5581043dc4d8b6eab91e9be4531e0c30f8e/src/types.ts#L38)
|
||||
`error` | `error, { event? }` | Called when an error occurs. | [error](https://github.com/unjs/nitro/blob/d20ffcbd16fc4003b774445e1a01e698c2bb078a/src/types/runtime/nitro.ts#L48), [event](https://github.com/unjs/h3/blob/f6ceb5581043dc4d8b6eab91e9be4531e0c30f8e/src/types.ts#L38)
|
||||
`request` | `event` | Called when a request is received. | [event](https://github.com/unjs/h3/blob/f6ceb5581043dc4d8b6eab91e9be4531e0c30f8e/src/types.ts#L38)
|
||||
`beforeResponse` | `event, { body }` | Called before sending the response. | [event](https://github.com/unjs/h3/blob/f6ceb5581043dc4d8b6eab91e9be4531e0c30f8e/src/types.ts#L38), unknown
|
||||
`afterResponse` | `event, { body }` | Called after sending the response. | [event](https://github.com/unjs/h3/blob/f6ceb5581043dc4d8b6eab91e9be4531e0c30f8e/src/types.ts#L38), unknown
|
||||
|
@ -382,7 +382,7 @@ export function createNuxtApp (options: CreateOptions) {
|
||||
|
||||
// Expose runtime config
|
||||
const runtimeConfig = import.meta.server ? options.ssrContext!.runtimeConfig : nuxtApp.payload.config!
|
||||
nuxtApp.provide('config', runtimeConfig)
|
||||
nuxtApp.provide('config', import.meta.client && import.meta.dev ? wrappedConfig(runtimeConfig) : runtimeConfig)
|
||||
|
||||
return nuxtApp
|
||||
}
|
||||
@ -545,3 +545,20 @@ function defineGetter<K extends string | number | symbol, V> (obj: Record<K, V>,
|
||||
export function defineAppConfig<C extends AppConfigInput> (config: C): C {
|
||||
return config
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure error getter on runtime secret property access that doesn't exist on the client side
|
||||
*/
|
||||
function wrappedConfig (runtimeConfig: Record<string, unknown>) {
|
||||
if (!import.meta.dev || import.meta.server) { return runtimeConfig }
|
||||
const keys = Object.keys(runtimeConfig).map(key => `\`${key}\``)
|
||||
const lastKey = keys.pop()
|
||||
return new Proxy(runtimeConfig, {
|
||||
get (target, p: string, receiver) {
|
||||
if (p !== 'public' && !(p in target) && !p.startsWith('__v') /* vue check for reactivity, e.g. `__v_isRef` */) {
|
||||
console.warn(`[nuxt] Could not access \`${p}\`. The only available runtime config keys on the client side are ${keys.join(', ')} and ${lastKey}. See \`https://nuxt.com/docs/guide/going-further/runtime-config\` for more information.`)
|
||||
}
|
||||
return Reflect.get(target, p, receiver)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import type { NuxtAppManifestMeta } from '../composables/manifest'
|
||||
import { onNuxtReady } from '../composables/ready'
|
||||
// @ts-expect-error virtual file
|
||||
import { buildAssetsURL } from '#internal/nuxt/paths'
|
||||
// @ts-expect-error virtual file
|
||||
import { outdatedBuildInterval } from '#build/nuxt.config.mjs'
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
if (import.meta.test) { return }
|
||||
@ -13,7 +15,7 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
async function getLatestManifest () {
|
||||
const currentManifest = await getAppManifest()
|
||||
if (timeout) { clearTimeout(timeout) }
|
||||
timeout = setTimeout(getLatestManifest, 1000 * 60 * 60)
|
||||
timeout = setTimeout(getLatestManifest, outdatedBuildInterval)
|
||||
try {
|
||||
const meta = await $fetch<NuxtAppManifestMeta>(buildAssetsURL('builds/latest.json') + `?${Date.now()}`)
|
||||
if (meta.id !== currentManifest.id) {
|
||||
@ -25,5 +27,5 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
}
|
||||
}
|
||||
|
||||
onNuxtReady(() => { timeout = setTimeout(getLatestManifest, 1000 * 60 * 60) })
|
||||
onNuxtReady(() => { timeout = setTimeout(getLatestManifest, outdatedBuildInterval) })
|
||||
})
|
||||
|
@ -508,7 +508,9 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
global: true,
|
||||
})
|
||||
|
||||
addPlugin(resolve(nuxt.options.appDir, 'plugins/check-outdated-build.client'))
|
||||
if (nuxt.options.experimental.checkOutdatedBuildInterval !== false) {
|
||||
addPlugin(resolve(nuxt.options.appDir, 'plugins/check-outdated-build.client'))
|
||||
}
|
||||
}
|
||||
|
||||
nuxt.hooks.hook('builder:watch', (event, relativePath) => {
|
||||
|
@ -413,6 +413,7 @@ export const nuxtConfigTemplate: NuxtTemplate = {
|
||||
`export const vueAppRootContainer = ${ctx.nuxt.options.app.rootId ? `'#${ctx.nuxt.options.app.rootId}'` : `'body > ${ctx.nuxt.options.app.rootTag}'`}`,
|
||||
`export const viewTransition = ${ctx.nuxt.options.experimental.viewTransition}`,
|
||||
`export const appId = ${JSON.stringify(ctx.nuxt.options.appId)}`,
|
||||
`export const outdatedBuildInterval = ${ctx.nuxt.options.experimental.checkOutdatedBuildInterval}`,
|
||||
].join('\n\n')
|
||||
},
|
||||
}
|
||||
|
@ -326,6 +326,14 @@ export default defineUntypedSchema({
|
||||
*/
|
||||
appManifest: true,
|
||||
|
||||
/**
|
||||
* Set the time interval (in ms) to check for new builds. Disabled when `experimental.appManifest` is `false`.
|
||||
*
|
||||
* Set to `false` to disable.
|
||||
* @type {number | false}
|
||||
*/
|
||||
checkOutdatedBuildInterval: 1000 * 60 * 60,
|
||||
|
||||
/**
|
||||
* Set an alternative watcher that will be used as the watching service for Nuxt.
|
||||
*
|
||||
|
@ -31,6 +31,8 @@
|
||||
"main"
|
||||
],
|
||||
"ignoreDeps": [
|
||||
"jiti",
|
||||
"@vitejs/plugin-vue",
|
||||
"nuxt",
|
||||
"nuxt3",
|
||||
"@nuxt/kit"
|
||||
|
@ -20,7 +20,6 @@ import { callOnce } from '#app/composables/once'
|
||||
import { useLoadingIndicator } from '#app/composables/loading-indicator'
|
||||
import { useRouteAnnouncer } from '#app/composables/route-announcer'
|
||||
|
||||
// @ts-expect-error virtual file
|
||||
import { asyncDataDefaults, nuxtDefaultErrorValue } from '#build/nuxt.config.mjs'
|
||||
|
||||
registerEndpoint('/api/test', defineEventHandler(event => ({
|
||||
@ -38,7 +37,6 @@ describe('app config', () => {
|
||||
`)
|
||||
updateAppConfig({
|
||||
new: 'value',
|
||||
// @ts-expect-error property does not exist
|
||||
nuxt: { nested: 42 },
|
||||
})
|
||||
expect(appConfig).toMatchInlineSnapshot(`
|
||||
@ -165,7 +163,7 @@ describe('useAsyncData', () => {
|
||||
|
||||
// https://github.com/nuxt/nuxt/issues/23411
|
||||
it('should initialize with error set to null when immediate: false', async () => {
|
||||
const { error, execute } = useAsyncData(() => ({}), { immediate: false })
|
||||
const { error, execute } = useAsyncData(() => Promise.resolve({}), { immediate: false })
|
||||
expect(error.value).toBe(asyncDataDefaults.errorValue)
|
||||
await execute()
|
||||
expect(error.value).toBe(asyncDataDefaults.errorValue)
|
||||
@ -217,7 +215,7 @@ describe('useAsyncData', () => {
|
||||
})
|
||||
|
||||
it('allows custom access to a cache', async () => {
|
||||
const { data } = await useAsyncData(() => ({ val: true }), { getCachedData: () => ({ val: false }) })
|
||||
const { data } = await useAsyncData(() => Promise.resolve({ val: true }), { getCachedData: () => ({ val: false }) })
|
||||
expect(data.value).toMatchInlineSnapshot(`
|
||||
{
|
||||
"val": false,
|
||||
@ -317,6 +315,7 @@ describe('useFetch', () => {
|
||||
|
||||
it('should timeout', async () => {
|
||||
const { status, error } = await useFetch(
|
||||
// @ts-expect-error should resolve to a string
|
||||
() => new Promise(resolve => setTimeout(resolve, 5000)),
|
||||
{ timeout: 1 },
|
||||
)
|
||||
@ -534,6 +533,7 @@ describe('loading state', () => {
|
||||
describe.skipIf(process.env.TEST_MANIFEST === 'manifest-off')('app manifests', () => {
|
||||
it('getAppManifest', async () => {
|
||||
const manifest = await getAppManifest()
|
||||
// @ts-expect-error timestamp is not optional
|
||||
delete manifest.timestamp
|
||||
expect(manifest).toMatchInlineSnapshot(`
|
||||
{
|
||||
|
@ -263,12 +263,12 @@ describe('client components', () => {
|
||||
const componentId = 'ClientWithSlot-12345'
|
||||
|
||||
vi.doMock(mockPath, () => ({
|
||||
default: {
|
||||
default: defineComponent({
|
||||
name: 'ClientWithSlot',
|
||||
setup (_, { slots }) {
|
||||
return () => h('div', { class: 'client-component' }, slots.default())
|
||||
return () => h('div', { class: 'client-component' }, slots.default?.())
|
||||
},
|
||||
},
|
||||
}),
|
||||
}))
|
||||
|
||||
const stubFetch = vi.fn(() => {
|
||||
|
@ -11,9 +11,10 @@ vi.mock('#app', async (original) => {
|
||||
}
|
||||
})
|
||||
|
||||
function pluginFactory (name: string, dependsOn?: string[], sequence: string[], parallel = true) {
|
||||
function pluginFactory (name: string, dependsOn: string[] | undefined, sequence: string[], parallel = true) {
|
||||
return defineNuxtPlugin({
|
||||
name,
|
||||
// @ts-expect-error we have a strong type for plugin names
|
||||
dependsOn,
|
||||
async setup () {
|
||||
sequence.push(`start ${name}`)
|
||||
@ -71,7 +72,7 @@ describe('plugin dependsOn', () => {
|
||||
pluginFactory('A', undefined, sequence),
|
||||
pluginFactory('B', ['A'], sequence),
|
||||
defineNuxtPlugin({
|
||||
name,
|
||||
name: 'some plugin',
|
||||
async setup () {
|
||||
sequence.push('start C')
|
||||
await new Promise(resolve => setTimeout(resolve, 5))
|
||||
@ -99,7 +100,7 @@ describe('plugin dependsOn', () => {
|
||||
const plugins = [
|
||||
pluginFactory('A', undefined, sequence),
|
||||
defineNuxtPlugin({
|
||||
name,
|
||||
name: 'some plugin',
|
||||
async setup () {
|
||||
sequence.push('start C')
|
||||
await new Promise(resolve => setTimeout(resolve, 50))
|
||||
|
3
test/nuxt/tsconfig.json
Normal file
3
test/nuxt/tsconfig.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../../.nuxt/tsconfig.json"
|
||||
}
|
@ -44,7 +44,8 @@
|
||||
"**/examples/**",
|
||||
"**/docs/**",
|
||||
"**/playground/**",
|
||||
"**/test/nuxt/**",
|
||||
"**/test/fixtures/**",
|
||||
"test/nuxt/**"
|
||||
"**/test/fixtures-temp/**"
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user