feat(seo): centralize route metadata and expand sitemap coverage
Remove static fallback tags from Nuxt config to prevent duplication. Auto-apply noindex to authenticated and permissioned routes. Add home, project updates, and legal pages to sitemap. Properly escape JSON-LD structured data.
This commit is contained in:
@@ -30,35 +30,10 @@ export default defineNuxtConfig({
|
||||
meta: [
|
||||
{ charset: 'utf-8' },
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' },
|
||||
{
|
||||
name: 'description',
|
||||
content:
|
||||
'Browse Pokopia Wiki for Pokemon, Event Pokemon, habitats, Event Habitats, items, Event Items, Ancient Artifacts, recipes, daily tasks, and Life community posts for Pokemon Pokopia.'
|
||||
},
|
||||
{ name: 'robots', content: 'index, follow' },
|
||||
{ name: 'theme-color', content: '#6ccf32' },
|
||||
{ property: 'og:site_name', content: 'Pokopia Wiki' },
|
||||
{ property: 'og:type', content: 'website' },
|
||||
{ property: 'og:title', content: 'Pokopia Wiki - Pokemon Pokopia Guide' },
|
||||
{
|
||||
property: 'og:description',
|
||||
content:
|
||||
'Browse Pokopia Wiki for Pokemon, Event Pokemon, habitats, Event Habitats, items, Event Items, Ancient Artifacts, recipes, daily tasks, and Life community posts for Pokemon Pokopia.'
|
||||
},
|
||||
{ property: 'og:image', content: `${normalizeSiteUrl(process.env.NUXT_PUBLIC_SITE_URL ?? process.env.VITE_SITE_URL)}/seo/pokopia-hero.jpg` },
|
||||
{ property: 'og:locale', content: 'en_US' },
|
||||
{ name: 'twitter:card', content: 'summary_large_image' },
|
||||
{ name: 'twitter:title', content: 'Pokopia Wiki - Pokemon Pokopia Guide' },
|
||||
{
|
||||
name: 'twitter:description',
|
||||
content:
|
||||
'Browse Pokopia Wiki for Pokemon, Event Pokemon, habitats, Event Habitats, items, Event Items, Ancient Artifacts, recipes, daily tasks, and Life community posts for Pokemon Pokopia.'
|
||||
},
|
||||
{ name: 'twitter:image', content: `${normalizeSiteUrl(process.env.NUXT_PUBLIC_SITE_URL ?? process.env.VITE_SITE_URL)}/seo/pokopia-hero.jpg` }
|
||||
{ name: 'theme-color', content: '#6ccf32' }
|
||||
],
|
||||
link: [
|
||||
{ rel: 'icon', href: '/favicon.ico', sizes: '32x32' },
|
||||
{ rel: 'canonical', href: `${normalizeSiteUrl(process.env.NUXT_PUBLIC_SITE_URL ?? process.env.VITE_SITE_URL)}/pokemon` }
|
||||
{ rel: 'icon', href: '/favicon.ico', sizes: '32x32' }
|
||||
],
|
||||
script: [
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ export default defineNuxtPlugin(() => {
|
||||
const t = (nuxtApp.$pokopiaI18n as { global: { t: (key: string, values?: Record<string, string | number>) => string } }).global.t;
|
||||
const dynamicSeo = ref<ResolvedSeoConfig | null>(null);
|
||||
const activeSeo = computed(() => dynamicSeo.value ?? resolveRouteSeo(router.currentRoute.value, t));
|
||||
const structuredDataJson = computed(() => JSON.stringify(activeSeo.value.structuredData).replace(/</g, '\\u003C'));
|
||||
|
||||
useHead(() => ({
|
||||
title: activeSeo.value.title,
|
||||
@@ -35,7 +36,7 @@ export default defineNuxtPlugin(() => {
|
||||
key: 'pokopia-structured-data',
|
||||
id: 'pokopia-structured-data',
|
||||
type: 'application/ld+json',
|
||||
children: JSON.stringify(activeSeo.value.structuredData)
|
||||
innerHTML: structuredDataJson.value
|
||||
}
|
||||
]
|
||||
}));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const fallbackSiteUrl = 'https://pokopiawiki.tootaio.com';
|
||||
|
||||
const sitemapPaths = [
|
||||
'/',
|
||||
'/pokemon',
|
||||
'/event-pokemon',
|
||||
'/habitats',
|
||||
@@ -11,7 +12,11 @@ const sitemapPaths = [
|
||||
'/recipes',
|
||||
'/dish',
|
||||
'/checklist',
|
||||
'/life'
|
||||
'/life',
|
||||
'/project-updates',
|
||||
'/privacy-policy',
|
||||
'/terms-of-service',
|
||||
'/disclaimers'
|
||||
];
|
||||
|
||||
const robotsDisallowPaths = [
|
||||
|
||||
@@ -149,13 +149,20 @@ export function routeSeoConfig(route: RouteLocationNormalizedLoaded, translator?
|
||||
typeof routeSeo?.canonicalPath === 'function'
|
||||
? routeSeo.canonicalPath(route)
|
||||
: routeSeo?.canonicalPath ?? route.path ?? defaultCanonicalPath;
|
||||
const requiresPrivateAccess = route.matched.some(
|
||||
(record) =>
|
||||
record.meta.requiresAuth === true ||
|
||||
record.meta.requiresVerified === true ||
|
||||
typeof record.meta.requiredPermission === 'string' ||
|
||||
(Array.isArray(record.meta.requiredAnyPermission) && record.meta.requiredAnyPermission.length > 0)
|
||||
);
|
||||
|
||||
return {
|
||||
title: routeSeo?.titleKey ? translateSeo(routeSeo.titleKey, undefined, translator) : routeSeo?.title,
|
||||
description: routeSeo?.descriptionKey ? translateSeo(routeSeo.descriptionKey, undefined, translator) : routeSeo?.description,
|
||||
canonicalPath,
|
||||
image: routeSeo?.image,
|
||||
noindex: routeSeo?.noindex
|
||||
noindex: routeSeo?.noindex === true || requiresPrivateAccess
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user