- seoSitemapPages (lib/sitemapPages.ts) builds the routes per locale; the module adds them from a pages:resolved hook registered on ready, after @nuxtjs/i18n has localised the app's routes, so no cross-locale copies exist and each name ends in the locale's i18n suffix (L4 WR-05) - a path an app catch-all covers is left to it and logged at build unless seo.htmlSitemap.overCatchAll is true (L4 WR-04) - SitemapLink falls back to the i18n locale and renders only when the path resolves to the layer's sitemap page of that locale - an app with only app.vue no longer gets its router turned on by the pages
42 lines
2.1 KiB
TypeScript
42 lines
2.1 KiB
TypeScript
// The layer's pagination and HTML-sitemap module options normalised for
|
|
// the runtime config (lib/seo-module.ts) and their readers in app and
|
|
// server code. Pure, so `node --test` drives it.
|
|
|
|
export interface SeoPaginationOption {
|
|
form: 'query' | 'path'
|
|
param: string
|
|
segments: Record<string, string>
|
|
}
|
|
|
|
/**
|
|
* The HTML sitemap pages by locale, or false for none. overCatchAll: serve
|
|
* the layer's pages ahead of an app catch-all page that covers the path
|
|
* (default false: the catch-all keeps the path, L4 WR-04).
|
|
*/
|
|
export type SeoHtmlSitemapOption = { paths: Record<string, string>; overCatchAll: boolean } | false
|
|
|
|
/** The pagination option with its defaults, as the runtime config carries it. */
|
|
export function paginationOption(value: unknown): SeoPaginationOption {
|
|
const v = value !== null && typeof value === 'object' ? (value as Record<string, unknown>) : {}
|
|
const segments: Record<string, string> = {}
|
|
const raw = v.segments !== null && typeof v.segments === 'object' ? (v.segments as Record<string, unknown>) : {}
|
|
for (const [locale, segment] of Object.entries(raw)) if (typeof segment === 'string' && /^[\p{L}\p{N}_-]+$/u.test(segment)) segments[locale] = segment
|
|
return {
|
|
form: v.form === 'path' ? 'path' : 'query',
|
|
param: typeof v.param === 'string' && /^[A-Za-z0-9_-]+$/.test(v.param) ? v.param : 'page',
|
|
segments,
|
|
}
|
|
}
|
|
|
|
/** The HTML sitemap option: false, or the paths (each '/' plus at least one character) and overCatchAll. */
|
|
export function htmlSitemapOption(value: unknown): SeoHtmlSitemapOption {
|
|
if (value === false) return false
|
|
const v = value !== null && typeof value === 'object' ? (value as Record<string, unknown>) : {}
|
|
const paths: Record<string, string> = {}
|
|
const raw = v.paths !== null && typeof v.paths === 'object' ? (v.paths as Record<string, unknown>) : {}
|
|
for (const [locale, path] of Object.entries(raw)) {
|
|
if (typeof path === 'string' && /^\/[^?#\s]+$/.test(path) && !path.includes(':') && !path.includes('*')) paths[locale] = path.replace(/\/+$/, '')
|
|
}
|
|
return { paths, overCatchAll: v.overCatchAll === true }
|
|
}
|