@clarify-labs/cli 0.11.18 → 0.11.20
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/output/cli.cjs +1 -1
- package/output/cli.js +1 -1
- package/output/core/engine/engine.d.ts +3 -2
- package/output/{engine-DeA6EGHW.js → engine-BgfGFPKK.js} +741 -125
- package/output/engine-BgfGFPKK.js.map +1 -0
- package/output/{engine-CNnlt59g.cjs → engine-CAis1rme.cjs} +741 -126
- package/output/engine-CAis1rme.cjs.map +1 -0
- package/output/index.cjs +1 -1
- package/output/index.js +1 -1
- package/output/parsers/content/content.d.ts +1 -0
- package/output/parsers/markdown/frontmatter.d.ts +1 -0
- package/output/parsers/markdown/headings.d.ts +24 -0
- package/output/parsers/markdown/mdx.d.ts +1 -0
- package/output/parsers/routes/heading-metadata.d.ts +7 -0
- package/output/parsers/routes/routes.d.ts +2 -1
- package/output/plugins/content-artifacts/provider.d.ts +3 -0
- package/output/plugins/openapi/aggregation.d.ts +12 -0
- package/output/plugins/openapi/artifacts.d.ts +6 -0
- package/output/plugins/openapi/parser.d.ts +1 -1
- package/output/plugins/openapi/virtual-modules.d.ts +1 -0
- package/output/types.d.ts +12 -1
- package/package.json +3 -3
- package/output/engine-CNnlt59g.cjs.map +0 -1
- package/output/engine-DeA6EGHW.js.map +0 -1
|
@@ -3,16 +3,13 @@ import mdxPlugin from "@mdx-js/rollup";
|
|
|
3
3
|
import tailwindcss from "@tailwindcss/vite";
|
|
4
4
|
import react from "@vitejs/plugin-react";
|
|
5
5
|
import rehypeRaw from "rehype-raw";
|
|
6
|
-
import { compile } from "@mdx-js/mdx";
|
|
6
|
+
import { compile, createProcessor } from "@mdx-js/mdx";
|
|
7
|
+
import GithubSlugger, { slug } from "github-slugger";
|
|
8
|
+
import { markdownRemarkPlugins, parseCodeMeta, themeBootstrapScript, themePresets } from "@clarify-labs/renderer";
|
|
7
9
|
import rehypeShiki from "@shikijs/rehype";
|
|
8
|
-
import rehypeSlug from "rehype-slug";
|
|
9
10
|
import { createCssVariablesTheme } from "shiki";
|
|
10
11
|
import { visit } from "unist-util-visit";
|
|
11
|
-
import { markdownRemarkPlugins, parseCodeMeta, themeBootstrapScript, themePresets } from "@clarify-labs/renderer";
|
|
12
12
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
13
|
-
import GithubSlugger, { slug } from "github-slugger";
|
|
14
|
-
import { toString } from "mdast-util-to-string";
|
|
15
|
-
import { remark } from "remark";
|
|
16
13
|
import matter from "gray-matter";
|
|
17
14
|
import { spawnSync } from "node:child_process";
|
|
18
15
|
import { createReadStream, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
@@ -45,6 +42,402 @@ function createContentDiagnostic(options) {
|
|
|
45
42
|
};
|
|
46
43
|
}
|
|
47
44
|
//#endregion
|
|
45
|
+
//#region source/parsers/markdown/headings.ts
|
|
46
|
+
var HEADING_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
47
|
+
var INTERNAL_HEADING_ID_PREFIX = "clarify-internal-heading-id:";
|
|
48
|
+
var EXPLICIT_ID_PATTERN = /\s+\{#([^{}]*)\}$/;
|
|
49
|
+
var HEADING_REMARK_PLUGINS = markdownRemarkPlugins;
|
|
50
|
+
function visitHeadings(node, callback) {
|
|
51
|
+
if (node.type === "heading") callback(node);
|
|
52
|
+
for (const child of node.children ?? []) visitHeadings(child, callback);
|
|
53
|
+
}
|
|
54
|
+
function visitHtml(node, callback) {
|
|
55
|
+
if (node.type === "html") callback(node);
|
|
56
|
+
for (const child of node.children ?? []) visitHtml(child, callback);
|
|
57
|
+
}
|
|
58
|
+
function visitMdxIntrinsicHeadings(node, callback) {
|
|
59
|
+
if ((node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") && node.name && /^h[1-6]$/.test(node.name)) callback(node);
|
|
60
|
+
for (const child of node.children ?? []) visitMdxIntrinsicHeadings(child, callback);
|
|
61
|
+
}
|
|
62
|
+
function visitHastHeadings(node, callback) {
|
|
63
|
+
if (node.type === "element" && node.tagName && /^h[1-6]$/.test(node.tagName)) callback(node);
|
|
64
|
+
for (const child of node.children ?? []) visitHastHeadings(child, callback);
|
|
65
|
+
}
|
|
66
|
+
function hastText$1(node) {
|
|
67
|
+
if (node.type === "text") return node.value ?? "";
|
|
68
|
+
return (node.children ?? []).map(hastText$1).join("");
|
|
69
|
+
}
|
|
70
|
+
function applySourceEdits(content, edits) {
|
|
71
|
+
return edits.sort((left, right) => right.start - left.start).reduce((result, edit) => result.slice(0, edit.start) + edit.replacement + result.slice(edit.end), content);
|
|
72
|
+
}
|
|
73
|
+
function headingPosition(node, lineOffset) {
|
|
74
|
+
return {
|
|
75
|
+
line: (node.position?.start.line ?? 1) + lineOffset,
|
|
76
|
+
column: node.position?.start.column ?? 1
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function formatPosition(position) {
|
|
80
|
+
return `${position.line}:${position.column}`;
|
|
81
|
+
}
|
|
82
|
+
function finalTextNode(heading) {
|
|
83
|
+
const node = heading.children?.at(-1);
|
|
84
|
+
return node?.type === "text" && typeof node.value === "string" ? node : void 0;
|
|
85
|
+
}
|
|
86
|
+
function contentEndOffset(heading) {
|
|
87
|
+
return (heading.children?.at(-1))?.position?.end.offset ?? heading.position?.end.offset;
|
|
88
|
+
}
|
|
89
|
+
function explicitIdInfo(heading, content) {
|
|
90
|
+
const text = finalTextNode(heading);
|
|
91
|
+
if (!text?.position) return void 0;
|
|
92
|
+
const startOffset = text.position.start.offset;
|
|
93
|
+
const endOffset = text.position.end.offset;
|
|
94
|
+
if (startOffset === void 0 || endOffset === void 0) return void 0;
|
|
95
|
+
const match = content.slice(startOffset, endOffset).match(EXPLICIT_ID_PATTERN);
|
|
96
|
+
if (!match || match.index === void 0) return void 0;
|
|
97
|
+
return {
|
|
98
|
+
id: match[1] ?? "",
|
|
99
|
+
start: startOffset + match.index,
|
|
100
|
+
end: startOffset + match.index + match[0].length
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function maskExplicitHeadingMarkers(content) {
|
|
104
|
+
const tree = createProcessor({
|
|
105
|
+
format: "md",
|
|
106
|
+
remarkPlugins: HEADING_REMARK_PLUGINS
|
|
107
|
+
}).parse(content);
|
|
108
|
+
const edits = [];
|
|
109
|
+
visitHeadings(tree, (heading) => {
|
|
110
|
+
const marker = explicitIdInfo(heading, content);
|
|
111
|
+
if (marker) edits.push({
|
|
112
|
+
start: marker.start,
|
|
113
|
+
end: marker.end,
|
|
114
|
+
replacement: "x".repeat(marker.end - marker.start)
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
return applySourceEdits(content, edits);
|
|
118
|
+
}
|
|
119
|
+
function parseHeadingTree(content, kind) {
|
|
120
|
+
if (kind === "markdown") return createProcessor({
|
|
121
|
+
format: "md",
|
|
122
|
+
remarkPlugins: HEADING_REMARK_PLUGINS
|
|
123
|
+
}).parse(content);
|
|
124
|
+
const maskedContent = maskExplicitHeadingMarkers(content);
|
|
125
|
+
try {
|
|
126
|
+
return createProcessor({
|
|
127
|
+
format: "mdx",
|
|
128
|
+
remarkPlugins: HEADING_REMARK_PLUGINS
|
|
129
|
+
}).parse(maskedContent);
|
|
130
|
+
} catch {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function renderedMarkdownHeadingTitles(tree, content, kind) {
|
|
135
|
+
const originalText = /* @__PURE__ */ new Map();
|
|
136
|
+
visitHeadings(tree, (heading) => {
|
|
137
|
+
const marker = explicitIdInfo(heading, content);
|
|
138
|
+
const text = finalTextNode(heading);
|
|
139
|
+
if (!marker || !text || text.value === void 0) return;
|
|
140
|
+
originalText.set(text, text.value);
|
|
141
|
+
text.value = text.value.slice(0, -(marker.end - marker.start)).replace(/\s+$/, "");
|
|
142
|
+
});
|
|
143
|
+
try {
|
|
144
|
+
let hast;
|
|
145
|
+
const captureHast = () => (tree) => {
|
|
146
|
+
hast = tree;
|
|
147
|
+
};
|
|
148
|
+
createProcessor({
|
|
149
|
+
format: kind === "markdown" ? "md" : "mdx",
|
|
150
|
+
remarkPlugins: HEADING_REMARK_PLUGINS,
|
|
151
|
+
...kind === "markdown" ? { remarkRehypeOptions: { allowDangerousHtml: true } } : {},
|
|
152
|
+
rehypePlugins: kind === "markdown" ? [rehypeRaw, captureHast] : [captureHast]
|
|
153
|
+
}).runSync(tree, { value: content });
|
|
154
|
+
const titles = /* @__PURE__ */ new Map();
|
|
155
|
+
if (!hast) return titles;
|
|
156
|
+
visitHastHeadings(hast, (heading) => {
|
|
157
|
+
const offset = heading.position?.start.offset;
|
|
158
|
+
if (offset !== void 0) titles.set(offset, hastText$1(heading));
|
|
159
|
+
});
|
|
160
|
+
return titles;
|
|
161
|
+
} finally {
|
|
162
|
+
for (const [text, value] of originalText) text.value = value;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function isHtmlWhitespace(character) {
|
|
166
|
+
return character === " " || character === "\n" || character === "\f" || character === "\r" || character === " ";
|
|
167
|
+
}
|
|
168
|
+
function isMdxWhitespace(character) {
|
|
169
|
+
return /\s/u.test(character ?? "");
|
|
170
|
+
}
|
|
171
|
+
function openingTagAttributeOffset(content, start, end, isWhitespace) {
|
|
172
|
+
let state = "tagName";
|
|
173
|
+
let quote;
|
|
174
|
+
for (let index = start; index < end; index++) {
|
|
175
|
+
const character = content[index];
|
|
176
|
+
const closesTag = character === ">";
|
|
177
|
+
const selfClosesTag = character === "/" && content[index + 1] === ">";
|
|
178
|
+
if (state === "quotedAttributeValue") {
|
|
179
|
+
if (character === quote) {
|
|
180
|
+
quote = void 0;
|
|
181
|
+
state = "afterQuotedAttributeValue";
|
|
182
|
+
}
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (state === "unquotedAttributeValue") {
|
|
186
|
+
if (closesTag) return index;
|
|
187
|
+
if (isWhitespace(character)) state = "beforeAttributeName";
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (closesTag) return index;
|
|
191
|
+
if (selfClosesTag && state !== "beforeAttributeValue") {
|
|
192
|
+
let cursor = index;
|
|
193
|
+
while (cursor > start && isWhitespace(content[cursor - 1])) cursor--;
|
|
194
|
+
return cursor;
|
|
195
|
+
}
|
|
196
|
+
if (state === "tagName") {
|
|
197
|
+
if (isWhitespace(character)) state = "beforeAttributeName";
|
|
198
|
+
} else if (state === "beforeAttributeName") {
|
|
199
|
+
if (!isWhitespace(character)) state = "attributeName";
|
|
200
|
+
} else if (state === "attributeName") {
|
|
201
|
+
if (character === "=") state = "beforeAttributeValue";
|
|
202
|
+
else if (isWhitespace(character)) state = "afterAttributeName";
|
|
203
|
+
} else if (state === "afterAttributeName") {
|
|
204
|
+
if (character === "=") state = "beforeAttributeValue";
|
|
205
|
+
else if (!isWhitespace(character)) state = "attributeName";
|
|
206
|
+
} else if (state === "beforeAttributeValue") {
|
|
207
|
+
if (character === "\"" || character === "'") {
|
|
208
|
+
quote = character;
|
|
209
|
+
state = "quotedAttributeValue";
|
|
210
|
+
} else if (!isWhitespace(character)) state = "unquotedAttributeValue";
|
|
211
|
+
} else if (state === "afterQuotedAttributeValue" && isWhitespace(character)) state = "beforeAttributeName";
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function rawHtmlHeadings(tree, content, lineOffset) {
|
|
215
|
+
let hasRawHtml = false;
|
|
216
|
+
visitHtml(tree, () => {
|
|
217
|
+
hasRawHtml = true;
|
|
218
|
+
});
|
|
219
|
+
if (!hasRawHtml) return [];
|
|
220
|
+
const markdownOffsets = /* @__PURE__ */ new Set();
|
|
221
|
+
visitHeadings(tree, (heading) => {
|
|
222
|
+
const offset = heading.position?.start.offset;
|
|
223
|
+
if (offset !== void 0) markdownOffsets.add(offset);
|
|
224
|
+
});
|
|
225
|
+
let hast;
|
|
226
|
+
const captureHast = () => (tree) => {
|
|
227
|
+
hast = tree;
|
|
228
|
+
};
|
|
229
|
+
createProcessor({
|
|
230
|
+
format: "md",
|
|
231
|
+
remarkRehypeOptions: { allowDangerousHtml: true },
|
|
232
|
+
rehypePlugins: [rehypeRaw, captureHast]
|
|
233
|
+
}).runSync(tree, { value: content });
|
|
234
|
+
if (!hast) return [];
|
|
235
|
+
const headings = [];
|
|
236
|
+
visitHastHeadings(hast, (heading) => {
|
|
237
|
+
const offset = heading.position?.start.offset;
|
|
238
|
+
const headingEnd = heading.position?.end.offset;
|
|
239
|
+
if (offset === void 0 || headingEnd === void 0 || markdownOffsets.has(offset)) return;
|
|
240
|
+
const tagEnd = openingTagAttributeOffset(content, offset, headingEnd, isHtmlWhitespace);
|
|
241
|
+
if (tagEnd === void 0) return;
|
|
242
|
+
const rawId = heading.properties?.id;
|
|
243
|
+
headings.push({
|
|
244
|
+
title: hastText$1(heading),
|
|
245
|
+
...typeof rawId === "string" && rawId ? { id: rawId } : {},
|
|
246
|
+
position: {
|
|
247
|
+
line: (heading.position?.start.line ?? 1) + lineOffset,
|
|
248
|
+
column: heading.position?.start.column ?? 1
|
|
249
|
+
},
|
|
250
|
+
offset,
|
|
251
|
+
attributeOffset: tagEnd
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
return headings;
|
|
255
|
+
}
|
|
256
|
+
function staticMdxText(node) {
|
|
257
|
+
if (node.type === "text" || node.type === "inlineCode") return node.value ?? "";
|
|
258
|
+
if (node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") {
|
|
259
|
+
if (!node.name || !/^[a-z][a-z0-9-]*$/.test(node.name)) return void 0;
|
|
260
|
+
} else if (!node.children) return;
|
|
261
|
+
const parts = [];
|
|
262
|
+
for (const child of node.children ?? []) {
|
|
263
|
+
const text = staticMdxText(child);
|
|
264
|
+
if (text === void 0) return void 0;
|
|
265
|
+
parts.push(text);
|
|
266
|
+
}
|
|
267
|
+
return parts.join("");
|
|
268
|
+
}
|
|
269
|
+
function literalMdxId(node) {
|
|
270
|
+
let result = { kind: "missing" };
|
|
271
|
+
for (const attribute of node.attributes ?? []) {
|
|
272
|
+
if (attribute.type !== "mdxJsxAttribute") {
|
|
273
|
+
result = { kind: "unknown" };
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (attribute.name !== "id") continue;
|
|
277
|
+
result = typeof attribute.value === "string" ? {
|
|
278
|
+
kind: "literal",
|
|
279
|
+
id: attribute.value
|
|
280
|
+
} : { kind: "unknown" };
|
|
281
|
+
}
|
|
282
|
+
return result;
|
|
283
|
+
}
|
|
284
|
+
function mdxIntrinsicHeadings(tree, content, lineOffset) {
|
|
285
|
+
const headings = [];
|
|
286
|
+
visitMdxIntrinsicHeadings(tree, (heading) => {
|
|
287
|
+
const position = heading.position;
|
|
288
|
+
const offset = position?.start.offset;
|
|
289
|
+
const end = position?.end.offset;
|
|
290
|
+
const title = staticMdxText(heading);
|
|
291
|
+
const literalId = literalMdxId(heading);
|
|
292
|
+
if (!position || offset === void 0 || end === void 0 || literalId.kind !== "literal") return;
|
|
293
|
+
const tagEnd = openingTagAttributeOffset(content, offset, end, isMdxWhitespace);
|
|
294
|
+
if (tagEnd === void 0) return;
|
|
295
|
+
headings.push({
|
|
296
|
+
...title !== void 0 ? { title } : {},
|
|
297
|
+
id: literalId.id,
|
|
298
|
+
position: {
|
|
299
|
+
line: position.start.line + lineOffset,
|
|
300
|
+
column: position.start.column
|
|
301
|
+
},
|
|
302
|
+
offset,
|
|
303
|
+
attributeOffset: tagEnd
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
return headings;
|
|
307
|
+
}
|
|
308
|
+
function escapeHtmlAttribute(value) {
|
|
309
|
+
return value.replaceAll("&", "&").replaceAll("\"", """);
|
|
310
|
+
}
|
|
311
|
+
function analyzeHeadings(content, options) {
|
|
312
|
+
const lineOffset = options.lineOffset ?? 0;
|
|
313
|
+
const headings = [];
|
|
314
|
+
const edits = [];
|
|
315
|
+
const errors = [];
|
|
316
|
+
const ids = /* @__PURE__ */ new Map();
|
|
317
|
+
const documentHeadings = [];
|
|
318
|
+
const markdownHeadingInputs = /* @__PURE__ */ new Map();
|
|
319
|
+
const tree = parseHeadingTree(content, options.kind);
|
|
320
|
+
if (!tree) return {
|
|
321
|
+
headings,
|
|
322
|
+
normalizedContent: content
|
|
323
|
+
};
|
|
324
|
+
const renderedTitles = renderedMarkdownHeadingTitles(tree, content, options.kind);
|
|
325
|
+
const claimId = (id, position) => {
|
|
326
|
+
const owner = ids.get(id);
|
|
327
|
+
if (owner) {
|
|
328
|
+
errors.push(`Heading ID "${id}" at ${formatPosition(position)} conflicts with the heading at ${formatPosition(owner.position)}.`);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
ids.set(id, {
|
|
332
|
+
id,
|
|
333
|
+
position
|
|
334
|
+
});
|
|
335
|
+
};
|
|
336
|
+
visitHeadings(tree, (node) => {
|
|
337
|
+
const marker = explicitIdInfo(node, content);
|
|
338
|
+
const title = renderedTitles.get(node.position?.start.offset ?? 0) ?? "";
|
|
339
|
+
const hasValidExplicitId = marker !== void 0 && HEADING_ID_PATTERN.test(marker.id);
|
|
340
|
+
const heading = {
|
|
341
|
+
level: node.depth ?? 1,
|
|
342
|
+
title,
|
|
343
|
+
id: hasValidExplicitId ? marker.id : "",
|
|
344
|
+
position: headingPosition(node, lineOffset)
|
|
345
|
+
};
|
|
346
|
+
if (marker && !hasValidExplicitId) errors.push(`Invalid heading ID "${marker.id}" at ${formatPosition(heading.position)}. IDs must match [a-z0-9]+(?:-[a-z0-9]+)*.`);
|
|
347
|
+
headings.push(heading);
|
|
348
|
+
markdownHeadingInputs.set(heading, {
|
|
349
|
+
...marker ? { marker } : {},
|
|
350
|
+
hasValidExplicitId,
|
|
351
|
+
contentEnd: contentEndOffset(node)
|
|
352
|
+
});
|
|
353
|
+
documentHeadings.push({
|
|
354
|
+
title,
|
|
355
|
+
offset: node.position?.start.offset ?? 0,
|
|
356
|
+
markdown: heading
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
const renderedHeadings = options.kind === "markdown" ? rawHtmlHeadings(tree, content, lineOffset) : mdxIntrinsicHeadings(tree, content, lineOffset);
|
|
360
|
+
for (const rendered of renderedHeadings) documentHeadings.push({
|
|
361
|
+
...rendered.title !== void 0 ? { title: rendered.title } : {},
|
|
362
|
+
offset: rendered.offset,
|
|
363
|
+
rendered
|
|
364
|
+
});
|
|
365
|
+
documentHeadings.sort((left, right) => left.offset - right.offset);
|
|
366
|
+
const slugger = new GithubSlugger();
|
|
367
|
+
for (const documentHeading of documentHeadings) {
|
|
368
|
+
if (documentHeading.markdown) {
|
|
369
|
+
const heading = documentHeading.markdown;
|
|
370
|
+
const input = markdownHeadingInputs.get(heading);
|
|
371
|
+
if (input.hasValidExplicitId) {
|
|
372
|
+
edits.push({
|
|
373
|
+
start: input.marker.start,
|
|
374
|
+
end: input.marker.end,
|
|
375
|
+
replacement: ` [](clarify-internal-heading-id:${heading.id})`
|
|
376
|
+
});
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (documentHeading.rendered?.id !== void 0) continue;
|
|
381
|
+
if (documentHeading.title === void 0) continue;
|
|
382
|
+
const generatedId = slugger.slug(documentHeading.title);
|
|
383
|
+
if (documentHeading.markdown) {
|
|
384
|
+
const heading = documentHeading.markdown;
|
|
385
|
+
const input = markdownHeadingInputs.get(heading);
|
|
386
|
+
heading.id = generatedId;
|
|
387
|
+
const link = ` [](clarify-internal-heading-id:${heading.id})`;
|
|
388
|
+
if (input.marker) edits.push({
|
|
389
|
+
start: input.marker.start,
|
|
390
|
+
end: input.marker.end,
|
|
391
|
+
replacement: link
|
|
392
|
+
});
|
|
393
|
+
else if (input.contentEnd !== void 0) edits.push({
|
|
394
|
+
start: input.contentEnd,
|
|
395
|
+
end: input.contentEnd,
|
|
396
|
+
replacement: link
|
|
397
|
+
});
|
|
398
|
+
} else if (documentHeading.rendered && documentHeading.rendered.id === void 0) {
|
|
399
|
+
documentHeading.rendered.id = generatedId;
|
|
400
|
+
edits.push({
|
|
401
|
+
start: documentHeading.rendered.attributeOffset,
|
|
402
|
+
end: documentHeading.rendered.attributeOffset,
|
|
403
|
+
replacement: ` id="${escapeHtmlAttribute(generatedId)}"`
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
for (const documentHeading of documentHeadings) if (documentHeading.markdown) claimId(documentHeading.markdown.id, documentHeading.markdown.position);
|
|
408
|
+
else if (documentHeading.rendered?.id) claimId(documentHeading.rendered.id, documentHeading.rendered.position);
|
|
409
|
+
const diagnostic = errors.length === 0 ? void 0 : createContentDiagnostic({
|
|
410
|
+
kind: options.kind,
|
|
411
|
+
title: "Heading ID error",
|
|
412
|
+
message: "This page has invalid or conflicting heading IDs. Fix the IDs below, then reload the route.",
|
|
413
|
+
error: new Error(errors.join("\n")),
|
|
414
|
+
filePath: options.filePath,
|
|
415
|
+
projectRoot: options.projectRoot
|
|
416
|
+
});
|
|
417
|
+
return {
|
|
418
|
+
headings,
|
|
419
|
+
normalizedContent: applySourceEdits(content, edits),
|
|
420
|
+
diagnostic
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
function remarkApplyHeadingIds() {
|
|
424
|
+
return (tree) => {
|
|
425
|
+
visitHeadings(tree, (heading) => {
|
|
426
|
+
const finalNode = heading.children?.at(-1);
|
|
427
|
+
if (finalNode?.type !== "link" || finalNode.children?.length !== 0 || typeof finalNode.url !== "string" || !finalNode.url.startsWith(INTERNAL_HEADING_ID_PREFIX)) return;
|
|
428
|
+
const id = finalNode.url.slice(28);
|
|
429
|
+
heading.children?.pop();
|
|
430
|
+
const previousNode = heading.children?.at(-1);
|
|
431
|
+
if (previousNode?.type === "text" && typeof previousNode.value === "string") previousNode.value = previousNode.value.replace(/ $/, "");
|
|
432
|
+
heading.data = heading.data ?? {};
|
|
433
|
+
heading.data.hProperties = {
|
|
434
|
+
...heading.data.hProperties,
|
|
435
|
+
id
|
|
436
|
+
};
|
|
437
|
+
});
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
//#endregion
|
|
48
441
|
//#region source/parsers/markdown/mdx.ts
|
|
49
442
|
function getLanguage(className) {
|
|
50
443
|
return (Array.isArray(className) ? className : typeof className === "string" ? className.split(/\s+/) : []).find((item) => typeof item === "string" && item.startsWith("language-"))?.replace(/^language-/, "") || "txt";
|
|
@@ -67,9 +460,34 @@ function rehypeParseCodeBlocks() {
|
|
|
67
460
|
});
|
|
68
461
|
};
|
|
69
462
|
}
|
|
70
|
-
|
|
463
|
+
function hastText(node) {
|
|
464
|
+
if (node.type === "text") return node.value ?? "";
|
|
465
|
+
return (node.children ?? []).map(hastText).join("");
|
|
466
|
+
}
|
|
467
|
+
function isHeadingElement(node) {
|
|
468
|
+
return Boolean(node.tagName && /^h[1-6]$/.test(node.tagName));
|
|
469
|
+
}
|
|
470
|
+
function rehypeAddMissingHeadingIds() {
|
|
471
|
+
return (tree) => {
|
|
472
|
+
const slugger = new GithubSlugger();
|
|
473
|
+
const reservedIds = /* @__PURE__ */ new Set();
|
|
474
|
+
visit(tree, "element", (node) => {
|
|
475
|
+
if (isHeadingElement(node) && typeof node.properties?.id === "string" && node.properties.id) reservedIds.add(node.properties.id);
|
|
476
|
+
});
|
|
477
|
+
visit(tree, "element", (node) => {
|
|
478
|
+
if (!isHeadingElement(node)) return;
|
|
479
|
+
const title = hastText(node);
|
|
480
|
+
let fallbackId = slugger.slug(title);
|
|
481
|
+
if (node.properties?.id) return;
|
|
482
|
+
while (reservedIds.has(fallbackId)) fallbackId = slugger.slug(title);
|
|
483
|
+
node.properties = node.properties ?? {};
|
|
484
|
+
node.properties.id = fallbackId;
|
|
485
|
+
});
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
var remarkPlugins = [remarkApplyHeadingIds, ...markdownRemarkPlugins];
|
|
71
489
|
var rehypePlugins = [
|
|
72
|
-
|
|
490
|
+
rehypeAddMissingHeadingIds,
|
|
73
491
|
rehypeParseCodeBlocks,
|
|
74
492
|
[rehypeShiki, {
|
|
75
493
|
theme: cssVariablesTheme,
|
|
@@ -224,9 +642,12 @@ async function runBuildDoneHooks(plugins, ctx) {
|
|
|
224
642
|
*/
|
|
225
643
|
function parseFrontmatter(content) {
|
|
226
644
|
const parsed = matter(content);
|
|
645
|
+
const body = parsed.content.replace(/^\r?\n/, "");
|
|
646
|
+
const prefix = content.slice(0, content.length - body.length);
|
|
227
647
|
return {
|
|
228
648
|
frontmatter: parsed.data,
|
|
229
|
-
content:
|
|
649
|
+
content: body,
|
|
650
|
+
lineOffset: [...prefix.matchAll(/\r\n|\r|\n/g)].length
|
|
230
651
|
};
|
|
231
652
|
}
|
|
232
653
|
//#endregion
|
|
@@ -237,7 +658,7 @@ async function transformContent(transform, input) {
|
|
|
237
658
|
function createContentProcessor(transform) {
|
|
238
659
|
return {
|
|
239
660
|
async processMdx(source, filePath) {
|
|
240
|
-
const { frontmatter, content } = parseFrontmatter(source);
|
|
661
|
+
const { frontmatter, content, lineOffset } = parseFrontmatter(source);
|
|
241
662
|
const transformed = await transformContent(transform, {
|
|
242
663
|
kind: "markdown+jsx",
|
|
243
664
|
source,
|
|
@@ -247,7 +668,8 @@ function createContentProcessor(transform) {
|
|
|
247
668
|
});
|
|
248
669
|
return {
|
|
249
670
|
frontmatter: transformed.frontmatter,
|
|
250
|
-
content: transformed.content
|
|
671
|
+
content: transformed.content,
|
|
672
|
+
lineOffset
|
|
251
673
|
};
|
|
252
674
|
},
|
|
253
675
|
async processText(source, kind, filePath) {
|
|
@@ -357,13 +779,47 @@ function resolveUpdatedAt(frontmatter, detectedAt) {
|
|
|
357
779
|
return normalizeDate(typeof frontmatter.updatedAt === "string" ? frontmatter.updatedAt : void 0) ?? detectedAt;
|
|
358
780
|
}
|
|
359
781
|
//#endregion
|
|
360
|
-
//#region source/parsers/routes/
|
|
782
|
+
//#region source/parsers/routes/heading-metadata.ts
|
|
361
783
|
function kebabToTitle(str) {
|
|
362
|
-
return str.split("-").map((
|
|
363
|
-
}
|
|
364
|
-
function
|
|
365
|
-
return
|
|
784
|
+
return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
785
|
+
}
|
|
786
|
+
function headingMetadata(analysis) {
|
|
787
|
+
return { sections: analysis.headings.filter((heading) => heading.level === 2 || heading.level === 3).map((heading) => ({
|
|
788
|
+
id: heading.id,
|
|
789
|
+
title: heading.title,
|
|
790
|
+
level: heading.level
|
|
791
|
+
})) };
|
|
792
|
+
}
|
|
793
|
+
function resolvedRouteTitle(route, analysis) {
|
|
794
|
+
const frontmatterTitle = route.source.frontmatter?.title;
|
|
795
|
+
if (typeof frontmatterTitle === "string" && frontmatterTitle) return frontmatterTitle;
|
|
796
|
+
const routePath = route.basePath ?? route.path;
|
|
797
|
+
return kebabToTitle(routePath === "/" ? analysis.headings.find((heading) => heading.level === 1)?.title ?? "" : routePath.split("/").filter(Boolean).at(-1) ?? "") || "Untitled";
|
|
798
|
+
}
|
|
799
|
+
async function reanalyzeMarkdownRoute(route, projectRoot) {
|
|
800
|
+
if (route.kind !== "markdown" && route.kind !== "markdown+jsx") return route;
|
|
801
|
+
const analysis = analyzeHeadings(route.source.content ?? "", {
|
|
802
|
+
kind: route.kind,
|
|
803
|
+
filePath: route.source.filePath,
|
|
804
|
+
projectRoot,
|
|
805
|
+
lineOffset: route.source.lineOffset
|
|
806
|
+
});
|
|
807
|
+
const result = await (route.kind === "markdown" ? compileMarkdownContent : compileMdxContent)(analysis.normalizedContent, {
|
|
808
|
+
filePath: route.source.filePath,
|
|
809
|
+
projectRoot
|
|
810
|
+
});
|
|
811
|
+
return {
|
|
812
|
+
...route,
|
|
813
|
+
meta: {
|
|
814
|
+
...route.meta,
|
|
815
|
+
title: resolvedRouteTitle(route, analysis),
|
|
816
|
+
...headingMetadata(analysis)
|
|
817
|
+
},
|
|
818
|
+
diagnostic: analysis.diagnostic ?? (result.ok ? void 0 : result.diagnostic)
|
|
819
|
+
};
|
|
366
820
|
}
|
|
821
|
+
//#endregion
|
|
822
|
+
//#region source/parsers/routes/routes.ts
|
|
367
823
|
async function compileRouteDiagnostic(content, filePath, baseDir) {
|
|
368
824
|
const result = await (extname(filePath).toLowerCase() === ".md" ? compileMarkdownContent : compileMdxContent)(content, {
|
|
369
825
|
filePath,
|
|
@@ -371,30 +827,6 @@ async function compileRouteDiagnostic(content, filePath, baseDir) {
|
|
|
371
827
|
});
|
|
372
828
|
return result.ok ? void 0 : result.diagnostic;
|
|
373
829
|
}
|
|
374
|
-
/** 从内容中提取第一个 H1 标题 */
|
|
375
|
-
function extractH1(content) {
|
|
376
|
-
const tree = parseMdxTree(content);
|
|
377
|
-
let title = "";
|
|
378
|
-
visit(tree, "heading", (node) => {
|
|
379
|
-
if (!title && node.depth === 1) title = toString(node);
|
|
380
|
-
});
|
|
381
|
-
return title;
|
|
382
|
-
}
|
|
383
|
-
/** 从 MDX/Markdown 内容中提取 H2/H3 章节 */
|
|
384
|
-
function extractMdxSections(content) {
|
|
385
|
-
const sections = [];
|
|
386
|
-
const slugger = new GithubSlugger();
|
|
387
|
-
visit(parseMdxTree(content), "heading", (node) => {
|
|
388
|
-
if (node.depth !== 2 && node.depth !== 3) return;
|
|
389
|
-
const title = toString(node);
|
|
390
|
-
sections.push({
|
|
391
|
-
id: slugger.slug(title),
|
|
392
|
-
title,
|
|
393
|
-
level: node.depth
|
|
394
|
-
});
|
|
395
|
-
});
|
|
396
|
-
return sections;
|
|
397
|
-
}
|
|
398
830
|
function navigationSections(sections) {
|
|
399
831
|
return sections.map((section) => ({
|
|
400
832
|
id: section.id,
|
|
@@ -451,11 +883,12 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
451
883
|
const fullPath = join(dir, entry.name);
|
|
452
884
|
if (entry.isDirectory()) routes.push(...await findContentRoutes(fullPath, base, options));
|
|
453
885
|
else if (entry.isFile() && /\.mdx?$/.test(entry.name)) {
|
|
886
|
+
const kind = entry.name.toLowerCase().endsWith(".mdx") ? "markdown+jsx" : "markdown";
|
|
454
887
|
const relativePath = relative(base, fullPath);
|
|
455
888
|
const pathParts = relativePath.replace(/\.mdx?$/, "").split("/");
|
|
456
889
|
const cleanPath = ("/" + pathParts.map((p) => p === "index" ? "" : p).filter(Boolean).join("/")).replace(/\/+/g, "/").replace(/\/$/, "") || "/";
|
|
457
890
|
const source = await readFile(fullPath, "utf-8");
|
|
458
|
-
const { frontmatter, content } = await (options.contentProcessor ?? createContentProcessor()).processMdx(source, fullPath);
|
|
891
|
+
const { frontmatter, content, lineOffset } = await (options.contentProcessor ?? createContentProcessor()).processMdx(source, fullPath);
|
|
459
892
|
const updatedAt = resolveUpdatedAt(frontmatter, await getFileUpdatedAt(fullPath, base));
|
|
460
893
|
const page = {
|
|
461
894
|
path: cleanPath,
|
|
@@ -463,14 +896,21 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
463
896
|
frontmatter,
|
|
464
897
|
content
|
|
465
898
|
};
|
|
466
|
-
const
|
|
899
|
+
const analysis = analyzeHeadings(page.content, {
|
|
900
|
+
kind,
|
|
901
|
+
filePath: fullPath,
|
|
902
|
+
projectRoot: base,
|
|
903
|
+
lineOffset
|
|
904
|
+
});
|
|
905
|
+
const compileDiagnostic = await compileRouteDiagnostic(analysis.normalizedContent, fullPath, base);
|
|
906
|
+
const { sections } = headingMetadata(analysis);
|
|
467
907
|
let title = typeof page.frontmatter.title === "string" ? page.frontmatter.title : "";
|
|
468
908
|
if (!title) {
|
|
469
909
|
const lastPart = pathParts[pathParts.length - 1] ?? "";
|
|
470
|
-
title = kebabToTitle(lastPart === "index" ? pathParts.length >= 2 ? pathParts[pathParts.length - 2] :
|
|
910
|
+
title = kebabToTitle(lastPart === "index" ? pathParts.length >= 2 ? pathParts[pathParts.length - 2] : analysis.headings.find((heading) => heading.level === 1)?.title ?? "" : lastPart) || "Untitled";
|
|
471
911
|
}
|
|
472
912
|
routes.push({
|
|
473
|
-
kind
|
|
913
|
+
kind,
|
|
474
914
|
path: cleanPath,
|
|
475
915
|
basePath: cleanPath,
|
|
476
916
|
meta: {
|
|
@@ -480,7 +920,7 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
480
920
|
group: typeof page.frontmatter.group === "string" ? page.frontmatter.group : void 0,
|
|
481
921
|
layout: page.frontmatter.layout === "documentation" || page.frontmatter.layout === "blog" ? page.frontmatter.layout : void 0,
|
|
482
922
|
updatedAt,
|
|
483
|
-
sections
|
|
923
|
+
sections
|
|
484
924
|
},
|
|
485
925
|
module: {
|
|
486
926
|
pageVirtualModuleId: pageVirtualModuleId(relativePath.replace(/\.mdx?$/, "")),
|
|
@@ -489,9 +929,10 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
489
929
|
source: {
|
|
490
930
|
filePath: fullPath,
|
|
491
931
|
frontmatter: page.frontmatter,
|
|
492
|
-
content: page.content
|
|
932
|
+
content: page.content,
|
|
933
|
+
lineOffset
|
|
493
934
|
},
|
|
494
|
-
diagnostic
|
|
935
|
+
diagnostic: analysis.diagnostic ?? compileDiagnostic
|
|
495
936
|
});
|
|
496
937
|
}
|
|
497
938
|
}
|
|
@@ -647,6 +1088,7 @@ function routeIntentFromPagesItem(item) {
|
|
|
647
1088
|
kind: "openapi",
|
|
648
1089
|
ref: item.openapi,
|
|
649
1090
|
tagFilter: item.filter?.tags,
|
|
1091
|
+
operationOrder: item.operationOrder,
|
|
650
1092
|
path: item.path,
|
|
651
1093
|
title: item.title,
|
|
652
1094
|
icon: item.icon
|
|
@@ -1113,6 +1555,7 @@ var clarifyPagesItemSchema = z.lazy(() => z.union([
|
|
|
1113
1555
|
z.object({
|
|
1114
1556
|
openapi: z.string(),
|
|
1115
1557
|
path: z.string().optional(),
|
|
1558
|
+
operationOrder: z.array(z.string()).optional(),
|
|
1116
1559
|
icon: z.string().optional(),
|
|
1117
1560
|
title: clarifyLocalizedTextSchema.optional(),
|
|
1118
1561
|
filter: z.object({ tags: z.array(z.string()).optional() }).strict().optional()
|
|
@@ -1595,10 +2038,14 @@ function createNormalizedContentPlugin(engine) {
|
|
|
1595
2038
|
transform(_code, id) {
|
|
1596
2039
|
if (!/\.mdx?(?:\?|$)/.test(id)) return null;
|
|
1597
2040
|
const filePath = id.replace(/\?.*$/, "");
|
|
1598
|
-
const route = engine.routes.find((route) =>
|
|
1599
|
-
if (!route || route.source.content === void 0) return null;
|
|
2041
|
+
const route = engine.routes.find((route) => route.source.filePath === filePath);
|
|
2042
|
+
if (!route || route.kind !== "markdown+jsx" && route.kind !== "markdown" || route.source.content === void 0) return null;
|
|
1600
2043
|
return {
|
|
1601
|
-
code: route.source.content,
|
|
2044
|
+
code: analyzeHeadings(route.source.content, {
|
|
2045
|
+
kind: route.kind,
|
|
2046
|
+
filePath,
|
|
2047
|
+
projectRoot: engine.root
|
|
2048
|
+
}).normalizedContent,
|
|
1602
2049
|
map: null
|
|
1603
2050
|
};
|
|
1604
2051
|
}
|
|
@@ -1788,7 +2235,7 @@ function createViteAdapter(engine) {
|
|
|
1788
2235
|
}
|
|
1789
2236
|
var cliPackageVersion = {
|
|
1790
2237
|
name: "@clarify-labs/cli",
|
|
1791
|
-
version: "0.11.
|
|
2238
|
+
version: "0.11.20",
|
|
1792
2239
|
license: "AGPL-3.0-only",
|
|
1793
2240
|
type: "module",
|
|
1794
2241
|
repository: {
|
|
@@ -1991,6 +2438,191 @@ function createLlmsTxtArtifact(routes, projectConfig) {
|
|
|
1991
2438
|
return withUtf8Signature(createLlmsTxt(routes, projectConfig));
|
|
1992
2439
|
}
|
|
1993
2440
|
//#endregion
|
|
2441
|
+
//#region source/plugins/openapi/aggregation.ts
|
|
2442
|
+
function mergeOpenAPIRecord(target, source, section, filePath) {
|
|
2443
|
+
if (!source) return;
|
|
2444
|
+
for (const [key, value] of Object.entries(source)) {
|
|
2445
|
+
const existing = target[key];
|
|
2446
|
+
if (existing !== void 0 && JSON.stringify(existing) !== JSON.stringify(value)) throw new Error(`[clarify] Cannot aggregate OpenAPI specs: conflicting ${section} entry "${key}" in ${filePath}.`);
|
|
2447
|
+
target[key] = value;
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
function openAPIVersionFamily(version) {
|
|
2451
|
+
return version.split(".").slice(0, 2).join(".");
|
|
2452
|
+
}
|
|
2453
|
+
var OPENAPI_OPERATION_METHODS = /* @__PURE__ */ new Set([
|
|
2454
|
+
"get",
|
|
2455
|
+
"put",
|
|
2456
|
+
"post",
|
|
2457
|
+
"delete",
|
|
2458
|
+
"options",
|
|
2459
|
+
"head",
|
|
2460
|
+
"patch",
|
|
2461
|
+
"trace"
|
|
2462
|
+
]);
|
|
2463
|
+
function sourceComponentPrefix(source) {
|
|
2464
|
+
return (source.id || source.filePath.split(/[\\/]/).pop()?.replace(/\.openapi\.(?:json|ya?ml)$/i, "") || "service").replace(/[^a-zA-Z0-9._-]/g, "_") || "service";
|
|
2465
|
+
}
|
|
2466
|
+
function securitySchemeNameMap(source, sourceSchemes, targetSchemes) {
|
|
2467
|
+
const names = /* @__PURE__ */ new Map();
|
|
2468
|
+
for (const [name, definition] of Object.entries(sourceSchemes)) {
|
|
2469
|
+
const existing = targetSchemes[name];
|
|
2470
|
+
if (existing === void 0 || JSON.stringify(existing) === JSON.stringify(definition)) {
|
|
2471
|
+
targetSchemes[name] = definition;
|
|
2472
|
+
names.set(name, name);
|
|
2473
|
+
continue;
|
|
2474
|
+
}
|
|
2475
|
+
const prefix = sourceComponentPrefix(source);
|
|
2476
|
+
let candidate = `${prefix}__${name}`;
|
|
2477
|
+
let suffix = 2;
|
|
2478
|
+
while (targetSchemes[candidate] !== void 0 && JSON.stringify(targetSchemes[candidate]) !== JSON.stringify(definition)) candidate = `${prefix}__${name}_${suffix++}`;
|
|
2479
|
+
targetSchemes[candidate] = definition;
|
|
2480
|
+
names.set(name, candidate);
|
|
2481
|
+
}
|
|
2482
|
+
return names;
|
|
2483
|
+
}
|
|
2484
|
+
function rewriteSecurityRequirements(value, schemeNames) {
|
|
2485
|
+
if (!Array.isArray(value)) return value;
|
|
2486
|
+
return value.map((requirement) => {
|
|
2487
|
+
if (!requirement || typeof requirement !== "object" || Array.isArray(requirement)) return requirement;
|
|
2488
|
+
return Object.fromEntries(Object.entries(requirement).map(([name, scopes]) => [schemeNames.get(name) ?? name, scopes]));
|
|
2489
|
+
});
|
|
2490
|
+
}
|
|
2491
|
+
function localizePathItemContext(pathItemValue, documentServers, documentSecurity, hasDocumentSecurity, schemeNames) {
|
|
2492
|
+
if (!pathItemValue || typeof pathItemValue !== "object" || Array.isArray(pathItemValue)) return pathItemValue;
|
|
2493
|
+
const pathItem = structuredClone(pathItemValue);
|
|
2494
|
+
const pathServers = Object.hasOwn(pathItem, "servers") ? pathItem.servers : documentServers;
|
|
2495
|
+
for (const [method, operationValue] of Object.entries(pathItem)) {
|
|
2496
|
+
if (!OPENAPI_OPERATION_METHODS.has(method) || !operationValue || typeof operationValue !== "object" || Array.isArray(operationValue)) continue;
|
|
2497
|
+
const operation = operationValue;
|
|
2498
|
+
if (!Object.hasOwn(operation, "servers") && pathServers !== void 0) operation.servers = structuredClone(pathServers);
|
|
2499
|
+
if (Object.hasOwn(operation, "security")) operation.security = rewriteSecurityRequirements(operation.security, schemeNames);
|
|
2500
|
+
else if (hasDocumentSecurity) operation.security = rewriteSecurityRequirements(structuredClone(documentSecurity), schemeNames);
|
|
2501
|
+
}
|
|
2502
|
+
delete pathItem.servers;
|
|
2503
|
+
return pathItem;
|
|
2504
|
+
}
|
|
2505
|
+
function localizeOpenAPIContexts(spec, schemeNames) {
|
|
2506
|
+
const document = spec;
|
|
2507
|
+
const hasDocumentSecurity = Object.hasOwn(document, "security");
|
|
2508
|
+
const localizeEntries = (entries) => entries && Object.fromEntries(Object.entries(entries).map(([name, pathItem]) => [name, localizePathItemContext(pathItem, document.servers, document.security, hasDocumentSecurity, schemeNames)]));
|
|
2509
|
+
return {
|
|
2510
|
+
paths: localizeEntries(spec.paths) ?? {},
|
|
2511
|
+
webhooks: localizeEntries("webhooks" in spec ? spec.webhooks : void 0)
|
|
2512
|
+
};
|
|
2513
|
+
}
|
|
2514
|
+
function aggregateOpenAPISources(sources, info) {
|
|
2515
|
+
const paths = {};
|
|
2516
|
+
const webhooks = {};
|
|
2517
|
+
const components = {};
|
|
2518
|
+
const tags = /* @__PURE__ */ new Map();
|
|
2519
|
+
let openapiVersion;
|
|
2520
|
+
for (const source of sources) {
|
|
2521
|
+
const { document: spec, filePath } = source;
|
|
2522
|
+
if (openapiVersion && openAPIVersionFamily(openapiVersion) !== openAPIVersionFamily(spec.openapi)) throw new Error(`[clarify] Cannot aggregate OpenAPI specs: incompatible versions "${openapiVersion}" and "${spec.openapi}" in ${filePath}.`);
|
|
2523
|
+
openapiVersion ??= spec.openapi;
|
|
2524
|
+
components.securitySchemes ??= {};
|
|
2525
|
+
const sourceComponents = spec.components;
|
|
2526
|
+
const localized = localizeOpenAPIContexts(spec, securitySchemeNameMap(source, sourceComponents?.securitySchemes ?? {}, components.securitySchemes));
|
|
2527
|
+
mergeOpenAPIRecord(paths, localized.paths, "paths", filePath);
|
|
2528
|
+
mergeOpenAPIRecord(webhooks, localized.webhooks, "webhooks", filePath);
|
|
2529
|
+
for (const [section, entries] of Object.entries(sourceComponents ?? {})) {
|
|
2530
|
+
if (section === "securitySchemes") continue;
|
|
2531
|
+
components[section] ??= {};
|
|
2532
|
+
mergeOpenAPIRecord(components[section], entries, `components.${section}`, filePath);
|
|
2533
|
+
}
|
|
2534
|
+
for (const tag of spec.tags ?? []) {
|
|
2535
|
+
const existing = tags.get(tag.name);
|
|
2536
|
+
if (existing !== void 0 && JSON.stringify(existing) !== JSON.stringify(tag)) throw new Error(`[clarify] Cannot aggregate OpenAPI specs: conflicting tag "${tag.name}" in ${filePath}.`);
|
|
2537
|
+
tags.set(tag.name, tag);
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
if (components.securitySchemes && Object.keys(components.securitySchemes).length === 0) delete components.securitySchemes;
|
|
2541
|
+
return {
|
|
2542
|
+
openapi: openapiVersion ?? "3.1.0",
|
|
2543
|
+
info: {
|
|
2544
|
+
title: info.title,
|
|
2545
|
+
description: info.description,
|
|
2546
|
+
version: info.version ?? "1.0.0"
|
|
2547
|
+
},
|
|
2548
|
+
paths,
|
|
2549
|
+
...Object.keys(webhooks).length > 0 ? { webhooks } : {},
|
|
2550
|
+
...Object.keys(components).length > 0 ? { components } : {},
|
|
2551
|
+
...tags.size > 0 ? { tags: [...tags.values()] } : {}
|
|
2552
|
+
};
|
|
2553
|
+
}
|
|
2554
|
+
//#endregion
|
|
2555
|
+
//#region source/plugins/openapi/artifacts.ts
|
|
2556
|
+
function selectRootOpenAPISources(routes, projectConfig) {
|
|
2557
|
+
const defaultLocale = projectConfig.locales?.default;
|
|
2558
|
+
const sourcesByFilePath = /* @__PURE__ */ new Map();
|
|
2559
|
+
for (const route of routes) {
|
|
2560
|
+
if (route.kind !== "openapi" || route.diagnostic || !route.source.content || route.openapi?.tagFilter?.length || route.isBareAlias || defaultLocale && route.locale && route.locale !== defaultLocale) continue;
|
|
2561
|
+
sourcesByFilePath.set(route.source.filePath, {
|
|
2562
|
+
id: route.openapi?.sourceSpecId ?? route.source.filePath.split(/[\\/]/).pop()?.replace(/\.openapi\.(?:json|ya?ml)$/i, "") ?? "service",
|
|
2563
|
+
filePath: route.source.filePath,
|
|
2564
|
+
document: JSON.parse(route.source.content)
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2567
|
+
return [...sourcesByFilePath.values()];
|
|
2568
|
+
}
|
|
2569
|
+
function createRootOpenAPISpec(routes, projectConfig) {
|
|
2570
|
+
return aggregateOpenAPISources(selectRootOpenAPISources(routes, projectConfig), {
|
|
2571
|
+
title: projectConfig.title,
|
|
2572
|
+
description: projectConfig.description
|
|
2573
|
+
});
|
|
2574
|
+
}
|
|
2575
|
+
function createOpenAPIRouteArtifacts(route) {
|
|
2576
|
+
if (route.kind !== "openapi" || !route.source.content) return [];
|
|
2577
|
+
const contentArtifactUrl = route.artifacts?.contentArtifactUrl;
|
|
2578
|
+
if (!contentArtifactUrl) return [];
|
|
2579
|
+
return [{
|
|
2580
|
+
fileName: contentArtifactUrl.replace(/^\//, ""),
|
|
2581
|
+
contentType: "application/json; charset=utf-8",
|
|
2582
|
+
source: route.source.content
|
|
2583
|
+
}, {
|
|
2584
|
+
fileName: contentArtifactUrl.replace(/\.json$/, ".yaml").replace(/^\//, ""),
|
|
2585
|
+
contentType: "text/yaml; charset=utf-8",
|
|
2586
|
+
source: stringify(JSON.parse(route.source.content), { lineWidth: 0 })
|
|
2587
|
+
}];
|
|
2588
|
+
}
|
|
2589
|
+
function createRootOpenAPIArtifact(routes, projectConfig) {
|
|
2590
|
+
return {
|
|
2591
|
+
fileName: "openapi.json",
|
|
2592
|
+
contentType: "application/json; charset=utf-8",
|
|
2593
|
+
source: JSON.stringify(createRootOpenAPISpec(routes, projectConfig), null, 2)
|
|
2594
|
+
};
|
|
2595
|
+
}
|
|
2596
|
+
//#endregion
|
|
2597
|
+
//#region source/plugins/content-artifacts/provider.ts
|
|
2598
|
+
function createContentArtifacts(routes, projectConfig) {
|
|
2599
|
+
const artifacts = [];
|
|
2600
|
+
for (const route of routes) {
|
|
2601
|
+
if (route.kind === "openapi") {
|
|
2602
|
+
artifacts.push(...createOpenAPIRouteArtifacts(route));
|
|
2603
|
+
continue;
|
|
2604
|
+
}
|
|
2605
|
+
const contentArtifactUrl = route.artifacts?.contentArtifactUrl;
|
|
2606
|
+
if (!contentArtifactUrl) continue;
|
|
2607
|
+
artifacts.push({
|
|
2608
|
+
fileName: contentArtifactUrl.replace(/^\//, ""),
|
|
2609
|
+
contentType: "text/markdown; charset=utf-8",
|
|
2610
|
+
source: readRouteArtifactContent(route)
|
|
2611
|
+
});
|
|
2612
|
+
}
|
|
2613
|
+
artifacts.push({
|
|
2614
|
+
fileName: "llms.txt",
|
|
2615
|
+
contentType: "text/plain; charset=utf-8",
|
|
2616
|
+
source: createLlmsTxtArtifact(routes, projectConfig)
|
|
2617
|
+
});
|
|
2618
|
+
artifacts.push(createRootOpenAPIArtifact(routes, projectConfig));
|
|
2619
|
+
return artifacts;
|
|
2620
|
+
}
|
|
2621
|
+
function findContentArtifact(path, routes, projectConfig) {
|
|
2622
|
+
const fileName = path.replace(/^\/+/, "");
|
|
2623
|
+
return createContentArtifacts(routes, projectConfig).find((artifact) => artifact.fileName === fileName);
|
|
2624
|
+
}
|
|
2625
|
+
//#endregion
|
|
1994
2626
|
//#region source/plugins/content-artifacts/server.ts
|
|
1995
2627
|
function normalizeRoutePrefix$1(routePrefix) {
|
|
1996
2628
|
if (!routePrefix || routePrefix === "/") return "";
|
|
@@ -2002,34 +2634,12 @@ function resolveContentArtifactPath(url, projectConfig) {
|
|
|
2002
2634
|
const hasBasePath = requestPath === basePath || requestPath.startsWith(`${basePath}/`);
|
|
2003
2635
|
return basePath && hasBasePath ? requestPath.slice(basePath.length) || "/" : requestPath;
|
|
2004
2636
|
}
|
|
2005
|
-
function resolveContentArtifactType(route) {
|
|
2006
|
-
return route.kind === "openapi" ? "application/json; charset=utf-8" : "text/markdown; charset=utf-8";
|
|
2007
|
-
}
|
|
2008
2637
|
function serveContentArtifacts(req, res, projectConfig, routes) {
|
|
2009
|
-
const
|
|
2010
|
-
if (
|
|
2011
|
-
res.statusCode = 200;
|
|
2012
|
-
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
2013
|
-
res.end(createLlmsTxtArtifact(routes, projectConfig), "utf8");
|
|
2014
|
-
return true;
|
|
2015
|
-
}
|
|
2016
|
-
if (contentPath.endsWith(".openapi.yaml")) {
|
|
2017
|
-
const jsonPath = contentPath.replace(/\.yaml$/, ".json");
|
|
2018
|
-
const route = routes.find((route) => route.artifacts?.contentArtifactUrl === jsonPath);
|
|
2019
|
-
if (route?.kind === "openapi" && route.source.content) {
|
|
2020
|
-
const spec = JSON.parse(route.source.content);
|
|
2021
|
-
res.statusCode = 200;
|
|
2022
|
-
res.setHeader("Content-Type", "text/yaml; charset=utf-8");
|
|
2023
|
-
res.end(stringify(spec, { lineWidth: 0 }), "utf8");
|
|
2024
|
-
return true;
|
|
2025
|
-
}
|
|
2026
|
-
return false;
|
|
2027
|
-
}
|
|
2028
|
-
const route = routes.find((route) => route.artifacts?.contentArtifactUrl === contentPath);
|
|
2029
|
-
if (!route) return false;
|
|
2638
|
+
const artifact = findContentArtifact(resolveContentArtifactPath(req.url, projectConfig), routes, projectConfig);
|
|
2639
|
+
if (!artifact) return false;
|
|
2030
2640
|
res.statusCode = 200;
|
|
2031
|
-
res.setHeader("Content-Type",
|
|
2032
|
-
res.end(
|
|
2641
|
+
res.setHeader("Content-Type", artifact.contentType);
|
|
2642
|
+
res.end(artifact.source);
|
|
2033
2643
|
return true;
|
|
2034
2644
|
}
|
|
2035
2645
|
//#endregion
|
|
@@ -2050,30 +2660,7 @@ function createContentArtifactsPlugin() {
|
|
|
2050
2660
|
});
|
|
2051
2661
|
};
|
|
2052
2662
|
},
|
|
2053
|
-
"build:assets": (ctx) =>
|
|
2054
|
-
const assets = [];
|
|
2055
|
-
for (const route of ctx.routes) {
|
|
2056
|
-
const contentArtifactUrl = route.artifacts?.contentArtifactUrl;
|
|
2057
|
-
if (!contentArtifactUrl) continue;
|
|
2058
|
-
assets.push({
|
|
2059
|
-
fileName: contentArtifactUrl.replace(/^\//, ""),
|
|
2060
|
-
source: readRouteArtifactContent(route)
|
|
2061
|
-
});
|
|
2062
|
-
if (route.kind === "openapi" && route.source.content) {
|
|
2063
|
-
const yamlFileName = contentArtifactUrl.replace(/\.json$/, ".yaml");
|
|
2064
|
-
const spec = JSON.parse(route.source.content);
|
|
2065
|
-
assets.push({
|
|
2066
|
-
fileName: yamlFileName.replace(/^\//, ""),
|
|
2067
|
-
source: stringify(spec, { lineWidth: 0 })
|
|
2068
|
-
});
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
assets.push({
|
|
2072
|
-
fileName: "llms.txt",
|
|
2073
|
-
source: createLlmsTxtArtifact(ctx.routes, ctx.projectConfig)
|
|
2074
|
-
});
|
|
2075
|
-
return assets;
|
|
2076
|
-
}
|
|
2663
|
+
"build:assets": (ctx) => createContentArtifacts(ctx.routes, ctx.projectConfig)
|
|
2077
2664
|
}
|
|
2078
2665
|
};
|
|
2079
2666
|
}
|
|
@@ -2597,9 +3184,9 @@ function filterSpecByTags(spec, tags) {
|
|
|
2597
3184
|
};
|
|
2598
3185
|
}
|
|
2599
3186
|
/** Extract endpoint operations from an OpenAPI spec as page sections. */
|
|
2600
|
-
function extractOpenAPISections(spec, filterTags) {
|
|
3187
|
+
function extractOpenAPISections(spec, filterTags, operationOrder) {
|
|
2601
3188
|
normalizeOpenAPISpecSectionIds(spec);
|
|
2602
|
-
const
|
|
3189
|
+
const entries = [];
|
|
2603
3190
|
const items = [...Object.entries(spec.paths ?? {}).map(([path, pathItem]) => ({
|
|
2604
3191
|
path,
|
|
2605
3192
|
pathItem,
|
|
@@ -2617,16 +3204,25 @@ function extractOpenAPISections(spec, filterTags) {
|
|
|
2617
3204
|
const tags = Array.isArray(op?.tags) ? op.tags : void 0;
|
|
2618
3205
|
if (!op || !operationMatchesTags(tags, filterTags)) continue;
|
|
2619
3206
|
const title = typeof op.summary === "string" ? op.summary : `${method.toUpperCase()} ${path}`;
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
3207
|
+
entries.push({
|
|
3208
|
+
section: {
|
|
3209
|
+
id: operationSectionId(op),
|
|
3210
|
+
title,
|
|
3211
|
+
level: 2,
|
|
3212
|
+
badge: kind === "webhook" ? "WEBHOOK" : method.toUpperCase(),
|
|
3213
|
+
tags
|
|
3214
|
+
},
|
|
3215
|
+
operationId: typeof op.operationId === "string" ? op.operationId : void 0
|
|
2626
3216
|
});
|
|
2627
3217
|
}
|
|
2628
3218
|
}
|
|
2629
|
-
return
|
|
3219
|
+
if (!operationOrder?.length) return entries.map(({ section }) => section);
|
|
3220
|
+
const orderById = new Map(operationOrder.map((operationId, index) => [operationId, index]));
|
|
3221
|
+
return entries.map((entry, index) => ({
|
|
3222
|
+
...entry,
|
|
3223
|
+
index,
|
|
3224
|
+
order: entry.operationId ? orderById.get(entry.operationId) : void 0
|
|
3225
|
+
})).sort((left, right) => (left.order ?? operationOrder.length) - (right.order ?? operationOrder.length) || left.index - right.index).map(({ section }) => section);
|
|
2630
3226
|
}
|
|
2631
3227
|
async function readOpenAPISpec(filePath, contentProcessor, projectRoot) {
|
|
2632
3228
|
try {
|
|
@@ -2673,7 +3269,7 @@ function collectOpenAPIPageIntents(config) {
|
|
|
2673
3269
|
continue;
|
|
2674
3270
|
}
|
|
2675
3271
|
const intent = routeIntentFromPagesItem(item);
|
|
2676
|
-
if (intent.kind === "openapi" && (intent.path || intent.tagFilter?.length)) intents.push(intent);
|
|
3272
|
+
if (intent.kind === "openapi" && (intent.path || intent.tagFilter?.length || intent.operationOrder?.length)) intents.push(intent);
|
|
2677
3273
|
}
|
|
2678
3274
|
}
|
|
2679
3275
|
function visitPages(pages) {
|
|
@@ -2694,7 +3290,8 @@ function cloneOpenAPIRoute(route, overrides = {}) {
|
|
|
2694
3290
|
source: { ...route.source },
|
|
2695
3291
|
openapi: route.openapi ? {
|
|
2696
3292
|
...route.openapi,
|
|
2697
|
-
tagFilter: route.openapi.tagFilter ? [...route.openapi.tagFilter] : void 0
|
|
3293
|
+
tagFilter: route.openapi.tagFilter ? [...route.openapi.tagFilter] : void 0,
|
|
3294
|
+
operationOrder: route.openapi.operationOrder ? [...route.openapi.operationOrder] : void 0
|
|
2698
3295
|
} : void 0,
|
|
2699
3296
|
alternates: route.alternates ? { ...route.alternates } : void 0,
|
|
2700
3297
|
...overrides
|
|
@@ -2743,9 +3340,21 @@ function expandConfiguredOpenAPIRoutes(routes, config) {
|
|
|
2743
3340
|
const existingKeys = new Set(routes.map((route) => `${route.locale ?? ""}:${route.basePath ?? route.path}`));
|
|
2744
3341
|
for (const intent of openAPIIntents) {
|
|
2745
3342
|
const tagFilter = intent.tagFilter?.filter(Boolean);
|
|
3343
|
+
const operationOrder = intent.operationOrder?.filter(Boolean);
|
|
2746
3344
|
const sourceBasePath = openAPIPagePathFromRef(intent.ref);
|
|
2747
3345
|
const targetBasePath = openAPIPagePathFromRef(intent.ref, tagFilter, intent.path);
|
|
2748
|
-
if (sourceBasePath === targetBasePath && !tagFilter?.length)
|
|
3346
|
+
if (sourceBasePath === targetBasePath && !tagFilter?.length) {
|
|
3347
|
+
if (!operationOrder?.length) continue;
|
|
3348
|
+
for (let index = 0; index < routes.length; index++) {
|
|
3349
|
+
const route = routes[index];
|
|
3350
|
+
if (route.kind !== "openapi" || (route.basePath ?? route.path) !== sourceBasePath) continue;
|
|
3351
|
+
routes[index] = cloneOpenAPIRoute(route, { openapi: {
|
|
3352
|
+
...route.openapi,
|
|
3353
|
+
operationOrder: [...operationOrder]
|
|
3354
|
+
} });
|
|
3355
|
+
}
|
|
3356
|
+
continue;
|
|
3357
|
+
}
|
|
2749
3358
|
for (const route of routes) {
|
|
2750
3359
|
if (route.kind !== "openapi" || (route.basePath ?? route.path) !== sourceBasePath) continue;
|
|
2751
3360
|
const key = `${route.locale ?? ""}:${targetBasePath}`;
|
|
@@ -2758,7 +3367,8 @@ function expandConfiguredOpenAPIRoutes(routes, config) {
|
|
|
2758
3367
|
module: { pageVirtualModuleId: pageVirtualModuleId(path) },
|
|
2759
3368
|
openapi: {
|
|
2760
3369
|
...route.openapi,
|
|
2761
|
-
tagFilter: tagFilter?.length ? tagFilter : void 0
|
|
3370
|
+
tagFilter: tagFilter?.length ? tagFilter : void 0,
|
|
3371
|
+
operationOrder: operationOrder?.length ? [...operationOrder] : void 0
|
|
2762
3372
|
}
|
|
2763
3373
|
}));
|
|
2764
3374
|
}
|
|
@@ -2800,10 +3410,11 @@ function generateOpenAPISpecModule(spec) {
|
|
|
2800
3410
|
return `export default ${JSON.stringify(spec)};`;
|
|
2801
3411
|
}
|
|
2802
3412
|
function generateOpenAPIPageModule(opts) {
|
|
3413
|
+
const routeData = opts.operationOrder?.length ? { operationOrder: opts.operationOrder } : {};
|
|
2803
3414
|
return `import { createOpenApiRouteComponent } from '@clarify-labs/renderer';
|
|
2804
3415
|
import spec from ${JSON.stringify(opts.specModuleId)};
|
|
2805
3416
|
|
|
2806
|
-
export const routeData = {};
|
|
3417
|
+
export const routeData = ${JSON.stringify(routeData)};
|
|
2807
3418
|
|
|
2808
3419
|
export default createOpenApiRouteComponent({ ...routeData, spec });
|
|
2809
3420
|
`;
|
|
@@ -2863,7 +3474,7 @@ var OpenAPIPluginState = class {
|
|
|
2863
3474
|
route.meta.title = spec.info?.title ?? route.meta.title;
|
|
2864
3475
|
route.meta.description = spec.info?.description ?? route.meta.description;
|
|
2865
3476
|
route.meta.updatedAt = updatedAt;
|
|
2866
|
-
route.meta.sections = extractOpenAPISections(spec, route.openapi.tagFilter);
|
|
3477
|
+
route.meta.sections = extractOpenAPISections(spec, route.openapi.tagFilter, route.openapi.operationOrder);
|
|
2867
3478
|
const pageSpec = route.openapi.tagFilter?.length ? filterSpecByTags(specWithMetadata, route.openapi.tagFilter) : specWithMetadata;
|
|
2868
3479
|
const routeSpecModuleId = routeSpecModuleIdFromSourceSpecId(sourceSpecId, route.openapi.tagFilter);
|
|
2869
3480
|
route.openapi.routeSpecModuleId = routeSpecModuleId;
|
|
@@ -2897,7 +3508,10 @@ var OpenAPIPluginState = class {
|
|
|
2897
3508
|
if (!route.openapi?.sourceSpecId) continue;
|
|
2898
3509
|
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2899
3510
|
if (!this.specModules.has(routeSpecModuleId)) continue;
|
|
2900
|
-
modules.set(route.module.pageVirtualModuleId, generateOpenAPIPageModule({
|
|
3511
|
+
modules.set(route.module.pageVirtualModuleId, generateOpenAPIPageModule({
|
|
3512
|
+
specModuleId: specVirtualModuleId(routeSpecModuleId),
|
|
3513
|
+
operationOrder: route.openapi.operationOrder
|
|
3514
|
+
}));
|
|
2901
3515
|
}
|
|
2902
3516
|
return modules;
|
|
2903
3517
|
}
|
|
@@ -3387,7 +4001,7 @@ async function resolveRoutePages(routes, plugins, ctx) {
|
|
|
3387
4001
|
pageByRouteIndex.set(routeIndex, page);
|
|
3388
4002
|
}
|
|
3389
4003
|
if (pageByRouteIndex.size !== routes.length) throw new Error("[clarify] pages:resolved hooks must return exactly one page for each route");
|
|
3390
|
-
|
|
4004
|
+
const updatedRoutes = routes.map((route, routeIndex) => {
|
|
3391
4005
|
const page = pageByRouteIndex.get(routeIndex);
|
|
3392
4006
|
return {
|
|
3393
4007
|
...route,
|
|
@@ -3398,6 +4012,7 @@ async function resolveRoutePages(routes, plugins, ctx) {
|
|
|
3398
4012
|
}
|
|
3399
4013
|
};
|
|
3400
4014
|
});
|
|
4015
|
+
return Promise.all(updatedRoutes.map((route) => reanalyzeMarkdownRoute(route, ctx.projectRoot)));
|
|
3401
4016
|
}
|
|
3402
4017
|
function buildNavigationTree(routes, ctx) {
|
|
3403
4018
|
const tabs = ctx.projectConfig.navigation?.tabs;
|
|
@@ -3995,8 +4610,9 @@ var ClarifyEngine = class {
|
|
|
3995
4610
|
* - `skipModules`: skip `buildModules()` (check command).
|
|
3996
4611
|
* - `skipHints`: skip `logStartupHints()` (check command).
|
|
3997
4612
|
*
|
|
3998
|
-
*
|
|
3999
|
-
*
|
|
4613
|
+
* Configuration initialization is cached unless `force` is set. Discovery
|
|
4614
|
+
* and virtual module generation intentionally run on every call so callers
|
|
4615
|
+
* observe content changes without rebuilding the Engine instance.
|
|
4000
4616
|
*/
|
|
4001
4617
|
async prepare(env = {
|
|
4002
4618
|
command: this.runtime.command,
|
|
@@ -4116,10 +4732,11 @@ var ClarifyEngine = class {
|
|
|
4116
4732
|
assertNoContentDiagnostics(this.routes);
|
|
4117
4733
|
const outputDir = this.runtime.outputDirectory ?? this.generateOptions.outputDirectory;
|
|
4118
4734
|
if (!outputDir) throw new Error("[clarify] outputDirectory is required before SSG runs");
|
|
4119
|
-
|
|
4735
|
+
let ssrOutputDir;
|
|
4120
4736
|
let tempEntryPath;
|
|
4121
4737
|
try {
|
|
4122
4738
|
tempEntryPath = createTempEntryFile(SSR_ENTRY_CODE);
|
|
4739
|
+
ssrOutputDir = createClarifyTempDir("ssr-output");
|
|
4123
4740
|
await (this.runtime.buildSSRBundle ?? ((options) => buildSSRBundle(options.root, options.ssrEntry, options.ssrOutDir, options.plugins)))({
|
|
4124
4741
|
root: this.root,
|
|
4125
4742
|
ssrEntry: tempEntryPath,
|
|
@@ -4132,9 +4749,8 @@ var ClarifyEngine = class {
|
|
|
4132
4749
|
console.error("[clarify] SSG failed:", err);
|
|
4133
4750
|
throw err;
|
|
4134
4751
|
} finally {
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
} catch {}
|
|
4752
|
+
removeClarifyTempDir(tempEntryPath ? resolve(tempEntryPath, "..") : void 0);
|
|
4753
|
+
removeClarifyTempDir(ssrOutputDir);
|
|
4138
4754
|
}
|
|
4139
4755
|
});
|
|
4140
4756
|
await this.runBuildDone();
|
|
@@ -4174,4 +4790,4 @@ function createClarifyEngine(options = {}) {
|
|
|
4174
4790
|
//#endregion
|
|
4175
4791
|
export { createClarifyTempDir as a, cliPackageVersion as c, createViteAdapter as d, defineConfig as f, findRouteConflicts as i, cliPackageVersionWithCaret as l, createClarifyEngine as n, deserializeSearchIndex as o, createClarifyRuntimeAliases as r, searchMcpIndex as s, ClarifyEngine as t, printCliVersion as u };
|
|
4176
4792
|
|
|
4177
|
-
//# sourceMappingURL=engine-
|
|
4793
|
+
//# sourceMappingURL=engine-BgfGFPKK.js.map
|