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
package/dist/index.js CHANGED
@@ -1,18 +1,12 @@
1
1
  import { readFile } from "node:fs/promises";
2
- import { resolve, extname, posix } from "node:path";
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 { loadMarkdownPage, slugFromPath } from "./core/markdown-loader.js";
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 { spec, outputDir: result.outputDir, pageCount: result.pageCount };
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 tabs = config.tabs;
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
- // Process all tabs
59
- for (const tab of tabs) {
60
- if (tab.openapi || tab.mcp) {
61
- const spec = specsBySlug.get(tab.slug);
62
- const navTab = buildNavFromSpec(spec, tab.slug);
63
- navTab.label = tab.label;
64
- siteTabs.push(navTab);
65
- sitePages.push({
66
- outputPath: tabPath(tab.slug, "index.html"),
67
- currentPage: { kind: "spec", spec },
68
- spec,
69
- tabSlug: tab.slug,
70
- pageSlug: "introduction",
71
- });
72
- }
73
- else if (tab.doxygen) {
74
- const { pages, navTab } = await loadDoxygenTab(tab.doxygen, tab.slug, tab.label);
75
- for (const [slug, page] of pages) {
76
- sitePages.push({
77
- outputPath: tabPath(tab.slug, `${slug}.html`),
78
- currentPage: { kind: "markdown", markdown: page },
79
- spec: primarySpec,
80
- tabSlug: tab.slug,
81
- pageSlug: slug,
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
- siteTabs.push(navTab);
67
+ page.ogImagePath = ogPath;
68
+ ogImages.set(ogPath, png);
69
+ }));
85
70
  }
86
- else if (tab.groups) {
87
- const pagesByPath = new Map();
88
- for (const group of tab.groups) {
89
- for (const rp of group.pages) {
90
- const slug = slugFromPath(rp.slug);
91
- const page = await loadMarkdownPage(rp.file, slug);
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
- const navigation = buildSiteNavigation(siteTabs);
107
- // Resolve internal links in markdown pages.
108
- // Builds a map from every plausible href to the correct output path,
109
- // then rewrites matching href attributes in each page's HTML.
110
- resolveInternalLinks(sitePages, config);
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
- info: { title: "", version: "", description: "" },
140
- servers: [],
141
- tags: [],
142
- operations: [],
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
- async function buildSiteConfig(config) {
149
- const logo = config.logo
150
- ? {
151
- light: await resolveAssetUrl(config.logo.light ?? ""),
152
- dark: config.logo.dark ? await resolveAssetUrl(config.logo.dark) : undefined,
153
- href: config.logo.href,
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
- return parts.join("\n");
177
- }
178
- const MIME_TYPES = {
179
- ".png": "image/png",
180
- ".jpg": "image/jpeg",
181
- ".jpeg": "image/jpeg",
182
- ".gif": "image/gif",
183
- ".svg": "image/svg+xml",
184
- ".webp": "image/webp",
185
- ".ico": "image/x-icon",
186
- };
187
- async function resolveAssetUrl(pathOrUrl) {
188
- if (!pathOrUrl || pathOrUrl.startsWith("http://") || pathOrUrl.startsWith("https://") || pathOrUrl.startsWith("data:")) {
189
- return pathOrUrl;
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
- const abs = resolve(pathOrUrl);
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
- * Rewrite internal links in markdown page HTML to correct relative .html paths.
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
- * Authors write links like [Page](/slug) or [Page](/tab/slug). This pass
204
- * rewrites matching href values so they resolve on static file servers
205
- * that don't support extensionless URLs.
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
- export function resolveInternalLinks(pages, config) {
208
- // Build a map from every plausible clean path to the output path.
209
- // e.g. "components" -> "components.html", "config/ref-theme-tokens" -> "config/ref-theme-tokens.html"
210
- const pathMap = new Map();
211
- for (const page of pages) {
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
- if (page.currentPage.kind !== "markdown" || !page.currentPage.markdown)
145
+ const canonical = toPublicPath(page.outputPath, baseUrl, "strip");
146
+ if (canonical === "/" || canonical === "")
229
147
  continue;
230
- const md = page.currentPage.markdown;
231
- const pageDir = page.outputPath.includes("/")
232
- ? page.outputPath.substring(0, page.outputPath.lastIndexOf("/"))
233
- : "";
234
- const depth = pageDir ? pageDir.split("/").length : 0;
235
- const toRoot = depth > 0 ? "../".repeat(depth) : "";
236
- md.html = md.html.replace(/href="([^"]+)"/g, (_match, href) => {
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";
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAwDA,eAAO,MAAM,IAAI,qDAyPf,CAAC"}
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} — add Doxygen API reference?`, {
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"}