SEO: multilingual homepage (homeSlug per locale in path/sitemap), Article + llms.txt generators, siteDescription fallback, composeTitle dedup
This commit is contained in:
+22
-12
@@ -131,7 +131,10 @@ import { buildRobots, buildSitemapEntries } from '../seo/index.js';
|
||||
* Handles automatically:
|
||||
* - pages collection + content collections (with their archive prefix)
|
||||
* - excludes the homepage (maps to { slug: [] } — the root)
|
||||
* - excludes drafts, 404/500/system slugs, and meta.noindex docs
|
||||
* - excludes drafts and 404/500/system slugs
|
||||
* - KEEPS noindex pages (they must still render — noindex controls indexing,
|
||||
* not existence; skipping them would force dynamic rendering)
|
||||
* - localized slugs (string or per-locale map) both handled
|
||||
* - single-locale → { slug }[]; multi-locale → { locale, slug }[]
|
||||
*
|
||||
* Wire it in the project:
|
||||
@@ -158,24 +161,27 @@ import { buildRobots, buildSitemapEntries } from '../seo/index.js';
|
||||
]);
|
||||
const params = [];
|
||||
for (const locale of locales){
|
||||
// NO where:{_status} filter — collections without drafts enabled don't
|
||||
// register the _status field, and querying it throws
|
||||
// "path cannot be queried: _status". We filter drafts in memory below,
|
||||
// which is safe for every collection (with or without drafts).
|
||||
const result = await payload.find({
|
||||
collection: pagesSlug,
|
||||
depth: 0,
|
||||
limit: 1000,
|
||||
locale: locale ?? 'all',
|
||||
where: {
|
||||
_status: {
|
||||
not_equals: 'draft'
|
||||
}
|
||||
}
|
||||
locale: locale ?? 'all'
|
||||
});
|
||||
for (const raw of result.docs){
|
||||
// Draft filter in memory (safe whether or not the collection has drafts).
|
||||
if (raw._status && raw._status !== 'published') {
|
||||
continue;
|
||||
}
|
||||
if (raw.meta?.noindex) {
|
||||
continue;
|
||||
}
|
||||
// NOTE: unlike the sitemap, we do NOT skip meta.noindex here. A noindex
|
||||
// page (privacy, cookies, terms) still needs to render — users reach it
|
||||
// from the footer and crawlers read its <meta robots=noindex>. Pre-render
|
||||
// it as SSG so it's fast and its <head> is complete; noindex controls
|
||||
// INDEXING, not whether the page exists. Skipping it would force dynamic
|
||||
// rendering (the very streaming problem we're avoiding).
|
||||
if (homeId && raw.id === homeId) {
|
||||
// Homepage → root. Emit an empty-slug param so '/' (or '/pl') builds.
|
||||
const empty = singleLocale ? {
|
||||
@@ -189,12 +195,16 @@ import { buildRobots, buildSitemapEntries } from '../seo/index.js';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const slug = typeof raw.slug === 'string' ? raw.slug : undefined;
|
||||
// Slug may be a plain string OR a localized map ({ pl: 'kontakt' }) when
|
||||
// read with locale:'all' or left unflattened. Handle both, or localized
|
||||
// pages get silently dropped.
|
||||
const rawSlug = raw.slug;
|
||||
const slug = typeof rawSlug === 'string' ? rawSlug : rawSlug && typeof rawSlug === 'object' ? rawSlug[locale ?? ''] ?? Object.values(rawSlug)[0] : undefined;
|
||||
if (!slug || EXCLUDED.has(slug)) {
|
||||
continue;
|
||||
}
|
||||
// Multi-level slugs ('atrakcje/telefon') → array segments.
|
||||
const segments = slug.split('/').filter(Boolean);
|
||||
const segments = String(slug).split('/').filter(Boolean);
|
||||
params.push(singleLocale ? {
|
||||
slug: segments
|
||||
} : {
|
||||
|
||||
Reference in New Issue
Block a user