sourcey 3.4.11 → 3.5.1

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 (102) hide show
  1. package/README.md +13 -12
  2. package/dist/cli.js +46 -4
  3. package/dist/components/changelog/ChangelogPage.d.ts +5 -0
  4. package/dist/components/changelog/ChangelogPage.d.ts.map +1 -0
  5. package/dist/components/changelog/ChangelogPage.js +8 -0
  6. package/dist/components/changelog/EntryList.d.ts +5 -0
  7. package/dist/components/changelog/EntryList.d.ts.map +1 -0
  8. package/dist/components/changelog/EntryList.js +6 -0
  9. package/dist/components/changelog/TypeBadge.d.ts +6 -0
  10. package/dist/components/changelog/TypeBadge.d.ts.map +1 -0
  11. package/dist/components/changelog/TypeBadge.js +14 -0
  12. package/dist/components/changelog/VersionCard.d.ts +6 -0
  13. package/dist/components/changelog/VersionCard.d.ts.map +1 -0
  14. package/dist/components/changelog/VersionCard.js +25 -0
  15. package/dist/components/layout/Head.d.ts.map +1 -1
  16. package/dist/components/layout/Head.js +15 -3
  17. package/dist/components/layout/Page.d.ts.map +1 -1
  18. package/dist/components/layout/Page.js +12 -6
  19. package/dist/components/openapi/Introduction.d.ts.map +1 -1
  20. package/dist/components/openapi/Introduction.js +1 -1
  21. package/dist/components/openapi/MediaTypeDetails.d.ts +11 -0
  22. package/dist/components/openapi/MediaTypeDetails.d.ts.map +1 -0
  23. package/dist/components/openapi/MediaTypeDetails.js +54 -0
  24. package/dist/components/openapi/Parameters.d.ts.map +1 -1
  25. package/dist/components/openapi/Parameters.js +6 -1
  26. package/dist/components/openapi/RequestBody.d.ts.map +1 -1
  27. package/dist/components/openapi/RequestBody.js +5 -1
  28. package/dist/components/openapi/Responses.d.ts.map +1 -1
  29. package/dist/components/openapi/Responses.js +1 -1
  30. package/dist/components/openapi/Security.d.ts.map +1 -1
  31. package/dist/components/openapi/Security.js +3 -2
  32. package/dist/components/openapi/Tags.d.ts.map +1 -1
  33. package/dist/components/openapi/Tags.js +8 -1
  34. package/dist/config.d.ts +50 -1
  35. package/dist/config.d.ts.map +1 -1
  36. package/dist/config.js +50 -4
  37. package/dist/core/changelog-normalizer.d.ts +8 -0
  38. package/dist/core/changelog-normalizer.d.ts.map +1 -0
  39. package/dist/core/changelog-normalizer.js +531 -0
  40. package/dist/core/doxygen-loader.d.ts.map +1 -1
  41. package/dist/core/doxygen-loader.js +2 -0
  42. package/dist/core/loader.js +2 -0
  43. package/dist/core/markdown-loader.d.ts +21 -0
  44. package/dist/core/markdown-loader.d.ts.map +1 -1
  45. package/dist/core/markdown-loader.js +43 -25
  46. package/dist/core/mcp-normalizer.d.ts.map +1 -1
  47. package/dist/core/navigation.d.ts +4 -3
  48. package/dist/core/navigation.d.ts.map +1 -1
  49. package/dist/core/navigation.js +20 -6
  50. package/dist/core/normalizer.d.ts.map +1 -1
  51. package/dist/core/normalizer.js +102 -4
  52. package/dist/core/parser.d.ts +2 -2
  53. package/dist/core/parser.d.ts.map +1 -1
  54. package/dist/core/parser.js +23 -16
  55. package/dist/core/search-indexer.d.ts +3 -2
  56. package/dist/core/search-indexer.d.ts.map +1 -1
  57. package/dist/core/search-indexer.js +50 -17
  58. package/dist/core/tag-utils.d.ts +5 -0
  59. package/dist/core/tag-utils.d.ts.map +1 -0
  60. package/dist/core/tag-utils.js +22 -0
  61. package/dist/core/types.d.ts +82 -4
  62. package/dist/core/types.d.ts.map +1 -1
  63. package/dist/core/types.js +1 -1
  64. package/dist/dev-server.d.ts +2 -0
  65. package/dist/dev-server.d.ts.map +1 -1
  66. package/dist/dev-server.js +83 -212
  67. package/dist/index.d.ts +6 -10
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +115 -247
  70. package/dist/init.d.ts.map +1 -1
  71. package/dist/init.js +7 -3
  72. package/dist/og/fonts/Inter-Bold.ttf +0 -0
  73. package/dist/og/fonts/Inter-Regular.ttf +0 -0
  74. package/dist/og/generate-og-image.d.ts +10 -0
  75. package/dist/og/generate-og-image.d.ts.map +1 -0
  76. package/dist/og/generate-og-image.js +198 -0
  77. package/dist/renderer/changelog-feed.d.ts +18 -0
  78. package/dist/renderer/changelog-feed.d.ts.map +1 -0
  79. package/dist/renderer/changelog-feed.js +91 -0
  80. package/dist/renderer/context.d.ts +27 -7
  81. package/dist/renderer/context.d.ts.map +1 -1
  82. package/dist/renderer/html-builder.d.ts +10 -1
  83. package/dist/renderer/html-builder.d.ts.map +1 -1
  84. package/dist/renderer/html-builder.js +58 -5
  85. package/dist/renderer/llms.d.ts.map +1 -1
  86. package/dist/renderer/llms.js +133 -24
  87. package/dist/site-assembly.d.ts +25 -0
  88. package/dist/site-assembly.d.ts.map +1 -0
  89. package/dist/site-assembly.js +467 -0
  90. package/dist/site-url.d.ts +12 -0
  91. package/dist/site-url.d.ts.map +1 -0
  92. package/dist/site-url.js +67 -0
  93. package/dist/themes/default/sourcey.css +236 -0
  94. package/dist/utils/code-samples.d.ts.map +1 -1
  95. package/dist/utils/code-samples.js +53 -12
  96. package/dist/utils/markdown.d.ts +6 -0
  97. package/dist/utils/markdown.d.ts.map +1 -1
  98. package/dist/utils/markdown.js +11 -2
  99. package/dist/vite-plugin.d.ts +2 -0
  100. package/dist/vite-plugin.d.ts.map +1 -1
  101. package/dist/vite-plugin.js +6 -2
  102. package/package.json +6 -3
@@ -0,0 +1,198 @@
1
+ import satori from "satori";
2
+ import { Resvg } from "@resvg/resvg-js";
3
+ import { readFile } from "node:fs/promises";
4
+ import { resolve, dirname } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ // ---------------------------------------------------------------------------
8
+ // Font loading (cached across all pages)
9
+ // ---------------------------------------------------------------------------
10
+ let fontsCache = null;
11
+ async function loadFonts() {
12
+ if (fontsCache)
13
+ return fontsCache;
14
+ // Works from both src/ (dev) and dist/ (published)
15
+ const fontsDir = resolve(__dirname, "fonts");
16
+ const [regular, bold] = await Promise.all([
17
+ readFile(resolve(fontsDir, "Inter-Regular.ttf")),
18
+ readFile(resolve(fontsDir, "Inter-Bold.ttf")),
19
+ ]);
20
+ fontsCache = {
21
+ regular: regular.buffer.slice(regular.byteOffset, regular.byteOffset + regular.byteLength),
22
+ bold: bold.buffer.slice(bold.byteOffset, bold.byteOffset + bold.byteLength),
23
+ };
24
+ return fontsCache;
25
+ }
26
+ // ---------------------------------------------------------------------------
27
+ // OG card template
28
+ // ---------------------------------------------------------------------------
29
+ function rgbCss(triplet) {
30
+ return `rgb(${triplet.replace(/ /g, ", ")})`;
31
+ }
32
+ function rgbaCss(triplet, alpha) {
33
+ return `rgba(${triplet.replace(/ /g, ", ")}, ${alpha})`;
34
+ }
35
+ function ogCard({ title, description, siteName, theme, logo }) {
36
+ const primary = rgbCss(theme.colors.primary);
37
+ const primaryGlow = rgbaCss(theme.colors.primary, 0.18);
38
+ const displayTitle = title.length > 90 ? title.slice(0, 87) + "..." : title;
39
+ const displayDesc = description
40
+ ? description.length > 160 ? description.slice(0, 157) + "..." : description
41
+ : undefined;
42
+ return {
43
+ type: "div",
44
+ props: {
45
+ style: {
46
+ display: "flex",
47
+ flexDirection: "column",
48
+ width: "100%",
49
+ height: "100%",
50
+ backgroundColor: "#0f172a",
51
+ padding: "72px",
52
+ fontFamily: "Inter",
53
+ backgroundImage: `radial-gradient(ellipse 70% 50% at 20% 70%, ${primaryGlow}, transparent 70%)`,
54
+ },
55
+ children: [
56
+ // Brand block: logo (or coloured mark) + site name, top-left
57
+ {
58
+ type: "div",
59
+ props: {
60
+ style: { display: "flex", alignItems: "center", gap: "14px" },
61
+ children: [
62
+ logo
63
+ ? {
64
+ type: "img",
65
+ props: {
66
+ src: logo,
67
+ width: 48,
68
+ height: 48,
69
+ style: { borderRadius: "8px" },
70
+ },
71
+ }
72
+ : {
73
+ type: "div",
74
+ props: {
75
+ style: {
76
+ width: "14px",
77
+ height: "14px",
78
+ backgroundColor: primary,
79
+ borderRadius: "4px",
80
+ },
81
+ },
82
+ },
83
+ ...(siteName
84
+ ? [
85
+ {
86
+ type: "div",
87
+ props: {
88
+ style: {
89
+ fontSize: 28,
90
+ fontWeight: 700,
91
+ color: "#ffffff",
92
+ letterSpacing: "-0.02em",
93
+ },
94
+ children: siteName,
95
+ },
96
+ },
97
+ ]
98
+ : []),
99
+ ],
100
+ },
101
+ },
102
+ // Middle: vertical accent bar + title + description
103
+ {
104
+ type: "div",
105
+ props: {
106
+ style: {
107
+ display: "flex",
108
+ flex: 1,
109
+ alignItems: "center",
110
+ marginTop: "48px",
111
+ },
112
+ children: [
113
+ {
114
+ type: "div",
115
+ props: {
116
+ style: {
117
+ width: "4px",
118
+ alignSelf: "stretch",
119
+ backgroundColor: primary,
120
+ borderRadius: "2px",
121
+ marginRight: "32px",
122
+ },
123
+ },
124
+ },
125
+ {
126
+ type: "div",
127
+ props: {
128
+ style: { display: "flex", flexDirection: "column", flex: 1 },
129
+ children: [
130
+ {
131
+ type: "div",
132
+ props: {
133
+ style: {
134
+ fontSize: 64,
135
+ fontWeight: 700,
136
+ color: "#ffffff",
137
+ lineHeight: 1.1,
138
+ letterSpacing: "-0.02em",
139
+ display: "-webkit-box",
140
+ WebkitLineClamp: 2,
141
+ WebkitBoxOrient: "vertical",
142
+ overflow: "hidden",
143
+ },
144
+ children: displayTitle,
145
+ },
146
+ },
147
+ ...(displayDesc
148
+ ? [
149
+ {
150
+ type: "div",
151
+ props: {
152
+ style: {
153
+ fontSize: 26,
154
+ fontWeight: 400,
155
+ color: "#cbd5e1",
156
+ marginTop: "24px",
157
+ lineHeight: 1.4,
158
+ letterSpacing: "-0.005em",
159
+ display: "-webkit-box",
160
+ WebkitLineClamp: 2,
161
+ WebkitBoxOrient: "vertical",
162
+ overflow: "hidden",
163
+ },
164
+ children: displayDesc,
165
+ },
166
+ },
167
+ ]
168
+ : []),
169
+ ],
170
+ },
171
+ },
172
+ ],
173
+ },
174
+ },
175
+ ],
176
+ },
177
+ };
178
+ }
179
+ // ---------------------------------------------------------------------------
180
+ // Public API
181
+ // ---------------------------------------------------------------------------
182
+ export async function generateOgImage(options) {
183
+ const fonts = await loadFonts();
184
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
185
+ const svg = await satori(ogCard(options), {
186
+ width: 1200,
187
+ height: 630,
188
+ fonts: [
189
+ { name: "Inter", data: fonts.regular, weight: 400, style: "normal" },
190
+ { name: "Inter", data: fonts.bold, weight: 700, style: "normal" },
191
+ ],
192
+ });
193
+ const resvg = new Resvg(svg, {
194
+ fitTo: { mode: "width", value: 1200 },
195
+ });
196
+ const png = resvg.render();
197
+ return Buffer.from(png.asPng());
198
+ }
@@ -0,0 +1,18 @@
1
+ import type { ChangelogPage } from "../core/markdown-loader.js";
2
+ import type { NormalizedChangelogVersion } from "../core/types.js";
3
+ export interface ChangelogFeedOptions {
4
+ atomPath: string;
5
+ rssPath: string;
6
+ pagePath: string;
7
+ buildDate?: Date;
8
+ siteName: string;
9
+ title?: string;
10
+ description?: string;
11
+ versionHref(version: NormalizedChangelogVersion): string;
12
+ }
13
+ export interface ChangelogFeedFiles {
14
+ atom: string;
15
+ rss: string;
16
+ }
17
+ export declare function generateChangelogFeeds(page: ChangelogPage, options: ChangelogFeedOptions): ChangelogFeedFiles;
18
+ //# sourceMappingURL=changelog-feed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changelog-feed.d.ts","sourceRoot":"","sources":["../../src/renderer/changelog-feed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAEnE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,OAAO,EAAE,0BAA0B,GAAG,MAAM,CAAC;CAC1D;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,oBAAoB,GAC5B,kBAAkB,CAsEpB"}
@@ -0,0 +1,91 @@
1
+ export function generateChangelogFeeds(page, options) {
2
+ const buildDate = options.buildDate ?? new Date();
3
+ const updated = page.changelog.versions
4
+ .map((version) => version.date ? new Date(`${version.date}T00:00:00Z`) : null)
5
+ .find(Boolean) ?? buildDate;
6
+ const title = options.title ?? [options.siteName, page.title].filter(Boolean).join(" ").trim();
7
+ const description = options.description ?? page.description ?? `${page.title} feed`;
8
+ const feedId = toFeedId(options.atomPath);
9
+ const atomEntries = page.changelog.versions.map((version) => {
10
+ const href = options.versionHref(version);
11
+ const content = renderVersionHtml(version);
12
+ const updatedAt = version.date ? new Date(`${version.date}T00:00:00Z`) : updated;
13
+ const label = version.version ?? "Unreleased";
14
+ return [
15
+ " <entry>",
16
+ ` <title>${escapeXml(label)}</title>`,
17
+ ` <id>${escapeXml(toFeedId(href))}</id>`,
18
+ ` <link href="${escapeXml(href)}" />`,
19
+ ` <updated>${updatedAt.toISOString()}</updated>`,
20
+ ` <content type="html">${escapeXml(content)}</content>`,
21
+ " </entry>",
22
+ ].join("\n");
23
+ }).join("\n");
24
+ const atom = [
25
+ `<?xml version="1.0" encoding="UTF-8"?>`,
26
+ `<feed xmlns="http://www.w3.org/2005/Atom">`,
27
+ ` <title>${escapeXml(title)}</title>`,
28
+ ` <id>${escapeXml(feedId)}</id>`,
29
+ ` <updated>${updated.toISOString()}</updated>`,
30
+ ` <subtitle>${escapeXml(description)}</subtitle>`,
31
+ ` <link rel="self" href="${escapeXml(options.atomPath)}" />`,
32
+ ` <link href="${escapeXml(options.pagePath)}" />`,
33
+ page.changelog.versions.length ? atomEntries : "",
34
+ `</feed>`,
35
+ ].filter(Boolean).join("\n");
36
+ const rssItems = page.changelog.versions.map((version) => {
37
+ const href = options.versionHref(version);
38
+ const label = version.version ?? "Unreleased";
39
+ const pubDate = version.date
40
+ ? new Date(`${version.date}T00:00:00Z`)
41
+ : updated;
42
+ return [
43
+ " <item>",
44
+ ` <title>${escapeXml(label)}</title>`,
45
+ ` <link>${escapeXml(href)}</link>`,
46
+ ` <guid>${escapeXml(href)}</guid>`,
47
+ ` <pubDate>${pubDate.toUTCString()}</pubDate>`,
48
+ ` <description>${escapeXml(renderVersionHtml(version))}</description>`,
49
+ " </item>",
50
+ ].join("\n");
51
+ }).join("\n");
52
+ const rss = [
53
+ `<?xml version="1.0" encoding="UTF-8"?>`,
54
+ `<rss version="2.0">`,
55
+ ` <channel>`,
56
+ ` <title>${escapeXml(title)}</title>`,
57
+ ` <link>${escapeXml(options.pagePath)}</link>`,
58
+ ` <description>${escapeXml(description)}</description>`,
59
+ page.changelog.versions.length ? rssItems : "",
60
+ ` </channel>`,
61
+ `</rss>`,
62
+ ].filter(Boolean).join("\n");
63
+ return { atom, rss };
64
+ }
65
+ function renderVersionHtml(version) {
66
+ const summary = version.summary ? `<p>${escapeHtml(version.summary)}</p>` : "";
67
+ const sections = version.sections.map((section) => {
68
+ const items = section.entries.map((entry) => `<li>${entry.html}</li>`).join("");
69
+ return `<section><h3>${escapeHtml(section.label)}</h3><ul>${items}</ul></section>`;
70
+ }).join("");
71
+ return `<article><h2>${escapeHtml(version.version ?? "Unreleased")}</h2>${summary}${sections}</article>`;
72
+ }
73
+ function escapeXml(value) {
74
+ return value
75
+ .replace(/&/g, "&amp;")
76
+ .replace(/</g, "&lt;")
77
+ .replace(/>/g, "&gt;")
78
+ .replace(/"/g, "&quot;")
79
+ .replace(/'/g, "&apos;");
80
+ }
81
+ function escapeHtml(value) {
82
+ return value
83
+ .replace(/&/g, "&amp;")
84
+ .replace(/</g, "&lt;")
85
+ .replace(/>/g, "&gt;");
86
+ }
87
+ function toFeedId(value) {
88
+ if (value.startsWith("http://") || value.startsWith("https://"))
89
+ return value;
90
+ return `urn:sourcey:${value.replace(/^\/+/, "").replace(/\//g, ":")}`;
91
+ }
@@ -1,7 +1,12 @@
1
1
  import type { NormalizedSpec } from "../core/types.js";
2
2
  import type { SiteNavigation } from "../core/navigation.js";
3
- import type { MarkdownPage } from "../core/markdown-loader.js";
4
- import type { NavbarLink, ResolvedTheme } from "../config.js";
3
+ import type { ChangelogPage, MarkdownPage } from "../core/markdown-loader.js";
4
+ import type { NavbarLink, PrettyUrls, ResolvedChangelogConfig, ResolvedTheme } from "../config.js";
5
+ export interface AlternateLink {
6
+ href: string;
7
+ type: string;
8
+ title?: string;
9
+ }
5
10
  /**
6
11
  * Options that control rendering behavior.
7
12
  */
@@ -10,6 +15,12 @@ export interface RenderOptions {
10
15
  embeddable: boolean;
11
16
  /** Base URL for assets (CSS, JS) */
12
17
  assetBase: string;
18
+ /** Canonical absolute URL for the current page, when configured. */
19
+ pageUrl?: string;
20
+ /** Public URL for the OG image for this page. */
21
+ ogImageUrl?: string;
22
+ /** Alternate link tags to emit for the current page. */
23
+ alternateLinks?: AlternateLink[];
13
24
  }
14
25
  /**
15
26
  * Full context available to all components during rendering.
@@ -21,11 +32,16 @@ export interface RenderContext {
21
32
  /**
22
33
  * What the current page is rendering.
23
34
  */
24
- export interface CurrentPage {
25
- kind: "spec" | "markdown";
26
- spec?: NormalizedSpec;
27
- markdown?: MarkdownPage;
28
- }
35
+ export type CurrentPage = {
36
+ kind: "spec";
37
+ spec: NormalizedSpec;
38
+ } | {
39
+ kind: "markdown";
40
+ markdown: MarkdownPage;
41
+ } | {
42
+ kind: "changelog";
43
+ changelog: ChangelogPage;
44
+ };
29
45
  /**
30
46
  * Preact context for passing spec data to deeply nested components.
31
47
  */
@@ -47,6 +63,9 @@ export declare const PageContext: import("preact").Context<CurrentPage>;
47
63
  */
48
64
  export interface SiteConfig {
49
65
  name: string;
66
+ siteUrl?: string;
67
+ baseUrl: string;
68
+ prettyUrls: PrettyUrls;
50
69
  theme: ResolvedTheme;
51
70
  logo?: {
52
71
  light?: string;
@@ -70,6 +89,7 @@ export interface SiteConfig {
70
89
  links: NavbarLink[];
71
90
  };
72
91
  customCSS?: string;
92
+ changelog: ResolvedChangelogConfig;
73
93
  }
74
94
  export declare const SiteContext: import("preact").Context<SiteConfig>;
75
95
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/renderer/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,UAAU,EAAE,OAAO,CAAC;IACpB,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,aAAa,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAC1B,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,0CAA+C,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,cAAc,yCAGzB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,0CAA+C,CAAC;AAE9E;;GAEG;AACH,eAAO,MAAM,WAAW,uCAA4C,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,aAAa,CAAC;IACrB,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE;QAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE;YAAE,IAAI,EAAE,QAAQ,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IAC3F,MAAM,EAAE;QAAE,KAAK,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,WAAW,sCAA2C,CAAC"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/renderer/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEnG,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,UAAU,EAAE,OAAO,CAAC;IACpB,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,aAAa,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,cAAc,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,aAAa,CAAA;CAAE,CAAC;AAEpD;;GAEG;AACH,eAAO,MAAM,WAAW,0CAA+C,CAAC;AAExE;;GAEG;AACH,eAAO,MAAM,cAAc,yCAGzB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,0CAA+C,CAAC;AAE9E;;GAEG;AACH,eAAO,MAAM,WAAW,uCAA4C,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,UAAU,CAAC;IACvB,KAAK,EAAE,aAAa,CAAC;IACrB,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE;QAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE;YAAE,IAAI,EAAE,QAAQ,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IAC3F,MAAM,EAAE;QAAE,KAAK,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,uBAAuB,CAAC;CACpC;AAED,eAAO,MAAM,WAAW,sCAA2C,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import type { NormalizedSpec } from "../core/types.js";
2
- import type { CurrentPage, SiteConfig } from "./context.js";
2
+ import type { AlternateLink, RenderOptions, CurrentPage, SiteConfig } from "./context.js";
3
3
  import type { SiteNavigation } from "../core/navigation.js";
4
4
  export interface BuildOutput {
5
5
  htmlPath: string;
@@ -11,7 +11,14 @@ export interface SitePage {
11
11
  spec: NormalizedSpec;
12
12
  tabSlug: string;
13
13
  pageSlug: string;
14
+ /** Relative path to the generated OG image (set during build) */
15
+ ogImagePath?: string;
16
+ alternateLinks?: AlternateLink[];
14
17
  }
18
+ export declare function createRenderOptions(page: SitePage, site: SiteConfig, options?: {
19
+ embeddable?: boolean;
20
+ assetBase?: string;
21
+ }): RenderOptions;
15
22
  /**
16
23
  * Build a documentation site.
17
24
  */
@@ -20,5 +27,7 @@ export declare function buildSite(pages: SitePage[], navigation: SiteNavigation,
20
27
  searchIndex?: string;
21
28
  llmsTxt?: string;
22
29
  llmsFullTxt?: string;
30
+ ogImages?: Map<string, Buffer>;
31
+ extraFiles?: Map<string, string | Buffer>;
23
32
  }): Promise<BuildOutput>;
24
33
  //# sourceMappingURL=html-builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"html-builder.d.ts","sourceRoot":"","sources":["../../src/renderer/html-builder.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAiB,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AA0B5D,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;IACzB,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,KAAK,EAAE,QAAQ,EAAE,EACjB,UAAU,EAAE,cAAc,EAC1B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,UAAU,EAChB,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/F,OAAO,CAAC,WAAW,CAAC,CAuDtB"}
1
+ {"version":3,"file":"html-builder.d.ts","sourceRoot":"","sources":["../../src/renderer/html-builder.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AA2B5D,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,WAAW,CAAC;IACzB,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;CAClC;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,UAAU,EAChB,OAAO,CAAC,EAAE;IACR,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACA,aAAa,CAcf;AAED;;GAEG;AACH,wBAAsB,SAAS,CAC7B,KAAK,EAAE,QAAQ,EAAE,EACjB,UAAU,EAAE,cAAc,EAC1B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,UAAU,EAChB,OAAO,CAAC,EAAE;IACR,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;CAC3C,GACA,OAAO,CAAC,WAAW,CAAC,CA8EtB"}
@@ -6,6 +6,7 @@ import tailwindcss from "@tailwindcss/vite";
6
6
  import preact from "@preact/preset-vite";
7
7
  import { renderPage } from "./static-renderer.js";
8
8
  import { withActivePage } from "../core/navigation.js";
9
+ import { isAbsoluteHttpUrl, toAbsoluteUrl, toPublicPath } from "../site-url.js";
9
10
  const __dirname = dirname(fileURLToPath(import.meta.url));
10
11
  const projectRoot = resolve(__dirname, "../..");
11
12
  async function exists(path) {
@@ -30,6 +31,20 @@ async function resolveAssetPaths() {
30
31
  const sourceyCssPath = await exists(srcCss) ? srcCss : distCss;
31
32
  return { clientEntry, sourceyCssPath };
32
33
  }
34
+ export function createRenderOptions(page, site, options) {
35
+ const assetBase = options?.assetBase ?? "";
36
+ const pagePublicPath = toPublicPath(page.outputPath, site.baseUrl, site.prettyUrls);
37
+ return {
38
+ embeddable: options?.embeddable ?? false,
39
+ assetBase,
40
+ pageUrl: site.siteUrl ? toAbsoluteUrl(pagePublicPath, site.siteUrl) : undefined,
41
+ ogImageUrl: page.ogImagePath ? resolveRenderHref(page.ogImagePath, assetBase, site) : undefined,
42
+ alternateLinks: page.alternateLinks?.map((link) => ({
43
+ ...link,
44
+ href: resolveRenderHref(link.href, assetBase, site),
45
+ })),
46
+ };
47
+ }
33
48
  /**
34
49
  * Build a documentation site.
35
50
  */
@@ -42,10 +57,10 @@ export async function buildSite(pages, navigation, outputDir, site, options) {
42
57
  await mkdir(pageDir, { recursive: true });
43
58
  const depth = page.outputPath.split("/").length - 1;
44
59
  const assetBase = depth > 0 ? "../".repeat(depth) : "";
45
- const renderOptions = {
46
- embeddable: options?.embeddable ?? false,
60
+ const renderOptions = createRenderOptions(page, site, {
61
+ embeddable: options?.embeddable,
47
62
  assetBase,
48
- };
63
+ });
49
64
  const activeNav = withActivePage(navigation, page.tabSlug, page.pageSlug);
50
65
  const html = renderPage(page.spec, renderOptions, activeNav, page.currentPage, site);
51
66
  await writeFile(resolve(resolvedDir, page.outputPath), html, "utf-8");
@@ -53,7 +68,10 @@ export async function buildSite(pages, navigation, outputDir, site, options) {
53
68
  if (pages.length > 0 && pages[0].outputPath !== "index.html") {
54
69
  const first = pages[0];
55
70
  const activeNav = withActivePage(navigation, first.tabSlug, first.pageSlug);
56
- const renderOptions = { embeddable: options?.embeddable ?? false, assetBase: "" };
71
+ const renderOptions = createRenderOptions(first, site, {
72
+ embeddable: options?.embeddable,
73
+ assetBase: "",
74
+ });
57
75
  const html = renderPage(first.spec, renderOptions, activeNav, first.currentPage, site);
58
76
  await writeFile(resolve(resolvedDir, "index.html"), html, "utf-8");
59
77
  }
@@ -67,7 +85,11 @@ export async function buildSite(pages, navigation, outputDir, site, options) {
67
85
  if (options?.llmsFullTxt) {
68
86
  await writeFile(resolve(resolvedDir, "llms-full.txt"), options.llmsFullTxt, "utf-8");
69
87
  }
70
- const urls = pages.map(p => ` <url><loc>${p.outputPath}</loc></url>`);
88
+ const urls = pages.map((page) => {
89
+ const publicPath = toPublicPath(page.outputPath, site.baseUrl, site.prettyUrls);
90
+ const loc = site.siteUrl ? toAbsoluteUrl(publicPath, site.siteUrl) : publicPath;
91
+ return ` <url><loc>${loc}</loc></url>`;
92
+ });
71
93
  const sitemap = [
72
94
  `<?xml version="1.0" encoding="UTF-8"?>`,
73
95
  `<!-- Generated by Sourcey https://sourcey.com -->`,
@@ -76,8 +98,39 @@ export async function buildSite(pages, navigation, outputDir, site, options) {
76
98
  `</urlset>`,
77
99
  ].join("\n");
78
100
  await writeFile(resolve(resolvedDir, "sitemap.xml"), sitemap, "utf-8");
101
+ // Write generated OG images
102
+ if (options?.ogImages) {
103
+ for (const [path, data] of options.ogImages) {
104
+ const fullPath = resolve(resolvedDir, path);
105
+ await mkdir(dirname(fullPath), { recursive: true });
106
+ await writeFile(fullPath, data);
107
+ }
108
+ }
109
+ if (options?.extraFiles) {
110
+ for (const [path, data] of options.extraFiles) {
111
+ const fullPath = resolve(resolvedDir, path);
112
+ await mkdir(dirname(fullPath), { recursive: true });
113
+ await writeFile(fullPath, data);
114
+ }
115
+ }
79
116
  return { htmlPath: resolve(resolvedDir, "index.html"), outputDir: resolvedDir };
80
117
  }
118
+ function resolveRenderHref(pathOrUrl, assetBase, site) {
119
+ if (!pathOrUrl ||
120
+ pathOrUrl.startsWith("#") ||
121
+ pathOrUrl.startsWith("mailto:") ||
122
+ pathOrUrl.startsWith("data:") ||
123
+ isAbsoluteHttpUrl(pathOrUrl)) {
124
+ return pathOrUrl;
125
+ }
126
+ if (site.siteUrl) {
127
+ return toAbsoluteUrl(toPublicPath(pathOrUrl, site.baseUrl), site.siteUrl);
128
+ }
129
+ if (site.baseUrl) {
130
+ return toPublicPath(pathOrUrl, site.baseUrl);
131
+ }
132
+ return `${assetBase}${pathOrUrl}`;
133
+ }
81
134
  /**
82
135
  * Build client JS + Tailwind CSS via Vite.
83
136
  * Same plugins as the dev server — preact() + tailwindcss().
@@ -1 +1 @@
1
- {"version":3,"file":"llms.d.ts","sourceRoot":"","sources":["../../src/renderer/llms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAG5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,QAAQ,EAAE,EACjB,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,UAAU,GACf,MAAM,CA4CR;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,QAAQ,EAAE,EACjB,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,UAAU,GACf,MAAM,CA8BR"}
1
+ {"version":3,"file":"llms.d.ts","sourceRoot":"","sources":["../../src/renderer/llms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAI5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,QAAQ,EAAE,EACjB,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,UAAU,GACf,MAAM,CAyDR;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,QAAQ,EAAE,EACjB,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,UAAU,GACf,MAAM,CAgCR"}