mdsvr 2.4.0 → 2.5.0

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.
Files changed (93) hide show
  1. package/dist/directory.d.ts +11 -0
  2. package/dist/directory.d.ts.map +1 -1
  3. package/dist/directory.js +31 -115
  4. package/dist/directory.js.map +1 -1
  5. package/dist/generators/feed.d.ts.map +1 -1
  6. package/dist/generators/feed.js +9 -2
  7. package/dist/generators/feed.js.map +1 -1
  8. package/dist/generators/robots.d.ts +11 -0
  9. package/dist/generators/robots.d.ts.map +1 -0
  10. package/dist/generators/robots.js +32 -0
  11. package/dist/generators/robots.js.map +1 -0
  12. package/dist/generators/search-index.d.ts.map +1 -1
  13. package/dist/generators/search-index.js +6 -5
  14. package/dist/generators/search-index.js.map +1 -1
  15. package/dist/generators/sitemap.d.ts +17 -1
  16. package/dist/generators/sitemap.d.ts.map +1 -1
  17. package/dist/generators/sitemap.js +119 -59
  18. package/dist/generators/sitemap.js.map +1 -1
  19. package/dist/generators/static-export.d.ts.map +1 -1
  20. package/dist/generators/static-export.js +66 -11
  21. package/dist/generators/static-export.js.map +1 -1
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/link-transform.d.ts +37 -5
  26. package/dist/link-transform.d.ts.map +1 -1
  27. package/dist/link-transform.js +120 -13
  28. package/dist/link-transform.js.map +1 -1
  29. package/dist/render-page.d.ts +1 -2
  30. package/dist/render-page.d.ts.map +1 -1
  31. package/dist/render-page.js +56 -40
  32. package/dist/render-page.js.map +1 -1
  33. package/dist/renderer/markdown.d.ts +0 -16
  34. package/dist/renderer/markdown.d.ts.map +1 -1
  35. package/dist/renderer/markdown.js +6 -63
  36. package/dist/renderer/markdown.js.map +1 -1
  37. package/dist/router.d.ts.map +1 -1
  38. package/dist/router.js +19 -1
  39. package/dist/router.js.map +1 -1
  40. package/dist/seo/extract.d.ts +26 -0
  41. package/dist/seo/extract.d.ts.map +1 -0
  42. package/dist/seo/extract.js +159 -0
  43. package/dist/seo/extract.js.map +1 -0
  44. package/dist/seo/index.d.ts +5 -0
  45. package/dist/seo/index.d.ts.map +1 -0
  46. package/dist/seo/index.js +5 -0
  47. package/dist/seo/index.js.map +1 -0
  48. package/dist/seo/jsonld.d.ts +15 -0
  49. package/dist/seo/jsonld.d.ts.map +1 -0
  50. package/dist/seo/jsonld.js +93 -0
  51. package/dist/seo/jsonld.js.map +1 -0
  52. package/dist/seo/metadata.d.ts +60 -0
  53. package/dist/seo/metadata.d.ts.map +1 -0
  54. package/dist/seo/metadata.js +144 -0
  55. package/dist/seo/metadata.js.map +1 -0
  56. package/dist/seo/url.d.ts +27 -0
  57. package/dist/seo/url.d.ts.map +1 -0
  58. package/dist/seo/url.js +76 -0
  59. package/dist/seo/url.js.map +1 -0
  60. package/dist/settings/defaults.d.ts.map +1 -1
  61. package/dist/settings/defaults.js +0 -2
  62. package/dist/settings/defaults.js.map +1 -1
  63. package/dist/settings/index.d.ts +1 -1
  64. package/dist/settings/index.d.ts.map +1 -1
  65. package/dist/settings/index.js +15 -0
  66. package/dist/settings/index.js.map +1 -1
  67. package/dist/settings/schema.d.ts +317 -5
  68. package/dist/settings/schema.d.ts.map +1 -1
  69. package/dist/settings/schema.js +45 -1
  70. package/dist/settings/schema.js.map +1 -1
  71. package/dist/template/analytics.d.ts +18 -0
  72. package/dist/template/analytics.d.ts.map +1 -0
  73. package/dist/template/analytics.js +83 -0
  74. package/dist/template/analytics.js.map +1 -0
  75. package/dist/template/index.d.ts +5 -0
  76. package/dist/template/index.d.ts.map +1 -1
  77. package/dist/template/index.js +59 -59
  78. package/dist/template/index.js.map +1 -1
  79. package/dist/template/search.d.ts.map +1 -1
  80. package/dist/template/search.js +9 -2
  81. package/dist/template/search.js.map +1 -1
  82. package/dist/template/seo.d.ts +7 -10
  83. package/dist/template/seo.d.ts.map +1 -1
  84. package/dist/template/seo.js +57 -33
  85. package/dist/template/seo.js.map +1 -1
  86. package/dist/template/sidebar.d.ts.map +1 -1
  87. package/dist/template/sidebar.js +11 -8
  88. package/dist/template/sidebar.js.map +1 -1
  89. package/dist/validator/index.d.ts +1 -1
  90. package/dist/validator/index.d.ts.map +1 -1
  91. package/dist/validator/index.js +21 -0
  92. package/dist/validator/index.js.map +1 -1
  93. package/package.json +1 -1
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Content extraction helpers for SEO metadata.
3
+ * (Moved from renderer/markdown.ts; description extraction is wired so it
4
+ * actually runs, and tightened to ~160 chars.)
5
+ */
6
+ function stripFrontmatter(content) {
7
+ return content.replace(/^---\r?\n[\s\S]*?\r?\n---/, "");
8
+ }
9
+ function stripFencedCode(content) {
10
+ return content
11
+ .replace(/^(`{3,}|~{3,})[^\n]*\n[\s\S]*?^\1\s*$/gm, "")
12
+ .replace(/(`{3,}|~{3,})[\s\S]*?\1/g, "");
13
+ }
14
+ function decodeEntities(text) {
15
+ const entities = {
16
+ "&": "&",
17
+ "&lt;": "<",
18
+ "&gt;": ">",
19
+ "&quot;": '"',
20
+ "&#39;": "'",
21
+ "&#x27;": "'",
22
+ "&nbsp;": " ",
23
+ };
24
+ return text.replace(/&(amp|lt|gt|quot|nbsp|#39|#x27);/g, (m) => entities[m] || m);
25
+ }
26
+ /** Strip inline markdown/HTML so only plain text remains. */
27
+ function cleanInline(text) {
28
+ return decodeEntities(text
29
+ .replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1") // images → alt text
30
+ .replace(/\[([^\]]+)\]\([^)]*\)/g, "$1") // links → text
31
+ .replace(/\[([^\]]+)\]\[[^\]]*\]/g, "$1") // ref links → text
32
+ .replace(/<[^>]+>/g, "") // HTML tags
33
+ .replace(/(\*\*|__)(.*?)\1/g, "$2") // bold
34
+ .replace(/\*([^*]+)\*/g, "$1") // italic
35
+ // _italic_ — only at word boundaries so SNAKE_CASE identifiers survive
36
+ .replace(/(^|\W)_([^_]+)_(?=\W|$)/g, "$1$2")
37
+ .replace(/~~([^~]+)~~/g, "$1") // strikethrough
38
+ .replace(/`([^`]+)`/g, "$1") // inline code
39
+ .replace(/\s+/g, " ")
40
+ .trim());
41
+ }
42
+ /**
43
+ * Whether a trimmed line can never begin a meaningful paragraph
44
+ * (headings, lists, tables, blockquotes, HTML, MDX statements, nav links).
45
+ */
46
+ function isSkippableLine(line) {
47
+ if (/^#{1,6}\s/.test(line))
48
+ return true; // heading
49
+ if (/^>/.test(line))
50
+ return true; // blockquote
51
+ if (/^[-*_]{3,}$/.test(line))
52
+ return true; // horizontal rule
53
+ if (/^[-*+]\s/.test(line))
54
+ return true; // bullet list
55
+ if (/^\d+\.\s/.test(line))
56
+ return true; // ordered list
57
+ if (/^\|/.test(line))
58
+ return true; // table row
59
+ const c = line[0];
60
+ if (c === "`" || c === "~")
61
+ return true; // fence remnants
62
+ if (c === "<")
63
+ return true; // HTML block/tag line
64
+ if (c === "!")
65
+ return true; // image
66
+ if (c === "[")
67
+ return true; // bare link / link reference (nav-like)
68
+ if (c === ":" || c === "{" || c === "}" || c === "=")
69
+ return true; // containers/MDX
70
+ if (/^(import|export)\s/.test(line))
71
+ return true; // MDX statements
72
+ if (/^\/\//.test(line))
73
+ return true; // JS-style comments
74
+ return false;
75
+ }
76
+ /** Truncate on a word boundary near `soft`, never exceeding `hard`. */
77
+ function truncate(text, soft, hard) {
78
+ if (text.length <= soft)
79
+ return text;
80
+ const window = text.slice(0, soft);
81
+ const boundary = window.lastIndexOf(" ");
82
+ let out = boundary > 40 ? window.slice(0, boundary) : text.slice(0, hard - 3);
83
+ if (out.length > hard - 3)
84
+ out = out.slice(0, hard - 3);
85
+ return `${out.trimEnd()}...`;
86
+ }
87
+ /**
88
+ * Extract the first H1 heading from markdown content (for SEO title fallback).
89
+ */
90
+ export function extractFirstHeading(content) {
91
+ const body = stripFencedCode(stripFrontmatter(content));
92
+ const match = body.match(/^#[ \t]+(.+?)[ \t]*#*[ \t]*$/m);
93
+ return match ? cleanInline(match[1]) || null : null;
94
+ }
95
+ /** Descriptions shorter than this are treated as degenerate — keep scanning. */
96
+ const MIN_DESCRIPTION_LENGTH = 40;
97
+ /**
98
+ * Whether a line is a bare label like `**Làm gì:**` or `Functional:` —
99
+ * markdown emphasis that cleans to a short colon-terminated string.
100
+ */
101
+ function isLabelOnly(line) {
102
+ const text = cleanInline(line);
103
+ return (text.length > 0 &&
104
+ text.length <= MIN_DESCRIPTION_LENGTH &&
105
+ text.endsWith(":"));
106
+ }
107
+ /**
108
+ * Extract the first meaningful paragraph from markdown content
109
+ * (for the meta description fallback). Skips frontmatter, code blocks,
110
+ * headings, HTML, comments, and nav-like lines; drops label-only lines;
111
+ * requires ~40 chars of real prose (shorter paragraphs are kept only as a
112
+ * last resort); collapses whitespace; cuts at ~160 chars on a word
113
+ * boundary (hard cap 200).
114
+ */
115
+ export function extractFirstParagraph(content) {
116
+ let body = stripFrontmatter(content);
117
+ body = stripFencedCode(body);
118
+ body = body.replace(/<!--[\s\S]*?-->/g, "");
119
+ let fallback = null;
120
+ let paraLines = [];
121
+ // Evaluate the accumulated paragraph: returns a qualifying description,
122
+ // records a shorter candidate as fallback, or null if the block is empty.
123
+ const flush = () => {
124
+ if (paraLines.length === 0)
125
+ return null;
126
+ const kept = paraLines.filter((l) => !isLabelOnly(l));
127
+ paraLines = [];
128
+ const text = cleanInline(kept.join(" "));
129
+ if (!text)
130
+ return null;
131
+ if (text.length >= MIN_DESCRIPTION_LENGTH)
132
+ return truncate(text, 160, 200);
133
+ if (!fallback || text.length > fallback.length)
134
+ fallback = text;
135
+ return null;
136
+ };
137
+ for (const rawLine of body.split("\n")) {
138
+ const line = rawLine.trim();
139
+ if (!line || isSkippableLine(line)) {
140
+ const result = flush();
141
+ if (result)
142
+ return result;
143
+ continue;
144
+ }
145
+ paraLines.push(line);
146
+ }
147
+ const result = flush();
148
+ return result ?? (fallback ? truncate(fallback, 160, 200) : null);
149
+ }
150
+ /**
151
+ * Extract SEO data (title + description) from markdown content.
152
+ */
153
+ export function extractSeoData(content) {
154
+ return {
155
+ title: extractFirstHeading(content),
156
+ description: extractFirstParagraph(content),
157
+ };
158
+ }
159
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/seo/extract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { normalizeUrlPath, normalizeBasePath, buildPageUrl, buildSiteUrl, toAbsoluteAssetUrl, } from "./url.js";
2
+ export { extractFirstHeading, extractFirstParagraph, extractSeoData, } from "./extract.js";
3
+ export { resolveSeoData, formatPageTitle, type SeoData, type SeoAuthor, type SeoBreadcrumb, type PageKind, type ResolveSeoInput, } from "./metadata.js";
4
+ export { buildJsonLd } from "./jsonld.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/seo/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,cAAc,EACd,eAAe,EACf,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { normalizeUrlPath, normalizeBasePath, buildPageUrl, buildSiteUrl, toAbsoluteAssetUrl, } from "./url.js";
2
+ export { extractFirstHeading, extractFirstParagraph, extractSeoData, } from "./extract.js";
3
+ export { resolveSeoData, formatPageTitle, } from "./metadata.js";
4
+ export { buildJsonLd } from "./jsonld.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/seo/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,cAAc,EACd,eAAe,GAMhB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { Settings } from "../settings/index.js";
2
+ import type { SeoData } from "./metadata.js";
3
+ /**
4
+ * Build the JSON-LD `<script>` tag for a page.
5
+ *
6
+ * - Homepage → `WebSite`
7
+ * - Document pages → `Article`
8
+ * - Nested routes → `BreadcrumbList`
9
+ *
10
+ * Fields are only emitted when real data exists (no invented dates).
11
+ * Returns an empty string when `seo.structuredData` is off or there is
12
+ * nothing worth emitting.
13
+ */
14
+ export declare function buildJsonLd(seo: SeoData, settings: Settings): string;
15
+ //# sourceMappingURL=jsonld.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonld.d.ts","sourceRoot":"","sources":["../../src/seo/jsonld.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,CA0EpE"}
@@ -0,0 +1,93 @@
1
+ import { buildPageUrl, buildSiteUrl, toAbsoluteAssetUrl } from "./url.js";
2
+ /**
3
+ * Build the JSON-LD `<script>` tag for a page.
4
+ *
5
+ * - Homepage → `WebSite`
6
+ * - Document pages → `Article`
7
+ * - Nested routes → `BreadcrumbList`
8
+ *
9
+ * Fields are only emitted when real data exists (no invented dates).
10
+ * Returns an empty string when `seo.structuredData` is off or there is
11
+ * nothing worth emitting.
12
+ */
13
+ export function buildJsonLd(seo, settings) {
14
+ if (!settings.seo.structuredData)
15
+ return "";
16
+ const graph = [];
17
+ if (seo.kind !== "generic" && seo.urlPath === "/") {
18
+ const site = {
19
+ "@type": "WebSite",
20
+ name: settings.site.title || seo.title,
21
+ };
22
+ const url = buildSiteUrl(settings);
23
+ if (url)
24
+ site.url = url;
25
+ const description = seo.description || settings.site.description;
26
+ if (description)
27
+ site.description = description;
28
+ // Sitelinks search box — client-side search reads the `q` query param.
29
+ if (settings.search.enabled && url) {
30
+ site.potentialAction = {
31
+ "@type": "SearchAction",
32
+ target: {
33
+ "@type": "EntryPoint",
34
+ urlTemplate: `${url.replace(/\/+$/, "")}/?q={search_term_string}`,
35
+ },
36
+ "query-input": "required name=search_term_string",
37
+ };
38
+ }
39
+ graph.push(site);
40
+ }
41
+ else if (seo.kind === "document" && seo.type === "article") {
42
+ const article = {
43
+ "@type": "Article",
44
+ headline: seo.title,
45
+ };
46
+ if (seo.description)
47
+ article.description = seo.description;
48
+ if (seo.absoluteUrl)
49
+ article.url = seo.absoluteUrl;
50
+ if (seo.image) {
51
+ const image = toAbsoluteAssetUrl(seo.image, settings);
52
+ if (image)
53
+ article.image = image;
54
+ }
55
+ if (seo.author) {
56
+ article.author = {
57
+ "@type": seo.author.type,
58
+ name: seo.author.name,
59
+ ...(seo.author.url ? { url: seo.author.url } : {}),
60
+ };
61
+ }
62
+ if (seo.datePublished)
63
+ article.datePublished = seo.datePublished;
64
+ if (seo.dateModified)
65
+ article.dateModified = seo.dateModified;
66
+ graph.push(article);
67
+ }
68
+ if (seo.breadcrumbs.length > 1) {
69
+ graph.push({
70
+ "@type": "BreadcrumbList",
71
+ itemListElement: seo.breadcrumbs.map((crumb, i) => {
72
+ const item = {
73
+ "@type": "ListItem",
74
+ position: i + 1,
75
+ name: crumb.name,
76
+ };
77
+ const url = buildPageUrl(crumb.urlPath, settings);
78
+ if (url)
79
+ item.item = url;
80
+ return item;
81
+ }),
82
+ });
83
+ }
84
+ if (graph.length === 0)
85
+ return "";
86
+ // `<\/` escaping prevents a literal `</script>` from breaking the tag.
87
+ const json = JSON.stringify({
88
+ "@context": "https://schema.org",
89
+ "@graph": graph,
90
+ }).replace(/<\//g, "<\\/");
91
+ return `<script type="application/ld+json">${json}</script>`;
92
+ }
93
+ //# sourceMappingURL=jsonld.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonld.js","sourceRoot":"","sources":["../../src/seo/jsonld.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import type { Settings } from "../settings/index.js";
2
+ import type { NavItem } from "../template/sidebar.js";
3
+ export type PageKind = "document" | "directory" | "generic";
4
+ export interface SeoAuthor {
5
+ name: string;
6
+ url?: string;
7
+ type: "Person" | "Organization";
8
+ }
9
+ export interface SeoBreadcrumb {
10
+ name: string;
11
+ urlPath: string;
12
+ }
13
+ export interface SeoData {
14
+ /** Resolved page title (before `seo.titleTemplate`). */
15
+ title: string;
16
+ description?: string;
17
+ image?: string;
18
+ /** Pixel dimensions, set only for generated OG images (known size). */
19
+ imageWidth?: number;
20
+ imageHeight?: number;
21
+ /** Absolute canonical URL, or null when no `site.baseUrl` is configured. */
22
+ absoluteUrl: string | null;
23
+ /** Normalized trailing-slash route. */
24
+ urlPath: string;
25
+ type: "website" | "article";
26
+ kind: PageKind;
27
+ datePublished?: string;
28
+ dateModified?: string;
29
+ author?: SeoAuthor;
30
+ noIndex: boolean;
31
+ /** Home → ancestors → current page (empty when disabled or at root). */
32
+ breadcrumbs: SeoBreadcrumb[];
33
+ /** Root-relative sitemap URL, or null when it should not be advertised. */
34
+ sitemapUrl: string | null;
35
+ }
36
+ export interface ResolveSeoInput {
37
+ frontmatter?: Record<string, unknown>;
38
+ /** Raw markdown/MDX source for description auto-extraction. */
39
+ content?: string;
40
+ /** Display title resolved by the caller (frontmatter → H1 → filename). */
41
+ title: string;
42
+ urlPath?: string;
43
+ kind?: PageKind;
44
+ settings: Settings;
45
+ sidebar?: NavItem[];
46
+ isStaticExport?: boolean;
47
+ }
48
+ /**
49
+ * Apply `seo.titleTemplate` to a page title. When the template is unset,
50
+ * defaults to `"<title> | <site title>"` (or the bare title when the site
51
+ * has no title).
52
+ */
53
+ export declare function formatPageTitle(pageTitle: string, settings: Settings): string;
54
+ /**
55
+ * Resolve all SEO metadata for a page from frontmatter, content, and
56
+ * settings. Centralizes the frontmatter → extracted → site-defaults chain
57
+ * so templates and generators share one source of truth.
58
+ */
59
+ export declare function resolveSeoData(input: ResolveSeoInput): SeoData;
60
+ //# sourceMappingURL=metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/seo/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAMtD,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,CAAC;AAE5D,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,GAAG,cAAc,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,OAAO;IACtB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,WAAW,EAAE,aAAa,EAAE,CAAC;IAC7B,2EAA2E;IAC3E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAyDD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAa7E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAsF9D"}
@@ -0,0 +1,144 @@
1
+ import { getOgImageUrl } from "../og/index.js";
2
+ import { OG_WIDTH, OG_HEIGHT } from "../og/template.js";
3
+ import { extractFirstParagraph } from "./extract.js";
4
+ import { buildPageUrl, normalizeBasePath, normalizeUrlPath } from "./url.js";
5
+ function str(value) {
6
+ if (typeof value !== "string")
7
+ return undefined;
8
+ const s = value.trim();
9
+ return s || undefined;
10
+ }
11
+ function toIsoDate(value) {
12
+ if (value == null || value === "")
13
+ return undefined;
14
+ const d = value instanceof Date ? value : new Date(String(value));
15
+ return Number.isNaN(d.getTime()) ? undefined : d.toISOString().split("T")[0];
16
+ }
17
+ function resolveAuthor(author) {
18
+ if (typeof author === "string") {
19
+ const name = author.trim();
20
+ return name ? { name, type: "Person" } : undefined;
21
+ }
22
+ if (author && typeof author === "object") {
23
+ const obj = author;
24
+ const name = str(obj.name);
25
+ if (!name)
26
+ return undefined;
27
+ const type = obj.type === "Organization" ? "Organization" : "Person";
28
+ return { name, url: str(obj.url), type };
29
+ }
30
+ return undefined;
31
+ }
32
+ function humanizeSegment(segment) {
33
+ let decoded = segment;
34
+ try {
35
+ decoded = decodeURIComponent(segment);
36
+ }
37
+ catch {
38
+ // Keep the raw segment when it is not valid percent-encoding
39
+ }
40
+ return decoded
41
+ .replace(/^\d+\./, "")
42
+ .replace(/[-_]/g, " ")
43
+ .replace(/\.\w+$/, "")
44
+ .trim()
45
+ .replace(/^\w/, (c) => c.toUpperCase());
46
+ }
47
+ function findNavTitle(items, urlPath) {
48
+ const target = urlPath.replace(/\/+$/, "") || "/";
49
+ for (const item of items) {
50
+ const href = item.href.replace(/\/+$/, "") || "/";
51
+ if (href === target)
52
+ return item.title;
53
+ if (item.children) {
54
+ const found = findNavTitle(item.children, urlPath);
55
+ if (found)
56
+ return found;
57
+ }
58
+ }
59
+ return undefined;
60
+ }
61
+ /**
62
+ * Apply `seo.titleTemplate` to a page title. When the template is unset,
63
+ * defaults to `"<title> | <site title>"` (or the bare title when the site
64
+ * has no title).
65
+ */
66
+ export function formatPageTitle(pageTitle, settings) {
67
+ if (settings.seo.titleTemplate != null) {
68
+ return settings.seo.titleTemplate.replace("%s", pageTitle);
69
+ }
70
+ // Default: "<title> | <site title>"; skip the suffix when it would
71
+ // duplicate the site title (e.g. homepage H1 == site.title).
72
+ if (!settings.site.title ||
73
+ pageTitle.trim().toLowerCase() === settings.site.title.trim().toLowerCase()) {
74
+ return pageTitle;
75
+ }
76
+ return `${pageTitle} | ${settings.site.title}`;
77
+ }
78
+ /**
79
+ * Resolve all SEO metadata for a page from frontmatter, content, and
80
+ * settings. Centralizes the frontmatter → extracted → site-defaults chain
81
+ * so templates and generators share one source of truth.
82
+ */
83
+ export function resolveSeoData(input) {
84
+ const { frontmatter = {}, content = "", title, settings, sidebar = [], isStaticExport = false, } = input;
85
+ const kind = input.kind ?? "document";
86
+ const urlPath = normalizeUrlPath(input.urlPath ?? "/");
87
+ const resolvedTitle = str(frontmatter.seoTitle) ?? title;
88
+ const description = str(frontmatter.description) ??
89
+ (content ? extractFirstParagraph(content) : null) ??
90
+ (settings.site.description || undefined);
91
+ const isHome = urlPath === "/";
92
+ const type = kind === "document" && !isHome ? "article" : "website";
93
+ let image = str(frontmatter.image);
94
+ let generatedOg = false;
95
+ if (!image && isStaticExport && settings.seo.og?.enabled) {
96
+ image = getOgImageUrl(urlPath, settings.generate.basePath || "", settings.seo.og.imageFormat);
97
+ generatedOg = true;
98
+ }
99
+ image ??= settings.seo.defaultImage;
100
+ const author = resolveAuthor(frontmatter.author) ??
101
+ (settings.site.author?.name
102
+ ? {
103
+ name: settings.site.author.name,
104
+ url: settings.site.author.url,
105
+ type: "Organization",
106
+ }
107
+ : undefined);
108
+ const breadcrumbs = [];
109
+ if (settings.navigation.breadcrumbs && !isHome && kind !== "generic") {
110
+ breadcrumbs.push({ name: "Home", urlPath: "/" });
111
+ const segments = urlPath.split("/").filter(Boolean);
112
+ let acc = "";
113
+ segments.forEach((segment, i) => {
114
+ acc += `/${segment}`;
115
+ const isLast = i === segments.length - 1;
116
+ const name = isLast
117
+ ? resolvedTitle
118
+ : (findNavTitle(sidebar, acc) ?? humanizeSegment(segment));
119
+ breadcrumbs.push({ name, urlPath: `${acc}/` });
120
+ });
121
+ }
122
+ const sitemapUrl = settings.seo.generateSitemap && !(isStaticExport && !settings.site.baseUrl)
123
+ ? `${normalizeBasePath(settings.generate.basePath)}/sitemap.xml`
124
+ : null;
125
+ return {
126
+ title: resolvedTitle,
127
+ description,
128
+ image,
129
+ imageWidth: generatedOg ? OG_WIDTH : undefined,
130
+ imageHeight: generatedOg ? OG_HEIGHT : undefined,
131
+ absoluteUrl: buildPageUrl(urlPath, settings),
132
+ urlPath,
133
+ type,
134
+ kind,
135
+ datePublished: toIsoDate(frontmatter.datePublished ?? frontmatter.date),
136
+ dateModified: toIsoDate(frontmatter.dateModified ?? frontmatter.updated ?? frontmatter.lastmod),
137
+ author,
138
+ noIndex: (frontmatter.noindex ?? frontmatter.noIndex) === true ||
139
+ (kind === "directory" && settings.seo.noIndexDirectoryPages),
140
+ breadcrumbs,
141
+ sitemapUrl,
142
+ };
143
+ }
144
+ //# sourceMappingURL=metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/seo/metadata.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { Settings } from "../settings/index.js";
2
+ /**
3
+ * Normalize a documentation route to its canonical form:
4
+ * leading `/`, trailing `/`, index/readme file names collapsed to the
5
+ * directory URL, `.md`/`.mdx` extensions stripped, `index.html` collapsed,
6
+ * and every path segment percent-encoded.
7
+ */
8
+ export declare function normalizeUrlPath(urlPath: string): string;
9
+ /** Normalize `generate.basePath` to `"/base"` form (empty when unset). */
10
+ export declare function normalizeBasePath(basePath: string | undefined): string;
11
+ /**
12
+ * Build the absolute URL for a documentation route.
13
+ * Returns `null` when no absolute base is available (`site.baseUrl`
14
+ * or an explicit request `origin`).
15
+ */
16
+ export declare function buildPageUrl(urlPath: string, settings: Settings, origin?: string): string | null;
17
+ /** Absolute site root URL (baseUrl + basePath + `/`), or `null`. */
18
+ export declare function buildSiteUrl(settings: Settings, origin?: string): string | null;
19
+ /**
20
+ * Resolve an image/asset reference to an absolute URL.
21
+ * Absolute http(s) URLs pass through. Site-relative paths are resolved
22
+ * against `site.baseUrl` + `generate.basePath` (a path that already starts
23
+ * with basePath is not prefixed twice). Returns `null` when the input is a
24
+ * relative path and no absolute base is configured.
25
+ */
26
+ export declare function toAbsoluteAssetUrl(pathOrUrl: string, settings: Settings, origin?: string): string | null;
27
+ //# sourceMappingURL=url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/seo/url.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAgBxD;AAED,0EAA0E;AAC1E,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAItE;AAMD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,EAClB,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,GAAG,IAAI,CAIf;AAED,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI/E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,QAAQ,EAClB,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,GAAG,IAAI,CAef"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Normalize a documentation route to its canonical form:
3
+ * leading `/`, trailing `/`, index/readme file names collapsed to the
4
+ * directory URL, `.md`/`.mdx` extensions stripped, `index.html` collapsed,
5
+ * and every path segment percent-encoded.
6
+ */
7
+ export function normalizeUrlPath(urlPath) {
8
+ let p = (urlPath || "/").split("?")[0].split("#")[0];
9
+ if (!p.startsWith("/"))
10
+ p = `/${p}`;
11
+ const segments = p.split("/").filter((s) => s.length > 0 && s !== ".");
12
+ const last = segments[segments.length - 1];
13
+ if (last) {
14
+ if (/^(readme|index)\.(md|mdx|html)$/i.test(last)) {
15
+ segments.pop();
16
+ }
17
+ else if (/\.(md|mdx)$/i.test(last)) {
18
+ segments[segments.length - 1] = last.replace(/\.(md|mdx)$/i, "");
19
+ }
20
+ }
21
+ const encoded = segments.map((s) => encodeURIComponent(s));
22
+ return `/${encoded.join("/")}${encoded.length ? "/" : ""}`;
23
+ }
24
+ /** Normalize `generate.basePath` to `"/base"` form (empty when unset). */
25
+ export function normalizeBasePath(basePath) {
26
+ const bp = (basePath || "").trim();
27
+ if (!bp || bp === "/")
28
+ return "";
29
+ return `/${bp.replace(/^\/+|\/+$/g, "")}`;
30
+ }
31
+ function resolveBase(settings, origin) {
32
+ return (settings.site.baseUrl || origin || "").replace(/\/+$/, "");
33
+ }
34
+ /**
35
+ * Build the absolute URL for a documentation route.
36
+ * Returns `null` when no absolute base is available (`site.baseUrl`
37
+ * or an explicit request `origin`).
38
+ */
39
+ export function buildPageUrl(urlPath, settings, origin) {
40
+ const base = resolveBase(settings, origin);
41
+ if (!base)
42
+ return null;
43
+ return `${base}${normalizeBasePath(settings.generate.basePath)}${normalizeUrlPath(urlPath)}`;
44
+ }
45
+ /** Absolute site root URL (baseUrl + basePath + `/`), or `null`. */
46
+ export function buildSiteUrl(settings, origin) {
47
+ const base = resolveBase(settings, origin);
48
+ if (!base)
49
+ return null;
50
+ return `${base}${normalizeBasePath(settings.generate.basePath)}/`;
51
+ }
52
+ /**
53
+ * Resolve an image/asset reference to an absolute URL.
54
+ * Absolute http(s) URLs pass through. Site-relative paths are resolved
55
+ * against `site.baseUrl` + `generate.basePath` (a path that already starts
56
+ * with basePath is not prefixed twice). Returns `null` when the input is a
57
+ * relative path and no absolute base is configured.
58
+ */
59
+ export function toAbsoluteAssetUrl(pathOrUrl, settings, origin) {
60
+ if (/^https?:\/\//i.test(pathOrUrl))
61
+ return pathOrUrl;
62
+ const siteUrl = buildSiteUrl(settings, origin);
63
+ if (!siteUrl)
64
+ return null;
65
+ let p = pathOrUrl.replace(/^\.\//, "").replace(/^\/+/, "");
66
+ const bp = normalizeBasePath(settings.generate.basePath).replace(/^\//, "");
67
+ if (bp && p.startsWith(`${bp}/`))
68
+ p = p.slice(bp.length + 1);
69
+ const encoded = p
70
+ .split("/")
71
+ .filter(Boolean)
72
+ .map(encodeURIComponent)
73
+ .join("/");
74
+ return `${siteUrl}${encoded}`;
75
+ }
76
+ //# sourceMappingURL=url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.js","sourceRoot":"","sources":["../../src/seo/url.ts"],"names":[],"mappings":"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"}