svedocs 0.1.0-beta.10
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/LICENSE +5 -0
- package/README.md +85 -0
- package/dist/ai/openai-compatible.d.ts +13 -0
- package/dist/ai/openai-compatible.d.ts.map +1 -0
- package/dist/ai/openai-compatible.js +92 -0
- package/dist/ai/providers.d.ts +20 -0
- package/dist/ai/providers.d.ts.map +1 -0
- package/dist/ai/providers.js +185 -0
- package/dist/ai/rate-limit.d.ts +18 -0
- package/dist/ai/rate-limit.d.ts.map +1 -0
- package/dist/ai/rate-limit.js +52 -0
- package/dist/ai/response.d.ts +4 -0
- package/dist/ai/response.d.ts.map +1 -0
- package/dist/ai/response.js +151 -0
- package/dist/ai/runtime.d.ts +32 -0
- package/dist/ai/runtime.d.ts.map +1 -0
- package/dist/ai/runtime.js +111 -0
- package/dist/ai/types.d.ts +93 -0
- package/dist/ai/types.d.ts.map +1 -0
- package/dist/ai/types.js +1 -0
- package/dist/ai.d.ts +7 -0
- package/dist/ai.d.ts.map +1 -0
- package/dist/ai.js +6 -0
- package/dist/ambient.d.ts +5 -0
- package/dist/cloudflare.d.ts +29 -0
- package/dist/cloudflare.d.ts.map +1 -0
- package/dist/cloudflare.js +149 -0
- package/dist/config.d.ts +373 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +299 -0
- package/dist/core/checks.d.ts +4 -0
- package/dist/core/checks.d.ts.map +1 -0
- package/dist/core/checks.js +267 -0
- package/dist/core/config.d.ts +6 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +178 -0
- package/dist/core/content.d.ts +7 -0
- package/dist/core/content.d.ts.map +1 -0
- package/dist/core/content.js +340 -0
- package/dist/core/i18n.d.ts +5 -0
- package/dist/core/i18n.d.ts.map +1 -0
- package/dist/core/i18n.js +117 -0
- package/dist/core/links.d.ts +5 -0
- package/dist/core/links.d.ts.map +1 -0
- package/dist/core/links.js +60 -0
- package/dist/core/messages.d.ts +149 -0
- package/dist/core/messages.d.ts.map +1 -0
- package/dist/core/messages.js +163 -0
- package/dist/core/navigation.d.ts +5 -0
- package/dist/core/navigation.d.ts.map +1 -0
- package/dist/core/navigation.js +144 -0
- package/dist/core/routes.d.ts +36 -0
- package/dist/core/routes.d.ts.map +1 -0
- package/dist/core/routes.js +152 -0
- package/dist/core/search.d.ts +5 -0
- package/dist/core/search.d.ts.map +1 -0
- package/dist/core/search.js +41 -0
- package/dist/core/types.d.ts +468 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +4 -0
- package/dist/core/utils.d.ts +13 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/core/utils.js +60 -0
- package/dist/core.d.ts +9 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +8 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/mdx/ast.d.ts +32 -0
- package/dist/mdx/ast.d.ts.map +1 -0
- package/dist/mdx/ast.js +148 -0
- package/dist/mdx/code.d.ts +17 -0
- package/dist/mdx/code.d.ts.map +1 -0
- package/dist/mdx/code.js +327 -0
- package/dist/mdx/compile.d.ts +29 -0
- package/dist/mdx/compile.d.ts.map +1 -0
- package/dist/mdx/compile.js +59 -0
- package/dist/mdx/diff.d.ts +9 -0
- package/dist/mdx/diff.d.ts.map +1 -0
- package/dist/mdx/diff.js +171 -0
- package/dist/mdx/headings.d.ts +4 -0
- package/dist/mdx/headings.d.ts.map +1 -0
- package/dist/mdx/headings.js +42 -0
- package/dist/mdx/links.d.ts +4 -0
- package/dist/mdx/links.d.ts.map +1 -0
- package/dist/mdx/links.js +144 -0
- package/dist/mdx/utils.d.ts +10 -0
- package/dist/mdx/utils.d.ts.map +1 -0
- package/dist/mdx/utils.js +64 -0
- package/dist/og/image.d.ts +25 -0
- package/dist/og/image.d.ts.map +1 -0
- package/dist/og/image.js +224 -0
- package/dist/og/metadata.d.ts +7 -0
- package/dist/og/metadata.d.ts.map +1 -0
- package/dist/og/metadata.js +198 -0
- package/dist/og/response.d.ts +4 -0
- package/dist/og/response.d.ts.map +1 -0
- package/dist/og/response.js +49 -0
- package/dist/og/rss.d.ts +4 -0
- package/dist/og/rss.d.ts.map +1 -0
- package/dist/og/rss.js +96 -0
- package/dist/og/sitemap.d.ts +7 -0
- package/dist/og/sitemap.d.ts.map +1 -0
- package/dist/og/sitemap.js +90 -0
- package/dist/og/types.d.ts +65 -0
- package/dist/og/types.d.ts.map +1 -0
- package/dist/og/types.js +1 -0
- package/dist/og.d.ts +6 -0
- package/dist/og.d.ts.map +1 -0
- package/dist/og.js +5 -0
- package/dist/routes.d.ts +2 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +1 -0
- package/dist/search/algolia.d.ts +10 -0
- package/dist/search/algolia.d.ts.map +1 -0
- package/dist/search/algolia.js +82 -0
- package/dist/search/cloudflare-sync.d.ts +5 -0
- package/dist/search/cloudflare-sync.d.ts.map +1 -0
- package/dist/search/cloudflare-sync.js +169 -0
- package/dist/search/cloudflare.d.ts +11 -0
- package/dist/search/cloudflare.d.ts.map +1 -0
- package/dist/search/cloudflare.js +126 -0
- package/dist/search/local.d.ts +9 -0
- package/dist/search/local.d.ts.map +1 -0
- package/dist/search/local.js +165 -0
- package/dist/search/runtime.d.ts +41 -0
- package/dist/search/runtime.d.ts.map +1 -0
- package/dist/search/runtime.js +152 -0
- package/dist/search/types.d.ts +172 -0
- package/dist/search/types.d.ts.map +1 -0
- package/dist/search/types.js +1 -0
- package/dist/search/typesense.d.ts +11 -0
- package/dist/search/typesense.d.ts.map +1 -0
- package/dist/search/typesense.js +91 -0
- package/dist/search/utils.d.ts +11 -0
- package/dist/search/utils.d.ts.map +1 -0
- package/dist/search/utils.js +93 -0
- package/dist/search.d.ts +8 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +7 -0
- package/dist/svelte.d.ts +27 -0
- package/dist/svelte.d.ts.map +1 -0
- package/dist/svelte.js +45 -0
- package/dist/testing.d.ts +3 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +23 -0
- package/dist/theme/Article.svelte +145 -0
- package/dist/theme/Article.svelte.d.ts +36 -0
- package/dist/theme/Article.svelte.d.ts.map +1 -0
- package/dist/theme/AskAiPanel.svelte +241 -0
- package/dist/theme/AskAiPanel.svelte.d.ts +31 -0
- package/dist/theme/AskAiPanel.svelte.d.ts.map +1 -0
- package/dist/theme/Brand.svelte +19 -0
- package/dist/theme/Brand.svelte.d.ts +22 -0
- package/dist/theme/Brand.svelte.d.ts.map +1 -0
- package/dist/theme/Button.svelte +34 -0
- package/dist/theme/Button.svelte.d.ts +41 -0
- package/dist/theme/Button.svelte.d.ts.map +1 -0
- package/dist/theme/Checkbox.svelte +63 -0
- package/dist/theme/Checkbox.svelte.d.ts +45 -0
- package/dist/theme/Checkbox.svelte.d.ts.map +1 -0
- package/dist/theme/CodeBlock.svelte +6 -0
- package/dist/theme/CodeBlock.svelte.d.ts +22 -0
- package/dist/theme/CodeBlock.svelte.d.ts.map +1 -0
- package/dist/theme/DocPage.svelte +15 -0
- package/dist/theme/DocPage.svelte.d.ts +33 -0
- package/dist/theme/DocPage.svelte.d.ts.map +1 -0
- package/dist/theme/DocsApp.svelte +194 -0
- package/dist/theme/DocsApp.svelte.d.ts +60 -0
- package/dist/theme/DocsApp.svelte.d.ts.map +1 -0
- package/dist/theme/DocsLayout.svelte +99 -0
- package/dist/theme/DocsLayout.svelte.d.ts +39 -0
- package/dist/theme/DocsLayout.svelte.d.ts.map +1 -0
- package/dist/theme/DocsShell.svelte +91 -0
- package/dist/theme/DocsShell.svelte.d.ts +34 -0
- package/dist/theme/DocsShell.svelte.d.ts.map +1 -0
- package/dist/theme/ErrorPage.svelte +113 -0
- package/dist/theme/ErrorPage.svelte.d.ts +34 -0
- package/dist/theme/ErrorPage.svelte.d.ts.map +1 -0
- package/dist/theme/FloatingToolbar.svelte +8 -0
- package/dist/theme/FloatingToolbar.svelte.d.ts +22 -0
- package/dist/theme/FloatingToolbar.svelte.d.ts.map +1 -0
- package/dist/theme/Footer.svelte +18 -0
- package/dist/theme/Footer.svelte.d.ts +24 -0
- package/dist/theme/Footer.svelte.d.ts.map +1 -0
- package/dist/theme/FooterLinks.svelte +37 -0
- package/dist/theme/FooterLinks.svelte.d.ts +22 -0
- package/dist/theme/FooterLinks.svelte.d.ts.map +1 -0
- package/dist/theme/FormField.svelte +41 -0
- package/dist/theme/FormField.svelte.d.ts +35 -0
- package/dist/theme/FormField.svelte.d.ts.map +1 -0
- package/dist/theme/HomePage.svelte +284 -0
- package/dist/theme/HomePage.svelte.d.ts +65 -0
- package/dist/theme/HomePage.svelte.d.ts.map +1 -0
- package/dist/theme/Input.svelte +36 -0
- package/dist/theme/Input.svelte.d.ts +34 -0
- package/dist/theme/Input.svelte.d.ts.map +1 -0
- package/dist/theme/LayoutShell.svelte +120 -0
- package/dist/theme/LayoutShell.svelte.d.ts +43 -0
- package/dist/theme/LayoutShell.svelte.d.ts.map +1 -0
- package/dist/theme/LocalizedLink.svelte +21 -0
- package/dist/theme/LocalizedLink.svelte.d.ts +38 -0
- package/dist/theme/LocalizedLink.svelte.d.ts.map +1 -0
- package/dist/theme/MobileNav.svelte +20 -0
- package/dist/theme/MobileNav.svelte.d.ts +26 -0
- package/dist/theme/MobileNav.svelte.d.ts.map +1 -0
- package/dist/theme/Navbar.svelte +69 -0
- package/dist/theme/Navbar.svelte.d.ts +29 -0
- package/dist/theme/Navbar.svelte.d.ts.map +1 -0
- package/dist/theme/PageLayout.svelte +49 -0
- package/dist/theme/PageLayout.svelte.d.ts +34 -0
- package/dist/theme/PageLayout.svelte.d.ts.map +1 -0
- package/dist/theme/PageShell.svelte +98 -0
- package/dist/theme/PageShell.svelte.d.ts +35 -0
- package/dist/theme/PageShell.svelte.d.ts.map +1 -0
- package/dist/theme/PageTools.svelte +87 -0
- package/dist/theme/PageTools.svelte.d.ts +25 -0
- package/dist/theme/PageTools.svelte.d.ts.map +1 -0
- package/dist/theme/RenderError.svelte +62 -0
- package/dist/theme/RenderError.svelte.d.ts +31 -0
- package/dist/theme/RenderError.svelte.d.ts.map +1 -0
- package/dist/theme/RootLayout.svelte +274 -0
- package/dist/theme/RootLayout.svelte.d.ts +47 -0
- package/dist/theme/RootLayout.svelte.d.ts.map +1 -0
- package/dist/theme/RouteRenderError.svelte +53 -0
- package/dist/theme/RouteRenderError.svelte.d.ts +34 -0
- package/dist/theme/RouteRenderError.svelte.d.ts.map +1 -0
- package/dist/theme/SafeRenderError.svelte +49 -0
- package/dist/theme/SafeRenderError.svelte.d.ts +33 -0
- package/dist/theme/SafeRenderError.svelte.d.ts.map +1 -0
- package/dist/theme/ScopeSwitcher.svelte +107 -0
- package/dist/theme/ScopeSwitcher.svelte.d.ts +26 -0
- package/dist/theme/ScopeSwitcher.svelte.d.ts.map +1 -0
- package/dist/theme/SearchDialog.svelte +200 -0
- package/dist/theme/SearchDialog.svelte.d.ts +31 -0
- package/dist/theme/SearchDialog.svelte.d.ts.map +1 -0
- package/dist/theme/Select.svelte +37 -0
- package/dist/theme/Select.svelte.d.ts +42 -0
- package/dist/theme/Select.svelte.d.ts.map +1 -0
- package/dist/theme/SidebarTree.svelte +148 -0
- package/dist/theme/SidebarTree.svelte.d.ts +24 -0
- package/dist/theme/SidebarTree.svelte.d.ts.map +1 -0
- package/dist/theme/SocialNav.svelte +17 -0
- package/dist/theme/SocialNav.svelte.d.ts +22 -0
- package/dist/theme/SocialNav.svelte.d.ts.map +1 -0
- package/dist/theme/TableOfContents.svelte +73 -0
- package/dist/theme/TableOfContents.svelte.d.ts +25 -0
- package/dist/theme/TableOfContents.svelte.d.ts.map +1 -0
- package/dist/theme/Textarea.svelte +33 -0
- package/dist/theme/Textarea.svelte.d.ts +33 -0
- package/dist/theme/Textarea.svelte.d.ts.map +1 -0
- package/dist/theme/ThemeInit.svelte +16 -0
- package/dist/theme/ThemeInit.svelte.d.ts +23 -0
- package/dist/theme/ThemeInit.svelte.d.ts.map +1 -0
- package/dist/theme/ThemeToggle.svelte +37 -0
- package/dist/theme/ThemeToggle.svelte.d.ts +23 -0
- package/dist/theme/ThemeToggle.svelte.d.ts.map +1 -0
- package/dist/theme/TopNav.svelte +23 -0
- package/dist/theme/TopNav.svelte.d.ts +22 -0
- package/dist/theme/TopNav.svelte.d.ts.map +1 -0
- package/dist/theme/base.css +240 -0
- package/dist/theme/defaults.d.ts +4 -0
- package/dist/theme/defaults.d.ts.map +1 -0
- package/dist/theme/defaults.js +55 -0
- package/dist/theme/forms.d.ts +4 -0
- package/dist/theme/forms.d.ts.map +1 -0
- package/dist/theme/forms.js +3 -0
- package/dist/theme/headless.d.ts +62 -0
- package/dist/theme/headless.d.ts.map +1 -0
- package/dist/theme/headless.js +1064 -0
- package/dist/theme/index.d.ts +41 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/index.js +38 -0
- package/dist/theme/portal.d.ts +6 -0
- package/dist/theme/portal.d.ts.map +1 -0
- package/dist/theme/portal.js +59 -0
- package/dist/theme/styles.css +4232 -0
- package/dist/theme/types.d.ts +363 -0
- package/dist/theme/types.d.ts.map +1 -0
- package/dist/theme/types.js +1 -0
- package/dist/virtual.d.ts +65 -0
- package/dist/vite.d.ts +16 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +386 -0
- package/package.json +132 -0
package/dist/og/image.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
export const defaultOgTemplate = {
|
|
2
|
+
name: 'default',
|
|
3
|
+
render: createDefaultOgTemplateNode
|
|
4
|
+
};
|
|
5
|
+
export function createPageOgImagePath(page, format = 'svg') {
|
|
6
|
+
const name = page.routePath === '/' ? 'index' : page.routePath.replace(/^\/+/, '').replace(/[^a-zA-Z0-9]+/g, '-');
|
|
7
|
+
return `/og/${name}.${format}`;
|
|
8
|
+
}
|
|
9
|
+
export function createConfiguredOgImageFormat(config) {
|
|
10
|
+
return config.seo.ogImage === false ? 'svg' : config.seo.ogImage.format;
|
|
11
|
+
}
|
|
12
|
+
export function createConfiguredOgImageRenderer(config) {
|
|
13
|
+
return config.seo.ogImage === false ? 'svg' : config.seo.ogImage.renderer;
|
|
14
|
+
}
|
|
15
|
+
export function createConfiguredOgImageTemplate(config) {
|
|
16
|
+
return config.seo.ogImage !== false && typeof config.seo.ogImage.template === 'function'
|
|
17
|
+
? config.seo.ogImage.template
|
|
18
|
+
: undefined;
|
|
19
|
+
}
|
|
20
|
+
export function isOgImageEnabled(config) {
|
|
21
|
+
return config.seo.ogImage !== false;
|
|
22
|
+
}
|
|
23
|
+
export function createPageOgImageEntries(pages, format = 'svg') {
|
|
24
|
+
return pages
|
|
25
|
+
.filter((page) => !page.hidden)
|
|
26
|
+
.map((page) => ({ path: createPageOgImagePath(page, format).replace(/^\/og\//, '') }));
|
|
27
|
+
}
|
|
28
|
+
export function createConfiguredPageOgImageEntries(config, pages) {
|
|
29
|
+
if (!isOgImageEnabled(config))
|
|
30
|
+
return [];
|
|
31
|
+
return createPageOgImageEntries(pages, createConfiguredOgImageFormat(config));
|
|
32
|
+
}
|
|
33
|
+
export async function createPageOgImageResponse(config, page, options = {}) {
|
|
34
|
+
const format = options.format ?? createConfiguredOgImageFormat(config);
|
|
35
|
+
const template = options.template ?? createConfiguredOgImageTemplate(config);
|
|
36
|
+
const asset = await createOgImage(createOgImageInput(config, page), {
|
|
37
|
+
...options,
|
|
38
|
+
format,
|
|
39
|
+
renderer: options.renderer ?? createConfiguredOgImageRenderer(config),
|
|
40
|
+
...(template ? { template } : {})
|
|
41
|
+
});
|
|
42
|
+
const body = typeof asset === 'string' ? asset : new Blob([toArrayBuffer(asset)], { type: 'image/png' });
|
|
43
|
+
return new Response(body, {
|
|
44
|
+
headers: {
|
|
45
|
+
'content-type': format === 'png' ? 'image/png' : 'image/svg+xml; charset=utf-8',
|
|
46
|
+
'cache-control': 'public, max-age=31536000, immutable'
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
export function createOgImageInput(config, page) {
|
|
51
|
+
return {
|
|
52
|
+
title: page.seo.title,
|
|
53
|
+
description: page.seo.description ?? config.site.description,
|
|
54
|
+
siteName: config.site.name
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function createOgSvg(input) {
|
|
58
|
+
const title = escapeHtml(input.title);
|
|
59
|
+
const description = escapeHtml(input.description ?? input.siteName ?? 'svedocs');
|
|
60
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630">
|
|
61
|
+
<rect width="1200" height="630" fill="#11130f"/>
|
|
62
|
+
<path d="M0 84h1200M0 168h1200M0 252h1200M0 336h1200M0 420h1200M0 504h1200M120 0v630M240 0v630M360 0v630M480 0v630M600 0v630M720 0v630M840 0v630M960 0v630M1080 0v630" stroke="#2f332d" stroke-width="2"/>
|
|
63
|
+
<rect x="84" y="84" width="96" height="96" fill="#50d6b3"/>
|
|
64
|
+
<rect x="180" y="180" width="96" height="96" fill="#ff8a66"/>
|
|
65
|
+
<rect x="84" y="276" width="96" height="96" fill="#f4f1e8"/>
|
|
66
|
+
<text x="300" y="270" fill="#f4f1e8" font-family="Arial, sans-serif" font-size="78" font-weight="800">${title}</text>
|
|
67
|
+
<text x="306" y="352" fill="#aaa698" font-family="Arial, sans-serif" font-size="34">${description}</text>
|
|
68
|
+
</svg>`;
|
|
69
|
+
}
|
|
70
|
+
export function createDefaultOgTemplateNode(input) {
|
|
71
|
+
return {
|
|
72
|
+
type: 'div',
|
|
73
|
+
props: {
|
|
74
|
+
style: {
|
|
75
|
+
display: 'flex',
|
|
76
|
+
width: '1200px',
|
|
77
|
+
height: '630px',
|
|
78
|
+
background: '#11130f',
|
|
79
|
+
color: '#f4f1e8',
|
|
80
|
+
position: 'relative',
|
|
81
|
+
fontFamily: 'Inter',
|
|
82
|
+
overflow: 'hidden'
|
|
83
|
+
},
|
|
84
|
+
children: [
|
|
85
|
+
gridLayer(),
|
|
86
|
+
pixelMark(),
|
|
87
|
+
{
|
|
88
|
+
type: 'div',
|
|
89
|
+
props: {
|
|
90
|
+
style: {
|
|
91
|
+
display: 'flex',
|
|
92
|
+
position: 'absolute',
|
|
93
|
+
left: '300px',
|
|
94
|
+
top: '200px',
|
|
95
|
+
width: '780px',
|
|
96
|
+
flexDirection: 'column'
|
|
97
|
+
},
|
|
98
|
+
children: [
|
|
99
|
+
{
|
|
100
|
+
type: 'div',
|
|
101
|
+
props: {
|
|
102
|
+
style: {
|
|
103
|
+
fontSize: 78,
|
|
104
|
+
fontWeight: 800,
|
|
105
|
+
lineHeight: 1,
|
|
106
|
+
color: '#f4f1e8'
|
|
107
|
+
},
|
|
108
|
+
children: input.title
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
type: 'div',
|
|
113
|
+
props: {
|
|
114
|
+
style: {
|
|
115
|
+
marginTop: 28,
|
|
116
|
+
fontSize: 34,
|
|
117
|
+
lineHeight: 1.25,
|
|
118
|
+
color: '#aaa698'
|
|
119
|
+
},
|
|
120
|
+
children: input.description ?? input.siteName ?? 'svedocs'
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export async function createSatoriOgSvg(input, options) {
|
|
131
|
+
if (!options.fonts?.length) {
|
|
132
|
+
throw new Error('Satori OG rendering requires at least one font.');
|
|
133
|
+
}
|
|
134
|
+
const { default: satori } = await importOptionalOgModule('satori');
|
|
135
|
+
return satori((options.template ?? createDefaultOgTemplateNode)(input), {
|
|
136
|
+
width: 1200,
|
|
137
|
+
height: 630,
|
|
138
|
+
fonts: options.fonts
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
export async function createOgImage(input, options = {}) {
|
|
142
|
+
return options.format === 'png' ? createOgPng(input, options) : renderOgSvg(input, options);
|
|
143
|
+
}
|
|
144
|
+
export async function createOgPng(input, options = {}) {
|
|
145
|
+
const { Resvg } = await importOptionalOgModule('@resvg/resvg-js');
|
|
146
|
+
const renderer = new Resvg(await renderOgSvg(input, options), {
|
|
147
|
+
fitTo: {
|
|
148
|
+
mode: 'width',
|
|
149
|
+
value: 1200
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
return renderer.render().asPng();
|
|
153
|
+
}
|
|
154
|
+
function gridLayer() {
|
|
155
|
+
return {
|
|
156
|
+
type: 'div',
|
|
157
|
+
props: {
|
|
158
|
+
style: {
|
|
159
|
+
display: 'flex',
|
|
160
|
+
position: 'absolute',
|
|
161
|
+
inset: 0,
|
|
162
|
+
backgroundImage: 'linear-gradient(#2f332d 2px, transparent 2px), linear-gradient(90deg, #2f332d 2px, transparent 2px)',
|
|
163
|
+
backgroundSize: '120px 84px'
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function pixelMark() {
|
|
169
|
+
return {
|
|
170
|
+
type: 'div',
|
|
171
|
+
props: {
|
|
172
|
+
style: {
|
|
173
|
+
display: 'flex',
|
|
174
|
+
position: 'absolute',
|
|
175
|
+
left: '84px',
|
|
176
|
+
top: '84px',
|
|
177
|
+
width: '192px',
|
|
178
|
+
height: '288px',
|
|
179
|
+
flexWrap: 'wrap'
|
|
180
|
+
},
|
|
181
|
+
children: [
|
|
182
|
+
colorBlock('#50d6b3'),
|
|
183
|
+
colorBlock('transparent'),
|
|
184
|
+
colorBlock('transparent'),
|
|
185
|
+
colorBlock('#ff8a66'),
|
|
186
|
+
colorBlock('#f4f1e8'),
|
|
187
|
+
colorBlock('transparent')
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function colorBlock(color) {
|
|
193
|
+
return {
|
|
194
|
+
type: 'div',
|
|
195
|
+
props: {
|
|
196
|
+
style: {
|
|
197
|
+
width: '96px',
|
|
198
|
+
height: '96px',
|
|
199
|
+
background: color
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
async function renderOgSvg(input, options) {
|
|
205
|
+
return options.renderer === 'satori' ? createSatoriOgSvg(input, options) : createOgSvg(input);
|
|
206
|
+
}
|
|
207
|
+
function escapeHtml(value) {
|
|
208
|
+
return value
|
|
209
|
+
.replace(/&/g, '&')
|
|
210
|
+
.replace(/</g, '<')
|
|
211
|
+
.replace(/>/g, '>')
|
|
212
|
+
.replace(/"/g, '"');
|
|
213
|
+
}
|
|
214
|
+
function toArrayBuffer(value) {
|
|
215
|
+
return value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength);
|
|
216
|
+
}
|
|
217
|
+
function importOptionalOgModule(specifier) {
|
|
218
|
+
if (typeof process !== 'undefined' && process.versions?.node) {
|
|
219
|
+
return import(specifier);
|
|
220
|
+
}
|
|
221
|
+
// Keep optional Node-only OG renderers out of edge bundles unless the caller actually uses them.
|
|
222
|
+
const dynamicImport = new Function('specifier', 'return import(specifier)');
|
|
223
|
+
return dynamicImport(specifier);
|
|
224
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SvedocsPage, SvedocsResolvedConfig } from '../core.js';
|
|
2
|
+
import type { SvedocsPageAlternate, SvedocsPageMetadata } from './types.js';
|
|
3
|
+
export declare function createPageMetadata(config: SvedocsResolvedConfig, page: SvedocsPage, pages?: SvedocsPage[]): SvedocsPageMetadata;
|
|
4
|
+
export declare function createPageAlternates(config: SvedocsResolvedConfig, page: SvedocsPage, pages: SvedocsPage[]): SvedocsPageAlternate[];
|
|
5
|
+
export declare function serializeJsonLd(value: unknown): string;
|
|
6
|
+
export declare function createJsonLdScript(value: unknown): string;
|
|
7
|
+
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/og/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAA0C,MAAM,YAAY,CAAC;AAG7G,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE5E,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,qBAAqB,EAC7B,IAAI,EAAE,WAAW,EACjB,KAAK,GAAE,WAAW,EAAO,GACxB,mBAAmB,CAuDrB;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,qBAAqB,EAC7B,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,EAAE,GACnB,oBAAoB,EAAE,CAuCxB;AA4FD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAOtD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEzD"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { formatRoutePathForBuildMode } from '../core/utils.js';
|
|
2
|
+
import { createConfiguredOgImageFormat, createPageOgImagePath } from './image.js';
|
|
3
|
+
export function createPageMetadata(config, page, pages = []) {
|
|
4
|
+
const title = page.routePath === '/' ? page.seo.title : `${page.seo.title} | ${config.site.name}`;
|
|
5
|
+
const description = page.seo.description ?? config.site.description;
|
|
6
|
+
const canonical = page.seo.canonical
|
|
7
|
+
? createAbsoluteUrl(config, page.seo.canonical) ?? page.seo.canonical
|
|
8
|
+
: createAbsoluteRouteUrl(config, page.routePath);
|
|
9
|
+
const keywords = page.seo.keywords ?? [];
|
|
10
|
+
const robots = page.seo.robots;
|
|
11
|
+
const head = withRssAlternate(config, mergeSeoHead(config.seo.head, page.seo.head));
|
|
12
|
+
const generatedImage = config.seo.ogImage === false
|
|
13
|
+
? undefined
|
|
14
|
+
: createAbsoluteUrl(config, createPageOgImagePath(page, createConfiguredOgImageFormat(config)));
|
|
15
|
+
const image = page.seo.image ? createAbsoluteUrl(config, page.seo.image) : generatedImage;
|
|
16
|
+
const type = page.seo.type ?? (page.kind === 'doc' ? 'article' : 'website');
|
|
17
|
+
const author = page.seo.author ?? config.seo.defaultAuthor;
|
|
18
|
+
const updatedTime = page.seo.updatedTime ?? page.lastUpdated;
|
|
19
|
+
const pageLanguage = getPageLanguage(config, page);
|
|
20
|
+
const ogLocale = toOpenGraphLocale(pageLanguage);
|
|
21
|
+
const alternateOgLocales = createPageAlternates(config, page, pages)
|
|
22
|
+
.filter((alternate) => alternate.lang !== 'x-default' && alternate.locale !== page.locale)
|
|
23
|
+
.map((alternate) => toOpenGraphLocale(alternate.lang));
|
|
24
|
+
return {
|
|
25
|
+
title,
|
|
26
|
+
description,
|
|
27
|
+
keywords,
|
|
28
|
+
head,
|
|
29
|
+
...(canonical ? { canonical } : {}),
|
|
30
|
+
...(image ? { image } : {}),
|
|
31
|
+
...(robots ? { robots } : {}),
|
|
32
|
+
openGraph: {
|
|
33
|
+
title,
|
|
34
|
+
description,
|
|
35
|
+
type,
|
|
36
|
+
...(canonical ? { url: canonical } : {}),
|
|
37
|
+
...(image ? { image } : {}),
|
|
38
|
+
siteName: config.site.name,
|
|
39
|
+
locale: ogLocale,
|
|
40
|
+
...(alternateOgLocales.length > 0 ? { alternateLocales: [...new Set(alternateOgLocales)] } : {}),
|
|
41
|
+
...(author ? { author } : {}),
|
|
42
|
+
...(page.seo.publishedTime ? { publishedTime: page.seo.publishedTime } : {}),
|
|
43
|
+
...(updatedTime ? { updatedTime } : {})
|
|
44
|
+
},
|
|
45
|
+
twitter: {
|
|
46
|
+
card: image ? 'summary_large_image' : 'summary',
|
|
47
|
+
title,
|
|
48
|
+
description,
|
|
49
|
+
...(image ? { image } : {})
|
|
50
|
+
},
|
|
51
|
+
jsonLd: createPageJsonLd(config, page, {
|
|
52
|
+
title,
|
|
53
|
+
description,
|
|
54
|
+
...(canonical ? { canonical } : {}),
|
|
55
|
+
...(image ? { image } : {})
|
|
56
|
+
})
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export function createPageAlternates(config, page, pages) {
|
|
60
|
+
if (config.i18n.locales.length === 0)
|
|
61
|
+
return [];
|
|
62
|
+
const candidates = pages
|
|
63
|
+
.filter((candidate) => !candidate.hidden)
|
|
64
|
+
.filter((candidate) => candidate.kind === page.kind)
|
|
65
|
+
.filter((candidate) => candidate.scopePath === page.scopePath);
|
|
66
|
+
const alternates = [];
|
|
67
|
+
for (const candidate of candidates) {
|
|
68
|
+
if (!candidate.locale)
|
|
69
|
+
continue;
|
|
70
|
+
const href = candidate.seo.canonical
|
|
71
|
+
? createAbsoluteUrl(config, candidate.seo.canonical) ?? candidate.seo.canonical
|
|
72
|
+
: createAbsoluteRouteUrl(config, candidate.routePath);
|
|
73
|
+
if (!href)
|
|
74
|
+
continue;
|
|
75
|
+
const locale = config.i18n.locales.find((item) => item.code === candidate.locale);
|
|
76
|
+
alternates.push({
|
|
77
|
+
lang: locale?.hreflang ?? candidate.locale,
|
|
78
|
+
href,
|
|
79
|
+
locale: candidate.locale
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const defaultLocale = config.i18n.defaultLocale;
|
|
83
|
+
const defaultPage = defaultLocale
|
|
84
|
+
? candidates.find((candidate) => candidate.locale === defaultLocale)
|
|
85
|
+
: undefined;
|
|
86
|
+
const defaultHref = defaultPage?.seo.canonical
|
|
87
|
+
? createAbsoluteUrl(config, defaultPage.seo.canonical) ?? defaultPage.seo.canonical
|
|
88
|
+
: defaultPage
|
|
89
|
+
? createAbsoluteRouteUrl(config, defaultPage.routePath)
|
|
90
|
+
: undefined;
|
|
91
|
+
const defaultAlternate = defaultHref
|
|
92
|
+
? [
|
|
93
|
+
{
|
|
94
|
+
lang: 'x-default',
|
|
95
|
+
href: defaultHref,
|
|
96
|
+
...(defaultLocale ? { locale: defaultLocale } : {})
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
: [];
|
|
100
|
+
return uniqueAlternates([...alternates, ...defaultAlternate]);
|
|
101
|
+
}
|
|
102
|
+
function mergeSeoHead(globalHead, pageHead) {
|
|
103
|
+
return {
|
|
104
|
+
meta: [...globalHead.meta, ...(pageHead?.meta ?? [])],
|
|
105
|
+
links: [...globalHead.links, ...(pageHead?.links ?? [])],
|
|
106
|
+
jsonLd: [...globalHead.jsonLd, ...(pageHead?.jsonLd ?? pageHead?.jsonld ?? pageHead?.['json-ld'] ?? [])]
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function withRssAlternate(config, head) {
|
|
110
|
+
if (!config.seo.rss || head.links.some((link) => link.type === 'application/rss+xml'))
|
|
111
|
+
return head;
|
|
112
|
+
return {
|
|
113
|
+
...head,
|
|
114
|
+
links: [
|
|
115
|
+
...head.links,
|
|
116
|
+
{
|
|
117
|
+
rel: 'alternate',
|
|
118
|
+
type: 'application/rss+xml',
|
|
119
|
+
href: createAbsoluteUrl(config, '/feed.xml') ?? '/feed.xml',
|
|
120
|
+
title: config.seo.rss.title
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function uniqueAlternates(alternates) {
|
|
126
|
+
const seen = new Set();
|
|
127
|
+
return alternates.filter((alternate) => {
|
|
128
|
+
const key = `${alternate.lang}:${alternate.href}`;
|
|
129
|
+
if (seen.has(key))
|
|
130
|
+
return false;
|
|
131
|
+
seen.add(key);
|
|
132
|
+
return true;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function createPageJsonLd(config, page, metadata) {
|
|
136
|
+
const graph = {
|
|
137
|
+
'@context': 'https://schema.org',
|
|
138
|
+
'@type': page.kind === 'doc' ? 'TechArticle' : 'WebPage',
|
|
139
|
+
headline: metadata.title,
|
|
140
|
+
description: metadata.description,
|
|
141
|
+
inLanguage: getPageLanguage(config, page),
|
|
142
|
+
isPartOf: {
|
|
143
|
+
'@type': 'WebSite',
|
|
144
|
+
name: config.site.name,
|
|
145
|
+
...(config.site.url ? { url: config.site.url } : {})
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
if (metadata.canonical)
|
|
149
|
+
graph.url = metadata.canonical;
|
|
150
|
+
if (metadata.image)
|
|
151
|
+
graph.image = metadata.image;
|
|
152
|
+
const author = page.seo.author ?? config.seo.defaultAuthor;
|
|
153
|
+
const updatedTime = page.seo.updatedTime ?? page.lastUpdated;
|
|
154
|
+
if (author) {
|
|
155
|
+
graph.author = {
|
|
156
|
+
'@type': 'Person',
|
|
157
|
+
name: author
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (page.seo.publishedTime)
|
|
161
|
+
graph.datePublished = page.seo.publishedTime;
|
|
162
|
+
if (updatedTime)
|
|
163
|
+
graph.dateModified = updatedTime;
|
|
164
|
+
if (page.kind === 'doc') {
|
|
165
|
+
graph.position = page.order;
|
|
166
|
+
graph.about = page.headings.map((heading) => heading.text);
|
|
167
|
+
}
|
|
168
|
+
return graph;
|
|
169
|
+
}
|
|
170
|
+
function getPageLanguage(config, page) {
|
|
171
|
+
const code = page.locale ?? config.i18n.defaultLocale ?? 'en';
|
|
172
|
+
const locale = config.i18n.locales.find((candidate) => candidate.code === code);
|
|
173
|
+
return locale?.hreflang ?? code;
|
|
174
|
+
}
|
|
175
|
+
function toOpenGraphLocale(language) {
|
|
176
|
+
return language.replaceAll('-', '_');
|
|
177
|
+
}
|
|
178
|
+
function createAbsoluteUrl(config, value) {
|
|
179
|
+
if (/^https?:\/\//.test(value))
|
|
180
|
+
return value;
|
|
181
|
+
if (!config.site.url)
|
|
182
|
+
return undefined;
|
|
183
|
+
return new URL(value, config.site.url).href;
|
|
184
|
+
}
|
|
185
|
+
function createAbsoluteRouteUrl(config, routePath) {
|
|
186
|
+
return createAbsoluteUrl(config, formatRoutePathForBuildMode(routePath, config.build.mode));
|
|
187
|
+
}
|
|
188
|
+
export function serializeJsonLd(value) {
|
|
189
|
+
return (JSON.stringify(value) ?? 'null')
|
|
190
|
+
.replace(/</g, '\\u003c')
|
|
191
|
+
.replace(/>/g, '\\u003e')
|
|
192
|
+
.replace(/&/g, '\\u0026')
|
|
193
|
+
.replace(/\u2028/g, '\\u2028')
|
|
194
|
+
.replace(/\u2029/g, '\\u2029');
|
|
195
|
+
}
|
|
196
|
+
export function createJsonLdScript(value) {
|
|
197
|
+
return `<script type="application/ld+json">${serializeJsonLd(value)}<${'/script'}>`;
|
|
198
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function createDiscoveryResponse(body: string, contentType: string, request?: Request): Response;
|
|
2
|
+
export declare function createDisabledDiscoveryResponse(label: string): Response;
|
|
3
|
+
export declare function escapeXml(value: string): string;
|
|
4
|
+
//# sourceMappingURL=response.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/og/response.ts"],"names":[],"mappings":"AAEA,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,OAAO,GAChB,QAAQ,CAWV;AAED,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAQvE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO/C"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const discoveryCacheControl = 'public, max-age=0, s-maxage=3600, stale-while-revalidate=86400';
|
|
2
|
+
export function createDiscoveryResponse(body, contentType, request) {
|
|
3
|
+
const etag = createWeakEtag(body);
|
|
4
|
+
const headers = {
|
|
5
|
+
'cache-control': discoveryCacheControl,
|
|
6
|
+
'content-type': contentType,
|
|
7
|
+
etag
|
|
8
|
+
};
|
|
9
|
+
if (request && matchesEtag(request.headers.get('if-none-match'), etag)) {
|
|
10
|
+
return new Response(null, { status: 304, headers });
|
|
11
|
+
}
|
|
12
|
+
return new Response(body, { headers });
|
|
13
|
+
}
|
|
14
|
+
export function createDisabledDiscoveryResponse(label) {
|
|
15
|
+
return new Response(`${label} is disabled.`, {
|
|
16
|
+
status: 404,
|
|
17
|
+
headers: {
|
|
18
|
+
'cache-control': 'no-store',
|
|
19
|
+
'content-type': 'text/plain; charset=utf-8'
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export function escapeXml(value) {
|
|
24
|
+
return value
|
|
25
|
+
.replace(/&/g, '&')
|
|
26
|
+
.replace(/</g, '<')
|
|
27
|
+
.replace(/>/g, '>')
|
|
28
|
+
.replace(/"/g, '"')
|
|
29
|
+
.replace(/'/g, ''');
|
|
30
|
+
}
|
|
31
|
+
function createWeakEtag(value) {
|
|
32
|
+
let hash = 0x811c9dc5;
|
|
33
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
34
|
+
hash = Math.imul(hash ^ value.charCodeAt(index), 0x01000193);
|
|
35
|
+
}
|
|
36
|
+
return `W/"${value.length.toString(16)}-${(hash >>> 0).toString(16)}"`;
|
|
37
|
+
}
|
|
38
|
+
function matchesEtag(header, etag) {
|
|
39
|
+
if (!header)
|
|
40
|
+
return false;
|
|
41
|
+
const expected = stripWeakPrefix(etag);
|
|
42
|
+
return header.split(',').some((candidate) => {
|
|
43
|
+
const value = candidate.trim();
|
|
44
|
+
return value === '*' || stripWeakPrefix(value) === expected;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function stripWeakPrefix(etag) {
|
|
48
|
+
return etag.startsWith('W/') ? etag.slice(2) : etag;
|
|
49
|
+
}
|
package/dist/og/rss.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SvedocsPage, SvedocsResolvedConfig } from '../core.js';
|
|
2
|
+
export declare function createRssXml(config: SvedocsResolvedConfig, pages: SvedocsPage[]): string;
|
|
3
|
+
export declare function createRssResponse(config: SvedocsResolvedConfig, pages: SvedocsPage[], request?: Request): Response;
|
|
4
|
+
//# sourceMappingURL=rss.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rss.d.ts","sourceRoot":"","sources":["../../src/og/rss.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAWrE,wBAAgB,YAAY,CAAC,MAAM,EAAE,qBAAqB,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CA+CxF;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,WAAW,EAAE,EACpB,OAAO,CAAC,EAAE,OAAO,GAChB,QAAQ,CAGV"}
|
package/dist/og/rss.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { formatRoutePathForBuildMode } from '../core/utils.js';
|
|
2
|
+
import { createDisabledDiscoveryResponse, createDiscoveryResponse, escapeXml } from './response.js';
|
|
3
|
+
import { isDiscoverablePage } from './sitemap.js';
|
|
4
|
+
export function createRssXml(config, pages) {
|
|
5
|
+
const rss = config.seo.rss;
|
|
6
|
+
if (!rss)
|
|
7
|
+
return '';
|
|
8
|
+
const locale = rss.locale ?? config.i18n.defaultLocale;
|
|
9
|
+
const entries = pages
|
|
10
|
+
.filter(isDiscoverablePage)
|
|
11
|
+
.filter((page) => isPageInLocale(config, page, locale))
|
|
12
|
+
.map((page) => {
|
|
13
|
+
const link = createPageUrl(config, page);
|
|
14
|
+
if (!link)
|
|
15
|
+
return undefined;
|
|
16
|
+
const date = parseDate(page.seo.updatedTime ?? page.seo.publishedTime ?? page.lastUpdated);
|
|
17
|
+
return { page, link, ...(date ? { date } : {}) };
|
|
18
|
+
})
|
|
19
|
+
.filter((entry) => Boolean(entry))
|
|
20
|
+
.sort((left, right) => (right.date?.getTime() ?? 0) - (left.date?.getTime() ?? 0))
|
|
21
|
+
.slice(0, rss.limit);
|
|
22
|
+
const channelUrl = config.site.url ?? '/';
|
|
23
|
+
const feedUrl = createAbsoluteUrl(config, '/feed.xml') ?? '/feed.xml';
|
|
24
|
+
const language = resolveFeedLanguage(config, locale);
|
|
25
|
+
const lastBuildDate = entries.find((entry) => entry.date)?.date;
|
|
26
|
+
const items = entries.map(({ page, date, link }) => [
|
|
27
|
+
' <item>',
|
|
28
|
+
` <title>${escapeXml(page.title)}</title>`,
|
|
29
|
+
` <link>${escapeXml(link)}</link>`,
|
|
30
|
+
` <guid isPermaLink="true">${escapeXml(link)}</guid>`,
|
|
31
|
+
...(page.seo.description ?? page.description
|
|
32
|
+
? [` <description>${escapeXml(page.seo.description ?? page.description ?? '')}</description>`]
|
|
33
|
+
: []),
|
|
34
|
+
...(date ? [` <pubDate>${date.toUTCString()}</pubDate>`] : []),
|
|
35
|
+
...(page.seo.keywords ?? []).map((keyword) => ` <category>${escapeXml(keyword)}</category>`),
|
|
36
|
+
' </item>'
|
|
37
|
+
].join('\n')).join('\n');
|
|
38
|
+
return [
|
|
39
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
40
|
+
'<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">',
|
|
41
|
+
' <channel>',
|
|
42
|
+
` <title>${escapeXml(rss.title)}</title>`,
|
|
43
|
+
` <link>${escapeXml(channelUrl)}</link>`,
|
|
44
|
+
` <description>${escapeXml(rss.description)}</description>`,
|
|
45
|
+
` <language>${escapeXml(language)}</language>`,
|
|
46
|
+
` <atom:link href="${escapeXml(feedUrl)}" rel="self" type="application/rss+xml" />`,
|
|
47
|
+
...(lastBuildDate ? [` <lastBuildDate>${lastBuildDate.toUTCString()}</lastBuildDate>`] : []),
|
|
48
|
+
items,
|
|
49
|
+
' </channel>',
|
|
50
|
+
'</rss>',
|
|
51
|
+
''
|
|
52
|
+
].join('\n');
|
|
53
|
+
}
|
|
54
|
+
export function createRssResponse(config, pages, request) {
|
|
55
|
+
if (!config.seo.rss)
|
|
56
|
+
return createDisabledDiscoveryResponse('RSS');
|
|
57
|
+
return createDiscoveryResponse(createRssXml(config, pages), 'application/rss+xml; charset=utf-8', request);
|
|
58
|
+
}
|
|
59
|
+
function createPageUrl(config, page) {
|
|
60
|
+
const routePath = formatRoutePathForBuildMode(page.routePath, config.build.mode);
|
|
61
|
+
const link = page.seo.canonical
|
|
62
|
+
? createAbsoluteUrl(config, page.seo.canonical) ?? page.seo.canonical
|
|
63
|
+
: createAbsoluteUrl(config, routePath) ?? routePath;
|
|
64
|
+
if (!config.site.url || !/^https?:\/\//.test(link))
|
|
65
|
+
return link;
|
|
66
|
+
try {
|
|
67
|
+
return new URL(link).origin === new URL(config.site.url).origin ? link : undefined;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function isPageInLocale(config, page, locale) {
|
|
74
|
+
if (!locale)
|
|
75
|
+
return true;
|
|
76
|
+
const pageLocale = page.locale ?? config.i18n.defaultLocale;
|
|
77
|
+
return pageLocale ? pageLocale === locale : config.i18n.locales.length === 0;
|
|
78
|
+
}
|
|
79
|
+
function createAbsoluteUrl(config, value) {
|
|
80
|
+
if (/^https?:\/\//.test(value))
|
|
81
|
+
return value;
|
|
82
|
+
if (!config.site.url)
|
|
83
|
+
return undefined;
|
|
84
|
+
return new URL(value, config.site.url).href;
|
|
85
|
+
}
|
|
86
|
+
function resolveFeedLanguage(config, locale) {
|
|
87
|
+
if (!locale)
|
|
88
|
+
return 'en';
|
|
89
|
+
return config.i18n.locales.find((candidate) => candidate.code === locale)?.hreflang ?? locale;
|
|
90
|
+
}
|
|
91
|
+
function parseDate(value) {
|
|
92
|
+
if (!value)
|
|
93
|
+
return undefined;
|
|
94
|
+
const date = new Date(value);
|
|
95
|
+
return Number.isNaN(date.getTime()) ? undefined : date;
|
|
96
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SvedocsPage, SvedocsResolvedConfig } from '../core.js';
|
|
2
|
+
export declare function createSitemapXml(config: SvedocsResolvedConfig, pages: SvedocsPage[]): string;
|
|
3
|
+
export declare function createRobotsTxt(config: SvedocsResolvedConfig): string;
|
|
4
|
+
export declare function createSitemapResponse(config: SvedocsResolvedConfig, pages: SvedocsPage[], request?: Request): Response;
|
|
5
|
+
export declare function createRobotsResponse(config: SvedocsResolvedConfig, request?: Request): Response;
|
|
6
|
+
export declare function isDiscoverablePage(page: SvedocsPage): boolean;
|
|
7
|
+
//# sourceMappingURL=sitemap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sitemap.d.ts","sourceRoot":"","sources":["../../src/og/sitemap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAMrE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,qBAAqB,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM,CAqB5F;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CAQrE;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,WAAW,EAAE,EACpB,OAAO,CAAC,EAAE,OAAO,GAChB,QAAQ,CAGV;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,qBAAqB,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAG/F;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAE7D"}
|