sourcey 3.4.10 → 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 +53 -28
- 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 +237 -1
- 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/cli.js.map +0 -1
- package/dist/client/copy.js +0 -21
- package/dist/client/index.js.map +0 -1
- package/dist/components/App.js.map +0 -1
- package/dist/components/layout/Head.js.map +0 -1
- package/dist/components/layout/Header.js.map +0 -1
- package/dist/components/layout/Page.js.map +0 -1
- package/dist/components/layout/Sidebar.js.map +0 -1
- package/dist/components/layout/TableOfContents.js.map +0 -1
- package/dist/components/openapi/CodeSamples.js.map +0 -1
- package/dist/components/openapi/Definition.js.map +0 -1
- package/dist/components/openapi/EndpointBar.js.map +0 -1
- package/dist/components/openapi/Introduction.js.map +0 -1
- package/dist/components/openapi/Operation.js.map +0 -1
- package/dist/components/openapi/Parameters.js.map +0 -1
- package/dist/components/openapi/RequestBody.js.map +0 -1
- package/dist/components/openapi/Responses.js.map +0 -1
- package/dist/components/openapi/Security.js.map +0 -1
- package/dist/components/openapi/Tags.js.map +0 -1
- package/dist/components/schema/ExampleView.js.map +0 -1
- package/dist/components/schema/SchemaDatatype.js.map +0 -1
- package/dist/components/schema/SchemaView.js.map +0 -1
- package/dist/components/ui/Badge.js.map +0 -1
- package/dist/components/ui/CopyButton.js.map +0 -1
- package/dist/components/ui/Logo.js.map +0 -1
- package/dist/components/ui/Markdown.js.map +0 -1
- package/dist/components/ui/SectionLabel.js.map +0 -1
- package/dist/components/ui/SocialIcon.js.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/core/converter.js.map +0 -1
- package/dist/core/doxygen-loader.js.map +0 -1
- package/dist/core/loader.js.map +0 -1
- package/dist/core/markdown-loader.js.map +0 -1
- package/dist/core/navigation.js.map +0 -1
- package/dist/core/normalizer.js.map +0 -1
- package/dist/core/parser.js.map +0 -1
- package/dist/core/search-indexer.js.map +0 -1
- package/dist/core/types.js.map +0 -1
- package/dist/dev-server.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/init.js.map +0 -1
- package/dist/renderer/context.js.map +0 -1
- package/dist/renderer/html-builder.js.map +0 -1
- package/dist/renderer/static-renderer.js.map +0 -1
- package/dist/utils/code-samples.js.map +0 -1
- package/dist/utils/copy-svg.js.map +0 -1
- package/dist/utils/example-generator.js.map +0 -1
- package/dist/utils/highlighter.js.map +0 -1
- package/dist/utils/html-id.js.map +0 -1
- package/dist/utils/http.js.map +0 -1
- package/dist/utils/icons.js.map +0 -1
- package/dist/utils/lang-icons.js.map +0 -1
- package/dist/utils/markdown.js.map +0 -1
- package/dist/vite-plugin.js.map +0 -1
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
import { htmlId } from "../utils/html-id.js";
|
|
2
|
+
import { lexMarkdown, renderMarkdownInline } from "../utils/markdown.js";
|
|
3
|
+
const STANDARD_SECTION_TYPES = {
|
|
4
|
+
added: "added",
|
|
5
|
+
changed: "changed",
|
|
6
|
+
fixed: "fixed",
|
|
7
|
+
removed: "removed",
|
|
8
|
+
deprecated: "deprecated",
|
|
9
|
+
security: "security",
|
|
10
|
+
};
|
|
11
|
+
const CONVENTIONAL_SECTION_TYPES = {
|
|
12
|
+
feat: "added",
|
|
13
|
+
feature: "added",
|
|
14
|
+
features: "added",
|
|
15
|
+
"bug fix": "fixed",
|
|
16
|
+
"bug fixes": "fixed",
|
|
17
|
+
fix: "fixed",
|
|
18
|
+
fixes: "fixed",
|
|
19
|
+
"breaking change": "changed",
|
|
20
|
+
"breaking changes": "changed",
|
|
21
|
+
performance: "changed",
|
|
22
|
+
"performance improvement": "changed",
|
|
23
|
+
"performance improvements": "changed",
|
|
24
|
+
revert: "removed",
|
|
25
|
+
reverts: "removed",
|
|
26
|
+
refactor: "changed",
|
|
27
|
+
refactors: "changed",
|
|
28
|
+
};
|
|
29
|
+
export function normalizeChangelog(markdown, options = {}) {
|
|
30
|
+
const diagnostics = [];
|
|
31
|
+
const tokens = lexMarkdown(markdown);
|
|
32
|
+
const tokenLines = buildTokenLineMap(tokens);
|
|
33
|
+
const versionLinks = parseReferenceLinks(markdown);
|
|
34
|
+
const versionLinkMap = new Map(versionLinks.map((link) => [normalizeVersionKey(link.version), link.url]));
|
|
35
|
+
let title = options.title?.trim();
|
|
36
|
+
const descriptionParts = [];
|
|
37
|
+
const rawBlocks = [];
|
|
38
|
+
let currentBlock = null;
|
|
39
|
+
for (const token of tokens) {
|
|
40
|
+
if (token.type === "heading") {
|
|
41
|
+
const heading = token;
|
|
42
|
+
const text = headingText(heading);
|
|
43
|
+
if (heading.depth === 1 && !title) {
|
|
44
|
+
title = text;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (heading.depth === 2) {
|
|
48
|
+
if (currentBlock)
|
|
49
|
+
rawBlocks.push(currentBlock);
|
|
50
|
+
currentBlock = null;
|
|
51
|
+
const parsed = parseVersionHeading(text);
|
|
52
|
+
if (!parsed) {
|
|
53
|
+
diagnostics.push({
|
|
54
|
+
severity: "warning",
|
|
55
|
+
code: "CHG003_INVALID_VERSION",
|
|
56
|
+
message: `Heading "${text}" is not a supported changelog version heading.`,
|
|
57
|
+
line: tokenLines.get(token),
|
|
58
|
+
});
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
currentBlock = {
|
|
62
|
+
heading: parsed,
|
|
63
|
+
line: tokenLines.get(token) ?? 1,
|
|
64
|
+
sourceOrder: rawBlocks.length,
|
|
65
|
+
rawHeading: heading.raw,
|
|
66
|
+
sections: [],
|
|
67
|
+
rawSectionKinds: new Set(),
|
|
68
|
+
};
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (currentBlock) {
|
|
73
|
+
appendTokenToVersion(token, currentBlock, diagnostics, options.repoUrl);
|
|
74
|
+
}
|
|
75
|
+
else if (!options.description && shouldContributeToDescription(token)) {
|
|
76
|
+
descriptionParts.push(token.raw);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (currentBlock)
|
|
80
|
+
rawBlocks.push(currentBlock);
|
|
81
|
+
title = title || "Changelog";
|
|
82
|
+
const description = (options.description ?? joinMarkdownFragments(descriptionParts)) || undefined;
|
|
83
|
+
const versions = rawBlocks.map((block) => {
|
|
84
|
+
const versionLabel = block.heading.version ?? "Unreleased";
|
|
85
|
+
if (block.heading.version && !block.heading.date) {
|
|
86
|
+
diagnostics.push({
|
|
87
|
+
severity: "warning",
|
|
88
|
+
code: "CHG001_MISSING_DATE",
|
|
89
|
+
message: `Version ${block.heading.version} does not include a release date.`,
|
|
90
|
+
line: block.line,
|
|
91
|
+
version: block.heading.version,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (!block.sections.length && !block.summary) {
|
|
95
|
+
diagnostics.push({
|
|
96
|
+
severity: "warning",
|
|
97
|
+
code: "CHG006_EMPTY_VERSION",
|
|
98
|
+
message: `Version ${versionLabel} has no release notes.`,
|
|
99
|
+
line: block.line,
|
|
100
|
+
version: block.heading.version ?? "Unreleased",
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
for (const section of block.sections) {
|
|
104
|
+
if (section.kind !== "unknown")
|
|
105
|
+
continue;
|
|
106
|
+
diagnostics.push({
|
|
107
|
+
severity: "warning",
|
|
108
|
+
code: "CHG002_UNKNOWN_TYPE",
|
|
109
|
+
message: `Section "${section.label}" is not a recognized changelog type and will render as Other.`,
|
|
110
|
+
line: block.line,
|
|
111
|
+
version: block.heading.version ?? "Unreleased",
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (!options.repoUrl && block.sections.some((section) => section.entries.some((entry) => entry.refs.length > 0))) {
|
|
115
|
+
diagnostics.push({
|
|
116
|
+
severity: "info",
|
|
117
|
+
code: "CHG007_UNRESOLVED_REF",
|
|
118
|
+
message: `Found repository references in ${versionLabel}, but no repo URL is configured.`,
|
|
119
|
+
line: block.line,
|
|
120
|
+
version: block.heading.version ?? "Unreleased",
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return normalizeVersionBlock(block, versionLinkMap);
|
|
124
|
+
});
|
|
125
|
+
validateDuplicates(versions, diagnostics);
|
|
126
|
+
validateSourceOrder(versions, diagnostics);
|
|
127
|
+
const format = detectFormat(rawBlocks);
|
|
128
|
+
return {
|
|
129
|
+
title,
|
|
130
|
+
description,
|
|
131
|
+
format,
|
|
132
|
+
versions,
|
|
133
|
+
links: versionLinks,
|
|
134
|
+
diagnostics,
|
|
135
|
+
rawMarkdown: markdown,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function buildTokenLineMap(tokens) {
|
|
139
|
+
const map = new Map();
|
|
140
|
+
let line = 1;
|
|
141
|
+
for (const token of tokens) {
|
|
142
|
+
map.set(token, line);
|
|
143
|
+
line += newlineCount(token.raw);
|
|
144
|
+
}
|
|
145
|
+
return map;
|
|
146
|
+
}
|
|
147
|
+
function newlineCount(input) {
|
|
148
|
+
if (!input)
|
|
149
|
+
return 0;
|
|
150
|
+
return (input.match(/\n/g) ?? []).length;
|
|
151
|
+
}
|
|
152
|
+
function parseReferenceLinks(markdown) {
|
|
153
|
+
const links = [];
|
|
154
|
+
const seen = new Set();
|
|
155
|
+
for (const match of markdown.matchAll(/^\[([^\]]+)\]:\s*(\S+)\s*$/gm)) {
|
|
156
|
+
const version = match[1].trim();
|
|
157
|
+
const url = match[2].trim();
|
|
158
|
+
const key = normalizeVersionKey(version);
|
|
159
|
+
if (seen.has(key))
|
|
160
|
+
continue;
|
|
161
|
+
seen.add(key);
|
|
162
|
+
links.push({ version, url });
|
|
163
|
+
}
|
|
164
|
+
return links;
|
|
165
|
+
}
|
|
166
|
+
function headingText(heading) {
|
|
167
|
+
return heading.tokens
|
|
168
|
+
.map((token) => ("text" in token ? token.text : token.raw))
|
|
169
|
+
.join("")
|
|
170
|
+
.trim();
|
|
171
|
+
}
|
|
172
|
+
function shouldContributeToDescription(token) {
|
|
173
|
+
return token.type === "paragraph" || token.type === "blockquote" || token.type === "list";
|
|
174
|
+
}
|
|
175
|
+
function appendTokenToVersion(token, version, diagnostics, repoUrl) {
|
|
176
|
+
if (token.type === "space")
|
|
177
|
+
return;
|
|
178
|
+
if (token.type === "heading" && token.depth === 3) {
|
|
179
|
+
const heading = token;
|
|
180
|
+
const label = headingText(heading);
|
|
181
|
+
const kind = resolveSectionKind(label);
|
|
182
|
+
version.rawSectionKinds.add(kind);
|
|
183
|
+
version.sections.push({
|
|
184
|
+
label,
|
|
185
|
+
kind,
|
|
186
|
+
entries: [],
|
|
187
|
+
});
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (version.sections.length === 0 && token.type === "paragraph") {
|
|
191
|
+
const summaryParts = version.summary ? [version.summary, token.raw] : [token.raw];
|
|
192
|
+
version.summary = joinMarkdownFragments(summaryParts);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const entries = entriesFromToken(token, repoUrl);
|
|
196
|
+
if (entries.length === 0)
|
|
197
|
+
return;
|
|
198
|
+
const targetSection = ensureTargetSection(version);
|
|
199
|
+
targetSection.entries.push(...entries);
|
|
200
|
+
}
|
|
201
|
+
function ensureTargetSection(version) {
|
|
202
|
+
if (version.sections.length === 0) {
|
|
203
|
+
const section = {
|
|
204
|
+
label: "Other",
|
|
205
|
+
kind: "implicit",
|
|
206
|
+
entries: [],
|
|
207
|
+
};
|
|
208
|
+
version.sections.push(section);
|
|
209
|
+
version.rawSectionKinds.add("implicit");
|
|
210
|
+
return section;
|
|
211
|
+
}
|
|
212
|
+
return version.sections[version.sections.length - 1];
|
|
213
|
+
}
|
|
214
|
+
function entriesFromToken(token, repoUrl) {
|
|
215
|
+
if (token.type === "list") {
|
|
216
|
+
const list = token;
|
|
217
|
+
return list.items.map((item) => createEntry(extractListItemMarkdown(item), repoUrl));
|
|
218
|
+
}
|
|
219
|
+
if (token.type === "paragraph") {
|
|
220
|
+
return [createEntry(token.raw, repoUrl)];
|
|
221
|
+
}
|
|
222
|
+
if (token.type === "blockquote") {
|
|
223
|
+
return [createEntry(stripBlockquotePrefix(token.raw), repoUrl)];
|
|
224
|
+
}
|
|
225
|
+
return [];
|
|
226
|
+
}
|
|
227
|
+
function extractListItemMarkdown(item) {
|
|
228
|
+
if (typeof item.text === "string" && item.text.trim())
|
|
229
|
+
return item.text.trim();
|
|
230
|
+
return item.tokens
|
|
231
|
+
.map((token) => token.raw)
|
|
232
|
+
.join("")
|
|
233
|
+
.trim();
|
|
234
|
+
}
|
|
235
|
+
function stripBlockquotePrefix(input) {
|
|
236
|
+
return input
|
|
237
|
+
.split("\n")
|
|
238
|
+
.map((line) => line.replace(/^\s*>\s?/, ""))
|
|
239
|
+
.join("\n")
|
|
240
|
+
.trim();
|
|
241
|
+
}
|
|
242
|
+
function createEntry(markdown, repoUrl) {
|
|
243
|
+
const compact = markdown.replace(/\n+/g, " ").trim();
|
|
244
|
+
const html = renderMarkdownInline(compact);
|
|
245
|
+
const text = stripHtml(html);
|
|
246
|
+
const links = extractInlineLinks(compact);
|
|
247
|
+
const refs = extractRefs(compact, repoUrl);
|
|
248
|
+
return { text, html, links, refs };
|
|
249
|
+
}
|
|
250
|
+
function extractInlineLinks(markdown) {
|
|
251
|
+
const links = [];
|
|
252
|
+
for (const match of markdown.matchAll(/\[([^\]]+)\]\(([^)]+)\)/g)) {
|
|
253
|
+
links.push({
|
|
254
|
+
text: match[1].trim(),
|
|
255
|
+
href: match[2].trim(),
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return links;
|
|
259
|
+
}
|
|
260
|
+
function extractRefs(markdown, repoUrl) {
|
|
261
|
+
const refs = [];
|
|
262
|
+
const seen = new Set();
|
|
263
|
+
const repoBase = repoUrl?.replace(/\.git$/, "").replace(/\/$/, "");
|
|
264
|
+
const pushRef = (type, id, url) => {
|
|
265
|
+
const key = `${type}:${id}`;
|
|
266
|
+
if (seen.has(key))
|
|
267
|
+
return;
|
|
268
|
+
seen.add(key);
|
|
269
|
+
refs.push({ type, id, url });
|
|
270
|
+
};
|
|
271
|
+
for (const match of markdown.matchAll(/(?:^|[^\w])(?:pr|pull request)\s*#(\d+)/gi)) {
|
|
272
|
+
const id = match[1];
|
|
273
|
+
pushRef("pr", id, repoBase ? `${repoBase}/pull/${id}` : undefined);
|
|
274
|
+
}
|
|
275
|
+
for (const match of markdown.matchAll(/(?:^|[^\w])#(\d+)/g)) {
|
|
276
|
+
const id = match[1];
|
|
277
|
+
const prefix = markdown.slice(Math.max(0, (match.index ?? 0) - 20), match.index ?? 0);
|
|
278
|
+
if (/(?:pr|pull request)\s*$/i.test(prefix))
|
|
279
|
+
continue;
|
|
280
|
+
pushRef("issue", id, repoBase ? `${repoBase}/issues/${id}` : undefined);
|
|
281
|
+
}
|
|
282
|
+
for (const match of markdown.matchAll(/\b([0-9a-f]{7,40})\b/gi)) {
|
|
283
|
+
const sha = match[1];
|
|
284
|
+
pushRef("commit", sha, repoBase ? `${repoBase}/commit/${sha}` : undefined);
|
|
285
|
+
}
|
|
286
|
+
return refs;
|
|
287
|
+
}
|
|
288
|
+
function normalizeVersionBlock(block, versionLinkMap) {
|
|
289
|
+
return {
|
|
290
|
+
id: htmlId(block.heading.version ?? "unreleased"),
|
|
291
|
+
version: block.heading.version,
|
|
292
|
+
date: block.heading.date,
|
|
293
|
+
yanked: block.heading.yanked,
|
|
294
|
+
prerelease: block.heading.prerelease,
|
|
295
|
+
summary: block.summary?.trim() || undefined,
|
|
296
|
+
sections: block.sections.map((section) => ({
|
|
297
|
+
type: mapSectionType(section.label),
|
|
298
|
+
label: section.label,
|
|
299
|
+
entries: section.entries,
|
|
300
|
+
})),
|
|
301
|
+
compareUrl: versionLinkMap.get(block.heading.key),
|
|
302
|
+
sourceOrder: block.sourceOrder,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function validateDuplicates(versions, diagnostics) {
|
|
306
|
+
const seen = new Map();
|
|
307
|
+
for (const version of versions) {
|
|
308
|
+
const key = version.version ?? "unreleased";
|
|
309
|
+
const existing = seen.get(key);
|
|
310
|
+
if (existing) {
|
|
311
|
+
diagnostics.push({
|
|
312
|
+
severity: "error",
|
|
313
|
+
code: "CHG005_DUPLICATE_VERSION",
|
|
314
|
+
message: `Version ${key === "unreleased" ? "Unreleased" : key} appears more than once.`,
|
|
315
|
+
version: version.version ?? "Unreleased",
|
|
316
|
+
});
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
seen.set(key, version);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function validateSourceOrder(versions, diagnostics) {
|
|
323
|
+
const releaseVersions = versions.filter((version) => version.version);
|
|
324
|
+
for (let i = 1; i < releaseVersions.length; i += 1) {
|
|
325
|
+
const prev = releaseVersions[i - 1];
|
|
326
|
+
const current = releaseVersions[i];
|
|
327
|
+
if (!prev.version || !current.version)
|
|
328
|
+
continue;
|
|
329
|
+
if (compareSemverDesc(prev.version, current.version) > 0)
|
|
330
|
+
continue;
|
|
331
|
+
diagnostics.push({
|
|
332
|
+
severity: "info",
|
|
333
|
+
code: "CHG004_OUT_OF_ORDER",
|
|
334
|
+
message: `Version ${current.version} appears after ${prev.version}, but is newer by semver.`,
|
|
335
|
+
version: current.version,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function detectFormat(blocks) {
|
|
340
|
+
const kinds = new Set();
|
|
341
|
+
let conventionalHeading = false;
|
|
342
|
+
for (const block of blocks) {
|
|
343
|
+
for (const kind of block.rawSectionKinds)
|
|
344
|
+
kinds.add(kind);
|
|
345
|
+
if (/\]\([^)]*\)\s*\([^)]*\)/.test(block.rawHeading))
|
|
346
|
+
conventionalHeading = true;
|
|
347
|
+
}
|
|
348
|
+
if (conventionalHeading || kinds.has("conventional"))
|
|
349
|
+
return "conventional";
|
|
350
|
+
if (kinds.has("standard") && !kinds.has("unknown") && !kinds.has("implicit"))
|
|
351
|
+
return "keepachangelog";
|
|
352
|
+
return "loose";
|
|
353
|
+
}
|
|
354
|
+
function resolveSectionKind(label) {
|
|
355
|
+
const normalized = normalizeSectionLabel(label);
|
|
356
|
+
if (STANDARD_SECTION_TYPES[normalized])
|
|
357
|
+
return "standard";
|
|
358
|
+
if (CONVENTIONAL_SECTION_TYPES[normalized])
|
|
359
|
+
return "conventional";
|
|
360
|
+
return "unknown";
|
|
361
|
+
}
|
|
362
|
+
function mapSectionType(label) {
|
|
363
|
+
const normalized = normalizeSectionLabel(label);
|
|
364
|
+
return STANDARD_SECTION_TYPES[normalized]
|
|
365
|
+
?? CONVENTIONAL_SECTION_TYPES[normalized]
|
|
366
|
+
?? "other";
|
|
367
|
+
}
|
|
368
|
+
function normalizeSectionLabel(label) {
|
|
369
|
+
return label
|
|
370
|
+
.trim()
|
|
371
|
+
.toLowerCase()
|
|
372
|
+
.replace(/[:\s]+$/g, "")
|
|
373
|
+
.replace(/\s+/g, " ");
|
|
374
|
+
}
|
|
375
|
+
function joinMarkdownFragments(parts) {
|
|
376
|
+
return parts
|
|
377
|
+
.map((part) => part.trim())
|
|
378
|
+
.filter(Boolean)
|
|
379
|
+
.join("\n\n")
|
|
380
|
+
.trim();
|
|
381
|
+
}
|
|
382
|
+
function parseVersionHeading(text) {
|
|
383
|
+
let value = text.trim();
|
|
384
|
+
if (!value)
|
|
385
|
+
return null;
|
|
386
|
+
let yanked = false;
|
|
387
|
+
if (/\s+\[yanked\]\s*$/i.test(value)) {
|
|
388
|
+
yanked = true;
|
|
389
|
+
value = value.replace(/\s+\[yanked\]\s*$/i, "").trim();
|
|
390
|
+
}
|
|
391
|
+
if (/^\[?unreleased\]?$/i.test(value)) {
|
|
392
|
+
return {
|
|
393
|
+
version: null,
|
|
394
|
+
date: null,
|
|
395
|
+
yanked,
|
|
396
|
+
prerelease: false,
|
|
397
|
+
key: "unreleased",
|
|
398
|
+
label: "Unreleased",
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
const conventional = value.match(/^\[?v?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)\]?\s*\(([^)]+)\)$/);
|
|
402
|
+
if (conventional) {
|
|
403
|
+
const version = conventional[1];
|
|
404
|
+
return buildParsedVersion(version, conventional[2], yanked);
|
|
405
|
+
}
|
|
406
|
+
const bracketed = value.match(/^\[?v?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)\]?$/);
|
|
407
|
+
if (bracketed) {
|
|
408
|
+
return buildParsedVersion(bracketed[1], null, yanked);
|
|
409
|
+
}
|
|
410
|
+
const match = value.match(/^\[?v?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)\]?\s*(?:[-—]\s*(.+)|\(([^)]+)\))?$/);
|
|
411
|
+
if (!match)
|
|
412
|
+
return null;
|
|
413
|
+
const version = match[1];
|
|
414
|
+
const dateText = match[2]?.trim() || match[3]?.trim() || null;
|
|
415
|
+
return buildParsedVersion(version, dateText, yanked);
|
|
416
|
+
}
|
|
417
|
+
function buildParsedVersion(version, dateText, yanked) {
|
|
418
|
+
return {
|
|
419
|
+
version,
|
|
420
|
+
date: parseDate(dateText),
|
|
421
|
+
yanked,
|
|
422
|
+
prerelease: /-/.test(version),
|
|
423
|
+
key: normalizeVersionKey(version),
|
|
424
|
+
label: version,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
function parseDate(input) {
|
|
428
|
+
if (!input)
|
|
429
|
+
return null;
|
|
430
|
+
const value = input.trim();
|
|
431
|
+
const iso = value.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
432
|
+
if (iso)
|
|
433
|
+
return `${iso[1]}-${iso[2]}-${iso[3]}`;
|
|
434
|
+
const slash = value.match(/^(\d{4})\/(\d{2})\/(\d{2})$/);
|
|
435
|
+
if (slash)
|
|
436
|
+
return `${slash[1]}-${slash[2]}-${slash[3]}`;
|
|
437
|
+
const namedOne = value.match(/^(\d{1,2})\s+([A-Za-z]+)\s+(\d{4})$/);
|
|
438
|
+
if (namedOne) {
|
|
439
|
+
const month = monthNumber(namedOne[2]);
|
|
440
|
+
if (!month)
|
|
441
|
+
return null;
|
|
442
|
+
return `${namedOne[3]}-${month}-${pad2(Number(namedOne[1]))}`;
|
|
443
|
+
}
|
|
444
|
+
const namedTwo = value.match(/^([A-Za-z]+)\s+(\d{1,2}),\s*(\d{4})$/);
|
|
445
|
+
if (namedTwo) {
|
|
446
|
+
const month = monthNumber(namedTwo[1]);
|
|
447
|
+
if (!month)
|
|
448
|
+
return null;
|
|
449
|
+
return `${namedTwo[3]}-${month}-${pad2(Number(namedTwo[2]))}`;
|
|
450
|
+
}
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
function monthNumber(month) {
|
|
454
|
+
const months = [
|
|
455
|
+
"january",
|
|
456
|
+
"february",
|
|
457
|
+
"march",
|
|
458
|
+
"april",
|
|
459
|
+
"may",
|
|
460
|
+
"june",
|
|
461
|
+
"july",
|
|
462
|
+
"august",
|
|
463
|
+
"september",
|
|
464
|
+
"october",
|
|
465
|
+
"november",
|
|
466
|
+
"december",
|
|
467
|
+
];
|
|
468
|
+
const index = months.indexOf(month.toLowerCase());
|
|
469
|
+
if (index === -1)
|
|
470
|
+
return null;
|
|
471
|
+
return pad2(index + 1);
|
|
472
|
+
}
|
|
473
|
+
function pad2(value) {
|
|
474
|
+
return value.toString().padStart(2, "0");
|
|
475
|
+
}
|
|
476
|
+
function normalizeVersionKey(version) {
|
|
477
|
+
const cleaned = version.trim().replace(/^\[|\]$/g, "");
|
|
478
|
+
if (/^unreleased$/i.test(cleaned))
|
|
479
|
+
return "unreleased";
|
|
480
|
+
return cleaned.replace(/^v/i, "").toLowerCase();
|
|
481
|
+
}
|
|
482
|
+
function stripHtml(html) {
|
|
483
|
+
return html.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
484
|
+
}
|
|
485
|
+
function parseSemver(value) {
|
|
486
|
+
const match = value.match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/);
|
|
487
|
+
if (!match)
|
|
488
|
+
return null;
|
|
489
|
+
return {
|
|
490
|
+
major: Number(match[1]),
|
|
491
|
+
minor: Number(match[2]),
|
|
492
|
+
patch: Number(match[3]),
|
|
493
|
+
prerelease: match[4] ? match[4].split(".") : [],
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
function compareSemverDesc(left, right) {
|
|
497
|
+
const a = parseSemver(left);
|
|
498
|
+
const b = parseSemver(right);
|
|
499
|
+
if (!a || !b)
|
|
500
|
+
return 0;
|
|
501
|
+
if (a.major !== b.major)
|
|
502
|
+
return a.major > b.major ? 1 : -1;
|
|
503
|
+
if (a.minor !== b.minor)
|
|
504
|
+
return a.minor > b.minor ? 1 : -1;
|
|
505
|
+
if (a.patch !== b.patch)
|
|
506
|
+
return a.patch > b.patch ? 1 : -1;
|
|
507
|
+
if (!a.prerelease.length && b.prerelease.length)
|
|
508
|
+
return 1;
|
|
509
|
+
if (a.prerelease.length && !b.prerelease.length)
|
|
510
|
+
return -1;
|
|
511
|
+
const length = Math.max(a.prerelease.length, b.prerelease.length);
|
|
512
|
+
for (let i = 0; i < length; i += 1) {
|
|
513
|
+
const leftPart = a.prerelease[i];
|
|
514
|
+
const rightPart = b.prerelease[i];
|
|
515
|
+
if (leftPart === undefined)
|
|
516
|
+
return -1;
|
|
517
|
+
if (rightPart === undefined)
|
|
518
|
+
return 1;
|
|
519
|
+
const leftNum = Number(leftPart);
|
|
520
|
+
const rightNum = Number(rightPart);
|
|
521
|
+
const leftIsNum = Number.isInteger(leftNum) && leftPart === String(leftNum);
|
|
522
|
+
const rightIsNum = Number.isInteger(rightNum) && rightPart === String(rightNum);
|
|
523
|
+
if (leftIsNum && rightIsNum && leftNum !== rightNum)
|
|
524
|
+
return leftNum > rightNum ? 1 : -1;
|
|
525
|
+
if (leftIsNum !== rightIsNum)
|
|
526
|
+
return leftIsNum ? -1 : 1;
|
|
527
|
+
if (leftPart !== rightPart)
|
|
528
|
+
return leftPart > rightPart ? 1 : -1;
|
|
529
|
+
}
|
|
530
|
+
return 0;
|
|
531
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doxygen-loader.d.ts","sourceRoot":"","sources":["../../src/core/doxygen-loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAqB,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAA6B,MAAM,iBAAiB,CAAC;AAM1E,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMzF;AAED,wBAAgB,kCAAkC,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAoB9E;AAED,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIxE;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAShE;AAED,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQrE;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"doxygen-loader.d.ts","sourceRoot":"","sources":["../../src/core/doxygen-loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAqB,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAA6B,MAAM,iBAAiB,CAAC;AAM1E,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMzF;AAED,wBAAgB,kCAAkC,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAoB9E;AAED,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIxE;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAShE;AAED,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQrE;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,qBAAqB,EAC7B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,CAAC,CAkGxB"}
|
|
@@ -65,6 +65,7 @@ export async function loadDoxygenTab(config, tabSlug, tabLabel) {
|
|
|
65
65
|
const html = rewriteGeneratedDoxygenHtmlLinks(renderMarkdown(markdown));
|
|
66
66
|
const headings = extractHeadings(markdown);
|
|
67
67
|
pages.set(page.slug, {
|
|
68
|
+
kind: "markdown",
|
|
68
69
|
title: page.title,
|
|
69
70
|
description,
|
|
70
71
|
slug: page.slug,
|
|
@@ -112,6 +113,7 @@ export async function loadDoxygenTab(config, tabSlug, tabLabel) {
|
|
|
112
113
|
const indexHtml = buildIndexHtml(indexStyle, tabSlug, tabLabel, groupMap, pages);
|
|
113
114
|
const indexHeadings = indexStyle === "rich" ? [] : extractHeadings(indexHtml);
|
|
114
115
|
pages.set(indexSlug, {
|
|
116
|
+
kind: "markdown",
|
|
115
117
|
title: tabLabel,
|
|
116
118
|
description: "",
|
|
117
119
|
slug: indexSlug,
|
package/dist/core/loader.js
CHANGED
|
@@ -79,6 +79,8 @@ function detectVersion(raw) {
|
|
|
79
79
|
return "swagger-2.0";
|
|
80
80
|
}
|
|
81
81
|
if (typeof raw.openapi === "string") {
|
|
82
|
+
if (raw.openapi.startsWith("3.2"))
|
|
83
|
+
return "openapi-3.2";
|
|
82
84
|
if (raw.openapi.startsWith("3.1"))
|
|
83
85
|
return "openapi-3.1";
|
|
84
86
|
if (raw.openapi.startsWith("3."))
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type PageHeading } from "../utils/markdown.js";
|
|
2
|
+
import type { NormalizedChangelog } from "./types.js";
|
|
2
3
|
export interface MarkdownPage {
|
|
4
|
+
kind: "markdown";
|
|
3
5
|
/** Page title from frontmatter */
|
|
4
6
|
title: string;
|
|
5
7
|
/** Page description from frontmatter */
|
|
@@ -15,11 +17,30 @@ export interface MarkdownPage {
|
|
|
15
17
|
/** Optional repo-relative path used for "Edit this page" links. Null disables the link. */
|
|
16
18
|
editPath?: string | null;
|
|
17
19
|
}
|
|
20
|
+
export interface ChangelogPage {
|
|
21
|
+
kind: "changelog";
|
|
22
|
+
title: string;
|
|
23
|
+
description: string;
|
|
24
|
+
slug: string;
|
|
25
|
+
headings: PageHeading[];
|
|
26
|
+
sourcePath: string;
|
|
27
|
+
editPath?: string | null;
|
|
28
|
+
changelog: NormalizedChangelog;
|
|
29
|
+
rawBody: string;
|
|
30
|
+
/** Set on generated per-version pages; undefined for the full changelog page. */
|
|
31
|
+
permalinkVersionId?: string;
|
|
32
|
+
}
|
|
33
|
+
export type DocsPage = MarkdownPage | ChangelogPage;
|
|
18
34
|
export type { PageHeading } from "../utils/markdown.js";
|
|
35
|
+
export interface LoadDocsPageOptions {
|
|
36
|
+
changelog?: boolean;
|
|
37
|
+
repoUrl?: string;
|
|
38
|
+
}
|
|
19
39
|
/**
|
|
20
40
|
* Load a single markdown file and return a MarkdownPage.
|
|
21
41
|
*/
|
|
22
42
|
export declare function loadMarkdownPage(filePath: string, slug: string): Promise<MarkdownPage>;
|
|
43
|
+
export declare function loadDocsPage(filePath: string, slug: string, options?: LoadDocsPageOptions): Promise<DocsPage>;
|
|
23
44
|
/**
|
|
24
45
|
* Derive a URL slug from a page path.
|
|
25
46
|
* Preserves directory structure to avoid collisions:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-loader.d.ts","sourceRoot":"","sources":["../../src/core/markdown-loader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"markdown-loader.d.ts","sourceRoot":"","sources":["../../src/core/markdown-loader.ts"],"names":[],"mappings":"AAMA,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAMtD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,UAAU,CAAC;IACjB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,mBAAmB,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,QAAQ,GAAG,YAAY,GAAG,aAAa,CAAC;AAEpD,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAUxD,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAgnCD;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,YAAY,CAAC,CAKvB;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,QAAQ,CAAC,CAiCnB;AAuDD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIrD"}
|