sourcey 3.6.4 → 3.6.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/README.md +38 -5
- package/dist/adapters/shared.js +1 -1
- package/dist/astro/index.d.ts +89 -0
- package/dist/astro/index.d.ts.map +1 -0
- package/dist/astro/index.js +299 -0
- package/dist/cli.js +8 -0
- package/dist/client/reader-controls.js +81 -0
- package/dist/client/reader.d.ts +5 -0
- package/dist/client/reader.d.ts.map +1 -0
- package/dist/client/reader.js +5 -0
- package/dist/client/tabs.js +144 -95
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +1 -1
- package/dist/components/layout/Head.d.ts.map +1 -1
- package/dist/components/layout/Head.js +6 -4
- package/dist/components/layout/Page.d.ts.map +1 -1
- package/dist/components/layout/Page.js +20 -14
- package/dist/components/layout/Reader.d.ts +5 -0
- package/dist/components/layout/Reader.d.ts.map +1 -0
- package/dist/components/layout/Reader.js +64 -0
- package/dist/components/layout/SearchDialog.d.ts +2 -0
- package/dist/components/layout/SearchDialog.d.ts.map +1 -0
- package/dist/components/layout/SearchDialog.js +4 -0
- package/dist/components/openapi/Operation.d.ts.map +1 -1
- package/dist/components/openapi/Operation.js +2 -2
- package/dist/components/openapi/Tags.js +1 -1
- package/dist/config.d.ts +56 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +18 -10
- package/dist/core/doxygen-loader.js +1 -1
- package/dist/core/loader.js +2 -2
- package/dist/core/markdown-loader.d.ts +2 -0
- package/dist/core/markdown-loader.d.ts.map +1 -1
- package/dist/core/markdown-loader.js +1 -0
- package/dist/core/navigation.js +1 -1
- package/dist/core/parser.js +1 -1
- package/dist/dev-server.d.ts.map +1 -1
- package/dist/dev-server.js +3 -4
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -112
- package/dist/init.js +4 -4
- package/dist/renderer/html-builder.d.ts.map +1 -1
- package/dist/renderer/html-builder.js +6 -27
- 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 +78 -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/assets.d.ts +8 -0
- package/dist/themes/assets.d.ts.map +1 -0
- package/dist/themes/assets.js +16 -0
- package/dist/themes/default/sourcey.css +305 -60
- package/dist/themes/reader/sourcey.css +1024 -0
- package/dist/themes/registry.d.ts +20 -0
- package/dist/themes/registry.d.ts.map +1 -0
- package/dist/themes/registry.js +8 -0
- 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 +9 -4
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";
|
package/dist/init.js
CHANGED
|
@@ -97,12 +97,12 @@ export const init = defineCommand({
|
|
|
97
97
|
}));
|
|
98
98
|
if (typeof name === "symbol")
|
|
99
99
|
process.exit(0);
|
|
100
|
-
// -- Theme
|
|
101
|
-
const
|
|
100
|
+
// -- Theme --
|
|
101
|
+
const theme = (await consola.prompt("Theme:", {
|
|
102
102
|
type: "select",
|
|
103
103
|
options: ["default", "minimal", "api-first"],
|
|
104
104
|
}));
|
|
105
|
-
if (typeof
|
|
105
|
+
if (typeof theme === "symbol")
|
|
106
106
|
process.exit(0);
|
|
107
107
|
// -- Detected sources --
|
|
108
108
|
let addOpenAPI = null;
|
|
@@ -200,7 +200,7 @@ export const init = defineCommand({
|
|
|
200
200
|
export default defineConfig({
|
|
201
201
|
name: "${name}",
|
|
202
202
|
theme: {
|
|
203
|
-
|
|
203
|
+
name: "${theme}",
|
|
204
204
|
},
|
|
205
205
|
navigation: {
|
|
206
206
|
tabs: [
|
|
@@ -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,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;
|
|
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;AAU5D,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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, writeFile, readFile,
|
|
1
|
+
import { mkdir, writeFile, readFile, rm } from "node:fs/promises";
|
|
2
2
|
import { resolve, dirname } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { build as viteBuild } from "vite";
|
|
@@ -8,30 +8,9 @@ import { renderPage } from "./static-renderer.js";
|
|
|
8
8
|
import { withActivePage } from "../core/navigation.js";
|
|
9
9
|
import { isAbsoluteHttpUrl, toAbsoluteUrl, toPublicPath } from "../site-url.js";
|
|
10
10
|
import { escapeHtml as escapeXml } from "../utils/html.js";
|
|
11
|
+
import { resolveThemeAssets } from "../themes/assets.js";
|
|
11
12
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
13
|
const projectRoot = resolve(__dirname, "../..");
|
|
13
|
-
async function exists(path) {
|
|
14
|
-
try {
|
|
15
|
-
await access(path);
|
|
16
|
-
return true;
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Resolve client entry and CSS paths. Works from both source tree (dev) and
|
|
24
|
-
* published npm package (dist-only) by checking which paths exist.
|
|
25
|
-
*/
|
|
26
|
-
async function resolveAssetPaths() {
|
|
27
|
-
const srcClient = resolve(projectRoot, "src/client/index.ts");
|
|
28
|
-
const distClient = resolve(projectRoot, "dist/client/index.js");
|
|
29
|
-
const srcCss = resolve(projectRoot, "src/themes/default/sourcey.css");
|
|
30
|
-
const distCss = resolve(projectRoot, "dist/themes/default/sourcey.css");
|
|
31
|
-
const clientEntry = (await exists(srcClient)) ? srcClient : distClient;
|
|
32
|
-
const sourceyCssPath = (await exists(srcCss)) ? srcCss : distCss;
|
|
33
|
-
return { clientEntry, sourceyCssPath };
|
|
34
|
-
}
|
|
35
14
|
export function createRenderOptions(page, site, options) {
|
|
36
15
|
const assetBase = options?.assetBase ?? "";
|
|
37
16
|
const pagePublicPath = toPublicPath(page.outputPath, site.baseUrl, site.prettyUrls);
|
|
@@ -76,7 +55,7 @@ export async function buildSite(pages, navigation, outputDir, site, options) {
|
|
|
76
55
|
const html = renderPage(first.spec, renderOptions, activeNav, first.currentPage, site);
|
|
77
56
|
await writeFile(resolve(resolvedDir, "index.html"), html, "utf-8");
|
|
78
57
|
}
|
|
79
|
-
await buildAssets(resolvedDir);
|
|
58
|
+
await buildAssets(resolvedDir, site.theme.name);
|
|
80
59
|
if (options?.searchIndex) {
|
|
81
60
|
await writeFile(resolve(resolvedDir, "search-index.json"), options.searchIndex, "utf-8");
|
|
82
61
|
}
|
|
@@ -136,8 +115,8 @@ function resolveRenderHref(pathOrUrl, assetBase, site) {
|
|
|
136
115
|
* Build client JS + Tailwind CSS via Vite.
|
|
137
116
|
* Same plugins as the dev server — preact() + tailwindcss().
|
|
138
117
|
*/
|
|
139
|
-
async function buildAssets(outputDir) {
|
|
140
|
-
const { clientEntry,
|
|
118
|
+
async function buildAssets(outputDir, theme) {
|
|
119
|
+
const { clientEntry, sourceyCssPaths } = resolveThemeAssets(theme);
|
|
141
120
|
await viteBuild({
|
|
142
121
|
root: projectRoot,
|
|
143
122
|
logLevel: "silent",
|
|
@@ -162,7 +141,7 @@ async function buildAssets(outputDir) {
|
|
|
162
141
|
},
|
|
163
142
|
});
|
|
164
143
|
// Append component CSS
|
|
165
|
-
const componentCSS = await readFile(
|
|
144
|
+
const componentCSS = (await Promise.all(sourceyCssPaths.map((path) => readFile(path, "utf-8")))).join("\n");
|
|
166
145
|
const builtCSS = await readFile(resolve(outputDir, "sourcey.css"), "utf-8").catch(() => "");
|
|
167
146
|
await writeFile(resolve(outputDir, "sourcey.css"), builtCSS + "\n" + componentCSS, "utf-8");
|
|
168
147
|
}
|
|
@@ -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,CAQjF;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA0CvD"}
|
|
@@ -0,0 +1,78 @@
|
|
|
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 ".html":
|
|
39
|
+
return "text/html; charset=utf-8";
|
|
40
|
+
case ".css":
|
|
41
|
+
return "text/css; charset=utf-8";
|
|
42
|
+
case ".js":
|
|
43
|
+
case ".mjs":
|
|
44
|
+
return "text/javascript; charset=utf-8";
|
|
45
|
+
case ".json":
|
|
46
|
+
return "application/json; charset=utf-8";
|
|
47
|
+
case ".svg":
|
|
48
|
+
return "image/svg+xml";
|
|
49
|
+
case ".png":
|
|
50
|
+
return "image/png";
|
|
51
|
+
case ".jpg":
|
|
52
|
+
case ".jpeg":
|
|
53
|
+
return "image/jpeg";
|
|
54
|
+
case ".gif":
|
|
55
|
+
return "image/gif";
|
|
56
|
+
case ".webp":
|
|
57
|
+
return "image/webp";
|
|
58
|
+
case ".avif":
|
|
59
|
+
return "image/avif";
|
|
60
|
+
case ".ico":
|
|
61
|
+
return "image/x-icon";
|
|
62
|
+
case ".txt":
|
|
63
|
+
case ".xml":
|
|
64
|
+
return "text/plain; charset=utf-8";
|
|
65
|
+
case ".pdf":
|
|
66
|
+
return "application/pdf";
|
|
67
|
+
case ".woff":
|
|
68
|
+
return "font/woff";
|
|
69
|
+
case ".woff2":
|
|
70
|
+
return "font/woff2";
|
|
71
|
+
case ".mp4":
|
|
72
|
+
return "video/mp4";
|
|
73
|
+
case ".webm":
|
|
74
|
+
return "video/webm";
|
|
75
|
+
default:
|
|
76
|
+
return "application/octet-stream";
|
|
77
|
+
}
|
|
78
|
+
}
|
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
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ThemeName } from "./registry.js";
|
|
2
|
+
/** The same asset selection is used by development and packaged static builds. */
|
|
3
|
+
export declare function resolveThemeAssets(name: ThemeName): {
|
|
4
|
+
clientEntry: string;
|
|
5
|
+
foundationCss: string;
|
|
6
|
+
sourceyCssPaths: string[];
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=assets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/themes/assets.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAEjE,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS;;;;EAUjD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { themeDefinitions } from "./registry.js";
|
|
5
|
+
/** The same asset selection is used by development and packaged static builds. */
|
|
6
|
+
export function resolveThemeAssets(name) {
|
|
7
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
|
8
|
+
const source = existsSync(resolve(root, "src/client/index.ts"));
|
|
9
|
+
const tree = source ? "src" : "dist";
|
|
10
|
+
const theme = themeDefinitions[name];
|
|
11
|
+
return {
|
|
12
|
+
clientEntry: resolve(root, tree, `client/${theme.client}.${source ? "ts" : "js"}`),
|
|
13
|
+
foundationCss: resolve(root, tree, "themes/default/main.css"),
|
|
14
|
+
sourceyCssPaths: theme.css.map((css) => resolve(root, tree, "themes", css)),
|
|
15
|
+
};
|
|
16
|
+
}
|