- Nitro middleware resolves page requests once and hands the answer to the render (event.context.seo); the route middleware reuses it - useCmsSeo: html lang, hreflang links, og:locale and exactly one JSON-LD script with one @graph (CMS nodes plus useSchemaNode nodes, merged by @id) - local module (configKey seo): cms (alias of public.seo.enabled), breadcrumbs, links.newTabHint, rootFiles, resolveFiles - GET /api/__seo__/resolve for client navigation through the Nuxt server - smoke harness: count=, exact header=, noheader=, reqheader=, repeated options, resolves/apicalls/noreport lines, --no-site; v0.1.1 suite runs unchanged against a backend without /site
32 lines
1.5 KiB
TypeScript
32 lines
1.5 KiB
TypeScript
// The page head from the CMS resolve answer: title, description, robots,
|
|
// canonical, Open Graph (with og:locale and its alternates), X/Twitter,
|
|
// verification meta, <html lang>, hreflang links and exactly one JSON-LD
|
|
// script holding one @graph (the CMS nodes plus those added through
|
|
// useSchemaNode, merged by @id). One entry with tagPriority 'critical', so
|
|
// its tags win over the same tags a page, a layer or a module sets; a value
|
|
// the answer lacks adds no tag and the page's own stays. The layer's app
|
|
// plugin (seo-head) is the only caller: pages and other layers never set CMS
|
|
// values themselves.
|
|
|
|
import type { Ref } from 'vue'
|
|
import { mergeGraph } from '../utils/graph.ts'
|
|
import { seoHeadInput } from '../utils/seo.ts'
|
|
import type { SeoResolveAnswer } from '../utils/types.ts'
|
|
|
|
export function useCmsSeo(answer: Ref<SeoResolveAnswer | null>): void {
|
|
const origin = useRequestURL().origin
|
|
const nodes = useSchemaNodes()
|
|
useHead(
|
|
computed(() => {
|
|
const head = seoHeadInput(answer.value, origin)
|
|
const cmsGraph = answer.value?.action === 'ok' ? answer.value.jsonld : null
|
|
const graph = mergeGraph(cmsGraph, nodes())
|
|
// unhead serializes the object and escapes "<" inside it, so a value
|
|
// such as "</script>" cannot end the script element.
|
|
const script = graph ? [{ type: 'application/ld+json', key: 'seo-jsonld', innerHTML: graph }] : []
|
|
return { ...head, script }
|
|
}),
|
|
{ tagPriority: 'critical' },
|
|
)
|
|
}
|