sourcey 3.6.3 → 3.6.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/README.md +58 -14
- package/dist/astro/index.d.ts +89 -0
- package/dist/astro/index.d.ts.map +1 -0
- package/dist/astro/index.js +297 -0
- package/dist/cli.js +8 -0
- package/dist/client/scroll-tracker.js +3 -3
- package/dist/client/tabs.js +144 -95
- package/dist/components/layout/Sidebar.d.ts.map +1 -1
- package/dist/components/layout/Sidebar.js +1 -1
- package/dist/components/openapi/Operation.js +1 -1
- package/dist/components/openapi/Tags.js +1 -1
- package/dist/core/navigation.d.ts +4 -0
- package/dist/core/navigation.d.ts.map +1 -1
- package/dist/core/navigation.js +7 -1
- package/dist/core/sourcey-rustdoc/Cargo.lock +14 -18
- package/dist/core/sourcey-rustdoc/Cargo.toml +1 -1
- package/dist/core/sourcey-rustdoc/README.md +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -112
- package/dist/renderer/static-files.d.ts +6 -0
- package/dist/renderer/static-files.d.ts.map +1 -0
- package/dist/renderer/static-files.js +76 -0
- package/dist/site.d.ts +38 -0
- package/dist/site.d.ts.map +1 -0
- package/dist/site.js +159 -0
- package/dist/themes/default/sourcey.css +323 -59
- package/dist/utils/server-warnings.d.ts +4 -0
- package/dist/utils/server-warnings.d.ts.map +1 -0
- package/dist/utils/server-warnings.js +41 -0
- package/dist/vite-plugin.d.ts.map +1 -1
- package/dist/vite-plugin.js +1 -53
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { extname, resolve } from "node:path";
|
|
3
|
-
import { buildSite as buildSiteHtml } from "./renderer/html-builder.js";
|
|
1
|
+
import { resolve } from "node:path";
|
|
4
2
|
import { loadConfig, configFromSpec } from "./config.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { generateLlmsTxt, generateLlmsFullTxt } from "./renderer/llms.js";
|
|
8
|
-
import { assembleSite, buildSiteConfig, collectDocsPagesByTab, createMinimalSpec, enforceChangelogDiagnostics, enforceGodocDiagnostics, enforceRustdocDiagnostics, } from "./site-assembly.js";
|
|
3
|
+
import { buildSourceySite, writeSourceySite } from "./site.js";
|
|
4
|
+
import { createMinimalSpec } from "./site-assembly.js";
|
|
9
5
|
/**
|
|
10
6
|
* Build API documentation from a single OpenAPI/Swagger spec.
|
|
11
7
|
* Wraps the spec in a single-tab site and renders through the modern layout.
|
|
@@ -30,117 +26,25 @@ export async function buildDocs(options) {
|
|
|
30
26
|
export async function buildSiteDocs(options = {}) {
|
|
31
27
|
const outputDir = resolve(options.outputDir ?? "dist");
|
|
32
28
|
const config = options.config ?? (await loadConfig(options.configDir));
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
enforceRustdocDiagnostics(assembled.rustdocDiagnostics);
|
|
40
|
-
const docsPagesByTab = collectDocsPagesByTab(assembled.pageMap, config.tabs);
|
|
41
|
-
const searchIndex = buildSearchIndex(assembled.specsBySlug, docsPagesByTab, navigation, config.baseUrl || "/", config.search.featured, config.prettyUrls);
|
|
42
|
-
const llmsTxt = generateLlmsTxt(sitePages, navigation, site);
|
|
43
|
-
const llmsFullTxt = generateLlmsFullTxt(sitePages, navigation, site);
|
|
44
|
-
const extraFiles = new Map(assembled.extraFiles);
|
|
45
|
-
const ogImages = new Map();
|
|
46
|
-
if (!config.ogImage) {
|
|
47
|
-
const { generateOgImage } = await import("./og/generate-og-image.js");
|
|
48
|
-
const CONCURRENCY = 8;
|
|
49
|
-
for (let i = 0; i < sitePages.length; i += CONCURRENCY) {
|
|
50
|
-
const batch = sitePages.slice(i, i + CONCURRENCY);
|
|
51
|
-
await Promise.all(batch.map(async (page) => {
|
|
52
|
-
const ogMeta = describePageForOg(page, config.name || "API", config.changelog.ogImages);
|
|
53
|
-
if (!ogMeta)
|
|
54
|
-
return;
|
|
55
|
-
try {
|
|
56
|
-
const ogPath = `_og/${page.outputPath.replace(/\.html$/, ".png")}`;
|
|
57
|
-
const png = await generateOgImage({
|
|
58
|
-
title: ogMeta.title,
|
|
59
|
-
description: ogMeta.description,
|
|
60
|
-
siteName: config.name,
|
|
61
|
-
theme: config.theme,
|
|
62
|
-
logo: site.logo?.light,
|
|
63
|
-
});
|
|
64
|
-
page.ogImagePath = ogPath;
|
|
65
|
-
ogImages.set(ogPath, png);
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
69
|
-
console.warn(`Sourcey: skipping OG image for ${page.outputPath}: ${message}`);
|
|
70
|
-
}
|
|
71
|
-
}));
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
const staticOg = config.ogImage;
|
|
76
|
-
if (staticOg.startsWith("http://") ||
|
|
77
|
-
staticOg.startsWith("https://") ||
|
|
78
|
-
staticOg.startsWith("data:")) {
|
|
79
|
-
for (const page of sitePages) {
|
|
80
|
-
page.ogImagePath = staticOg;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
const ogPath = `_og/static${extname(staticOg) || ".png"}`;
|
|
85
|
-
extraFiles.set(ogPath, await readFile(staticOg));
|
|
86
|
-
for (const page of sitePages) {
|
|
87
|
-
page.ogImagePath = ogPath;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
29
|
+
const sourceySite = await buildSourceySite({
|
|
30
|
+
config,
|
|
31
|
+
outputDir,
|
|
32
|
+
strictChangelog: options.strictChangelog,
|
|
33
|
+
generateOgImages: options.generateOgImages,
|
|
34
|
+
});
|
|
91
35
|
if (!options.skipWrite) {
|
|
92
|
-
await
|
|
93
|
-
searchIndex,
|
|
94
|
-
llmsTxt,
|
|
95
|
-
llmsFullTxt,
|
|
96
|
-
embeddable: options.embeddable,
|
|
97
|
-
ogImages,
|
|
98
|
-
extraFiles,
|
|
99
|
-
});
|
|
36
|
+
await writeSourceySite(sourceySite, { embeddable: options.embeddable });
|
|
100
37
|
}
|
|
101
38
|
return {
|
|
102
39
|
outputDir,
|
|
103
|
-
pageCount:
|
|
104
|
-
changelogDiagnostics:
|
|
105
|
-
godocDiagnostics:
|
|
106
|
-
rustdocDiagnostics:
|
|
107
|
-
_specs:
|
|
40
|
+
pageCount: sourceySite.pageCount,
|
|
41
|
+
changelogDiagnostics: sourceySite.changelogDiagnostics,
|
|
42
|
+
godocDiagnostics: sourceySite.godocDiagnostics,
|
|
43
|
+
rustdocDiagnostics: sourceySite.rustdocDiagnostics,
|
|
44
|
+
_specs: sourceySite.specsBySlug,
|
|
108
45
|
};
|
|
109
46
|
}
|
|
110
|
-
function describePageForOg(page, defaultSiteName, changelogPermalinkOg) {
|
|
111
|
-
if (page.currentPage.kind === "markdown") {
|
|
112
|
-
return {
|
|
113
|
-
title: page.currentPage.markdown.title,
|
|
114
|
-
description: page.currentPage.markdown.description || undefined,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
if (page.currentPage.kind === "changelog") {
|
|
118
|
-
const changelog = page.currentPage.changelog;
|
|
119
|
-
if (changelog.permalinkVersionId) {
|
|
120
|
-
if (!changelogPermalinkOg)
|
|
121
|
-
return null;
|
|
122
|
-
const version = changelog.changelog.versions.find((candidate) => candidate.id === changelog.permalinkVersionId);
|
|
123
|
-
if (!version)
|
|
124
|
-
return null;
|
|
125
|
-
return {
|
|
126
|
-
title: `${version.version ?? "Unreleased"} - ${changelog.title}`,
|
|
127
|
-
description: version.summary || summarizeVersion(version),
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
return {
|
|
131
|
-
title: changelog.title,
|
|
132
|
-
description: changelog.description || changelog.changelog.description,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
return { title: `${defaultSiteName} Reference` };
|
|
136
|
-
}
|
|
137
|
-
function summarizeVersion(version) {
|
|
138
|
-
if (version.summary)
|
|
139
|
-
return version.summary;
|
|
140
|
-
const texts = version.sections.flatMap((section) => section.entries.map((entry) => entry.text));
|
|
141
|
-
const summary = texts.slice(0, 3).join(" ");
|
|
142
|
-
return summary || undefined;
|
|
143
|
-
}
|
|
144
47
|
export { defineConfig } from "./config.js";
|
|
145
48
|
export { doxygen, godoc, markdown, mcp, mkdocs, openapi, rustdoc } from "./adapters/index.js";
|
|
146
49
|
export { resolveInternalLinks } from "./site-assembly.js";
|
|
50
|
+
export { buildSourceySite, collectSourceyWatchPaths, writeSourceySite } from "./site.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { PrettyUrls } from "../site-url.js";
|
|
2
|
+
export declare function outputPathFromRequestPath(pathname: string, baseUrl: string): string;
|
|
3
|
+
export declare function outputPathCandidatesForRequest(pathname: string, baseUrl: string, prettyUrls: PrettyUrls): string[];
|
|
4
|
+
export declare function requestPathMatchesBase(pathname: string, baseUrl: string): boolean;
|
|
5
|
+
export declare function contentTypeForPath(path: string): string;
|
|
6
|
+
//# sourceMappingURL=static-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-files.d.ts","sourceRoot":"","sources":["../../src/renderer/static-files.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGjD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAcnF;AAED,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,UAAU,GACrB,MAAM,EAAE,CAYV;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAMjF;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAwCvD"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { extname } from "node:path";
|
|
2
|
+
import { normalizeBaseUrl, stripBaseUrl } from "../site-url.js";
|
|
3
|
+
export function outputPathFromRequestPath(pathname, baseUrl) {
|
|
4
|
+
let path = pathname;
|
|
5
|
+
const normalizedBase = normalizeBaseUrl(baseUrl);
|
|
6
|
+
if (normalizedBase && path.startsWith(normalizedBase)) {
|
|
7
|
+
path = path.slice(normalizedBase.length);
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
path = decodeURIComponent(path);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
// Keep the raw path; a malformed escape cannot match generated output.
|
|
14
|
+
}
|
|
15
|
+
return path.replace(/^\/+/, "");
|
|
16
|
+
}
|
|
17
|
+
export function outputPathCandidatesForRequest(pathname, baseUrl, prettyUrls) {
|
|
18
|
+
const requestPath = stripBaseUrl(pathname, baseUrl);
|
|
19
|
+
const clean = outputPathFromRequestPath(requestPath, "");
|
|
20
|
+
if (!clean)
|
|
21
|
+
return ["index.html"];
|
|
22
|
+
if (extname(clean))
|
|
23
|
+
return [clean];
|
|
24
|
+
const candidates = prettyUrls === "strip"
|
|
25
|
+
? [`${clean}.html`, `${clean}/index.html`]
|
|
26
|
+
: [`${clean}/index.html`, `${clean}.html`];
|
|
27
|
+
return [...candidates, clean];
|
|
28
|
+
}
|
|
29
|
+
export function requestPathMatchesBase(pathname, baseUrl) {
|
|
30
|
+
const normalizedBase = normalizeBaseUrl(baseUrl);
|
|
31
|
+
if (!normalizedBase)
|
|
32
|
+
return true;
|
|
33
|
+
const bareBase = normalizedBase.slice(0, -1);
|
|
34
|
+
return pathname === bareBase || pathname === normalizedBase || pathname.startsWith(normalizedBase);
|
|
35
|
+
}
|
|
36
|
+
export function contentTypeForPath(path) {
|
|
37
|
+
switch (extname(path).toLowerCase()) {
|
|
38
|
+
case ".css":
|
|
39
|
+
return "text/css; charset=utf-8";
|
|
40
|
+
case ".js":
|
|
41
|
+
case ".mjs":
|
|
42
|
+
return "text/javascript; charset=utf-8";
|
|
43
|
+
case ".json":
|
|
44
|
+
return "application/json; charset=utf-8";
|
|
45
|
+
case ".svg":
|
|
46
|
+
return "image/svg+xml";
|
|
47
|
+
case ".png":
|
|
48
|
+
return "image/png";
|
|
49
|
+
case ".jpg":
|
|
50
|
+
case ".jpeg":
|
|
51
|
+
return "image/jpeg";
|
|
52
|
+
case ".gif":
|
|
53
|
+
return "image/gif";
|
|
54
|
+
case ".webp":
|
|
55
|
+
return "image/webp";
|
|
56
|
+
case ".avif":
|
|
57
|
+
return "image/avif";
|
|
58
|
+
case ".ico":
|
|
59
|
+
return "image/x-icon";
|
|
60
|
+
case ".txt":
|
|
61
|
+
case ".xml":
|
|
62
|
+
return "text/plain; charset=utf-8";
|
|
63
|
+
case ".pdf":
|
|
64
|
+
return "application/pdf";
|
|
65
|
+
case ".woff":
|
|
66
|
+
return "font/woff";
|
|
67
|
+
case ".woff2":
|
|
68
|
+
return "font/woff2";
|
|
69
|
+
case ".mp4":
|
|
70
|
+
return "video/mp4";
|
|
71
|
+
case ".webm":
|
|
72
|
+
return "video/webm";
|
|
73
|
+
default:
|
|
74
|
+
return "application/octet-stream";
|
|
75
|
+
}
|
|
76
|
+
}
|
package/dist/site.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ResolvedConfig } from "./config.js";
|
|
2
|
+
import type { ChangelogDiagnostic } from "./core/types.js";
|
|
3
|
+
import type { GodocLoaderDiagnostic } from "./core/godoc-loader.js";
|
|
4
|
+
import type { RustdocLoaderDiagnostic } from "./core/rustdoc-loader.js";
|
|
5
|
+
import type { SiteNavigation } from "./core/navigation.js";
|
|
6
|
+
import type { SiteConfig } from "./renderer/context.js";
|
|
7
|
+
import type { SitePage } from "./renderer/html-builder.js";
|
|
8
|
+
export interface SourceySiteOptions {
|
|
9
|
+
configDir?: string;
|
|
10
|
+
config?: ResolvedConfig;
|
|
11
|
+
outputDir?: string;
|
|
12
|
+
strictChangelog?: boolean;
|
|
13
|
+
generateOgImages?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface SourceySiteArtifacts {
|
|
16
|
+
config: ResolvedConfig;
|
|
17
|
+
outputDir: string;
|
|
18
|
+
site: SiteConfig;
|
|
19
|
+
navigation: SiteNavigation;
|
|
20
|
+
pages: SitePage[];
|
|
21
|
+
pageCount: number;
|
|
22
|
+
searchIndex: string;
|
|
23
|
+
llmsTxt: string;
|
|
24
|
+
llmsFullTxt: string;
|
|
25
|
+
extraFiles: Map<string, string | Buffer>;
|
|
26
|
+
ogImages: Map<string, Buffer>;
|
|
27
|
+
changelogDiagnostics: ChangelogDiagnostic[];
|
|
28
|
+
godocDiagnostics: GodocLoaderDiagnostic[];
|
|
29
|
+
rustdocDiagnostics: RustdocLoaderDiagnostic[];
|
|
30
|
+
specsBySlug: Map<string, import("./core/types.js").NormalizedSpec>;
|
|
31
|
+
}
|
|
32
|
+
export interface SourceySiteWriteOptions {
|
|
33
|
+
embeddable?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare function buildSourceySite(options?: SourceySiteOptions): Promise<SourceySiteArtifacts>;
|
|
36
|
+
export declare function writeSourceySite(site: SourceySiteArtifacts, options?: SourceySiteWriteOptions): Promise<void>;
|
|
37
|
+
export declare function collectSourceyWatchPaths(config: ResolvedConfig, configPath?: string): string[];
|
|
38
|
+
//# sourceMappingURL=site.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site.d.ts","sourceRoot":"","sources":["../src/site.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD,OAAO,KAAK,EAAE,mBAAmB,EAA8B,MAAM,iBAAiB,CAAC;AACvF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAW3D,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,cAAc,CAAC;IAC3B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IACzC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,oBAAoB,EAAE,mBAAmB,EAAE,CAAC;IAC5C,gBAAgB,EAAE,qBAAqB,EAAE,CAAC;IAC1C,kBAAkB,EAAE,uBAAuB,EAAE,CAAC;IAC9C,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,iBAAiB,EAAE,cAAc,CAAC,CAAC;CACpE;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAsB,gBAAgB,CACpC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,oBAAoB,CAAC,CAiD/B;AAED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,oBAAoB,EAC1B,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CASf;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,cAAc,EACtB,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,EAAE,CA0BV"}
|
package/dist/site.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { extname, resolve } from "node:path";
|
|
3
|
+
import { loadConfig } from "./config.js";
|
|
4
|
+
import { buildSiteNavigation } from "./core/navigation.js";
|
|
5
|
+
import { buildSearchIndex } from "./core/search-indexer.js";
|
|
6
|
+
import { buildSite as buildSiteHtml } from "./renderer/html-builder.js";
|
|
7
|
+
import { generateLlmsFullTxt, generateLlmsTxt } from "./renderer/llms.js";
|
|
8
|
+
import { assembleSite, buildSiteConfig, collectDocsPagesByTab, enforceChangelogDiagnostics, enforceGodocDiagnostics, enforceRustdocDiagnostics, } from "./site-assembly.js";
|
|
9
|
+
export async function buildSourceySite(options = {}) {
|
|
10
|
+
const outputDir = resolve(options.outputDir ?? "dist");
|
|
11
|
+
const config = options.config ?? (await loadConfig(options.configDir));
|
|
12
|
+
const assembled = await assembleSite(config);
|
|
13
|
+
const site = await buildSiteConfig(config);
|
|
14
|
+
const pages = Array.from(assembled.pageMap.values());
|
|
15
|
+
const navigation = buildSiteNavigation(assembled.siteTabs);
|
|
16
|
+
enforceChangelogDiagnostics(assembled.changelogDiagnostics, options.strictChangelog);
|
|
17
|
+
enforceGodocDiagnostics(assembled.godocDiagnostics);
|
|
18
|
+
enforceRustdocDiagnostics(assembled.rustdocDiagnostics);
|
|
19
|
+
const docsPagesByTab = collectDocsPagesByTab(assembled.pageMap, config.tabs);
|
|
20
|
+
const searchIndex = buildSearchIndex(assembled.specsBySlug, docsPagesByTab, navigation, config.baseUrl || "/", config.search.featured, config.prettyUrls);
|
|
21
|
+
const llmsTxt = generateLlmsTxt(pages, navigation, site);
|
|
22
|
+
const llmsFullTxt = generateLlmsFullTxt(pages, navigation, site);
|
|
23
|
+
const extraFiles = new Map(assembled.extraFiles);
|
|
24
|
+
const ogImages = new Map();
|
|
25
|
+
if (options.generateOgImages !== false) {
|
|
26
|
+
await attachOgImages(pages, config, site, extraFiles, ogImages);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
config,
|
|
30
|
+
outputDir,
|
|
31
|
+
site,
|
|
32
|
+
navigation,
|
|
33
|
+
pages,
|
|
34
|
+
pageCount: pages.length,
|
|
35
|
+
searchIndex,
|
|
36
|
+
llmsTxt,
|
|
37
|
+
llmsFullTxt,
|
|
38
|
+
extraFiles,
|
|
39
|
+
ogImages,
|
|
40
|
+
changelogDiagnostics: assembled.changelogDiagnostics,
|
|
41
|
+
godocDiagnostics: assembled.godocDiagnostics,
|
|
42
|
+
rustdocDiagnostics: assembled.rustdocDiagnostics,
|
|
43
|
+
specsBySlug: assembled.specsBySlug,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export async function writeSourceySite(site, options = {}) {
|
|
47
|
+
await buildSiteHtml(site.pages, site.navigation, site.outputDir, site.site, {
|
|
48
|
+
searchIndex: site.searchIndex,
|
|
49
|
+
llmsTxt: site.llmsTxt,
|
|
50
|
+
llmsFullTxt: site.llmsFullTxt,
|
|
51
|
+
embeddable: options.embeddable,
|
|
52
|
+
ogImages: site.ogImages,
|
|
53
|
+
extraFiles: site.extraFiles,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function collectSourceyWatchPaths(config, configPath) {
|
|
57
|
+
const paths = new Set();
|
|
58
|
+
if (configPath)
|
|
59
|
+
paths.add(resolve(configPath));
|
|
60
|
+
for (const tab of config.tabs) {
|
|
61
|
+
for (const watchPath of tab.source.watchPaths ?? []) {
|
|
62
|
+
paths.add(resolve(watchPath));
|
|
63
|
+
}
|
|
64
|
+
if (tab.source.kind === "rustdoc") {
|
|
65
|
+
paths.add(resolve(tab.source.config.manifest));
|
|
66
|
+
if (tab.source.config.snapshot) {
|
|
67
|
+
paths.add(resolve(tab.source.config.snapshot));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (tab.source.kind === "markdown") {
|
|
71
|
+
for (const group of tab.source.groups) {
|
|
72
|
+
for (const page of group.pages) {
|
|
73
|
+
paths.add(resolve(page.file));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return Array.from(paths);
|
|
79
|
+
}
|
|
80
|
+
async function attachOgImages(pages, config, site, extraFiles, ogImages) {
|
|
81
|
+
if (config.ogImage) {
|
|
82
|
+
const staticOg = config.ogImage;
|
|
83
|
+
if (staticOg.startsWith("http://") ||
|
|
84
|
+
staticOg.startsWith("https://") ||
|
|
85
|
+
staticOg.startsWith("data:")) {
|
|
86
|
+
for (const page of pages) {
|
|
87
|
+
page.ogImagePath = staticOg;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const ogPath = `_og/static${extname(staticOg) || ".png"}`;
|
|
92
|
+
extraFiles.set(ogPath, await readFile(staticOg));
|
|
93
|
+
for (const page of pages) {
|
|
94
|
+
page.ogImagePath = ogPath;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const { generateOgImage } = await import("./og/generate-og-image.js");
|
|
100
|
+
const concurrency = 8;
|
|
101
|
+
for (let i = 0; i < pages.length; i += concurrency) {
|
|
102
|
+
const batch = pages.slice(i, i + concurrency);
|
|
103
|
+
await Promise.all(batch.map(async (page) => {
|
|
104
|
+
const ogMeta = describePageForOg(page, config.name || "API", config.changelog.ogImages);
|
|
105
|
+
if (!ogMeta)
|
|
106
|
+
return;
|
|
107
|
+
try {
|
|
108
|
+
const ogPath = `_og/${page.outputPath.replace(/\.html$/, ".png")}`;
|
|
109
|
+
const png = await generateOgImage({
|
|
110
|
+
title: ogMeta.title,
|
|
111
|
+
description: ogMeta.description,
|
|
112
|
+
siteName: config.name,
|
|
113
|
+
theme: config.theme,
|
|
114
|
+
logo: site.logo?.light,
|
|
115
|
+
});
|
|
116
|
+
page.ogImagePath = ogPath;
|
|
117
|
+
ogImages.set(ogPath, png);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
121
|
+
console.warn(`Sourcey: skipping OG image for ${page.outputPath}: ${message}`);
|
|
122
|
+
}
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function describePageForOg(page, defaultSiteName, changelogPermalinkOg) {
|
|
127
|
+
if (page.currentPage.kind === "markdown") {
|
|
128
|
+
return {
|
|
129
|
+
title: page.currentPage.markdown.title,
|
|
130
|
+
description: page.currentPage.markdown.description || undefined,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (page.currentPage.kind === "changelog") {
|
|
134
|
+
const changelog = page.currentPage.changelog;
|
|
135
|
+
if (changelog.permalinkVersionId) {
|
|
136
|
+
if (!changelogPermalinkOg)
|
|
137
|
+
return null;
|
|
138
|
+
const version = changelog.changelog.versions.find((candidate) => candidate.id === changelog.permalinkVersionId);
|
|
139
|
+
if (!version)
|
|
140
|
+
return null;
|
|
141
|
+
return {
|
|
142
|
+
title: `${version.version ?? "Unreleased"} - ${changelog.title}`,
|
|
143
|
+
description: version.summary || summarizeVersion(version),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
title: changelog.title,
|
|
148
|
+
description: changelog.description || changelog.changelog.description,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return { title: `${defaultSiteName} Reference` };
|
|
152
|
+
}
|
|
153
|
+
function summarizeVersion(version) {
|
|
154
|
+
if (version.summary)
|
|
155
|
+
return version.summary;
|
|
156
|
+
const texts = version.sections.flatMap((section) => section.entries.map((entry) => entry.text));
|
|
157
|
+
const summary = texts.slice(0, 3).join(" ");
|
|
158
|
+
return summary || undefined;
|
|
159
|
+
}
|