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:
Daniel Roe 2023-05-03 15:14:12 +01:00 committed by GitHub
parent da3357449f
commit 566fa85fc8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 60 additions and 32 deletions

View File

@ -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))

View File

@ -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

View File

@ -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')
} }
} }

View File

@ -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']
} }
) )
} }

View File

@ -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)

View 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')
})
})
}
})

View 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>

View File

@ -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>