@hagicode/hagilight-starlight 0.3.2-dev.55.1.1e29eba → 0.3.2-dev.58.1.bee3693
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AIDisclosureNotice.astro +1 -1
- package/ArticlePromotion.astro +1 -1
- package/ContentLayoutToggle.astro +2 -2
- package/Footer.astro +10 -2
- package/Header.astro +1 -1
- package/LanguageChooser.astro +2 -2
- package/MarkdownContent.astro +2 -2
- package/NotFoundHero.astro +1 -1
- package/PromotoFooter.astro +1 -1
- package/README.md +32 -0
- package/SEOHead.astro +2 -4
- package/dist/ai-disclosures.d.ts +31 -0
- package/dist/ai-disclosures.js +105 -0
- package/dist/article-promotion.d.ts +15 -0
- package/dist/article-promotion.js +134 -0
- package/dist/content-width-i18n.d.ts +6 -0
- package/dist/content-width-i18n.js +15 -0
- package/dist/content-width.d.ts +5 -0
- package/dist/content-width.js +42 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +490 -0
- package/dist/language-routing.d.ts +18 -0
- package/dist/language-routing.js +95 -0
- package/dist/locales.d.ts +48 -0
- package/dist/locales.js +13 -0
- package/dist/not-found.d.ts +9 -0
- package/dist/not-found.js +28 -0
- package/dist/rss-renderer.d.ts +14 -0
- package/dist/rss-renderer.js +30 -0
- package/dist/rss-utils.d.ts +27 -0
- package/dist/rss-utils.js +65 -0
- package/dist/schema.d.ts +32 -0
- package/dist/schema.js +25 -0
- package/dist/seo-utils.d.ts +74 -0
- package/dist/seo-utils.js +219 -0
- package/package.json +23 -27
- package/rss.[language].xml.ts +1 -1
- package/rss.xml.ts +1 -1
- package/ai-disclosure-schema.d.ts +0 -8
- package/ai-disclosure-schema.mjs +0 -9
- package/ai-disclosures.mjs +0 -108
- package/article-promotion-schema.d.ts +0 -7
- package/article-promotion-schema.mjs +0 -8
- package/article-promotion.mjs +0 -138
- package/content-width-i18n.mjs +0 -16
- package/content-width.mjs +0 -46
- package/index.d.ts +0 -17
- package/index.mjs +0 -535
- package/language-routing.mjs +0 -109
- package/locales.d.ts +0 -8
- package/locales.mjs +0 -14
- package/not-found.mjs +0 -33
- package/rss-renderer.mjs +0 -31
- package/rss-schema.d.ts +0 -7
- package/rss-schema.mjs +0 -8
- package/rss-utils.mjs +0 -62
- package/seo-schema.d.ts +0 -5
- package/seo-schema.mjs +0 -5
- package/seo-utils.mjs +0 -266
package/not-found.mjs
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
const homeLabels = {
|
|
2
|
-
de: 'Zur Startseite',
|
|
3
|
-
en: 'Back to home',
|
|
4
|
-
es: 'Volver al inicio',
|
|
5
|
-
fr: 'Retour à l’accueil',
|
|
6
|
-
ja: 'ホームに戻る',
|
|
7
|
-
ko: '홈으로 돌아가기',
|
|
8
|
-
pt: 'Voltar ao início',
|
|
9
|
-
ru: 'На главную',
|
|
10
|
-
'zh-cn': '返回首页',
|
|
11
|
-
'zh-hant': '返回首頁',
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export function isNotFoundEntry(entryId) {
|
|
15
|
-
return entryId === '404' || entryId.endsWith('/404');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function getNotFoundHomeHref({ basePath = '/', locale, trailingSlash = 'ignore' }) {
|
|
19
|
-
const base = basePath.replace(/\/+$/u, '');
|
|
20
|
-
const localePath = locale && locale !== 'root' ? `/${locale}` : '';
|
|
21
|
-
const path = `${base}${localePath}` || '/';
|
|
22
|
-
|
|
23
|
-
if (trailingSlash === 'never') {
|
|
24
|
-
return path.replace(/\/+$/u, '') || '/';
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return `${path.replace(/\/+$/u, '')}/`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function getNotFoundHomeLabel(lang) {
|
|
31
|
-
const language = lang?.replaceAll('_', '-').toLowerCase() ?? 'en';
|
|
32
|
-
return homeLabels[language] ?? homeLabels[language.split('-')[0]] ?? homeLabels.en;
|
|
33
|
-
}
|
package/rss-renderer.mjs
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { generateRssFeed as generateCoreRssFeed } from '@hagicode/hagilight/rss';
|
|
2
|
-
import { selectRssEntries } from './rss-utils.mjs';
|
|
3
|
-
|
|
4
|
-
export function generateRssFeed({ site, baseUrl, filename, locales, options, entries }) {
|
|
5
|
-
if (!site) throw new Error('Hagilight RSS requires the Astro site option.');
|
|
6
|
-
const locale = locales.find((item) => item.filename === filename);
|
|
7
|
-
if (!locale && filename !== 'en') {
|
|
8
|
-
throw new Error(`Hagilight RSS has no configured locale for "${filename}".`);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const items = selectRssEntries(entries, { filename, locales, options }).map(({ id, data }) => {
|
|
12
|
-
const slug = id.replace(/(?:^|\/)index$/u, '');
|
|
13
|
-
return {
|
|
14
|
-
title: data.title,
|
|
15
|
-
description: data.description,
|
|
16
|
-
...(data.lastUpdated instanceof Date && !Number.isNaN(data.lastUpdated.getTime())
|
|
17
|
-
? { date: data.lastUpdated }
|
|
18
|
-
: {}),
|
|
19
|
-
link: slug ? `${slug}/` : '/',
|
|
20
|
-
};
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
return generateCoreRssFeed({
|
|
24
|
-
site,
|
|
25
|
-
baseUrl,
|
|
26
|
-
language: locale?.lang ?? 'en',
|
|
27
|
-
title: 'Documentation',
|
|
28
|
-
description: 'Recently updated documentation pages',
|
|
29
|
-
items,
|
|
30
|
-
});
|
|
31
|
-
}
|
package/rss-schema.d.ts
DELETED
package/rss-schema.mjs
DELETED
package/rss-utils.mjs
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
export { resolveRssLocales } from '@hagicode/hagilight/rss-locales';
|
|
2
|
-
|
|
3
|
-
export function resolveRssOptions(options = {}) {
|
|
4
|
-
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
5
|
-
throw new TypeError('Hagilight rss options must be an object.');
|
|
6
|
-
}
|
|
7
|
-
for (const key of ['includeDocs', 'includeBlog']) {
|
|
8
|
-
if (options[key] !== undefined && typeof options[key] !== 'boolean') {
|
|
9
|
-
throw new TypeError(`Hagilight rss ${key} option must be a boolean.`);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
return {
|
|
13
|
-
includeDocs: options.includeDocs ?? true,
|
|
14
|
-
includeBlog: options.includeBlog ?? true,
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function getLocaleForEntry(id, locales) {
|
|
19
|
-
const localized = locales
|
|
20
|
-
.filter(({ route }) => route !== 'root')
|
|
21
|
-
.sort((a, b) => b.route.length - a.route.length)
|
|
22
|
-
.find(({ route }) => id === route || id.startsWith(`${route}/`));
|
|
23
|
-
if (localized) return { locale: localized, relativeId: id.slice(localized.route.length).replace(/^\/+/, '') };
|
|
24
|
-
|
|
25
|
-
const root = locales.find(({ route }) => route === 'root');
|
|
26
|
-
if (root) return { locale: root, relativeId: id };
|
|
27
|
-
return undefined;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function isBlogEntry(id) {
|
|
31
|
-
const segments = id.split('/').filter(Boolean);
|
|
32
|
-
if (segments.at(-1) === 'index') segments.pop();
|
|
33
|
-
return segments[0] === 'blog' && segments.length > 1;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function selectRssEntries(entries, { filename, locales, options }) {
|
|
37
|
-
const locale = locales.find((item) => item.filename === filename);
|
|
38
|
-
if (!locale) {
|
|
39
|
-
if (filename === 'en') return [];
|
|
40
|
-
throw new Error(`Hagilight RSS has no configured locale for "${filename}".`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return entries
|
|
44
|
-
.filter(({ id, data }) => {
|
|
45
|
-
if (data.rss !== undefined && typeof data.rss !== 'boolean') {
|
|
46
|
-
throw new TypeError(`Hagilight RSS frontmatter field "rss" for "${id}" must be a boolean.`);
|
|
47
|
-
}
|
|
48
|
-
if (data.draft || data.rss === false) return false;
|
|
49
|
-
|
|
50
|
-
const entryLocale = getLocaleForEntry(id, locales);
|
|
51
|
-
if (entryLocale?.locale.filename !== filename) return false;
|
|
52
|
-
const isBlog = isBlogEntry(entryLocale.relativeId);
|
|
53
|
-
return isBlog ? options.includeBlog : options.includeDocs;
|
|
54
|
-
})
|
|
55
|
-
.sort((a, b) => {
|
|
56
|
-
const aDate = a.data.lastUpdated instanceof Date ? a.data.lastUpdated.getTime() : undefined;
|
|
57
|
-
const bDate = b.data.lastUpdated instanceof Date ? b.data.lastUpdated.getTime() : undefined;
|
|
58
|
-
if (aDate === undefined || Number.isNaN(aDate)) return bDate === undefined || Number.isNaN(bDate) ? 0 : 1;
|
|
59
|
-
if (bDate === undefined || Number.isNaN(bDate)) return -1;
|
|
60
|
-
return bDate - aDate;
|
|
61
|
-
});
|
|
62
|
-
}
|
package/seo-schema.d.ts
DELETED
package/seo-schema.mjs
DELETED
package/seo-utils.mjs
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import { isBlogEntry } from './rss-utils.mjs';
|
|
2
|
-
import {
|
|
3
|
-
buildArticleStructuredData,
|
|
4
|
-
buildBreadcrumbStructuredData,
|
|
5
|
-
buildOrganizationStructuredData,
|
|
6
|
-
} from '@hagicode/hagilight/seo-utils';
|
|
7
|
-
|
|
8
|
-
export {
|
|
9
|
-
composeSeoHead,
|
|
10
|
-
extractSeoDescription,
|
|
11
|
-
isValidSeoImageReference,
|
|
12
|
-
resolveSeoImageUrl,
|
|
13
|
-
resolveSeoMetadata,
|
|
14
|
-
serializeJsonLd,
|
|
15
|
-
} from '@hagicode/hagilight/seo-utils';
|
|
16
|
-
|
|
17
|
-
const normalizeSlug = (slug) => slug
|
|
18
|
-
.replace(/^\/+|\/+$/gu, '')
|
|
19
|
-
.replace(/(?:^|\/)index$/u, '');
|
|
20
|
-
const publishedLookupCache = new WeakMap();
|
|
21
|
-
|
|
22
|
-
export function resolveSeoLocales(locales) {
|
|
23
|
-
const entries = locales === undefined
|
|
24
|
-
? [['root', { lang: 'en' }]]
|
|
25
|
-
: Object.entries(locales);
|
|
26
|
-
const usedLanguages = new Map();
|
|
27
|
-
const resolved = entries.map(([route, config]) => {
|
|
28
|
-
const lang = typeof config === 'string'
|
|
29
|
-
? config
|
|
30
|
-
: config?.lang ?? (route === 'root' ? undefined : route);
|
|
31
|
-
if (typeof lang !== 'string' || !lang.trim()) {
|
|
32
|
-
throw new TypeError(`Hagilight SEO locale "${route}" must have a language tag.`);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
let normalizedLang;
|
|
36
|
-
try {
|
|
37
|
-
[normalizedLang] = Intl.getCanonicalLocales(lang);
|
|
38
|
-
} catch {
|
|
39
|
-
throw new TypeError(`Hagilight SEO locale "${route}" has an invalid language tag "${lang}".`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const collisionKey = normalizedLang.toLowerCase();
|
|
43
|
-
const existingRoute = usedLanguages.get(collisionKey);
|
|
44
|
-
if (existingRoute) {
|
|
45
|
-
throw new Error(
|
|
46
|
-
`Hagilight SEO locales "${existingRoute}" and "${route}" use the same language tag "${normalizedLang}".`,
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
usedLanguages.set(collisionKey, route);
|
|
50
|
-
return { route, lang: normalizedLang };
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
if (resolved.length === 0) {
|
|
54
|
-
throw new Error('Hagilight SEO requires at least one configured Starlight locale.');
|
|
55
|
-
}
|
|
56
|
-
return resolved;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function resolveSeoPage(entryId, locales) {
|
|
60
|
-
const id = normalizeSlug(entryId ?? '');
|
|
61
|
-
const localizedRoutes = locales
|
|
62
|
-
.filter(({ route }) => route !== 'root')
|
|
63
|
-
.sort((left, right) => right.route.length - left.route.length);
|
|
64
|
-
const localized = localizedRoutes.find(({ route }) => id === route || id.startsWith(`${route}/`));
|
|
65
|
-
if (localized) {
|
|
66
|
-
return {
|
|
67
|
-
route: localized.route,
|
|
68
|
-
slug: normalizeSlug(id.slice(localized.route.length)),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
return { route: 'root', slug: id };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function createPublishedDocsLookup(entries, locales) {
|
|
75
|
-
const localeKey = JSON.stringify(locales);
|
|
76
|
-
let lookups = publishedLookupCache.get(entries);
|
|
77
|
-
if (lookups?.has(localeKey)) return lookups.get(localeKey);
|
|
78
|
-
|
|
79
|
-
const pages = new Map();
|
|
80
|
-
for (const entry of entries) {
|
|
81
|
-
if (!entry || entry.data?.draft === true) continue;
|
|
82
|
-
const page = resolveSeoPage(entry.id, locales);
|
|
83
|
-
pages.set(`${page.route}\u0000${page.slug}`, entry);
|
|
84
|
-
}
|
|
85
|
-
const lookup = {
|
|
86
|
-
get(route, slug) {
|
|
87
|
-
return pages.get(`${route}\u0000${normalizeSlug(slug)}`);
|
|
88
|
-
},
|
|
89
|
-
has(route, slug) {
|
|
90
|
-
return pages.has(`${route}\u0000${normalizeSlug(slug)}`);
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
if (!lookups) {
|
|
94
|
-
lookups = new Map();
|
|
95
|
-
publishedLookupCache.set(entries, lookups);
|
|
96
|
-
}
|
|
97
|
-
lookups.set(localeKey, lookup);
|
|
98
|
-
return lookup;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function parseCandidatePage(href, { locales, basePath }) {
|
|
102
|
-
let pathname;
|
|
103
|
-
try {
|
|
104
|
-
pathname = new URL(href).pathname;
|
|
105
|
-
} catch {
|
|
106
|
-
return undefined;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const base = `/${(basePath ?? '/').replace(/^\/+|\/+$/gu, '')}`;
|
|
110
|
-
if (base !== '/' && pathname !== base && !pathname.startsWith(`${base}/`)) return undefined;
|
|
111
|
-
if (base !== '/') pathname = pathname.slice(base.length) || '/';
|
|
112
|
-
|
|
113
|
-
const segments = pathname.split('/').filter(Boolean);
|
|
114
|
-
if (segments.at(-1) === 'index.html') segments.pop();
|
|
115
|
-
else if (segments.at(-1)?.endsWith('.html')) {
|
|
116
|
-
segments[segments.length - 1] = segments.at(-1).slice(0, -5);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const routes = locales
|
|
120
|
-
.filter(({ route }) => route !== 'root')
|
|
121
|
-
.sort((left, right) => right.route.length - left.route.length);
|
|
122
|
-
let route = 'root';
|
|
123
|
-
if (segments[0]?.endsWith('.html')) {
|
|
124
|
-
const first = segments[0].slice(0, -5);
|
|
125
|
-
const matched = routes.find((locale) => locale.route === first);
|
|
126
|
-
if (matched) {
|
|
127
|
-
route = matched.route;
|
|
128
|
-
segments.shift();
|
|
129
|
-
}
|
|
130
|
-
} else {
|
|
131
|
-
const matched = routes.find(({ route: localeRoute }) => segments[0] === localeRoute);
|
|
132
|
-
if (matched) {
|
|
133
|
-
route = matched.route;
|
|
134
|
-
segments.shift();
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return { route, slug: normalizeSlug(segments.join('/')) };
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function isAlternateLink(entry) {
|
|
142
|
-
return entry?.tag === 'link'
|
|
143
|
-
&& entry.attrs?.rel === 'alternate'
|
|
144
|
-
&& typeof entry.attrs.hreflang === 'string'
|
|
145
|
-
&& typeof entry.attrs.href === 'string';
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function explicitAlternateKeys(heads) {
|
|
149
|
-
return new Set(heads.flatMap((head) => (Array.isArray(head) ? head : [])
|
|
150
|
-
.filter(isAlternateLink)
|
|
151
|
-
.map(({ attrs }) => `${attrs.hreflang.toLowerCase()}\u0000${attrs.href}`)));
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export function filterPublishedLocaleAlternates(head, {
|
|
155
|
-
entryId,
|
|
156
|
-
locales,
|
|
157
|
-
lookup,
|
|
158
|
-
basePath = '/',
|
|
159
|
-
explicitHead = [],
|
|
160
|
-
}) {
|
|
161
|
-
const page = resolveSeoPage(entryId, locales);
|
|
162
|
-
const explicit = explicitAlternateKeys(explicitHead);
|
|
163
|
-
const routesByLanguage = new Map(locales.map(({ route, lang }) => [lang.toLowerCase(), route]));
|
|
164
|
-
|
|
165
|
-
return head.filter((entry) => {
|
|
166
|
-
if (!isAlternateLink(entry)) return true;
|
|
167
|
-
const { href, hreflang } = entry.attrs;
|
|
168
|
-
if (explicit.has(`${hreflang.toLowerCase()}\u0000${href}`)) return true;
|
|
169
|
-
|
|
170
|
-
const candidate = parseCandidatePage(href, { locales, basePath });
|
|
171
|
-
if (!candidate || candidate.slug !== page.slug) return true;
|
|
172
|
-
if (hreflang.toLowerCase() === 'x-default') {
|
|
173
|
-
return lookup.has(candidate.route, candidate.slug);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const configuredRoute = routesByLanguage.get(hreflang.toLowerCase());
|
|
177
|
-
if (!configuredRoute || configuredRoute !== candidate.route) return true;
|
|
178
|
-
return lookup.has(configuredRoute, candidate.slug);
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function normalizeBasePath(basePath = '/') {
|
|
183
|
-
const normalized = `/${basePath.replace(/^\/+|\/+$/gu, '')}`;
|
|
184
|
-
return normalized === '/' ? '' : normalized;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export function buildDocsPageUrl(route, slug, {
|
|
188
|
-
site,
|
|
189
|
-
basePath = '/',
|
|
190
|
-
format = 'directory',
|
|
191
|
-
trailingSlash = 'always',
|
|
192
|
-
}) {
|
|
193
|
-
const id = [route === 'root' ? '' : route, normalizeSlug(slug)].filter(Boolean).join('/');
|
|
194
|
-
let pagePath;
|
|
195
|
-
if (format === 'file') {
|
|
196
|
-
pagePath = `/${id ? `${id}.html` : 'index.html'}`;
|
|
197
|
-
} else if (!id) {
|
|
198
|
-
pagePath = '/';
|
|
199
|
-
} else {
|
|
200
|
-
pagePath = `/${id}${trailingSlash === 'never' ? '' : '/'}`;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const base = normalizeBasePath(basePath);
|
|
204
|
-
return new URL(`${base}${pagePath}`, site).href;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export function buildStructuredData({
|
|
208
|
-
entry,
|
|
209
|
-
entryId,
|
|
210
|
-
canonicalUrl,
|
|
211
|
-
site,
|
|
212
|
-
siteTitle,
|
|
213
|
-
pageSeo = {},
|
|
214
|
-
organization,
|
|
215
|
-
locales,
|
|
216
|
-
defaultLocale = 'root',
|
|
217
|
-
lookup,
|
|
218
|
-
basePath = '/',
|
|
219
|
-
format = 'directory',
|
|
220
|
-
trailingSlash = 'always',
|
|
221
|
-
}) {
|
|
222
|
-
if (!entry || entry.data?.draft === true || !canonicalUrl) return [];
|
|
223
|
-
const page = resolveSeoPage(entryId, locales);
|
|
224
|
-
if (!lookup.has(page.route, page.slug)) return [];
|
|
225
|
-
|
|
226
|
-
const data = entry.data ?? {};
|
|
227
|
-
const structuredData = [];
|
|
228
|
-
if (isBlogEntry(page.slug)) {
|
|
229
|
-
structuredData.push(buildArticleStructuredData({
|
|
230
|
-
url: canonicalUrl,
|
|
231
|
-
title: data.title,
|
|
232
|
-
description: pageSeo.description ?? data.description,
|
|
233
|
-
author: pageSeo.author ?? data.author,
|
|
234
|
-
publishedDate: pageSeo.publishedDate ?? data.publishedDate,
|
|
235
|
-
modifiedDate: data.lastUpdated,
|
|
236
|
-
}));
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const slugParts = page.slug.split('/').filter(Boolean);
|
|
240
|
-
const crumbs = [];
|
|
241
|
-
for (let index = 0; index <= slugParts.length; index += 1) {
|
|
242
|
-
const ancestorSlug = slugParts.slice(0, index).join('/');
|
|
243
|
-
const ancestor = lookup.get(page.route, ancestorSlug);
|
|
244
|
-
if (!ancestor) continue;
|
|
245
|
-
const url = ancestorSlug === page.slug
|
|
246
|
-
? canonicalUrl
|
|
247
|
-
: buildDocsPageUrl(page.route, ancestorSlug, {
|
|
248
|
-
site,
|
|
249
|
-
basePath,
|
|
250
|
-
format,
|
|
251
|
-
trailingSlash,
|
|
252
|
-
});
|
|
253
|
-
crumbs.push({ name: ancestor.data.title, url });
|
|
254
|
-
}
|
|
255
|
-
if (crumbs.length > 0) {
|
|
256
|
-
structuredData.push(buildBreadcrumbStructuredData(crumbs));
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (organization && page.route === defaultLocale && page.slug === '') {
|
|
260
|
-
structuredData.push(buildOrganizationStructuredData({
|
|
261
|
-
...organization,
|
|
262
|
-
alternateName: siteTitle,
|
|
263
|
-
}));
|
|
264
|
-
}
|
|
265
|
-
return structuredData;
|
|
266
|
-
}
|