@rimelight/seo 0.0.4 → 0.0.5
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/dist/index.d.mts +6 -38
- package/dist/index.mjs +5 -176
- package/dist/integration.d.mts +23 -0
- package/dist/integration.mjs +33 -0
- package/dist/llms-BVLxzjr-.d.mts +41 -0
- package/dist/llms.d.mts +2 -0
- package/dist/llms.mjs +75 -0
- package/dist/meta.d.mts +42 -0
- package/dist/meta.mjs +66 -0
- package/dist/robots.d.mts +3 -4
- package/dist/routes/llms.txt.d.mts +4 -0
- package/dist/routes/llms.txt.mjs +20 -0
- package/dist/routes/robots.txt.d.mts +4 -0
- package/dist/routes/robots.txt.mjs +18 -0
- package/dist/routes/sitemap.xml.d.mts +4 -0
- package/dist/routes/sitemap.xml.mjs +13 -0
- package/dist/schema-C8v69blQ.d.mts +28 -0
- package/dist/schema.d.mts +2 -0
- package/dist/schema.mjs +52 -0
- package/dist/sitemap.d.mts +6 -7
- package/dist/types.d.mts +277 -2
- package/package.json +9 -4
- package/src/components/SEOHead.astro +65 -18
- package/src/env.d.ts +5 -0
- package/src/index.ts +5 -1
- package/src/integration.ts +74 -0
- package/src/meta.test.ts +25 -1
- package/src/meta.ts +32 -2
- package/src/routes/llms.txt.ts +22 -0
- package/src/routes/robots.txt.ts +20 -0
- package/src/routes/sitemap.xml.ts +16 -0
- package/src/types.ts +39 -0
- package/dist/types-e7gqYZwc.d.mts +0 -305
package/dist/index.d.mts
CHANGED
|
@@ -1,40 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as buildBreadcrumbSchema, n as BreadcrumbItem, r as buildArticleSchema, t as ArticleSchemaOptions } from "./schema-C8v69blQ.mjs";
|
|
2
|
+
import { a as buildLlmsTxt, i as buildLlmsFullTxt, n as LlmsPage, r as LlmsTxtOptions, t as LlmsFullTxtOptions } from "./llms-BVLxzjr-.mjs";
|
|
3
|
+
import { ChangeFreq, HeadProps, LocaleConfig, OGImage, OpenGraphMeta, RSSFeed, RobotsOptions, SeoEntry, SiteConfig, SitemapAlternate, SitemapUrl, TwitterMeta, UserAgentRule } from "./types.mjs";
|
|
2
4
|
import { buildSitemapIndexXml, buildSitemapUrls, buildSitemapXml, chunkSitemapUrls } from "./sitemap.mjs";
|
|
3
5
|
import { buildRobotsTxt } from "./robots.mjs";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* query params (where trailing slash is removed too). - Returns undefined-safe: pass Astro.url and
|
|
8
|
-
* Astro.site directly.
|
|
9
|
-
*/
|
|
10
|
-
declare function buildCanonicalUrl(url: string | URL, base: string | URL): string;
|
|
11
|
-
/**
|
|
12
|
-
* Build a page title using a site's title template. Falls back to siteName for untitled pages; uses
|
|
13
|
-
* a 404 label for error pages.
|
|
14
|
-
*/
|
|
15
|
-
declare function buildPageTitle(opts: {
|
|
16
|
-
title?: string;
|
|
17
|
-
siteName: string;
|
|
18
|
-
titleTemplate: string;
|
|
19
|
-
is404?: boolean;
|
|
20
|
-
}): string;
|
|
21
|
-
/**
|
|
22
|
-
* Truncate a description to a maximum character length, appending "…" if trimmed.
|
|
23
|
-
*/
|
|
24
|
-
declare function buildSafeDescription(description: string, maxLength: number): string;
|
|
25
|
-
/**
|
|
26
|
-
* Build the value for the <meta name="robots"> tag. Returns the full directive string including
|
|
27
|
-
* max-image-preview for indexable pages.
|
|
28
|
-
*/
|
|
29
|
-
declare function buildRobotsMetaContent(opts: {
|
|
30
|
-
noindex?: boolean;
|
|
31
|
-
is404?: boolean;
|
|
32
|
-
override?: string;
|
|
33
|
-
}): string;
|
|
34
|
-
/**
|
|
35
|
-
* \n * Build a Schema.org WebSite JSON-LD object.\n * Pass the result to JSON.stringify and emit
|
|
36
|
-
* via <script type="application/ld+json">.\n
|
|
37
|
-
*/
|
|
38
|
-
declare function buildWebSiteSchema(config: Pick<SiteConfig, "name" | "url" | "description">, site?: string): Record<string, unknown>;
|
|
39
|
-
//#endregion
|
|
40
|
-
export { type ArticleSchemaOptions, type BreadcrumbItem, type ChangeFreq, type HeadProps, type LlmsFullTxtOptions, type LlmsPage, type LlmsTxtOptions, type LocaleConfig, type OGImage, type RSSFeed, type RobotsOptions, type SeoEntry, type SiteConfig, type SitemapAlternate, type SitemapUrl, type UserAgentRule, buildArticleSchema, buildBreadcrumbSchema, buildCanonicalUrl, buildLlmsFullTxt, buildLlmsTxt, buildPageTitle, buildRobotsMetaContent, buildRobotsTxt, buildSafeDescription, buildSitemapIndexXml, buildSitemapUrls, buildSitemapXml, buildWebSiteSchema, chunkSitemapUrls };
|
|
6
|
+
import { buildCanonicalUrl, buildPageTitle, buildRobotsMetaContent, buildSafeDescription, buildWebSiteSchema, deepMerge } from "./meta.mjs";
|
|
7
|
+
import { RimelightSeoOptions, rimelightSeo } from "./integration.mjs";
|
|
8
|
+
export { type ArticleSchemaOptions, type BreadcrumbItem, type ChangeFreq, type HeadProps, type LlmsFullTxtOptions, type LlmsPage, type LlmsTxtOptions, type LocaleConfig, type OGImage, type OpenGraphMeta, type RSSFeed, type RimelightSeoOptions, type RobotsOptions, type SeoEntry, type SiteConfig, type SitemapAlternate, type SitemapUrl, type TwitterMeta, type UserAgentRule, buildArticleSchema, buildBreadcrumbSchema, buildCanonicalUrl, buildLlmsFullTxt, buildLlmsTxt, buildPageTitle, buildRobotsMetaContent, buildRobotsTxt, buildSafeDescription, buildSitemapIndexXml, buildSitemapUrls, buildSitemapXml, buildWebSiteSchema, chunkSitemapUrls, deepMerge, rimelightSeo };
|
package/dist/index.mjs
CHANGED
|
@@ -1,178 +1,7 @@
|
|
|
1
1
|
import { buildSitemapIndexXml, buildSitemapUrls, buildSitemapXml, chunkSitemapUrls } from "./sitemap.mjs";
|
|
2
2
|
import { buildRobotsTxt } from "./robots.mjs";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*/
|
|
9
|
-
function buildCanonicalUrl(url, base) {
|
|
10
|
-
const path = new URL(url, base).toString();
|
|
11
|
-
return path.includes("?") ? path.replace(/\/?$/, "") : path.replace(/\/?$/, "/");
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Build a page title using a site's title template. Falls back to siteName for untitled pages; uses
|
|
15
|
-
* a 404 label for error pages.
|
|
16
|
-
*/
|
|
17
|
-
function buildPageTitle(opts) {
|
|
18
|
-
const { title, siteName, titleTemplate, is404 } = opts;
|
|
19
|
-
if (is404) return `404 - Not Found | ${siteName}`;
|
|
20
|
-
if (!title) return siteName;
|
|
21
|
-
return titleTemplate.replace("%s", title);
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Truncate a description to a maximum character length, appending "…" if trimmed.
|
|
25
|
-
*/
|
|
26
|
-
function buildSafeDescription(description, maxLength) {
|
|
27
|
-
if (!description) return description;
|
|
28
|
-
if (description.length <= maxLength) return description;
|
|
29
|
-
return description.slice(0, maxLength - 3) + "...";
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Build the value for the <meta name="robots"> tag. Returns the full directive string including
|
|
33
|
-
* max-image-preview for indexable pages.
|
|
34
|
-
*/
|
|
35
|
-
function buildRobotsMetaContent(opts) {
|
|
36
|
-
if (opts.override) return opts.override;
|
|
37
|
-
return !opts.noindex && !opts.is404 ? "index, follow, max-image-preview:large" : "noindex, nofollow";
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* \n * Build a Schema.org WebSite JSON-LD object.\n * Pass the result to JSON.stringify and emit
|
|
41
|
-
* via <script type="application/ld+json">.\n
|
|
42
|
-
*/
|
|
43
|
-
function buildWebSiteSchema(config, site) {
|
|
44
|
-
return {
|
|
45
|
-
"@context": "https://schema.org",
|
|
46
|
-
"@type": "WebSite",
|
|
47
|
-
"name": config.name,
|
|
48
|
-
"url": site ?? config.url,
|
|
49
|
-
"description": config.description
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
//#endregion
|
|
53
|
-
//#region src/llms.ts
|
|
54
|
-
/**
|
|
55
|
-
* Builds standard /llms.txt document format for AI discovery.
|
|
56
|
-
*/
|
|
57
|
-
function buildLlmsTxt(options) {
|
|
58
|
-
const { site, title, description, fullCorpusUrl = `${site.replace(/\/$/, "")}/llms-full.txt`, pages, sections = [] } = options;
|
|
59
|
-
const lines = [
|
|
60
|
-
`# ${title}`,
|
|
61
|
-
"",
|
|
62
|
-
description || "Documentation index for AI agents.",
|
|
63
|
-
"",
|
|
64
|
-
`Full corpus (all pages, one document): ${fullCorpusUrl}`,
|
|
65
|
-
""
|
|
66
|
-
];
|
|
67
|
-
if (sections.length > 0) {
|
|
68
|
-
lines.push("## Sections", "");
|
|
69
|
-
for (const sec of sections) lines.push(`- [${sec.label}](${sec.url})`);
|
|
70
|
-
lines.push("");
|
|
71
|
-
}
|
|
72
|
-
lines.push("## Pages", "");
|
|
73
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
74
|
-
for (const page of pages) {
|
|
75
|
-
const sec = page.section || "General";
|
|
76
|
-
if (!grouped.has(sec)) grouped.set(sec, []);
|
|
77
|
-
grouped.get(sec).push(page);
|
|
78
|
-
}
|
|
79
|
-
if (grouped.size === 1 && grouped.has("General")) for (const page of pages) {
|
|
80
|
-
const pageUrl = page.markdownUrl || page.url;
|
|
81
|
-
const desc = page.description ? ` — ${page.description}` : "";
|
|
82
|
-
lines.push(`- [${page.title}](${pageUrl})${desc}`);
|
|
83
|
-
}
|
|
84
|
-
else for (const [secName, secPages] of grouped.entries()) {
|
|
85
|
-
lines.push(`### ${secName}`, "");
|
|
86
|
-
for (const page of secPages) {
|
|
87
|
-
const pageUrl = page.markdownUrl || page.url;
|
|
88
|
-
const desc = page.description ? ` — ${page.description}` : "";
|
|
89
|
-
lines.push(`- [${page.title}](${pageUrl})${desc}`);
|
|
90
|
-
}
|
|
91
|
-
lines.push("");
|
|
92
|
-
}
|
|
93
|
-
lines.push("");
|
|
94
|
-
return lines.join("\n");
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Builds concatenated /llms-full.txt single corpus document for AI agents.
|
|
98
|
-
*/
|
|
99
|
-
function buildLlmsFullTxt(options) {
|
|
100
|
-
const { title, description, pages } = options;
|
|
101
|
-
const parts = [
|
|
102
|
-
`# ${title} — Full Documentation Corpus`,
|
|
103
|
-
"",
|
|
104
|
-
description || "Complete documentation corpus for AI agents.",
|
|
105
|
-
"",
|
|
106
|
-
"---",
|
|
107
|
-
""
|
|
108
|
-
];
|
|
109
|
-
for (const page of pages) {
|
|
110
|
-
const metaHeader = [
|
|
111
|
-
`## ${page.title}`,
|
|
112
|
-
"",
|
|
113
|
-
`URL: ${page.url}`,
|
|
114
|
-
...page.description ? [`Description: ${page.description}`] : [],
|
|
115
|
-
...page.version ? [`Version: ${page.version}`] : [],
|
|
116
|
-
"",
|
|
117
|
-
page.markdown.trim(),
|
|
118
|
-
"",
|
|
119
|
-
"---",
|
|
120
|
-
""
|
|
121
|
-
];
|
|
122
|
-
parts.push(...metaHeader);
|
|
123
|
-
}
|
|
124
|
-
return parts.join("\n");
|
|
125
|
-
}
|
|
126
|
-
//#endregion
|
|
127
|
-
//#region src/schema.ts
|
|
128
|
-
/**
|
|
129
|
-
* Builds Schema.org Article / TechArticle / BlogPosting JSON-LD.
|
|
130
|
-
*/
|
|
131
|
-
function buildArticleSchema(options) {
|
|
132
|
-
const { type = "TechArticle", title, description, url, datePublished, dateModified, authorName, authorUrl, image, publisherName = "Rimelight Entertainment", publisherLogo } = options;
|
|
133
|
-
const schema = {
|
|
134
|
-
"@context": "https://schema.org",
|
|
135
|
-
"@type": type,
|
|
136
|
-
"headline": title,
|
|
137
|
-
"mainEntityOfPage": {
|
|
138
|
-
"@type": "WebPage",
|
|
139
|
-
"@id": url
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
if (description) schema["description"] = description;
|
|
143
|
-
if (image) schema["image"] = image;
|
|
144
|
-
if (datePublished) schema["datePublished"] = datePublished instanceof Date ? datePublished.toISOString() : datePublished;
|
|
145
|
-
if (dateModified) schema["dateModified"] = dateModified instanceof Date ? dateModified.toISOString() : dateModified;
|
|
146
|
-
else if (datePublished) schema["dateModified"] = schema["datePublished"];
|
|
147
|
-
if (authorName) schema["author"] = {
|
|
148
|
-
"@type": "Person",
|
|
149
|
-
"name": authorName,
|
|
150
|
-
...authorUrl ? { url: authorUrl } : {}
|
|
151
|
-
};
|
|
152
|
-
if (publisherName) schema["publisher"] = {
|
|
153
|
-
"@type": "Organization",
|
|
154
|
-
"name": publisherName,
|
|
155
|
-
...publisherLogo ? { logo: {
|
|
156
|
-
"@type": "ImageObject",
|
|
157
|
-
"url": publisherLogo
|
|
158
|
-
} } : {}
|
|
159
|
-
};
|
|
160
|
-
return schema;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Builds Schema.org BreadcrumbList JSON-LD.
|
|
164
|
-
*/
|
|
165
|
-
function buildBreadcrumbSchema(items) {
|
|
166
|
-
return {
|
|
167
|
-
"@context": "https://schema.org",
|
|
168
|
-
"@type": "BreadcrumbList",
|
|
169
|
-
"itemListElement": items.map((item, index) => ({
|
|
170
|
-
"@type": "ListItem",
|
|
171
|
-
"position": index + 1,
|
|
172
|
-
"name": item.name,
|
|
173
|
-
"item": item.url
|
|
174
|
-
}))
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
//#endregion
|
|
178
|
-
export { buildArticleSchema, buildBreadcrumbSchema, buildCanonicalUrl, buildLlmsFullTxt, buildLlmsTxt, buildPageTitle, buildRobotsMetaContent, buildRobotsTxt, buildSafeDescription, buildSitemapIndexXml, buildSitemapUrls, buildSitemapXml, buildWebSiteSchema, chunkSitemapUrls };
|
|
3
|
+
import { buildCanonicalUrl, buildPageTitle, buildRobotsMetaContent, buildSafeDescription, buildWebSiteSchema, deepMerge } from "./meta.mjs";
|
|
4
|
+
import { buildLlmsFullTxt, buildLlmsTxt } from "./llms.mjs";
|
|
5
|
+
import { buildArticleSchema, buildBreadcrumbSchema } from "./schema.mjs";
|
|
6
|
+
import { rimelightSeo } from "./integration.mjs";
|
|
7
|
+
export { buildArticleSchema, buildBreadcrumbSchema, buildCanonicalUrl, buildLlmsFullTxt, buildLlmsTxt, buildPageTitle, buildRobotsMetaContent, buildRobotsTxt, buildSafeDescription, buildSitemapIndexXml, buildSitemapUrls, buildSitemapXml, buildWebSiteSchema, chunkSitemapUrls, deepMerge, rimelightSeo };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { r as LlmsTxtOptions } from "./llms-BVLxzjr-.mjs";
|
|
2
|
+
import { RobotsOptions, SitemapUrl } from "./types.mjs";
|
|
3
|
+
import { AstroIntegration } from "astro";
|
|
4
|
+
//#region src/integration.d.ts
|
|
5
|
+
export interface RimelightSeoOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Sitemap configuration or false to disable automatic /sitemap.xml
|
|
8
|
+
*/
|
|
9
|
+
sitemap?: boolean | {
|
|
10
|
+
urls?: SitemapUrl[];
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Robots.txt configuration or false to disable automatic /robots.txt
|
|
15
|
+
*/
|
|
16
|
+
robots?: boolean | RobotsOptions;
|
|
17
|
+
/**
|
|
18
|
+
* LLMs.txt configuration or false to disable automatic /llms.txt
|
|
19
|
+
*/
|
|
20
|
+
llms?: boolean | LlmsTxtOptions;
|
|
21
|
+
}
|
|
22
|
+
export declare function rimelightSeo(options?: RimelightSeoOptions): AstroIntegration;
|
|
23
|
+
//#endregion
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/integration.ts
|
|
2
|
+
function rimelightSeo(options = {}) {
|
|
3
|
+
return {
|
|
4
|
+
name: "@rimelight/seo",
|
|
5
|
+
hooks: { "astro:config:setup": ({ injectRoute, updateConfig }) => {
|
|
6
|
+
if (options.robots !== false) injectRoute({
|
|
7
|
+
pattern: "/robots.txt",
|
|
8
|
+
entrypoint: "@rimelight/seo/routes/robots.txt.ts"
|
|
9
|
+
});
|
|
10
|
+
if (options.sitemap !== false) injectRoute({
|
|
11
|
+
pattern: "/sitemap.xml",
|
|
12
|
+
entrypoint: "@rimelight/seo/routes/sitemap.xml.ts"
|
|
13
|
+
});
|
|
14
|
+
if (options.llms !== false) injectRoute({
|
|
15
|
+
pattern: "/llms.txt",
|
|
16
|
+
entrypoint: "@rimelight/seo/routes/llms.txt.ts"
|
|
17
|
+
});
|
|
18
|
+
updateConfig({ vite: { plugins: [{
|
|
19
|
+
name: "vite-plugin-rimelight-seo",
|
|
20
|
+
resolveId(id) {
|
|
21
|
+
if (id === "virtual:rimelight-seo-config") return "\0virtual:rimelight-seo-config";
|
|
22
|
+
return null;
|
|
23
|
+
},
|
|
24
|
+
load(id) {
|
|
25
|
+
if (id === "\0virtual:rimelight-seo-config") return `export default ${JSON.stringify(options)};`;
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}] } });
|
|
29
|
+
} }
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { rimelightSeo };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region src/llms.d.ts
|
|
2
|
+
interface LlmsPage {
|
|
3
|
+
title: string;
|
|
4
|
+
url: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
markdownUrl?: string;
|
|
7
|
+
section?: string;
|
|
8
|
+
}
|
|
9
|
+
interface LlmsTxtOptions {
|
|
10
|
+
site: string;
|
|
11
|
+
title: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
fullCorpusUrl?: string;
|
|
14
|
+
pages: LlmsPage[];
|
|
15
|
+
sections?: Array<{
|
|
16
|
+
label: string;
|
|
17
|
+
url: string;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
interface LlmsFullTxtOptions {
|
|
21
|
+
site: string;
|
|
22
|
+
title: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
pages: Array<{
|
|
25
|
+
title: string;
|
|
26
|
+
url: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
markdown: string;
|
|
29
|
+
version?: string;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Builds standard /llms.txt document format for AI discovery.
|
|
34
|
+
*/
|
|
35
|
+
declare function buildLlmsTxt(options: LlmsTxtOptions): string;
|
|
36
|
+
/**
|
|
37
|
+
* Builds concatenated /llms-full.txt single corpus document for AI agents.
|
|
38
|
+
*/
|
|
39
|
+
declare function buildLlmsFullTxt(options: LlmsFullTxtOptions): string;
|
|
40
|
+
//#endregion
|
|
41
|
+
export { buildLlmsTxt as a, buildLlmsFullTxt as i, LlmsPage as n, LlmsTxtOptions as r, LlmsFullTxtOptions as t };
|
package/dist/llms.d.mts
ADDED
package/dist/llms.mjs
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
//#region src/llms.ts
|
|
2
|
+
/**
|
|
3
|
+
* Builds standard /llms.txt document format for AI discovery.
|
|
4
|
+
*/
|
|
5
|
+
function buildLlmsTxt(options) {
|
|
6
|
+
const { site, title, description, fullCorpusUrl = `${site.replace(/\/$/, "")}/llms-full.txt`, pages, sections = [] } = options;
|
|
7
|
+
const lines = [
|
|
8
|
+
`# ${title}`,
|
|
9
|
+
"",
|
|
10
|
+
description || "Documentation index for AI agents.",
|
|
11
|
+
"",
|
|
12
|
+
`Full corpus (all pages, one document): ${fullCorpusUrl}`,
|
|
13
|
+
""
|
|
14
|
+
];
|
|
15
|
+
if (sections.length > 0) {
|
|
16
|
+
lines.push("## Sections", "");
|
|
17
|
+
for (const sec of sections) lines.push(`- [${sec.label}](${sec.url})`);
|
|
18
|
+
lines.push("");
|
|
19
|
+
}
|
|
20
|
+
lines.push("## Pages", "");
|
|
21
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
22
|
+
for (const page of pages) {
|
|
23
|
+
const sec = page.section || "General";
|
|
24
|
+
if (!grouped.has(sec)) grouped.set(sec, []);
|
|
25
|
+
grouped.get(sec).push(page);
|
|
26
|
+
}
|
|
27
|
+
if (grouped.size === 1 && grouped.has("General")) for (const page of pages) {
|
|
28
|
+
const pageUrl = page.markdownUrl || page.url;
|
|
29
|
+
const desc = page.description ? ` — ${page.description}` : "";
|
|
30
|
+
lines.push(`- [${page.title}](${pageUrl})${desc}`);
|
|
31
|
+
}
|
|
32
|
+
else for (const [secName, secPages] of grouped.entries()) {
|
|
33
|
+
lines.push(`### ${secName}`, "");
|
|
34
|
+
for (const page of secPages) {
|
|
35
|
+
const pageUrl = page.markdownUrl || page.url;
|
|
36
|
+
const desc = page.description ? ` — ${page.description}` : "";
|
|
37
|
+
lines.push(`- [${page.title}](${pageUrl})${desc}`);
|
|
38
|
+
}
|
|
39
|
+
lines.push("");
|
|
40
|
+
}
|
|
41
|
+
lines.push("");
|
|
42
|
+
return lines.join("\n");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Builds concatenated /llms-full.txt single corpus document for AI agents.
|
|
46
|
+
*/
|
|
47
|
+
function buildLlmsFullTxt(options) {
|
|
48
|
+
const { title, description, pages } = options;
|
|
49
|
+
const parts = [
|
|
50
|
+
`# ${title} — Full Documentation Corpus`,
|
|
51
|
+
"",
|
|
52
|
+
description || "Complete documentation corpus for AI agents.",
|
|
53
|
+
"",
|
|
54
|
+
"---",
|
|
55
|
+
""
|
|
56
|
+
];
|
|
57
|
+
for (const page of pages) {
|
|
58
|
+
const metaHeader = [
|
|
59
|
+
`## ${page.title}`,
|
|
60
|
+
"",
|
|
61
|
+
`URL: ${page.url}`,
|
|
62
|
+
...page.description ? [`Description: ${page.description}`] : [],
|
|
63
|
+
...page.version ? [`Version: ${page.version}`] : [],
|
|
64
|
+
"",
|
|
65
|
+
page.markdown.trim(),
|
|
66
|
+
"",
|
|
67
|
+
"---",
|
|
68
|
+
""
|
|
69
|
+
];
|
|
70
|
+
parts.push(...metaHeader);
|
|
71
|
+
}
|
|
72
|
+
return parts.join("\n");
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
export { buildLlmsFullTxt, buildLlmsTxt };
|
package/dist/meta.d.mts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { SiteConfig } from "./types.mjs";
|
|
2
|
+
//#region src/meta.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Build a canonical URL from a page URL and site base. - Strips trailing slash unless the path has
|
|
5
|
+
* query params (where trailing slash is removed too). - Returns undefined-safe: pass Astro.url and
|
|
6
|
+
* Astro.site directly.
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildCanonicalUrl(url: string | URL, base: string | URL): string;
|
|
9
|
+
/**
|
|
10
|
+
* Deep merge two metadata or configuration objects.
|
|
11
|
+
*/
|
|
12
|
+
export declare function deepMerge<T extends Record<string, any>>(base: T, override?: Record<string, any>): T;
|
|
13
|
+
/**
|
|
14
|
+
* Build a page title using a site's title template. Falls back to siteName for untitled pages; uses
|
|
15
|
+
* a 404 label for error pages. Supports absolute title bypass.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildPageTitle(opts: {
|
|
18
|
+
title?: string;
|
|
19
|
+
siteName: string;
|
|
20
|
+
titleTemplate: string;
|
|
21
|
+
is404?: boolean;
|
|
22
|
+
absolute?: boolean;
|
|
23
|
+
}): string;
|
|
24
|
+
/**
|
|
25
|
+
* Truncate a description to a maximum character length, appending "…" if trimmed.
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildSafeDescription(description: string, maxLength: number): string;
|
|
28
|
+
/**
|
|
29
|
+
* Build the value for the <meta name="robots"> tag. Returns the full directive string including
|
|
30
|
+
* max-image-preview for indexable pages.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildRobotsMetaContent(opts: {
|
|
33
|
+
noindex?: boolean;
|
|
34
|
+
is404?: boolean;
|
|
35
|
+
override?: string;
|
|
36
|
+
}): string;
|
|
37
|
+
/**
|
|
38
|
+
* \n * Build a Schema.org WebSite JSON-LD object.\n * Pass the result to JSON.stringify and emit
|
|
39
|
+
* via <script type="application/ld+json">.\n
|
|
40
|
+
*/
|
|
41
|
+
export declare function buildWebSiteSchema(config: Pick<SiteConfig, "name" | "url" | "description">, site?: string): Record<string, unknown>;
|
|
42
|
+
//#endregion
|
package/dist/meta.mjs
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
//#region src/meta.ts
|
|
2
|
+
/**
|
|
3
|
+
* Build a canonical URL from a page URL and site base. - Strips trailing slash unless the path has
|
|
4
|
+
* query params (where trailing slash is removed too). - Returns undefined-safe: pass Astro.url and
|
|
5
|
+
* Astro.site directly.
|
|
6
|
+
*/
|
|
7
|
+
function buildCanonicalUrl(url, base) {
|
|
8
|
+
const path = new URL(url, base).toString();
|
|
9
|
+
return path.includes("?") ? path.replace(/\/?$/, "") : path.replace(/\/?$/, "/");
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Deep merge two metadata or configuration objects.
|
|
13
|
+
*/
|
|
14
|
+
function deepMerge(base, override) {
|
|
15
|
+
if (!override) return { ...base };
|
|
16
|
+
const result = { ...base };
|
|
17
|
+
for (const key of Object.keys(override)) {
|
|
18
|
+
const baseVal = base[key];
|
|
19
|
+
const overrideVal = override[key];
|
|
20
|
+
if (baseVal && overrideVal && typeof baseVal === "object" && typeof overrideVal === "object" && !Array.isArray(baseVal) && !Array.isArray(overrideVal)) result[key] = deepMerge(baseVal, overrideVal);
|
|
21
|
+
else if (overrideVal !== void 0) result[key] = overrideVal;
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build a page title using a site's title template. Falls back to siteName for untitled pages; uses
|
|
27
|
+
* a 404 label for error pages. Supports absolute title bypass.
|
|
28
|
+
*/
|
|
29
|
+
function buildPageTitle(opts) {
|
|
30
|
+
const { title, siteName, titleTemplate, is404, absolute } = opts;
|
|
31
|
+
if (is404) return `404 - Not Found | ${siteName}`;
|
|
32
|
+
if (!title) return siteName;
|
|
33
|
+
if (absolute) return title;
|
|
34
|
+
return titleTemplate.replace("%s", title);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Truncate a description to a maximum character length, appending "…" if trimmed.
|
|
38
|
+
*/
|
|
39
|
+
function buildSafeDescription(description, maxLength) {
|
|
40
|
+
if (!description) return description;
|
|
41
|
+
if (description.length <= maxLength) return description;
|
|
42
|
+
return description.slice(0, maxLength - 3) + "...";
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Build the value for the <meta name="robots"> tag. Returns the full directive string including
|
|
46
|
+
* max-image-preview for indexable pages.
|
|
47
|
+
*/
|
|
48
|
+
function buildRobotsMetaContent(opts) {
|
|
49
|
+
if (opts.override) return opts.override;
|
|
50
|
+
return !opts.noindex && !opts.is404 ? "index, follow, max-image-preview:large" : "noindex, nofollow";
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* \n * Build a Schema.org WebSite JSON-LD object.\n * Pass the result to JSON.stringify and emit
|
|
54
|
+
* via <script type="application/ld+json">.\n
|
|
55
|
+
*/
|
|
56
|
+
function buildWebSiteSchema(config, site) {
|
|
57
|
+
return {
|
|
58
|
+
"@context": "https://schema.org",
|
|
59
|
+
"@type": "WebSite",
|
|
60
|
+
"name": config.name,
|
|
61
|
+
"url": site ?? config.url,
|
|
62
|
+
"description": config.description
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
export { buildCanonicalUrl, buildPageTitle, buildRobotsMetaContent, buildSafeDescription, buildWebSiteSchema, deepMerge };
|
package/dist/robots.d.mts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RobotsOptions } from "./types.mjs";
|
|
2
2
|
//#region src/robots.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Build robots.txt content from options
|
|
5
5
|
*/
|
|
6
|
-
declare function buildRobotsTxt(options: RobotsOptions): string;
|
|
7
|
-
//#endregion
|
|
8
|
-
export { buildRobotsTxt };
|
|
6
|
+
export declare function buildRobotsTxt(options: RobotsOptions): string;
|
|
7
|
+
//#endregion
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { buildLlmsTxt } from "../llms.mjs";
|
|
2
|
+
import seoConfig from "virtual:rimelight-seo-config";
|
|
3
|
+
//#region src/routes/llms.txt.ts
|
|
4
|
+
const GET = ({ site }) => {
|
|
5
|
+
const llmsOpts = typeof seoConfig?.llms === "object" ? seoConfig.llms : {};
|
|
6
|
+
const siteStr = site ? site.toString().replace(/\/$/, "") : "";
|
|
7
|
+
const hostname = site ? new URL(site).hostname : "Site";
|
|
8
|
+
const content = buildLlmsTxt({
|
|
9
|
+
site: siteStr,
|
|
10
|
+
title: hostname,
|
|
11
|
+
pages: [],
|
|
12
|
+
...llmsOpts
|
|
13
|
+
});
|
|
14
|
+
return new Response(content, { headers: {
|
|
15
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
16
|
+
"Cache-Control": "public, max-age=3600, stale-while-revalidate=86400"
|
|
17
|
+
} });
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
export { GET };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { buildRobotsTxt } from "../robots.mjs";
|
|
2
|
+
import seoConfig from "virtual:rimelight-seo-config";
|
|
3
|
+
//#region src/routes/robots.txt.ts
|
|
4
|
+
const GET = ({ site }) => {
|
|
5
|
+
const sitemapURL = site ? new URL("sitemap.xml", site).toString() : "";
|
|
6
|
+
const robotsOpts = typeof seoConfig?.robots === "object" ? seoConfig.robots : {};
|
|
7
|
+
const robots = buildRobotsTxt({
|
|
8
|
+
sitemapUrl: sitemapURL,
|
|
9
|
+
allow: ["/"],
|
|
10
|
+
...robotsOpts
|
|
11
|
+
});
|
|
12
|
+
return new Response(robots, { headers: {
|
|
13
|
+
"Content-Type": "text/plain; charset=utf-8",
|
|
14
|
+
"Cache-Control": "public, max-age=3600, stale-while-revalidate=86400"
|
|
15
|
+
} });
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { GET };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { buildSitemapXml } from "../sitemap.mjs";
|
|
2
|
+
import seoConfig from "virtual:rimelight-seo-config";
|
|
3
|
+
//#region src/routes/sitemap.xml.ts
|
|
4
|
+
const GET = () => {
|
|
5
|
+
const urls = (typeof seoConfig?.sitemap === "object" ? seoConfig.sitemap : {}).urls || [];
|
|
6
|
+
const xml = buildSitemapXml(urls);
|
|
7
|
+
return new Response(xml, { headers: {
|
|
8
|
+
"Content-Type": "application/xml; charset=utf-8",
|
|
9
|
+
"Cache-Control": "public, max-age=3600, stale-while-revalidate=86400"
|
|
10
|
+
} });
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
export { GET };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/schema.d.ts
|
|
2
|
+
interface ArticleSchemaOptions {
|
|
3
|
+
type?: "TechArticle" | "Article" | "BlogPosting";
|
|
4
|
+
title: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
url: string;
|
|
7
|
+
datePublished?: string | Date;
|
|
8
|
+
dateModified?: string | Date;
|
|
9
|
+
authorName?: string;
|
|
10
|
+
authorUrl?: string;
|
|
11
|
+
image?: string;
|
|
12
|
+
publisherName?: string;
|
|
13
|
+
publisherLogo?: string;
|
|
14
|
+
}
|
|
15
|
+
interface BreadcrumbItem {
|
|
16
|
+
name: string;
|
|
17
|
+
url: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Builds Schema.org Article / TechArticle / BlogPosting JSON-LD.
|
|
21
|
+
*/
|
|
22
|
+
declare function buildArticleSchema(options: ArticleSchemaOptions): Record<string, any>;
|
|
23
|
+
/**
|
|
24
|
+
* Builds Schema.org BreadcrumbList JSON-LD.
|
|
25
|
+
*/
|
|
26
|
+
declare function buildBreadcrumbSchema(items: BreadcrumbItem[]): Record<string, any>;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { buildBreadcrumbSchema as i, BreadcrumbItem as n, buildArticleSchema as r, ArticleSchemaOptions as t };
|