@rimelight/seo 0.0.4 → 0.0.6
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 +8 -38
- package/dist/index.mjs +8 -175
- package/dist/integration.d.mts +6 -0
- package/dist/integration.mjs +101 -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/og.d.mts +117 -0
- package/dist/og.mjs +302 -0
- package/dist/robots.d.mts +3 -4
- package/dist/routes/llms-full.txt.d.mts +5 -0
- package/dist/routes/llms-full.txt.mjs +57 -0
- package/dist/routes/llms.txt.d.mts +5 -0
- package/dist/routes/llms.txt.mjs +68 -0
- package/dist/routes/robots.txt.d.mts +4 -0
- package/dist/routes/robots.txt.mjs +46 -0
- package/dist/routes/rss.xml.d.mts +4 -0
- package/dist/routes/rss.xml.mjs +51 -0
- package/dist/routes/sitemap.xml.d.mts +4 -0
- package/dist/routes/sitemap.xml.mjs +27 -0
- package/dist/rss.d.mts +8 -0
- package/dist/rss.mjs +44 -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 +354 -2
- package/package.json +24 -5
- package/src/components/SEOHead.astro +65 -18
- package/src/env.d.ts +33 -3
- package/src/index.ts +19 -1
- package/src/integration.ts +172 -0
- package/src/meta.test.ts +25 -1
- package/src/meta.ts +32 -2
- package/src/og.ts +444 -0
- package/src/routes/llms-full.txt.ts +77 -0
- package/src/routes/llms.txt.ts +90 -0
- package/src/routes/robots.txt.ts +63 -0
- package/src/routes/rss.xml.ts +87 -0
- package/src/routes/sitemap.xml.ts +43 -0
- package/src/rss.test.ts +28 -0
- package/src/rss.ts +63 -0
- package/src/types.ts +367 -242
- package/dist/types-e7gqYZwc.d.mts +0 -305
package/dist/index.d.mts
CHANGED
|
@@ -1,40 +1,10 @@
|
|
|
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, LlmsFullRouteOptions, LlmsRouteOptions, LocaleConfig, OGImage, OpenGraphMeta, RSSFeed, RimelightSeoOptions, RobotsOptions, RobotsRouteOptions, RssFeedOptions, RssItem, RssOptions, SeoEntry, SiteConfig, SitemapAlternate, SitemapOptions, 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
|
-
|
|
8
|
-
|
|
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 { buildRssXml, escapeXml } from "./rss.mjs";
|
|
7
|
+
import { buildCanonicalUrl, buildPageTitle, buildRobotsMetaContent, buildSafeDescription, buildWebSiteSchema, deepMerge } from "./meta.mjs";
|
|
8
|
+
import { OgFontConfig, OgLayoutOptions, OgRenderConfig } from "./og.mjs";
|
|
9
|
+
import rimelightSeo from "./integration.mjs";
|
|
10
|
+
export { type ArticleSchemaOptions, type BreadcrumbItem, type ChangeFreq, type HeadProps, type LlmsFullRouteOptions, type LlmsFullTxtOptions, type LlmsPage, type LlmsRouteOptions, type LlmsTxtOptions, type LocaleConfig, type OGImage, type OgFontConfig, type OgLayoutOptions, type OgRenderConfig, type OpenGraphMeta, type RSSFeed, type RimelightSeoOptions, type RobotsOptions, type RobotsRouteOptions, type RssFeedOptions, type RssItem, type RssOptions, type SeoEntry, type SiteConfig, type SitemapAlternate, type SitemapOptions, type SitemapUrl, type TwitterMeta, type UserAgentRule, buildArticleSchema, buildBreadcrumbSchema, buildCanonicalUrl, buildLlmsFullTxt, buildLlmsTxt, buildPageTitle, buildRobotsMetaContent, buildRobotsTxt, buildRssXml, buildSafeDescription, buildSitemapIndexXml, buildSitemapUrls, buildSitemapXml, buildWebSiteSchema, chunkSitemapUrls, deepMerge, rimelightSeo as default, rimelightSeo, escapeXml };
|
package/dist/index.mjs
CHANGED
|
@@ -1,178 +1,11 @@
|
|
|
1
1
|
import { buildSitemapIndexXml, buildSitemapUrls, buildSitemapXml, chunkSitemapUrls } from "./sitemap.mjs";
|
|
2
2
|
import { buildRobotsTxt } from "./robots.mjs";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
}
|
|
3
|
+
import { buildRssXml, escapeXml } from "./rss.mjs";
|
|
4
|
+
import { buildCanonicalUrl, buildPageTitle, buildRobotsMetaContent, buildSafeDescription, buildWebSiteSchema, deepMerge } from "./meta.mjs";
|
|
5
|
+
import { buildLlmsFullTxt, buildLlmsTxt } from "./llms.mjs";
|
|
6
|
+
import { buildArticleSchema, buildBreadcrumbSchema } from "./schema.mjs";
|
|
7
|
+
import rimelightSeo from "./integration.mjs";
|
|
8
|
+
//#region src/index.ts
|
|
9
|
+
var src_default = rimelightSeo;
|
|
52
10
|
//#endregion
|
|
53
|
-
|
|
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 };
|
|
11
|
+
export { buildArticleSchema, buildBreadcrumbSchema, buildCanonicalUrl, buildLlmsFullTxt, buildLlmsTxt, buildPageTitle, buildRobotsMetaContent, buildRobotsTxt, buildRssXml, buildSafeDescription, buildSitemapIndexXml, buildSitemapUrls, buildSitemapXml, buildWebSiteSchema, chunkSitemapUrls, deepMerge, src_default as default, escapeXml, rimelightSeo };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { RimelightSeoOptions } from "./types.mjs";
|
|
2
|
+
import { AstroIntegration } from "astro";
|
|
3
|
+
//#region src/integration.d.ts
|
|
4
|
+
export declare function rimelightSeo(options?: RimelightSeoOptions): AstroIntegration;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { type RimelightSeoOptions, rimelightSeo as default };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
//#region src/integration.ts
|
|
5
|
+
function rimelightSeo(options = {}) {
|
|
6
|
+
return {
|
|
7
|
+
name: "@rimelight/seo",
|
|
8
|
+
hooks: { "astro:config:setup": ({ injectRoute, updateConfig, config }) => {
|
|
9
|
+
const rootDir = fileURLToPath(config.root);
|
|
10
|
+
const findFile = (candidates) => {
|
|
11
|
+
for (const c of candidates) {
|
|
12
|
+
const resolved = path.resolve(rootDir, c);
|
|
13
|
+
if (fs.existsSync(resolved)) return resolved;
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
};
|
|
17
|
+
const seoConfigPath = findFile([
|
|
18
|
+
"src/config/seo.config.ts",
|
|
19
|
+
"src/config/seo.config.js",
|
|
20
|
+
"src/config/seo.config.mjs"
|
|
21
|
+
]);
|
|
22
|
+
const siteConfigPath = findFile([
|
|
23
|
+
"src/config/site.config.ts",
|
|
24
|
+
"src/config/site.config.js",
|
|
25
|
+
"src/config/site.config.mjs"
|
|
26
|
+
]);
|
|
27
|
+
const dbPath = findFile([
|
|
28
|
+
"src/db/index.ts",
|
|
29
|
+
"src/db/index.js",
|
|
30
|
+
"src/db/index.mjs"
|
|
31
|
+
]);
|
|
32
|
+
const corpusPath = findFile([
|
|
33
|
+
"src/config/llms.corpus.ts",
|
|
34
|
+
"src/config/llms.corpus.js",
|
|
35
|
+
"src/config/llms.corpus.mjs"
|
|
36
|
+
]);
|
|
37
|
+
if (options.robots !== false) injectRoute({
|
|
38
|
+
pattern: typeof options.robots === "object" && options.robots.path ? options.robots.path : "/robots.txt",
|
|
39
|
+
entrypoint: "@rimelight/seo/routes/robots.txt.ts"
|
|
40
|
+
});
|
|
41
|
+
if (options.sitemap !== false) injectRoute({
|
|
42
|
+
pattern: typeof options.sitemap === "object" && options.sitemap.path ? options.sitemap.path : "/sitemap.xml",
|
|
43
|
+
entrypoint: "@rimelight/seo/routes/sitemap.xml.ts"
|
|
44
|
+
});
|
|
45
|
+
if (options.rss !== false) injectRoute({
|
|
46
|
+
pattern: typeof options.rss === "object" && options.rss.path ? options.rss.path : "/rss.xml",
|
|
47
|
+
entrypoint: "@rimelight/seo/routes/rss.xml.ts"
|
|
48
|
+
});
|
|
49
|
+
if (options.llms !== false) injectRoute({
|
|
50
|
+
pattern: typeof options.llms === "object" && options.llms.path ? options.llms.path : "/llms.txt",
|
|
51
|
+
entrypoint: "@rimelight/seo/routes/llms.txt.ts"
|
|
52
|
+
});
|
|
53
|
+
if (options.llmsFull !== false) injectRoute({
|
|
54
|
+
pattern: typeof options.llmsFull === "object" && options.llmsFull.path ? options.llmsFull.path : "/llms-full.txt",
|
|
55
|
+
entrypoint: "@rimelight/seo/routes/llms-full.txt.ts"
|
|
56
|
+
});
|
|
57
|
+
updateConfig({ vite: { plugins: [{
|
|
58
|
+
name: "vite-plugin-rimelight-seo",
|
|
59
|
+
resolveId(id) {
|
|
60
|
+
if (id === "virtual:rimelight-seo-config" || id === "virtual:rimelight-seo/options") return "\0virtual:rimelight-seo/options";
|
|
61
|
+
if (id === "virtual:rimelight-seo/config") return "\0virtual:rimelight-seo/config";
|
|
62
|
+
if (id === "virtual:rimelight-seo/site-config") return "\0virtual:rimelight-seo/site-config";
|
|
63
|
+
if (id === "virtual:rimelight-seo/db") return "\0virtual:rimelight-seo/db";
|
|
64
|
+
if (id === "virtual:rimelight-seo/corpus") return "\0virtual:rimelight-seo/corpus";
|
|
65
|
+
return null;
|
|
66
|
+
},
|
|
67
|
+
load(id) {
|
|
68
|
+
if (id === "\0virtual:rimelight-seo/options") return `export default ${JSON.stringify(options)};`;
|
|
69
|
+
if (id === "\0virtual:rimelight-seo/config") {
|
|
70
|
+
if (seoConfigPath) return `import * as customConfig from ${JSON.stringify(seoConfigPath)};
|
|
71
|
+
export const SEO_LOCALES = customConfig.SEO_LOCALES;
|
|
72
|
+
export const PRIVATE_PATH_PREFIXES = customConfig.PRIVATE_PATH_PREFIXES || [];
|
|
73
|
+
export const seoEntries = customConfig.seoEntries;
|
|
74
|
+
export const rssEntries = customConfig.rssEntries;
|
|
75
|
+
export default customConfig;`;
|
|
76
|
+
return `export const SEO_LOCALES = undefined;
|
|
77
|
+
export const PRIVATE_PATH_PREFIXES = [];
|
|
78
|
+
export const seoEntries = async () => [];
|
|
79
|
+
export const rssEntries = undefined;`;
|
|
80
|
+
}
|
|
81
|
+
if (id === "\0virtual:rimelight-seo/site-config") {
|
|
82
|
+
if (siteConfigPath) return `export * from ${JSON.stringify(siteConfigPath)};`;
|
|
83
|
+
return `export const siteConfig = { name: "Site", description: "", url: "" };`;
|
|
84
|
+
}
|
|
85
|
+
if (id === "\0virtual:rimelight-seo/db") {
|
|
86
|
+
if (dbPath) return `export * from ${JSON.stringify(dbPath)};`;
|
|
87
|
+
return `export const db = null;
|
|
88
|
+
export const pages = null;`;
|
|
89
|
+
}
|
|
90
|
+
if (id === "\0virtual:rimelight-seo/corpus") {
|
|
91
|
+
if (corpusPath) return `export * from ${JSON.stringify(corpusPath)};`;
|
|
92
|
+
return `export const corpusPages = [];`;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}] } });
|
|
97
|
+
} }
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
101
|
+
export { rimelightSeo as default, 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 };
|