mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 15:15:19 +00:00
fix(nuxt): run page meta plugin on all pages (and only pages) (#20628)
Co-authored-by: Julien Huang <julien.huang@outlook.fr>
This commit is contained in:
parent
da3357449f
commit
566fa85fc8
@ -151,10 +151,7 @@ export default defineNuxtModule({
|
|||||||
// Extract macros from pages
|
// Extract macros from pages
|
||||||
const pageMetaOptions: PageMetaPluginOptions = {
|
const pageMetaOptions: PageMetaPluginOptions = {
|
||||||
dev: nuxt.options.dev,
|
dev: nuxt.options.dev,
|
||||||
sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client,
|
sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client
|
||||||
dirs: nuxt.options._layers.map(
|
|
||||||
layer => resolve(layer.config.srcDir, layer.config.dir?.pages || 'pages')
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
nuxt.hook('modules:done', () => {
|
nuxt.hook('modules:done', () => {
|
||||||
addVitePlugin(() => PageMetaPlugin.vite(pageMetaOptions))
|
addVitePlugin(() => PageMetaPlugin.vite(pageMetaOptions))
|
||||||
|
@ -10,7 +10,6 @@ import MagicString from 'magic-string'
|
|||||||
import { isAbsolute, normalize } from 'pathe'
|
import { isAbsolute, normalize } from 'pathe'
|
||||||
|
|
||||||
export interface PageMetaPluginOptions {
|
export interface PageMetaPluginOptions {
|
||||||
dirs: Array<string | RegExp>
|
|
||||||
dev?: boolean
|
dev?: boolean
|
||||||
sourcemap?: boolean
|
sourcemap?: boolean
|
||||||
}
|
}
|
||||||
@ -42,11 +41,7 @@ export const PageMetaPlugin = createUnplugin((options: PageMetaPluginOptions) =>
|
|||||||
const query = parseMacroQuery(id)
|
const query = parseMacroQuery(id)
|
||||||
id = normalize(id)
|
id = normalize(id)
|
||||||
|
|
||||||
const isPagesDir = options.dirs.some(dir => typeof dir === 'string' ? id.startsWith(dir) : dir.test(id))
|
return !!query.macro
|
||||||
if (!isPagesDir && !query.macro) { return false }
|
|
||||||
|
|
||||||
const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href))
|
|
||||||
return /\.(m?[jt]sx?|vue)/.test(pathname)
|
|
||||||
},
|
},
|
||||||
transform (code, id) {
|
transform (code, id) {
|
||||||
const query = parseMacroQuery(id)
|
const query = parseMacroQuery(id)
|
||||||
@ -66,26 +61,6 @@ export const PageMetaPlugin = createUnplugin((options: PageMetaPluginOptions) =>
|
|||||||
|
|
||||||
const hasMacro = code.match(/\bdefinePageMeta\s*\(\s*/)
|
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)
|
const imports = findStaticImports(code)
|
||||||
|
|
||||||
// [vite] Re-export any script imports
|
// [vite] Re-export any script imports
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import type { KeepAliveProps, TransitionProps, UnwrapRef } from 'vue'
|
import type { KeepAliveProps, TransitionProps, UnwrapRef } from 'vue'
|
||||||
|
import { getCurrentInstance } from 'vue'
|
||||||
import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteRecordRedirectOption } from 'vue-router'
|
import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteRecordRedirectOption } from 'vue-router'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
import type { NuxtError } from '#app'
|
import type { NuxtError } from '#app'
|
||||||
|
|
||||||
export interface PageMeta {
|
export interface PageMeta {
|
||||||
@ -51,6 +53,14 @@ const warnRuntimeUsage = (method: string) =>
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
export const definePageMeta = (meta: PageMeta): void => {
|
export const definePageMeta = (meta: PageMeta): void => {
|
||||||
if (process.dev) {
|
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')
|
warnRuntimeUsage('definePageMeta')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,7 @@ export default defineUntypedSchema({
|
|||||||
$resolve: async (val, get) => defu(val || {},
|
$resolve: async (val, get) => defu(val || {},
|
||||||
await get('dev') ? {} : {
|
await get('dev') ? {} : {
|
||||||
vue: ['onBeforeMount', 'onMounted', 'onBeforeUpdate', 'onRenderTracked', 'onRenderTriggered', 'onActivated', 'onDeactivated', 'onBeforeUnmount'],
|
vue: ['onBeforeMount', 'onMounted', 'onBeforeUpdate', 'onRenderTracked', 'onRenderTriggered', 'onActivated', 'onDeactivated', 'onBeforeUnmount'],
|
||||||
'#app': ['definePayloadReviver']
|
'#app': ['definePayloadReviver', 'definePageMeta']
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -179,7 +179,7 @@ export default defineUntypedSchema({
|
|||||||
$resolve: async (val, get) => defu(val || {},
|
$resolve: async (val, get) => defu(val || {},
|
||||||
await get('dev') ? {} : {
|
await get('dev') ? {} : {
|
||||||
vue: ['onServerPrefetch', 'onRenderTracked', 'onRenderTriggered'],
|
vue: ['onServerPrefetch', 'onRenderTracked', 'onRenderTriggered'],
|
||||||
'#app': ['definePayloadReducer']
|
'#app': ['definePayloadReducer', 'definePageMeta']
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,11 @@ describe('pages', () => {
|
|||||||
expect(headers.get('location')).toEqual('/')
|
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 () => {
|
it('validates routes', async () => {
|
||||||
const { status, headers } = await fetch('/forbidden')
|
const { status, headers } = await fetch('/forbidden')
|
||||||
expect(status).toEqual(404)
|
expect(status).toEqual(404)
|
||||||
|
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>
|
5
test/fixtures/minimal/app.vue
vendored
5
test/fixtures/minimal/app.vue
vendored
@ -1,6 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { componentNames } from '#components'
|
import { componentNames } from '#components'
|
||||||
console.log(componentNames)
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
Loading…
Reference in New Issue
Block a user