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

View File

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

View File

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

View File

@ -171,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']
}
)
},
@ -179,7 +179,7 @@ export default defineUntypedSchema({
$resolve: async (val, get) => defu(val || {},
await get('dev') ? {} : {
vue: ['onServerPrefetch', 'onRenderTracked', 'onRenderTriggered'],
'#app': ['definePayloadReducer']
'#app': ['definePayloadReducer', 'definePageMeta']
}
)
}

View File

@ -113,6 +113,11 @@ 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, headers } = await fetch('/forbidden')
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">
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>