sourcey 3.4.11 → 3.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.
- package/README.md +13 -12
- package/dist/cli.js +46 -4
- package/dist/components/changelog/ChangelogPage.d.ts +5 -0
- package/dist/components/changelog/ChangelogPage.d.ts.map +1 -0
- package/dist/components/changelog/ChangelogPage.js +8 -0
- package/dist/components/changelog/EntryList.d.ts +5 -0
- package/dist/components/changelog/EntryList.d.ts.map +1 -0
- package/dist/components/changelog/EntryList.js +6 -0
- package/dist/components/changelog/TypeBadge.d.ts +6 -0
- package/dist/components/changelog/TypeBadge.d.ts.map +1 -0
- package/dist/components/changelog/TypeBadge.js +14 -0
- package/dist/components/changelog/VersionCard.d.ts +6 -0
- package/dist/components/changelog/VersionCard.d.ts.map +1 -0
- package/dist/components/changelog/VersionCard.js +25 -0
- package/dist/components/layout/Head.d.ts.map +1 -1
- package/dist/components/layout/Head.js +15 -3
- package/dist/components/layout/Page.d.ts.map +1 -1
- package/dist/components/layout/Page.js +12 -6
- package/dist/components/openapi/Introduction.d.ts.map +1 -1
- package/dist/components/openapi/Introduction.js +1 -1
- package/dist/components/openapi/MediaTypeDetails.d.ts +11 -0
- package/dist/components/openapi/MediaTypeDetails.d.ts.map +1 -0
- package/dist/components/openapi/MediaTypeDetails.js +54 -0
- package/dist/components/openapi/Parameters.d.ts.map +1 -1
- package/dist/components/openapi/Parameters.js +6 -1
- package/dist/components/openapi/RequestBody.d.ts.map +1 -1
- package/dist/components/openapi/RequestBody.js +5 -1
- package/dist/components/openapi/Responses.d.ts.map +1 -1
- package/dist/components/openapi/Responses.js +1 -1
- package/dist/components/openapi/Security.d.ts.map +1 -1
- package/dist/components/openapi/Security.js +3 -2
- package/dist/components/openapi/Tags.d.ts.map +1 -1
- package/dist/components/openapi/Tags.js +8 -1
- package/dist/config.d.ts +50 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +50 -4
- package/dist/core/changelog-normalizer.d.ts +8 -0
- package/dist/core/changelog-normalizer.d.ts.map +1 -0
- package/dist/core/changelog-normalizer.js +531 -0
- package/dist/core/doxygen-loader.d.ts.map +1 -1
- package/dist/core/doxygen-loader.js +2 -0
- package/dist/core/loader.js +2 -0
- package/dist/core/markdown-loader.d.ts +21 -0
- package/dist/core/markdown-loader.d.ts.map +1 -1
- package/dist/core/markdown-loader.js +43 -25
- package/dist/core/mcp-normalizer.d.ts.map +1 -1
- package/dist/core/navigation.d.ts +2 -2
- package/dist/core/navigation.d.ts.map +1 -1
- package/dist/core/navigation.js +5 -1
- package/dist/core/normalizer.d.ts.map +1 -1
- package/dist/core/normalizer.js +102 -4
- package/dist/core/parser.d.ts +2 -2
- package/dist/core/parser.d.ts.map +1 -1
- package/dist/core/parser.js +23 -16
- package/dist/core/search-indexer.d.ts +3 -2
- package/dist/core/search-indexer.d.ts.map +1 -1
- package/dist/core/search-indexer.js +50 -17
- package/dist/core/tag-utils.d.ts +5 -0
- package/dist/core/tag-utils.d.ts.map +1 -0
- package/dist/core/tag-utils.js +22 -0
- package/dist/core/types.d.ts +82 -4
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +1 -1
- package/dist/dev-server.d.ts +2 -0
- package/dist/dev-server.d.ts.map +1 -1
- package/dist/dev-server.js +81 -210
- package/dist/index.d.ts +6 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +115 -247
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +7 -3
- package/dist/og/fonts/Inter-Bold.ttf +0 -0
- package/dist/og/fonts/Inter-Regular.ttf +0 -0
- package/dist/og/generate-og-image.d.ts +10 -0
- package/dist/og/generate-og-image.d.ts.map +1 -0
- package/dist/og/generate-og-image.js +198 -0
- package/dist/renderer/changelog-feed.d.ts +18 -0
- package/dist/renderer/changelog-feed.d.ts.map +1 -0
- package/dist/renderer/changelog-feed.js +91 -0
- package/dist/renderer/context.d.ts +27 -7
- package/dist/renderer/context.d.ts.map +1 -1
- package/dist/renderer/html-builder.d.ts +10 -1
- package/dist/renderer/html-builder.d.ts.map +1 -1
- package/dist/renderer/html-builder.js +58 -5
- package/dist/renderer/llms.d.ts.map +1 -1
- package/dist/renderer/llms.js +133 -24
- package/dist/site-assembly.d.ts +25 -0
- package/dist/site-assembly.d.ts.map +1 -0
- package/dist/site-assembly.js +467 -0
- package/dist/site-url.d.ts +12 -0
- package/dist/site-url.d.ts.map +1 -0
- package/dist/site-url.js +67 -0
- package/dist/themes/default/sourcey.css +236 -0
- package/dist/utils/code-samples.d.ts.map +1 -1
- package/dist/utils/code-samples.js +53 -12
- package/dist/utils/markdown.d.ts +6 -0
- package/dist/utils/markdown.d.ts.map +1 -1
- package/dist/utils/markdown.js +11 -2
- package/dist/vite-plugin.d.ts +2 -0
- package/dist/vite-plugin.d.ts.map +1 -1
- package/dist/vite-plugin.js +6 -2
- package/package.json +6 -3
package/dist/index.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
|
-
import {
|
|
3
|
-
import { loadSpec } from "./core/loader.js";
|
|
4
|
-
import { convertToOpenApi3 } from "./core/converter.js";
|
|
5
|
-
import { parseSpec } from "./core/parser.js";
|
|
6
|
-
import { normalizeSpec } from "./core/normalizer.js";
|
|
7
|
-
import { normalizeMcpSpec } from "./core/mcp-normalizer.js";
|
|
2
|
+
import { extname, resolve } from "node:path";
|
|
8
3
|
import { buildSite as buildSiteHtml } from "./renderer/html-builder.js";
|
|
9
|
-
import { tabPath } from "./config.js";
|
|
10
4
|
import { loadConfig, configFromSpec } from "./config.js";
|
|
11
|
-
import {
|
|
12
|
-
import { loadDoxygenTab } from "./core/doxygen-loader.js";
|
|
13
|
-
import { buildNavFromSpec, buildNavFromPages, buildSiteNavigation } from "./core/navigation.js";
|
|
5
|
+
import { buildSiteNavigation } from "./core/navigation.js";
|
|
14
6
|
import { buildSearchIndex } from "./core/search-indexer.js";
|
|
15
7
|
import { generateLlmsTxt, generateLlmsFullTxt } from "./renderer/llms.js";
|
|
8
|
+
import { assembleSite, buildSiteConfig, collectDocsPagesByTab, createMinimalSpec, enforceChangelogDiagnostics, } from "./site-assembly.js";
|
|
9
|
+
import { toPublicPath } from "./site-url.js";
|
|
16
10
|
/**
|
|
17
11
|
* Build API documentation from a single OpenAPI/Swagger spec.
|
|
18
12
|
* Wraps the spec in a single-tab site and renders through the modern layout.
|
|
@@ -24,275 +18,149 @@ export async function buildDocs(options) {
|
|
|
24
18
|
outputDir: options.outputDir,
|
|
25
19
|
skipWrite: options.skipWrite,
|
|
26
20
|
embeddable: options.embeddable,
|
|
21
|
+
strictChangelog: options.strictChangelog,
|
|
27
22
|
});
|
|
28
23
|
const spec = result._specs?.values().next().value ?? createMinimalSpec();
|
|
29
|
-
return {
|
|
24
|
+
return {
|
|
25
|
+
spec,
|
|
26
|
+
outputDir: result.outputDir,
|
|
27
|
+
pageCount: result.pageCount,
|
|
28
|
+
changelogDiagnostics: result.changelogDiagnostics,
|
|
29
|
+
};
|
|
30
30
|
}
|
|
31
31
|
export async function buildSiteDocs(options = {}) {
|
|
32
32
|
const outputDir = resolve(options.outputDir ?? "dist");
|
|
33
33
|
const config = options.config ?? await loadConfig(options.configDir);
|
|
34
|
-
const
|
|
35
|
-
const sitePages = [];
|
|
36
|
-
const siteTabs = [];
|
|
37
|
-
const specsBySlug = new Map();
|
|
38
|
-
// Load all specs (OpenAPI and MCP)
|
|
39
|
-
for (const tab of tabs) {
|
|
40
|
-
if (tab.openapi) {
|
|
41
|
-
const loaded = await loadSpec(tab.openapi);
|
|
42
|
-
const parsed = await parseSpec(loaded);
|
|
43
|
-
const openapi3 = await convertToOpenApi3(parsed);
|
|
44
|
-
const spec = normalizeSpec(openapi3);
|
|
45
|
-
specsBySlug.set(tab.slug, spec);
|
|
46
|
-
}
|
|
47
|
-
else if (tab.mcp) {
|
|
48
|
-
const { parse } = await import("mcp-parser");
|
|
49
|
-
const mcpSpec = await parse(tab.mcp);
|
|
50
|
-
const spec = normalizeMcpSpec(mcpSpec);
|
|
51
|
-
specsBySlug.set(tab.slug, spec);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
// Primary spec for SpecContext on markdown pages
|
|
55
|
-
const primarySpec = specsBySlug.values().next().value ?? createMinimalSpec();
|
|
56
|
-
// Build SiteConfig from ResolvedConfig
|
|
34
|
+
const assembled = await assembleSite(config);
|
|
57
35
|
const site = await buildSiteConfig(config);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
36
|
+
const sitePages = Array.from(assembled.pageMap.values());
|
|
37
|
+
const navigation = buildSiteNavigation(assembled.siteTabs);
|
|
38
|
+
enforceChangelogDiagnostics(assembled.changelogDiagnostics, options.strictChangelog);
|
|
39
|
+
const docsPagesByTab = collectDocsPagesByTab(assembled.pageMap, config.tabs);
|
|
40
|
+
const searchIndex = buildSearchIndex(assembled.specsBySlug, docsPagesByTab, navigation, config.baseUrl || "/", config.search.featured, config.prettyUrls);
|
|
41
|
+
const llmsTxt = generateLlmsTxt(sitePages, navigation, site);
|
|
42
|
+
const llmsFullTxt = generateLlmsFullTxt(sitePages, navigation, site);
|
|
43
|
+
const extraFiles = new Map(assembled.extraFiles);
|
|
44
|
+
const ogImages = new Map();
|
|
45
|
+
if (config.prettyUrls === "strip") {
|
|
46
|
+
const rules = buildStripRedirectRules(sitePages, config.baseUrl);
|
|
47
|
+
if (rules)
|
|
48
|
+
extraFiles.set("_redirects", rules);
|
|
49
|
+
}
|
|
50
|
+
if (!config.ogImage) {
|
|
51
|
+
const { generateOgImage } = await import("./og/generate-og-image.js");
|
|
52
|
+
const CONCURRENCY = 8;
|
|
53
|
+
for (let i = 0; i < sitePages.length; i += CONCURRENCY) {
|
|
54
|
+
const batch = sitePages.slice(i, i + CONCURRENCY);
|
|
55
|
+
await Promise.all(batch.map(async (page) => {
|
|
56
|
+
const ogMeta = describePageForOg(page, config.name || "API", config.changelog.ogImages);
|
|
57
|
+
if (!ogMeta)
|
|
58
|
+
return;
|
|
59
|
+
const ogPath = `_og/${page.outputPath.replace(/\.html$/, ".png")}`;
|
|
60
|
+
const png = await generateOgImage({
|
|
61
|
+
title: ogMeta.title,
|
|
62
|
+
description: ogMeta.description,
|
|
63
|
+
siteName: config.name,
|
|
64
|
+
theme: config.theme,
|
|
65
|
+
logo: site.logo?.light,
|
|
82
66
|
});
|
|
83
|
-
|
|
84
|
-
|
|
67
|
+
page.ogImagePath = ogPath;
|
|
68
|
+
ogImages.set(ogPath, png);
|
|
69
|
+
}));
|
|
85
70
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
pagesByPath.set(rp.slug, page);
|
|
93
|
-
sitePages.push({
|
|
94
|
-
outputPath: tabPath(tab.slug, `${slug}.html`),
|
|
95
|
-
currentPage: { kind: "markdown", markdown: page },
|
|
96
|
-
spec: primarySpec,
|
|
97
|
-
tabSlug: tab.slug,
|
|
98
|
-
pageSlug: slug,
|
|
99
|
-
});
|
|
100
|
-
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
const staticOg = config.ogImage;
|
|
74
|
+
if (staticOg.startsWith("http://") || staticOg.startsWith("https://") || staticOg.startsWith("data:")) {
|
|
75
|
+
for (const page of sitePages) {
|
|
76
|
+
page.ogImagePath = staticOg;
|
|
101
77
|
}
|
|
102
|
-
const navTab = buildNavFromPages(tab, pagesByPath);
|
|
103
|
-
siteTabs.push(navTab);
|
|
104
78
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// Build search index
|
|
112
|
-
const markdownPagesByTab = new Map();
|
|
113
|
-
for (const tab of tabs) {
|
|
114
|
-
if (tab.groups || tab.doxygen) {
|
|
115
|
-
const tabPages = sitePages
|
|
116
|
-
.filter((p) => p.tabSlug === tab.slug && p.currentPage.kind === "markdown")
|
|
117
|
-
.map((p) => p.currentPage.markdown);
|
|
118
|
-
markdownPagesByTab.set(tab.slug, tabPages);
|
|
79
|
+
else {
|
|
80
|
+
const ogPath = `_og/static${extname(staticOg) || ".png"}`;
|
|
81
|
+
extraFiles.set(ogPath, await readFile(staticOg));
|
|
82
|
+
for (const page of sitePages) {
|
|
83
|
+
page.ogImagePath = ogPath;
|
|
84
|
+
}
|
|
119
85
|
}
|
|
120
86
|
}
|
|
121
|
-
const searchIndex = buildSearchIndex(specsBySlug, markdownPagesByTab, navigation, "/", config.search.featured);
|
|
122
|
-
const llmsTxt = generateLlmsTxt(sitePages, navigation, site);
|
|
123
|
-
const llmsFullTxt = generateLlmsFullTxt(sitePages, navigation, site);
|
|
124
87
|
if (!options.skipWrite) {
|
|
125
88
|
await buildSiteHtml(sitePages, navigation, outputDir, site, {
|
|
126
89
|
searchIndex,
|
|
127
90
|
llmsTxt,
|
|
128
91
|
llmsFullTxt,
|
|
129
92
|
embeddable: options.embeddable,
|
|
93
|
+
ogImages,
|
|
94
|
+
extraFiles,
|
|
130
95
|
});
|
|
131
96
|
}
|
|
132
|
-
return { outputDir, pageCount: sitePages.length, _specs: specsBySlug };
|
|
133
|
-
}
|
|
134
|
-
// ---------------------------------------------------------------------------
|
|
135
|
-
// Helpers
|
|
136
|
-
// ---------------------------------------------------------------------------
|
|
137
|
-
function createMinimalSpec() {
|
|
138
97
|
return {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
schemas: {},
|
|
144
|
-
securitySchemes: {},
|
|
145
|
-
webhooks: [],
|
|
98
|
+
outputDir,
|
|
99
|
+
pageCount: sitePages.length,
|
|
100
|
+
changelogDiagnostics: assembled.changelogDiagnostics,
|
|
101
|
+
_specs: assembled.specsBySlug,
|
|
146
102
|
};
|
|
147
103
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
: undefined;
|
|
156
|
-
const customCSS = await loadCustomCSS(config.theme.css);
|
|
157
|
-
return {
|
|
158
|
-
name: config.name,
|
|
159
|
-
theme: config.theme,
|
|
160
|
-
logo: logo?.light ? logo : undefined,
|
|
161
|
-
favicon: config.favicon ? await resolveAssetUrl(config.favicon) : undefined,
|
|
162
|
-
repo: config.repo,
|
|
163
|
-
editBranch: config.editBranch,
|
|
164
|
-
editBasePath: config.editBasePath,
|
|
165
|
-
codeSamples: config.codeSamples,
|
|
166
|
-
navbar: config.navbar,
|
|
167
|
-
footer: config.footer,
|
|
168
|
-
customCSS: customCSS || undefined,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
async function loadCustomCSS(paths) {
|
|
172
|
-
const parts = [];
|
|
173
|
-
for (const p of paths) {
|
|
174
|
-
parts.push(await readFile(p, "utf-8"));
|
|
104
|
+
function describePageForOg(page, defaultSiteName, changelogPermalinkOg) {
|
|
105
|
+
if (page.currentPage.kind === "markdown") {
|
|
106
|
+
return {
|
|
107
|
+
title: page.currentPage.markdown.title,
|
|
108
|
+
description: page.currentPage.markdown.description || undefined,
|
|
109
|
+
};
|
|
175
110
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
return
|
|
111
|
+
if (page.currentPage.kind === "changelog") {
|
|
112
|
+
const changelog = page.currentPage.changelog;
|
|
113
|
+
if (changelog.permalinkVersionId) {
|
|
114
|
+
if (!changelogPermalinkOg)
|
|
115
|
+
return null;
|
|
116
|
+
const version = changelog.changelog.versions.find((candidate) => candidate.id === changelog.permalinkVersionId);
|
|
117
|
+
if (!version)
|
|
118
|
+
return null;
|
|
119
|
+
return {
|
|
120
|
+
title: `${version.version ?? "Unreleased"} — ${changelog.title}`,
|
|
121
|
+
description: version.summary || summarizeVersion(version),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
title: changelog.title,
|
|
126
|
+
description: changelog.description || changelog.changelog.description,
|
|
127
|
+
};
|
|
190
128
|
}
|
|
191
|
-
|
|
192
|
-
const ext = extname(abs).toLowerCase();
|
|
193
|
-
const mime = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
194
|
-
const data = await readFile(abs);
|
|
195
|
-
return `data:${mime};base64,${data.toString("base64")}`;
|
|
129
|
+
return { title: `${defaultSiteName} Reference` };
|
|
196
130
|
}
|
|
197
|
-
// ---------------------------------------------------------------------------
|
|
198
|
-
// Internal link resolution
|
|
199
|
-
// ---------------------------------------------------------------------------
|
|
200
131
|
/**
|
|
201
|
-
*
|
|
132
|
+
* Build Netlify/Cloudflare-Pages redirect rules that canonicalise every page onto
|
|
133
|
+
* its extensionless, slash-less URL. Emitted only when `prettyUrls === "strip"`.
|
|
202
134
|
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
135
|
+
* For each page at `dir/slug/index.html` the user sees `/dir/slug`; redirect
|
|
136
|
+
* `/dir/slug/` and (for backwards compatibility with pre-rename bookmarks)
|
|
137
|
+
* `/dir/slug.html` onto it.
|
|
206
138
|
*/
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const out = page.outputPath; // e.g. "components.html" or "config/ref-theme-tokens.html"
|
|
213
|
-
const clean = out.replace(/\.html$/, ""); // "components" or "config/ref-theme-tokens"
|
|
214
|
-
pathMap.set(clean, out);
|
|
215
|
-
if (page.currentPage.kind === "markdown" && page.currentPage.markdown?.sourcePath) {
|
|
216
|
-
const sourceClean = page.currentPage.markdown.sourcePath
|
|
217
|
-
.replace(/\\/g, "/")
|
|
218
|
-
.replace(/^\/+/, "")
|
|
219
|
-
.replace(/\.(md|mdx)$/, "");
|
|
220
|
-
pathMap.set(sourceClean, out);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
// Repo source link base: e.g. "https://github.com/user/repo/tree/main"
|
|
224
|
-
const repoBase = config.repo?.replace(/\/$/, "");
|
|
225
|
-
const branch = config.editBranch;
|
|
226
|
-
const sourceBase = repoBase && branch ? `${repoBase}/tree/${branch}` : undefined;
|
|
139
|
+
function buildStripRedirectRules(pages, baseUrl) {
|
|
140
|
+
const lines = [
|
|
141
|
+
"# Generated by Sourcey: pretty URL canonicalisation",
|
|
142
|
+
];
|
|
143
|
+
const seen = new Set();
|
|
227
144
|
for (const page of pages) {
|
|
228
|
-
|
|
145
|
+
const canonical = toPublicPath(page.outputPath, baseUrl, "strip");
|
|
146
|
+
if (canonical === "/" || canonical === "")
|
|
229
147
|
continue;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
// Only rewrite internal non-anchor, non-protocol links
|
|
238
|
-
if (href.startsWith("http://") || href.startsWith("https://") || href.startsWith("#") || href.startsWith("mailto:")) {
|
|
239
|
-
return _match;
|
|
240
|
-
}
|
|
241
|
-
// Split off hash fragment
|
|
242
|
-
const [path, hash] = href.split("#", 2);
|
|
243
|
-
const hashSuffix = hash ? `#${hash}` : "";
|
|
244
|
-
// Normalize: strip leading slash, trailing slash, and markdown extension
|
|
245
|
-
const sourcePath = path.replace(/\\/g, "/");
|
|
246
|
-
const clean = sourcePath.replace(/^\/+/, "").replace(/\/+$/, "").replace(/\.(md|mdx)$/, "");
|
|
247
|
-
// Skip if already has .html extension
|
|
248
|
-
if (clean.endsWith(".html"))
|
|
249
|
-
return _match;
|
|
250
|
-
const candidates = new Set();
|
|
251
|
-
// Relative markdown links should resolve against the current page's
|
|
252
|
-
// source path, not just the built output path.
|
|
253
|
-
if (!sourcePath.startsWith("/") && md.sourcePath) {
|
|
254
|
-
const sourceDir = posix.dirname(md.sourcePath.replace(/\\/g, "/"));
|
|
255
|
-
candidates.add(posix.normalize(posix.join(sourceDir, clean)));
|
|
256
|
-
}
|
|
257
|
-
candidates.add(clean);
|
|
258
|
-
// Look up in path map — try the resolved path first, then
|
|
259
|
-
// progressively strip leading segments to handle deployment prefixes
|
|
260
|
-
// (e.g. "docs/components" → "components").
|
|
261
|
-
let target;
|
|
262
|
-
for (const initialCandidate of candidates) {
|
|
263
|
-
let candidate = initialCandidate;
|
|
264
|
-
while (!target && candidate && candidate !== ".") {
|
|
265
|
-
target = pathMap.get(candidate);
|
|
266
|
-
if (target)
|
|
267
|
-
break;
|
|
268
|
-
const slash = candidate.indexOf("/");
|
|
269
|
-
if (slash === -1)
|
|
270
|
-
break;
|
|
271
|
-
candidate = candidate.substring(slash + 1);
|
|
272
|
-
}
|
|
273
|
-
if (target)
|
|
274
|
-
break;
|
|
275
|
-
}
|
|
276
|
-
if (target) {
|
|
277
|
-
// Build relative path from this page to the target
|
|
278
|
-
const relativePath = toRoot + target;
|
|
279
|
-
return `href="${relativePath}${hashSuffix}"`;
|
|
280
|
-
}
|
|
281
|
-
// If the link contains ../ and we have a repo URL configured,
|
|
282
|
-
// resolve it relative to the markdown source file. Links to docs
|
|
283
|
-
// pages are already handled above (pathMap lookup catches them after
|
|
284
|
-
// .md stripping and prefix stripping). What remains are links to
|
|
285
|
-
// repo source code outside the docs directory.
|
|
286
|
-
if (sourceBase && href.includes("../") && md.sourcePath) {
|
|
287
|
-
const sourceDir = posix.dirname(md.sourcePath);
|
|
288
|
-
const resolved = posix.normalize(posix.join(sourceDir, path));
|
|
289
|
-
// Only rewrite if the path stays inside the repo root
|
|
290
|
-
if (!resolved.startsWith("..")) {
|
|
291
|
-
return `href="${sourceBase}/${resolved}${hashSuffix}"`;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
return _match;
|
|
295
|
-
});
|
|
148
|
+
if (seen.has(canonical))
|
|
149
|
+
continue;
|
|
150
|
+
seen.add(canonical);
|
|
151
|
+
// Trailing-slash variant (what the file system would naturally serve).
|
|
152
|
+
lines.push(`${canonical}/ ${canonical} 301`);
|
|
153
|
+
// Stale `.html` bookmarks from before the migration.
|
|
154
|
+
lines.push(`${canonical}.html ${canonical} 301`);
|
|
296
155
|
}
|
|
156
|
+
return lines.length > 1 ? `${lines.join("\n")}\n` : null;
|
|
157
|
+
}
|
|
158
|
+
function summarizeVersion(version) {
|
|
159
|
+
if (version.summary)
|
|
160
|
+
return version.summary;
|
|
161
|
+
const texts = version.sections.flatMap((section) => section.entries.map((entry) => entry.text));
|
|
162
|
+
const summary = texts.slice(0, 3).join(" ");
|
|
163
|
+
return summary || undefined;
|
|
297
164
|
}
|
|
298
165
|
export { defineConfig } from "./config.js";
|
|
166
|
+
export { resolveInternalLinks } from "./site-assembly.js";
|
package/dist/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AA4DA,eAAO,MAAM,IAAI,qDAyPf,CAAC"}
|
package/dist/init.js
CHANGED
|
@@ -30,10 +30,14 @@ function findOpenAPISpecs(dir) {
|
|
|
30
30
|
specs.push(file);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
catch {
|
|
33
|
+
catch {
|
|
34
|
+
// Ignore unreadable candidate files during spec auto-detection.
|
|
35
|
+
}
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
|
-
catch {
|
|
38
|
+
catch {
|
|
39
|
+
// Ignore unreadable directories during spec auto-detection.
|
|
40
|
+
}
|
|
37
41
|
return specs;
|
|
38
42
|
}
|
|
39
43
|
function isExistingProject(dir) {
|
|
@@ -120,7 +124,7 @@ export const init = defineCommand({
|
|
|
120
124
|
addOpenAPI = spec;
|
|
121
125
|
}
|
|
122
126
|
if (doxyfile) {
|
|
123
|
-
const useDoxygen = (await consola.prompt(`Found ${doxyfile}
|
|
127
|
+
const useDoxygen = (await consola.prompt(`Found ${doxyfile}. Add Doxygen API reference?`, {
|
|
124
128
|
type: "confirm",
|
|
125
129
|
initial: true,
|
|
126
130
|
}));
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ResolvedTheme } from "../config.js";
|
|
2
|
+
export interface OgImageOptions {
|
|
3
|
+
title: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
siteName: string;
|
|
6
|
+
theme: ResolvedTheme;
|
|
7
|
+
logo?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function generateOgImage(options: OgImageOptions): Promise<Buffer>;
|
|
10
|
+
//# sourceMappingURL=generate-og-image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-og-image.d.ts","sourceRoot":"","sources":["../../src/og/generate-og-image.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAIlD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AA4LD,wBAAsB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAkB9E"}
|