@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
|
@@ -30,18 +30,14 @@ _vitejs_plugin_react = __toESM(_vitejs_plugin_react, 1);
|
|
|
30
30
|
let rehype_raw = require("rehype-raw");
|
|
31
31
|
rehype_raw = __toESM(rehype_raw, 1);
|
|
32
32
|
let _mdx_js_mdx = require("@mdx-js/mdx");
|
|
33
|
+
let github_slugger = require("github-slugger");
|
|
34
|
+
github_slugger = __toESM(github_slugger, 1);
|
|
35
|
+
let _clarify_labs_renderer = require("@clarify-labs/renderer");
|
|
33
36
|
let _shikijs_rehype = require("@shikijs/rehype");
|
|
34
37
|
_shikijs_rehype = __toESM(_shikijs_rehype, 1);
|
|
35
|
-
let rehype_slug = require("rehype-slug");
|
|
36
|
-
rehype_slug = __toESM(rehype_slug, 1);
|
|
37
38
|
let shiki = require("shiki");
|
|
38
39
|
let unist_util_visit = require("unist-util-visit");
|
|
39
|
-
let _clarify_labs_renderer = require("@clarify-labs/renderer");
|
|
40
40
|
let node_fs_promises = require("node:fs/promises");
|
|
41
|
-
let github_slugger = require("github-slugger");
|
|
42
|
-
github_slugger = __toESM(github_slugger, 1);
|
|
43
|
-
let mdast_util_to_string = require("mdast-util-to-string");
|
|
44
|
-
let remark = require("remark");
|
|
45
41
|
let gray_matter = require("gray-matter");
|
|
46
42
|
gray_matter = __toESM(gray_matter, 1);
|
|
47
43
|
let node_child_process = require("node:child_process");
|
|
@@ -76,6 +72,402 @@ function createContentDiagnostic(options) {
|
|
|
76
72
|
};
|
|
77
73
|
}
|
|
78
74
|
//#endregion
|
|
75
|
+
//#region source/parsers/markdown/headings.ts
|
|
76
|
+
var HEADING_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
77
|
+
var INTERNAL_HEADING_ID_PREFIX = "clarify-internal-heading-id:";
|
|
78
|
+
var EXPLICIT_ID_PATTERN = /\s+\{#([^{}]*)\}$/;
|
|
79
|
+
var HEADING_REMARK_PLUGINS = _clarify_labs_renderer.markdownRemarkPlugins;
|
|
80
|
+
function visitHeadings(node, callback) {
|
|
81
|
+
if (node.type === "heading") callback(node);
|
|
82
|
+
for (const child of node.children ?? []) visitHeadings(child, callback);
|
|
83
|
+
}
|
|
84
|
+
function visitHtml(node, callback) {
|
|
85
|
+
if (node.type === "html") callback(node);
|
|
86
|
+
for (const child of node.children ?? []) visitHtml(child, callback);
|
|
87
|
+
}
|
|
88
|
+
function visitMdxIntrinsicHeadings(node, callback) {
|
|
89
|
+
if ((node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") && node.name && /^h[1-6]$/.test(node.name)) callback(node);
|
|
90
|
+
for (const child of node.children ?? []) visitMdxIntrinsicHeadings(child, callback);
|
|
91
|
+
}
|
|
92
|
+
function visitHastHeadings(node, callback) {
|
|
93
|
+
if (node.type === "element" && node.tagName && /^h[1-6]$/.test(node.tagName)) callback(node);
|
|
94
|
+
for (const child of node.children ?? []) visitHastHeadings(child, callback);
|
|
95
|
+
}
|
|
96
|
+
function hastText$1(node) {
|
|
97
|
+
if (node.type === "text") return node.value ?? "";
|
|
98
|
+
return (node.children ?? []).map(hastText$1).join("");
|
|
99
|
+
}
|
|
100
|
+
function applySourceEdits(content, edits) {
|
|
101
|
+
return edits.sort((left, right) => right.start - left.start).reduce((result, edit) => result.slice(0, edit.start) + edit.replacement + result.slice(edit.end), content);
|
|
102
|
+
}
|
|
103
|
+
function headingPosition(node, lineOffset) {
|
|
104
|
+
return {
|
|
105
|
+
line: (node.position?.start.line ?? 1) + lineOffset,
|
|
106
|
+
column: node.position?.start.column ?? 1
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function formatPosition(position) {
|
|
110
|
+
return `${position.line}:${position.column}`;
|
|
111
|
+
}
|
|
112
|
+
function finalTextNode(heading) {
|
|
113
|
+
const node = heading.children?.at(-1);
|
|
114
|
+
return node?.type === "text" && typeof node.value === "string" ? node : void 0;
|
|
115
|
+
}
|
|
116
|
+
function contentEndOffset(heading) {
|
|
117
|
+
return (heading.children?.at(-1))?.position?.end.offset ?? heading.position?.end.offset;
|
|
118
|
+
}
|
|
119
|
+
function explicitIdInfo(heading, content) {
|
|
120
|
+
const text = finalTextNode(heading);
|
|
121
|
+
if (!text?.position) return void 0;
|
|
122
|
+
const startOffset = text.position.start.offset;
|
|
123
|
+
const endOffset = text.position.end.offset;
|
|
124
|
+
if (startOffset === void 0 || endOffset === void 0) return void 0;
|
|
125
|
+
const match = content.slice(startOffset, endOffset).match(EXPLICIT_ID_PATTERN);
|
|
126
|
+
if (!match || match.index === void 0) return void 0;
|
|
127
|
+
return {
|
|
128
|
+
id: match[1] ?? "",
|
|
129
|
+
start: startOffset + match.index,
|
|
130
|
+
end: startOffset + match.index + match[0].length
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function maskExplicitHeadingMarkers(content) {
|
|
134
|
+
const tree = (0, _mdx_js_mdx.createProcessor)({
|
|
135
|
+
format: "md",
|
|
136
|
+
remarkPlugins: HEADING_REMARK_PLUGINS
|
|
137
|
+
}).parse(content);
|
|
138
|
+
const edits = [];
|
|
139
|
+
visitHeadings(tree, (heading) => {
|
|
140
|
+
const marker = explicitIdInfo(heading, content);
|
|
141
|
+
if (marker) edits.push({
|
|
142
|
+
start: marker.start,
|
|
143
|
+
end: marker.end,
|
|
144
|
+
replacement: "x".repeat(marker.end - marker.start)
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
return applySourceEdits(content, edits);
|
|
148
|
+
}
|
|
149
|
+
function parseHeadingTree(content, kind) {
|
|
150
|
+
if (kind === "markdown") return (0, _mdx_js_mdx.createProcessor)({
|
|
151
|
+
format: "md",
|
|
152
|
+
remarkPlugins: HEADING_REMARK_PLUGINS
|
|
153
|
+
}).parse(content);
|
|
154
|
+
const maskedContent = maskExplicitHeadingMarkers(content);
|
|
155
|
+
try {
|
|
156
|
+
return (0, _mdx_js_mdx.createProcessor)({
|
|
157
|
+
format: "mdx",
|
|
158
|
+
remarkPlugins: HEADING_REMARK_PLUGINS
|
|
159
|
+
}).parse(maskedContent);
|
|
160
|
+
} catch {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function renderedMarkdownHeadingTitles(tree, content, kind) {
|
|
165
|
+
const originalText = /* @__PURE__ */ new Map();
|
|
166
|
+
visitHeadings(tree, (heading) => {
|
|
167
|
+
const marker = explicitIdInfo(heading, content);
|
|
168
|
+
const text = finalTextNode(heading);
|
|
169
|
+
if (!marker || !text || text.value === void 0) return;
|
|
170
|
+
originalText.set(text, text.value);
|
|
171
|
+
text.value = text.value.slice(0, -(marker.end - marker.start)).replace(/\s+$/, "");
|
|
172
|
+
});
|
|
173
|
+
try {
|
|
174
|
+
let hast;
|
|
175
|
+
const captureHast = () => (tree) => {
|
|
176
|
+
hast = tree;
|
|
177
|
+
};
|
|
178
|
+
(0, _mdx_js_mdx.createProcessor)({
|
|
179
|
+
format: kind === "markdown" ? "md" : "mdx",
|
|
180
|
+
remarkPlugins: HEADING_REMARK_PLUGINS,
|
|
181
|
+
...kind === "markdown" ? { remarkRehypeOptions: { allowDangerousHtml: true } } : {},
|
|
182
|
+
rehypePlugins: kind === "markdown" ? [rehype_raw.default, captureHast] : [captureHast]
|
|
183
|
+
}).runSync(tree, { value: content });
|
|
184
|
+
const titles = /* @__PURE__ */ new Map();
|
|
185
|
+
if (!hast) return titles;
|
|
186
|
+
visitHastHeadings(hast, (heading) => {
|
|
187
|
+
const offset = heading.position?.start.offset;
|
|
188
|
+
if (offset !== void 0) titles.set(offset, hastText$1(heading));
|
|
189
|
+
});
|
|
190
|
+
return titles;
|
|
191
|
+
} finally {
|
|
192
|
+
for (const [text, value] of originalText) text.value = value;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function isHtmlWhitespace(character) {
|
|
196
|
+
return character === " " || character === "\n" || character === "\f" || character === "\r" || character === " ";
|
|
197
|
+
}
|
|
198
|
+
function isMdxWhitespace(character) {
|
|
199
|
+
return /\s/u.test(character ?? "");
|
|
200
|
+
}
|
|
201
|
+
function openingTagAttributeOffset(content, start, end, isWhitespace) {
|
|
202
|
+
let state = "tagName";
|
|
203
|
+
let quote;
|
|
204
|
+
for (let index = start; index < end; index++) {
|
|
205
|
+
const character = content[index];
|
|
206
|
+
const closesTag = character === ">";
|
|
207
|
+
const selfClosesTag = character === "/" && content[index + 1] === ">";
|
|
208
|
+
if (state === "quotedAttributeValue") {
|
|
209
|
+
if (character === quote) {
|
|
210
|
+
quote = void 0;
|
|
211
|
+
state = "afterQuotedAttributeValue";
|
|
212
|
+
}
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (state === "unquotedAttributeValue") {
|
|
216
|
+
if (closesTag) return index;
|
|
217
|
+
if (isWhitespace(character)) state = "beforeAttributeName";
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (closesTag) return index;
|
|
221
|
+
if (selfClosesTag && state !== "beforeAttributeValue") {
|
|
222
|
+
let cursor = index;
|
|
223
|
+
while (cursor > start && isWhitespace(content[cursor - 1])) cursor--;
|
|
224
|
+
return cursor;
|
|
225
|
+
}
|
|
226
|
+
if (state === "tagName") {
|
|
227
|
+
if (isWhitespace(character)) state = "beforeAttributeName";
|
|
228
|
+
} else if (state === "beforeAttributeName") {
|
|
229
|
+
if (!isWhitespace(character)) state = "attributeName";
|
|
230
|
+
} else if (state === "attributeName") {
|
|
231
|
+
if (character === "=") state = "beforeAttributeValue";
|
|
232
|
+
else if (isWhitespace(character)) state = "afterAttributeName";
|
|
233
|
+
} else if (state === "afterAttributeName") {
|
|
234
|
+
if (character === "=") state = "beforeAttributeValue";
|
|
235
|
+
else if (!isWhitespace(character)) state = "attributeName";
|
|
236
|
+
} else if (state === "beforeAttributeValue") {
|
|
237
|
+
if (character === "\"" || character === "'") {
|
|
238
|
+
quote = character;
|
|
239
|
+
state = "quotedAttributeValue";
|
|
240
|
+
} else if (!isWhitespace(character)) state = "unquotedAttributeValue";
|
|
241
|
+
} else if (state === "afterQuotedAttributeValue" && isWhitespace(character)) state = "beforeAttributeName";
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function rawHtmlHeadings(tree, content, lineOffset) {
|
|
245
|
+
let hasRawHtml = false;
|
|
246
|
+
visitHtml(tree, () => {
|
|
247
|
+
hasRawHtml = true;
|
|
248
|
+
});
|
|
249
|
+
if (!hasRawHtml) return [];
|
|
250
|
+
const markdownOffsets = /* @__PURE__ */ new Set();
|
|
251
|
+
visitHeadings(tree, (heading) => {
|
|
252
|
+
const offset = heading.position?.start.offset;
|
|
253
|
+
if (offset !== void 0) markdownOffsets.add(offset);
|
|
254
|
+
});
|
|
255
|
+
let hast;
|
|
256
|
+
const captureHast = () => (tree) => {
|
|
257
|
+
hast = tree;
|
|
258
|
+
};
|
|
259
|
+
(0, _mdx_js_mdx.createProcessor)({
|
|
260
|
+
format: "md",
|
|
261
|
+
remarkRehypeOptions: { allowDangerousHtml: true },
|
|
262
|
+
rehypePlugins: [rehype_raw.default, captureHast]
|
|
263
|
+
}).runSync(tree, { value: content });
|
|
264
|
+
if (!hast) return [];
|
|
265
|
+
const headings = [];
|
|
266
|
+
visitHastHeadings(hast, (heading) => {
|
|
267
|
+
const offset = heading.position?.start.offset;
|
|
268
|
+
const headingEnd = heading.position?.end.offset;
|
|
269
|
+
if (offset === void 0 || headingEnd === void 0 || markdownOffsets.has(offset)) return;
|
|
270
|
+
const tagEnd = openingTagAttributeOffset(content, offset, headingEnd, isHtmlWhitespace);
|
|
271
|
+
if (tagEnd === void 0) return;
|
|
272
|
+
const rawId = heading.properties?.id;
|
|
273
|
+
headings.push({
|
|
274
|
+
title: hastText$1(heading),
|
|
275
|
+
...typeof rawId === "string" && rawId ? { id: rawId } : {},
|
|
276
|
+
position: {
|
|
277
|
+
line: (heading.position?.start.line ?? 1) + lineOffset,
|
|
278
|
+
column: heading.position?.start.column ?? 1
|
|
279
|
+
},
|
|
280
|
+
offset,
|
|
281
|
+
attributeOffset: tagEnd
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
return headings;
|
|
285
|
+
}
|
|
286
|
+
function staticMdxText(node) {
|
|
287
|
+
if (node.type === "text" || node.type === "inlineCode") return node.value ?? "";
|
|
288
|
+
if (node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") {
|
|
289
|
+
if (!node.name || !/^[a-z][a-z0-9-]*$/.test(node.name)) return void 0;
|
|
290
|
+
} else if (!node.children) return;
|
|
291
|
+
const parts = [];
|
|
292
|
+
for (const child of node.children ?? []) {
|
|
293
|
+
const text = staticMdxText(child);
|
|
294
|
+
if (text === void 0) return void 0;
|
|
295
|
+
parts.push(text);
|
|
296
|
+
}
|
|
297
|
+
return parts.join("");
|
|
298
|
+
}
|
|
299
|
+
function literalMdxId(node) {
|
|
300
|
+
let result = { kind: "missing" };
|
|
301
|
+
for (const attribute of node.attributes ?? []) {
|
|
302
|
+
if (attribute.type !== "mdxJsxAttribute") {
|
|
303
|
+
result = { kind: "unknown" };
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
if (attribute.name !== "id") continue;
|
|
307
|
+
result = typeof attribute.value === "string" ? {
|
|
308
|
+
kind: "literal",
|
|
309
|
+
id: attribute.value
|
|
310
|
+
} : { kind: "unknown" };
|
|
311
|
+
}
|
|
312
|
+
return result;
|
|
313
|
+
}
|
|
314
|
+
function mdxIntrinsicHeadings(tree, content, lineOffset) {
|
|
315
|
+
const headings = [];
|
|
316
|
+
visitMdxIntrinsicHeadings(tree, (heading) => {
|
|
317
|
+
const position = heading.position;
|
|
318
|
+
const offset = position?.start.offset;
|
|
319
|
+
const end = position?.end.offset;
|
|
320
|
+
const title = staticMdxText(heading);
|
|
321
|
+
const literalId = literalMdxId(heading);
|
|
322
|
+
if (!position || offset === void 0 || end === void 0 || literalId.kind !== "literal") return;
|
|
323
|
+
const tagEnd = openingTagAttributeOffset(content, offset, end, isMdxWhitespace);
|
|
324
|
+
if (tagEnd === void 0) return;
|
|
325
|
+
headings.push({
|
|
326
|
+
...title !== void 0 ? { title } : {},
|
|
327
|
+
id: literalId.id,
|
|
328
|
+
position: {
|
|
329
|
+
line: position.start.line + lineOffset,
|
|
330
|
+
column: position.start.column
|
|
331
|
+
},
|
|
332
|
+
offset,
|
|
333
|
+
attributeOffset: tagEnd
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
return headings;
|
|
337
|
+
}
|
|
338
|
+
function escapeHtmlAttribute(value) {
|
|
339
|
+
return value.replaceAll("&", "&").replaceAll("\"", """);
|
|
340
|
+
}
|
|
341
|
+
function analyzeHeadings(content, options) {
|
|
342
|
+
const lineOffset = options.lineOffset ?? 0;
|
|
343
|
+
const headings = [];
|
|
344
|
+
const edits = [];
|
|
345
|
+
const errors = [];
|
|
346
|
+
const ids = /* @__PURE__ */ new Map();
|
|
347
|
+
const documentHeadings = [];
|
|
348
|
+
const markdownHeadingInputs = /* @__PURE__ */ new Map();
|
|
349
|
+
const tree = parseHeadingTree(content, options.kind);
|
|
350
|
+
if (!tree) return {
|
|
351
|
+
headings,
|
|
352
|
+
normalizedContent: content
|
|
353
|
+
};
|
|
354
|
+
const renderedTitles = renderedMarkdownHeadingTitles(tree, content, options.kind);
|
|
355
|
+
const claimId = (id, position) => {
|
|
356
|
+
const owner = ids.get(id);
|
|
357
|
+
if (owner) {
|
|
358
|
+
errors.push(`Heading ID "${id}" at ${formatPosition(position)} conflicts with the heading at ${formatPosition(owner.position)}.`);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
ids.set(id, {
|
|
362
|
+
id,
|
|
363
|
+
position
|
|
364
|
+
});
|
|
365
|
+
};
|
|
366
|
+
visitHeadings(tree, (node) => {
|
|
367
|
+
const marker = explicitIdInfo(node, content);
|
|
368
|
+
const title = renderedTitles.get(node.position?.start.offset ?? 0) ?? "";
|
|
369
|
+
const hasValidExplicitId = marker !== void 0 && HEADING_ID_PATTERN.test(marker.id);
|
|
370
|
+
const heading = {
|
|
371
|
+
level: node.depth ?? 1,
|
|
372
|
+
title,
|
|
373
|
+
id: hasValidExplicitId ? marker.id : "",
|
|
374
|
+
position: headingPosition(node, lineOffset)
|
|
375
|
+
};
|
|
376
|
+
if (marker && !hasValidExplicitId) errors.push(`Invalid heading ID "${marker.id}" at ${formatPosition(heading.position)}. IDs must match [a-z0-9]+(?:-[a-z0-9]+)*.`);
|
|
377
|
+
headings.push(heading);
|
|
378
|
+
markdownHeadingInputs.set(heading, {
|
|
379
|
+
...marker ? { marker } : {},
|
|
380
|
+
hasValidExplicitId,
|
|
381
|
+
contentEnd: contentEndOffset(node)
|
|
382
|
+
});
|
|
383
|
+
documentHeadings.push({
|
|
384
|
+
title,
|
|
385
|
+
offset: node.position?.start.offset ?? 0,
|
|
386
|
+
markdown: heading
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
const renderedHeadings = options.kind === "markdown" ? rawHtmlHeadings(tree, content, lineOffset) : mdxIntrinsicHeadings(tree, content, lineOffset);
|
|
390
|
+
for (const rendered of renderedHeadings) documentHeadings.push({
|
|
391
|
+
...rendered.title !== void 0 ? { title: rendered.title } : {},
|
|
392
|
+
offset: rendered.offset,
|
|
393
|
+
rendered
|
|
394
|
+
});
|
|
395
|
+
documentHeadings.sort((left, right) => left.offset - right.offset);
|
|
396
|
+
const slugger = new github_slugger.default();
|
|
397
|
+
for (const documentHeading of documentHeadings) {
|
|
398
|
+
if (documentHeading.markdown) {
|
|
399
|
+
const heading = documentHeading.markdown;
|
|
400
|
+
const input = markdownHeadingInputs.get(heading);
|
|
401
|
+
if (input.hasValidExplicitId) {
|
|
402
|
+
edits.push({
|
|
403
|
+
start: input.marker.start,
|
|
404
|
+
end: input.marker.end,
|
|
405
|
+
replacement: ` [](clarify-internal-heading-id:${heading.id})`
|
|
406
|
+
});
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (documentHeading.rendered?.id !== void 0) continue;
|
|
411
|
+
if (documentHeading.title === void 0) continue;
|
|
412
|
+
const generatedId = slugger.slug(documentHeading.title);
|
|
413
|
+
if (documentHeading.markdown) {
|
|
414
|
+
const heading = documentHeading.markdown;
|
|
415
|
+
const input = markdownHeadingInputs.get(heading);
|
|
416
|
+
heading.id = generatedId;
|
|
417
|
+
const link = ` [](clarify-internal-heading-id:${heading.id})`;
|
|
418
|
+
if (input.marker) edits.push({
|
|
419
|
+
start: input.marker.start,
|
|
420
|
+
end: input.marker.end,
|
|
421
|
+
replacement: link
|
|
422
|
+
});
|
|
423
|
+
else if (input.contentEnd !== void 0) edits.push({
|
|
424
|
+
start: input.contentEnd,
|
|
425
|
+
end: input.contentEnd,
|
|
426
|
+
replacement: link
|
|
427
|
+
});
|
|
428
|
+
} else if (documentHeading.rendered && documentHeading.rendered.id === void 0) {
|
|
429
|
+
documentHeading.rendered.id = generatedId;
|
|
430
|
+
edits.push({
|
|
431
|
+
start: documentHeading.rendered.attributeOffset,
|
|
432
|
+
end: documentHeading.rendered.attributeOffset,
|
|
433
|
+
replacement: ` id="${escapeHtmlAttribute(generatedId)}"`
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
for (const documentHeading of documentHeadings) if (documentHeading.markdown) claimId(documentHeading.markdown.id, documentHeading.markdown.position);
|
|
438
|
+
else if (documentHeading.rendered?.id) claimId(documentHeading.rendered.id, documentHeading.rendered.position);
|
|
439
|
+
const diagnostic = errors.length === 0 ? void 0 : createContentDiagnostic({
|
|
440
|
+
kind: options.kind,
|
|
441
|
+
title: "Heading ID error",
|
|
442
|
+
message: "This page has invalid or conflicting heading IDs. Fix the IDs below, then reload the route.",
|
|
443
|
+
error: new Error(errors.join("\n")),
|
|
444
|
+
filePath: options.filePath,
|
|
445
|
+
projectRoot: options.projectRoot
|
|
446
|
+
});
|
|
447
|
+
return {
|
|
448
|
+
headings,
|
|
449
|
+
normalizedContent: applySourceEdits(content, edits),
|
|
450
|
+
diagnostic
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
function remarkApplyHeadingIds() {
|
|
454
|
+
return (tree) => {
|
|
455
|
+
visitHeadings(tree, (heading) => {
|
|
456
|
+
const finalNode = heading.children?.at(-1);
|
|
457
|
+
if (finalNode?.type !== "link" || finalNode.children?.length !== 0 || typeof finalNode.url !== "string" || !finalNode.url.startsWith(INTERNAL_HEADING_ID_PREFIX)) return;
|
|
458
|
+
const id = finalNode.url.slice(28);
|
|
459
|
+
heading.children?.pop();
|
|
460
|
+
const previousNode = heading.children?.at(-1);
|
|
461
|
+
if (previousNode?.type === "text" && typeof previousNode.value === "string") previousNode.value = previousNode.value.replace(/ $/, "");
|
|
462
|
+
heading.data = heading.data ?? {};
|
|
463
|
+
heading.data.hProperties = {
|
|
464
|
+
...heading.data.hProperties,
|
|
465
|
+
id
|
|
466
|
+
};
|
|
467
|
+
});
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
//#endregion
|
|
79
471
|
//#region source/parsers/markdown/mdx.ts
|
|
80
472
|
function getLanguage(className) {
|
|
81
473
|
return (Array.isArray(className) ? className : typeof className === "string" ? className.split(/\s+/) : []).find((item) => typeof item === "string" && item.startsWith("language-"))?.replace(/^language-/, "") || "txt";
|
|
@@ -98,9 +490,34 @@ function rehypeParseCodeBlocks() {
|
|
|
98
490
|
});
|
|
99
491
|
};
|
|
100
492
|
}
|
|
101
|
-
|
|
493
|
+
function hastText(node) {
|
|
494
|
+
if (node.type === "text") return node.value ?? "";
|
|
495
|
+
return (node.children ?? []).map(hastText).join("");
|
|
496
|
+
}
|
|
497
|
+
function isHeadingElement(node) {
|
|
498
|
+
return Boolean(node.tagName && /^h[1-6]$/.test(node.tagName));
|
|
499
|
+
}
|
|
500
|
+
function rehypeAddMissingHeadingIds() {
|
|
501
|
+
return (tree) => {
|
|
502
|
+
const slugger = new github_slugger.default();
|
|
503
|
+
const reservedIds = /* @__PURE__ */ new Set();
|
|
504
|
+
(0, unist_util_visit.visit)(tree, "element", (node) => {
|
|
505
|
+
if (isHeadingElement(node) && typeof node.properties?.id === "string" && node.properties.id) reservedIds.add(node.properties.id);
|
|
506
|
+
});
|
|
507
|
+
(0, unist_util_visit.visit)(tree, "element", (node) => {
|
|
508
|
+
if (!isHeadingElement(node)) return;
|
|
509
|
+
const title = hastText(node);
|
|
510
|
+
let fallbackId = slugger.slug(title);
|
|
511
|
+
if (node.properties?.id) return;
|
|
512
|
+
while (reservedIds.has(fallbackId)) fallbackId = slugger.slug(title);
|
|
513
|
+
node.properties = node.properties ?? {};
|
|
514
|
+
node.properties.id = fallbackId;
|
|
515
|
+
});
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
var remarkPlugins = [remarkApplyHeadingIds, ..._clarify_labs_renderer.markdownRemarkPlugins];
|
|
102
519
|
var rehypePlugins = [
|
|
103
|
-
|
|
520
|
+
rehypeAddMissingHeadingIds,
|
|
104
521
|
rehypeParseCodeBlocks,
|
|
105
522
|
[_shikijs_rehype.default, {
|
|
106
523
|
theme: cssVariablesTheme,
|
|
@@ -255,9 +672,12 @@ async function runBuildDoneHooks(plugins, ctx) {
|
|
|
255
672
|
*/
|
|
256
673
|
function parseFrontmatter(content) {
|
|
257
674
|
const parsed = (0, gray_matter.default)(content);
|
|
675
|
+
const body = parsed.content.replace(/^\r?\n/, "");
|
|
676
|
+
const prefix = content.slice(0, content.length - body.length);
|
|
258
677
|
return {
|
|
259
678
|
frontmatter: parsed.data,
|
|
260
|
-
content:
|
|
679
|
+
content: body,
|
|
680
|
+
lineOffset: [...prefix.matchAll(/\r\n|\r|\n/g)].length
|
|
261
681
|
};
|
|
262
682
|
}
|
|
263
683
|
//#endregion
|
|
@@ -268,7 +688,7 @@ async function transformContent(transform, input) {
|
|
|
268
688
|
function createContentProcessor(transform) {
|
|
269
689
|
return {
|
|
270
690
|
async processMdx(source, filePath) {
|
|
271
|
-
const { frontmatter, content } = parseFrontmatter(source);
|
|
691
|
+
const { frontmatter, content, lineOffset } = parseFrontmatter(source);
|
|
272
692
|
const transformed = await transformContent(transform, {
|
|
273
693
|
kind: "markdown+jsx",
|
|
274
694
|
source,
|
|
@@ -278,7 +698,8 @@ function createContentProcessor(transform) {
|
|
|
278
698
|
});
|
|
279
699
|
return {
|
|
280
700
|
frontmatter: transformed.frontmatter,
|
|
281
|
-
content: transformed.content
|
|
701
|
+
content: transformed.content,
|
|
702
|
+
lineOffset
|
|
282
703
|
};
|
|
283
704
|
},
|
|
284
705
|
async processText(source, kind, filePath) {
|
|
@@ -388,13 +809,47 @@ function resolveUpdatedAt(frontmatter, detectedAt) {
|
|
|
388
809
|
return normalizeDate(typeof frontmatter.updatedAt === "string" ? frontmatter.updatedAt : void 0) ?? detectedAt;
|
|
389
810
|
}
|
|
390
811
|
//#endregion
|
|
391
|
-
//#region source/parsers/routes/
|
|
812
|
+
//#region source/parsers/routes/heading-metadata.ts
|
|
392
813
|
function kebabToTitle(str) {
|
|
393
|
-
return str.split("-").map((
|
|
394
|
-
}
|
|
395
|
-
function
|
|
396
|
-
return
|
|
814
|
+
return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
815
|
+
}
|
|
816
|
+
function headingMetadata(analysis) {
|
|
817
|
+
return { sections: analysis.headings.filter((heading) => heading.level === 2 || heading.level === 3).map((heading) => ({
|
|
818
|
+
id: heading.id,
|
|
819
|
+
title: heading.title,
|
|
820
|
+
level: heading.level
|
|
821
|
+
})) };
|
|
822
|
+
}
|
|
823
|
+
function resolvedRouteTitle(route, analysis) {
|
|
824
|
+
const frontmatterTitle = route.source.frontmatter?.title;
|
|
825
|
+
if (typeof frontmatterTitle === "string" && frontmatterTitle) return frontmatterTitle;
|
|
826
|
+
const routePath = route.basePath ?? route.path;
|
|
827
|
+
return kebabToTitle(routePath === "/" ? analysis.headings.find((heading) => heading.level === 1)?.title ?? "" : routePath.split("/").filter(Boolean).at(-1) ?? "") || "Untitled";
|
|
828
|
+
}
|
|
829
|
+
async function reanalyzeMarkdownRoute(route, projectRoot) {
|
|
830
|
+
if (route.kind !== "markdown" && route.kind !== "markdown+jsx") return route;
|
|
831
|
+
const analysis = analyzeHeadings(route.source.content ?? "", {
|
|
832
|
+
kind: route.kind,
|
|
833
|
+
filePath: route.source.filePath,
|
|
834
|
+
projectRoot,
|
|
835
|
+
lineOffset: route.source.lineOffset
|
|
836
|
+
});
|
|
837
|
+
const result = await (route.kind === "markdown" ? compileMarkdownContent : compileMdxContent)(analysis.normalizedContent, {
|
|
838
|
+
filePath: route.source.filePath,
|
|
839
|
+
projectRoot
|
|
840
|
+
});
|
|
841
|
+
return {
|
|
842
|
+
...route,
|
|
843
|
+
meta: {
|
|
844
|
+
...route.meta,
|
|
845
|
+
title: resolvedRouteTitle(route, analysis),
|
|
846
|
+
...headingMetadata(analysis)
|
|
847
|
+
},
|
|
848
|
+
diagnostic: analysis.diagnostic ?? (result.ok ? void 0 : result.diagnostic)
|
|
849
|
+
};
|
|
397
850
|
}
|
|
851
|
+
//#endregion
|
|
852
|
+
//#region source/parsers/routes/routes.ts
|
|
398
853
|
async function compileRouteDiagnostic(content, filePath, baseDir) {
|
|
399
854
|
const result = await ((0, node_path.extname)(filePath).toLowerCase() === ".md" ? compileMarkdownContent : compileMdxContent)(content, {
|
|
400
855
|
filePath,
|
|
@@ -402,30 +857,6 @@ async function compileRouteDiagnostic(content, filePath, baseDir) {
|
|
|
402
857
|
});
|
|
403
858
|
return result.ok ? void 0 : result.diagnostic;
|
|
404
859
|
}
|
|
405
|
-
/** 从内容中提取第一个 H1 标题 */
|
|
406
|
-
function extractH1(content) {
|
|
407
|
-
const tree = parseMdxTree(content);
|
|
408
|
-
let title = "";
|
|
409
|
-
(0, unist_util_visit.visit)(tree, "heading", (node) => {
|
|
410
|
-
if (!title && node.depth === 1) title = (0, mdast_util_to_string.toString)(node);
|
|
411
|
-
});
|
|
412
|
-
return title;
|
|
413
|
-
}
|
|
414
|
-
/** 从 MDX/Markdown 内容中提取 H2/H3 章节 */
|
|
415
|
-
function extractMdxSections(content) {
|
|
416
|
-
const sections = [];
|
|
417
|
-
const slugger = new github_slugger.default();
|
|
418
|
-
(0, unist_util_visit.visit)(parseMdxTree(content), "heading", (node) => {
|
|
419
|
-
if (node.depth !== 2 && node.depth !== 3) return;
|
|
420
|
-
const title = (0, mdast_util_to_string.toString)(node);
|
|
421
|
-
sections.push({
|
|
422
|
-
id: slugger.slug(title),
|
|
423
|
-
title,
|
|
424
|
-
level: node.depth
|
|
425
|
-
});
|
|
426
|
-
});
|
|
427
|
-
return sections;
|
|
428
|
-
}
|
|
429
860
|
function navigationSections(sections) {
|
|
430
861
|
return sections.map((section) => ({
|
|
431
862
|
id: section.id,
|
|
@@ -482,11 +913,12 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
482
913
|
const fullPath = (0, node_path.join)(dir, entry.name);
|
|
483
914
|
if (entry.isDirectory()) routes.push(...await findContentRoutes(fullPath, base, options));
|
|
484
915
|
else if (entry.isFile() && /\.mdx?$/.test(entry.name)) {
|
|
916
|
+
const kind = entry.name.toLowerCase().endsWith(".mdx") ? "markdown+jsx" : "markdown";
|
|
485
917
|
const relativePath = (0, node_path.relative)(base, fullPath);
|
|
486
918
|
const pathParts = relativePath.replace(/\.mdx?$/, "").split("/");
|
|
487
919
|
const cleanPath = ("/" + pathParts.map((p) => p === "index" ? "" : p).filter(Boolean).join("/")).replace(/\/+/g, "/").replace(/\/$/, "") || "/";
|
|
488
920
|
const source = await (0, node_fs_promises.readFile)(fullPath, "utf-8");
|
|
489
|
-
const { frontmatter, content } = await (options.contentProcessor ?? createContentProcessor()).processMdx(source, fullPath);
|
|
921
|
+
const { frontmatter, content, lineOffset } = await (options.contentProcessor ?? createContentProcessor()).processMdx(source, fullPath);
|
|
490
922
|
const updatedAt = resolveUpdatedAt(frontmatter, await getFileUpdatedAt(fullPath, base));
|
|
491
923
|
const page = {
|
|
492
924
|
path: cleanPath,
|
|
@@ -494,14 +926,21 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
494
926
|
frontmatter,
|
|
495
927
|
content
|
|
496
928
|
};
|
|
497
|
-
const
|
|
929
|
+
const analysis = analyzeHeadings(page.content, {
|
|
930
|
+
kind,
|
|
931
|
+
filePath: fullPath,
|
|
932
|
+
projectRoot: base,
|
|
933
|
+
lineOffset
|
|
934
|
+
});
|
|
935
|
+
const compileDiagnostic = await compileRouteDiagnostic(analysis.normalizedContent, fullPath, base);
|
|
936
|
+
const { sections } = headingMetadata(analysis);
|
|
498
937
|
let title = typeof page.frontmatter.title === "string" ? page.frontmatter.title : "";
|
|
499
938
|
if (!title) {
|
|
500
939
|
const lastPart = pathParts[pathParts.length - 1] ?? "";
|
|
501
|
-
title = kebabToTitle(lastPart === "index" ? pathParts.length >= 2 ? pathParts[pathParts.length - 2] :
|
|
940
|
+
title = kebabToTitle(lastPart === "index" ? pathParts.length >= 2 ? pathParts[pathParts.length - 2] : analysis.headings.find((heading) => heading.level === 1)?.title ?? "" : lastPart) || "Untitled";
|
|
502
941
|
}
|
|
503
942
|
routes.push({
|
|
504
|
-
kind
|
|
943
|
+
kind,
|
|
505
944
|
path: cleanPath,
|
|
506
945
|
basePath: cleanPath,
|
|
507
946
|
meta: {
|
|
@@ -511,7 +950,7 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
511
950
|
group: typeof page.frontmatter.group === "string" ? page.frontmatter.group : void 0,
|
|
512
951
|
layout: page.frontmatter.layout === "documentation" || page.frontmatter.layout === "blog" ? page.frontmatter.layout : void 0,
|
|
513
952
|
updatedAt,
|
|
514
|
-
sections
|
|
953
|
+
sections
|
|
515
954
|
},
|
|
516
955
|
module: {
|
|
517
956
|
pageVirtualModuleId: pageVirtualModuleId(relativePath.replace(/\.mdx?$/, "")),
|
|
@@ -520,9 +959,10 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
520
959
|
source: {
|
|
521
960
|
filePath: fullPath,
|
|
522
961
|
frontmatter: page.frontmatter,
|
|
523
|
-
content: page.content
|
|
962
|
+
content: page.content,
|
|
963
|
+
lineOffset
|
|
524
964
|
},
|
|
525
|
-
diagnostic
|
|
965
|
+
diagnostic: analysis.diagnostic ?? compileDiagnostic
|
|
526
966
|
});
|
|
527
967
|
}
|
|
528
968
|
}
|
|
@@ -678,6 +1118,7 @@ function routeIntentFromPagesItem(item) {
|
|
|
678
1118
|
kind: "openapi",
|
|
679
1119
|
ref: item.openapi,
|
|
680
1120
|
tagFilter: item.filter?.tags,
|
|
1121
|
+
operationOrder: item.operationOrder,
|
|
681
1122
|
path: item.path,
|
|
682
1123
|
title: item.title,
|
|
683
1124
|
icon: item.icon
|
|
@@ -1144,6 +1585,7 @@ var clarifyPagesItemSchema = zod.z.lazy(() => zod.z.union([
|
|
|
1144
1585
|
zod.z.object({
|
|
1145
1586
|
openapi: zod.z.string(),
|
|
1146
1587
|
path: zod.z.string().optional(),
|
|
1588
|
+
operationOrder: zod.z.array(zod.z.string()).optional(),
|
|
1147
1589
|
icon: zod.z.string().optional(),
|
|
1148
1590
|
title: clarifyLocalizedTextSchema.optional(),
|
|
1149
1591
|
filter: zod.z.object({ tags: zod.z.array(zod.z.string()).optional() }).strict().optional()
|
|
@@ -1626,10 +2068,14 @@ function createNormalizedContentPlugin(engine) {
|
|
|
1626
2068
|
transform(_code, id) {
|
|
1627
2069
|
if (!/\.mdx?(?:\?|$)/.test(id)) return null;
|
|
1628
2070
|
const filePath = id.replace(/\?.*$/, "");
|
|
1629
|
-
const route = engine.routes.find((route) =>
|
|
1630
|
-
if (!route || route.source.content === void 0) return null;
|
|
2071
|
+
const route = engine.routes.find((route) => route.source.filePath === filePath);
|
|
2072
|
+
if (!route || route.kind !== "markdown+jsx" && route.kind !== "markdown" || route.source.content === void 0) return null;
|
|
1631
2073
|
return {
|
|
1632
|
-
code: route.source.content,
|
|
2074
|
+
code: analyzeHeadings(route.source.content, {
|
|
2075
|
+
kind: route.kind,
|
|
2076
|
+
filePath,
|
|
2077
|
+
projectRoot: engine.root
|
|
2078
|
+
}).normalizedContent,
|
|
1633
2079
|
map: null
|
|
1634
2080
|
};
|
|
1635
2081
|
}
|
|
@@ -1819,7 +2265,7 @@ function createViteAdapter(engine) {
|
|
|
1819
2265
|
}
|
|
1820
2266
|
var cliPackageVersion = {
|
|
1821
2267
|
name: "@clarify-labs/cli",
|
|
1822
|
-
version: "0.11.
|
|
2268
|
+
version: "0.11.20",
|
|
1823
2269
|
license: "AGPL-3.0-only",
|
|
1824
2270
|
type: "module",
|
|
1825
2271
|
repository: {
|
|
@@ -2022,6 +2468,191 @@ function createLlmsTxtArtifact(routes, projectConfig) {
|
|
|
2022
2468
|
return withUtf8Signature(createLlmsTxt(routes, projectConfig));
|
|
2023
2469
|
}
|
|
2024
2470
|
//#endregion
|
|
2471
|
+
//#region source/plugins/openapi/aggregation.ts
|
|
2472
|
+
function mergeOpenAPIRecord(target, source, section, filePath) {
|
|
2473
|
+
if (!source) return;
|
|
2474
|
+
for (const [key, value] of Object.entries(source)) {
|
|
2475
|
+
const existing = target[key];
|
|
2476
|
+
if (existing !== void 0 && JSON.stringify(existing) !== JSON.stringify(value)) throw new Error(`[clarify] Cannot aggregate OpenAPI specs: conflicting ${section} entry "${key}" in ${filePath}.`);
|
|
2477
|
+
target[key] = value;
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
function openAPIVersionFamily(version) {
|
|
2481
|
+
return version.split(".").slice(0, 2).join(".");
|
|
2482
|
+
}
|
|
2483
|
+
var OPENAPI_OPERATION_METHODS = /* @__PURE__ */ new Set([
|
|
2484
|
+
"get",
|
|
2485
|
+
"put",
|
|
2486
|
+
"post",
|
|
2487
|
+
"delete",
|
|
2488
|
+
"options",
|
|
2489
|
+
"head",
|
|
2490
|
+
"patch",
|
|
2491
|
+
"trace"
|
|
2492
|
+
]);
|
|
2493
|
+
function sourceComponentPrefix(source) {
|
|
2494
|
+
return (source.id || source.filePath.split(/[\\/]/).pop()?.replace(/\.openapi\.(?:json|ya?ml)$/i, "") || "service").replace(/[^a-zA-Z0-9._-]/g, "_") || "service";
|
|
2495
|
+
}
|
|
2496
|
+
function securitySchemeNameMap(source, sourceSchemes, targetSchemes) {
|
|
2497
|
+
const names = /* @__PURE__ */ new Map();
|
|
2498
|
+
for (const [name, definition] of Object.entries(sourceSchemes)) {
|
|
2499
|
+
const existing = targetSchemes[name];
|
|
2500
|
+
if (existing === void 0 || JSON.stringify(existing) === JSON.stringify(definition)) {
|
|
2501
|
+
targetSchemes[name] = definition;
|
|
2502
|
+
names.set(name, name);
|
|
2503
|
+
continue;
|
|
2504
|
+
}
|
|
2505
|
+
const prefix = sourceComponentPrefix(source);
|
|
2506
|
+
let candidate = `${prefix}__${name}`;
|
|
2507
|
+
let suffix = 2;
|
|
2508
|
+
while (targetSchemes[candidate] !== void 0 && JSON.stringify(targetSchemes[candidate]) !== JSON.stringify(definition)) candidate = `${prefix}__${name}_${suffix++}`;
|
|
2509
|
+
targetSchemes[candidate] = definition;
|
|
2510
|
+
names.set(name, candidate);
|
|
2511
|
+
}
|
|
2512
|
+
return names;
|
|
2513
|
+
}
|
|
2514
|
+
function rewriteSecurityRequirements(value, schemeNames) {
|
|
2515
|
+
if (!Array.isArray(value)) return value;
|
|
2516
|
+
return value.map((requirement) => {
|
|
2517
|
+
if (!requirement || typeof requirement !== "object" || Array.isArray(requirement)) return requirement;
|
|
2518
|
+
return Object.fromEntries(Object.entries(requirement).map(([name, scopes]) => [schemeNames.get(name) ?? name, scopes]));
|
|
2519
|
+
});
|
|
2520
|
+
}
|
|
2521
|
+
function localizePathItemContext(pathItemValue, documentServers, documentSecurity, hasDocumentSecurity, schemeNames) {
|
|
2522
|
+
if (!pathItemValue || typeof pathItemValue !== "object" || Array.isArray(pathItemValue)) return pathItemValue;
|
|
2523
|
+
const pathItem = structuredClone(pathItemValue);
|
|
2524
|
+
const pathServers = Object.hasOwn(pathItem, "servers") ? pathItem.servers : documentServers;
|
|
2525
|
+
for (const [method, operationValue] of Object.entries(pathItem)) {
|
|
2526
|
+
if (!OPENAPI_OPERATION_METHODS.has(method) || !operationValue || typeof operationValue !== "object" || Array.isArray(operationValue)) continue;
|
|
2527
|
+
const operation = operationValue;
|
|
2528
|
+
if (!Object.hasOwn(operation, "servers") && pathServers !== void 0) operation.servers = structuredClone(pathServers);
|
|
2529
|
+
if (Object.hasOwn(operation, "security")) operation.security = rewriteSecurityRequirements(operation.security, schemeNames);
|
|
2530
|
+
else if (hasDocumentSecurity) operation.security = rewriteSecurityRequirements(structuredClone(documentSecurity), schemeNames);
|
|
2531
|
+
}
|
|
2532
|
+
delete pathItem.servers;
|
|
2533
|
+
return pathItem;
|
|
2534
|
+
}
|
|
2535
|
+
function localizeOpenAPIContexts(spec, schemeNames) {
|
|
2536
|
+
const document = spec;
|
|
2537
|
+
const hasDocumentSecurity = Object.hasOwn(document, "security");
|
|
2538
|
+
const localizeEntries = (entries) => entries && Object.fromEntries(Object.entries(entries).map(([name, pathItem]) => [name, localizePathItemContext(pathItem, document.servers, document.security, hasDocumentSecurity, schemeNames)]));
|
|
2539
|
+
return {
|
|
2540
|
+
paths: localizeEntries(spec.paths) ?? {},
|
|
2541
|
+
webhooks: localizeEntries("webhooks" in spec ? spec.webhooks : void 0)
|
|
2542
|
+
};
|
|
2543
|
+
}
|
|
2544
|
+
function aggregateOpenAPISources(sources, info) {
|
|
2545
|
+
const paths = {};
|
|
2546
|
+
const webhooks = {};
|
|
2547
|
+
const components = {};
|
|
2548
|
+
const tags = /* @__PURE__ */ new Map();
|
|
2549
|
+
let openapiVersion;
|
|
2550
|
+
for (const source of sources) {
|
|
2551
|
+
const { document: spec, filePath } = source;
|
|
2552
|
+
if (openapiVersion && openAPIVersionFamily(openapiVersion) !== openAPIVersionFamily(spec.openapi)) throw new Error(`[clarify] Cannot aggregate OpenAPI specs: incompatible versions "${openapiVersion}" and "${spec.openapi}" in ${filePath}.`);
|
|
2553
|
+
openapiVersion ??= spec.openapi;
|
|
2554
|
+
components.securitySchemes ??= {};
|
|
2555
|
+
const sourceComponents = spec.components;
|
|
2556
|
+
const localized = localizeOpenAPIContexts(spec, securitySchemeNameMap(source, sourceComponents?.securitySchemes ?? {}, components.securitySchemes));
|
|
2557
|
+
mergeOpenAPIRecord(paths, localized.paths, "paths", filePath);
|
|
2558
|
+
mergeOpenAPIRecord(webhooks, localized.webhooks, "webhooks", filePath);
|
|
2559
|
+
for (const [section, entries] of Object.entries(sourceComponents ?? {})) {
|
|
2560
|
+
if (section === "securitySchemes") continue;
|
|
2561
|
+
components[section] ??= {};
|
|
2562
|
+
mergeOpenAPIRecord(components[section], entries, `components.${section}`, filePath);
|
|
2563
|
+
}
|
|
2564
|
+
for (const tag of spec.tags ?? []) {
|
|
2565
|
+
const existing = tags.get(tag.name);
|
|
2566
|
+
if (existing !== void 0 && JSON.stringify(existing) !== JSON.stringify(tag)) throw new Error(`[clarify] Cannot aggregate OpenAPI specs: conflicting tag "${tag.name}" in ${filePath}.`);
|
|
2567
|
+
tags.set(tag.name, tag);
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
if (components.securitySchemes && Object.keys(components.securitySchemes).length === 0) delete components.securitySchemes;
|
|
2571
|
+
return {
|
|
2572
|
+
openapi: openapiVersion ?? "3.1.0",
|
|
2573
|
+
info: {
|
|
2574
|
+
title: info.title,
|
|
2575
|
+
description: info.description,
|
|
2576
|
+
version: info.version ?? "1.0.0"
|
|
2577
|
+
},
|
|
2578
|
+
paths,
|
|
2579
|
+
...Object.keys(webhooks).length > 0 ? { webhooks } : {},
|
|
2580
|
+
...Object.keys(components).length > 0 ? { components } : {},
|
|
2581
|
+
...tags.size > 0 ? { tags: [...tags.values()] } : {}
|
|
2582
|
+
};
|
|
2583
|
+
}
|
|
2584
|
+
//#endregion
|
|
2585
|
+
//#region source/plugins/openapi/artifacts.ts
|
|
2586
|
+
function selectRootOpenAPISources(routes, projectConfig) {
|
|
2587
|
+
const defaultLocale = projectConfig.locales?.default;
|
|
2588
|
+
const sourcesByFilePath = /* @__PURE__ */ new Map();
|
|
2589
|
+
for (const route of routes) {
|
|
2590
|
+
if (route.kind !== "openapi" || route.diagnostic || !route.source.content || route.openapi?.tagFilter?.length || route.isBareAlias || defaultLocale && route.locale && route.locale !== defaultLocale) continue;
|
|
2591
|
+
sourcesByFilePath.set(route.source.filePath, {
|
|
2592
|
+
id: route.openapi?.sourceSpecId ?? route.source.filePath.split(/[\\/]/).pop()?.replace(/\.openapi\.(?:json|ya?ml)$/i, "") ?? "service",
|
|
2593
|
+
filePath: route.source.filePath,
|
|
2594
|
+
document: JSON.parse(route.source.content)
|
|
2595
|
+
});
|
|
2596
|
+
}
|
|
2597
|
+
return [...sourcesByFilePath.values()];
|
|
2598
|
+
}
|
|
2599
|
+
function createRootOpenAPISpec(routes, projectConfig) {
|
|
2600
|
+
return aggregateOpenAPISources(selectRootOpenAPISources(routes, projectConfig), {
|
|
2601
|
+
title: projectConfig.title,
|
|
2602
|
+
description: projectConfig.description
|
|
2603
|
+
});
|
|
2604
|
+
}
|
|
2605
|
+
function createOpenAPIRouteArtifacts(route) {
|
|
2606
|
+
if (route.kind !== "openapi" || !route.source.content) return [];
|
|
2607
|
+
const contentArtifactUrl = route.artifacts?.contentArtifactUrl;
|
|
2608
|
+
if (!contentArtifactUrl) return [];
|
|
2609
|
+
return [{
|
|
2610
|
+
fileName: contentArtifactUrl.replace(/^\//, ""),
|
|
2611
|
+
contentType: "application/json; charset=utf-8",
|
|
2612
|
+
source: route.source.content
|
|
2613
|
+
}, {
|
|
2614
|
+
fileName: contentArtifactUrl.replace(/\.json$/, ".yaml").replace(/^\//, ""),
|
|
2615
|
+
contentType: "text/yaml; charset=utf-8",
|
|
2616
|
+
source: (0, yaml.stringify)(JSON.parse(route.source.content), { lineWidth: 0 })
|
|
2617
|
+
}];
|
|
2618
|
+
}
|
|
2619
|
+
function createRootOpenAPIArtifact(routes, projectConfig) {
|
|
2620
|
+
return {
|
|
2621
|
+
fileName: "openapi.json",
|
|
2622
|
+
contentType: "application/json; charset=utf-8",
|
|
2623
|
+
source: JSON.stringify(createRootOpenAPISpec(routes, projectConfig), null, 2)
|
|
2624
|
+
};
|
|
2625
|
+
}
|
|
2626
|
+
//#endregion
|
|
2627
|
+
//#region source/plugins/content-artifacts/provider.ts
|
|
2628
|
+
function createContentArtifacts(routes, projectConfig) {
|
|
2629
|
+
const artifacts = [];
|
|
2630
|
+
for (const route of routes) {
|
|
2631
|
+
if (route.kind === "openapi") {
|
|
2632
|
+
artifacts.push(...createOpenAPIRouteArtifacts(route));
|
|
2633
|
+
continue;
|
|
2634
|
+
}
|
|
2635
|
+
const contentArtifactUrl = route.artifacts?.contentArtifactUrl;
|
|
2636
|
+
if (!contentArtifactUrl) continue;
|
|
2637
|
+
artifacts.push({
|
|
2638
|
+
fileName: contentArtifactUrl.replace(/^\//, ""),
|
|
2639
|
+
contentType: "text/markdown; charset=utf-8",
|
|
2640
|
+
source: readRouteArtifactContent(route)
|
|
2641
|
+
});
|
|
2642
|
+
}
|
|
2643
|
+
artifacts.push({
|
|
2644
|
+
fileName: "llms.txt",
|
|
2645
|
+
contentType: "text/plain; charset=utf-8",
|
|
2646
|
+
source: createLlmsTxtArtifact(routes, projectConfig)
|
|
2647
|
+
});
|
|
2648
|
+
artifacts.push(createRootOpenAPIArtifact(routes, projectConfig));
|
|
2649
|
+
return artifacts;
|
|
2650
|
+
}
|
|
2651
|
+
function findContentArtifact(path, routes, projectConfig) {
|
|
2652
|
+
const fileName = path.replace(/^\/+/, "");
|
|
2653
|
+
return createContentArtifacts(routes, projectConfig).find((artifact) => artifact.fileName === fileName);
|
|
2654
|
+
}
|
|
2655
|
+
//#endregion
|
|
2025
2656
|
//#region source/plugins/content-artifacts/server.ts
|
|
2026
2657
|
function normalizeRoutePrefix$1(routePrefix) {
|
|
2027
2658
|
if (!routePrefix || routePrefix === "/") return "";
|
|
@@ -2033,34 +2664,12 @@ function resolveContentArtifactPath(url, projectConfig) {
|
|
|
2033
2664
|
const hasBasePath = requestPath === basePath || requestPath.startsWith(`${basePath}/`);
|
|
2034
2665
|
return basePath && hasBasePath ? requestPath.slice(basePath.length) || "/" : requestPath;
|
|
2035
2666
|
}
|
|
2036
|
-
function resolveContentArtifactType(route) {
|
|
2037
|
-
return route.kind === "openapi" ? "application/json; charset=utf-8" : "text/markdown; charset=utf-8";
|
|
2038
|
-
}
|
|
2039
2667
|
function serveContentArtifacts(req, res, projectConfig, routes) {
|
|
2040
|
-
const
|
|
2041
|
-
if (
|
|
2042
|
-
res.statusCode = 200;
|
|
2043
|
-
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
2044
|
-
res.end(createLlmsTxtArtifact(routes, projectConfig), "utf8");
|
|
2045
|
-
return true;
|
|
2046
|
-
}
|
|
2047
|
-
if (contentPath.endsWith(".openapi.yaml")) {
|
|
2048
|
-
const jsonPath = contentPath.replace(/\.yaml$/, ".json");
|
|
2049
|
-
const route = routes.find((route) => route.artifacts?.contentArtifactUrl === jsonPath);
|
|
2050
|
-
if (route?.kind === "openapi" && route.source.content) {
|
|
2051
|
-
const spec = JSON.parse(route.source.content);
|
|
2052
|
-
res.statusCode = 200;
|
|
2053
|
-
res.setHeader("Content-Type", "text/yaml; charset=utf-8");
|
|
2054
|
-
res.end((0, yaml.stringify)(spec, { lineWidth: 0 }), "utf8");
|
|
2055
|
-
return true;
|
|
2056
|
-
}
|
|
2057
|
-
return false;
|
|
2058
|
-
}
|
|
2059
|
-
const route = routes.find((route) => route.artifacts?.contentArtifactUrl === contentPath);
|
|
2060
|
-
if (!route) return false;
|
|
2668
|
+
const artifact = findContentArtifact(resolveContentArtifactPath(req.url, projectConfig), routes, projectConfig);
|
|
2669
|
+
if (!artifact) return false;
|
|
2061
2670
|
res.statusCode = 200;
|
|
2062
|
-
res.setHeader("Content-Type",
|
|
2063
|
-
res.end(
|
|
2671
|
+
res.setHeader("Content-Type", artifact.contentType);
|
|
2672
|
+
res.end(artifact.source);
|
|
2064
2673
|
return true;
|
|
2065
2674
|
}
|
|
2066
2675
|
//#endregion
|
|
@@ -2081,30 +2690,7 @@ function createContentArtifactsPlugin() {
|
|
|
2081
2690
|
});
|
|
2082
2691
|
};
|
|
2083
2692
|
},
|
|
2084
|
-
"build:assets": (ctx) =>
|
|
2085
|
-
const assets = [];
|
|
2086
|
-
for (const route of ctx.routes) {
|
|
2087
|
-
const contentArtifactUrl = route.artifacts?.contentArtifactUrl;
|
|
2088
|
-
if (!contentArtifactUrl) continue;
|
|
2089
|
-
assets.push({
|
|
2090
|
-
fileName: contentArtifactUrl.replace(/^\//, ""),
|
|
2091
|
-
source: readRouteArtifactContent(route)
|
|
2092
|
-
});
|
|
2093
|
-
if (route.kind === "openapi" && route.source.content) {
|
|
2094
|
-
const yamlFileName = contentArtifactUrl.replace(/\.json$/, ".yaml");
|
|
2095
|
-
const spec = JSON.parse(route.source.content);
|
|
2096
|
-
assets.push({
|
|
2097
|
-
fileName: yamlFileName.replace(/^\//, ""),
|
|
2098
|
-
source: (0, yaml.stringify)(spec, { lineWidth: 0 })
|
|
2099
|
-
});
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
assets.push({
|
|
2103
|
-
fileName: "llms.txt",
|
|
2104
|
-
source: createLlmsTxtArtifact(ctx.routes, ctx.projectConfig)
|
|
2105
|
-
});
|
|
2106
|
-
return assets;
|
|
2107
|
-
}
|
|
2693
|
+
"build:assets": (ctx) => createContentArtifacts(ctx.routes, ctx.projectConfig)
|
|
2108
2694
|
}
|
|
2109
2695
|
};
|
|
2110
2696
|
}
|
|
@@ -2628,9 +3214,9 @@ function filterSpecByTags(spec, tags) {
|
|
|
2628
3214
|
};
|
|
2629
3215
|
}
|
|
2630
3216
|
/** Extract endpoint operations from an OpenAPI spec as page sections. */
|
|
2631
|
-
function extractOpenAPISections(spec, filterTags) {
|
|
3217
|
+
function extractOpenAPISections(spec, filterTags, operationOrder) {
|
|
2632
3218
|
normalizeOpenAPISpecSectionIds(spec);
|
|
2633
|
-
const
|
|
3219
|
+
const entries = [];
|
|
2634
3220
|
const items = [...Object.entries(spec.paths ?? {}).map(([path, pathItem]) => ({
|
|
2635
3221
|
path,
|
|
2636
3222
|
pathItem,
|
|
@@ -2648,16 +3234,25 @@ function extractOpenAPISections(spec, filterTags) {
|
|
|
2648
3234
|
const tags = Array.isArray(op?.tags) ? op.tags : void 0;
|
|
2649
3235
|
if (!op || !operationMatchesTags(tags, filterTags)) continue;
|
|
2650
3236
|
const title = typeof op.summary === "string" ? op.summary : `${method.toUpperCase()} ${path}`;
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
3237
|
+
entries.push({
|
|
3238
|
+
section: {
|
|
3239
|
+
id: operationSectionId(op),
|
|
3240
|
+
title,
|
|
3241
|
+
level: 2,
|
|
3242
|
+
badge: kind === "webhook" ? "WEBHOOK" : method.toUpperCase(),
|
|
3243
|
+
tags
|
|
3244
|
+
},
|
|
3245
|
+
operationId: typeof op.operationId === "string" ? op.operationId : void 0
|
|
2657
3246
|
});
|
|
2658
3247
|
}
|
|
2659
3248
|
}
|
|
2660
|
-
return
|
|
3249
|
+
if (!operationOrder?.length) return entries.map(({ section }) => section);
|
|
3250
|
+
const orderById = new Map(operationOrder.map((operationId, index) => [operationId, index]));
|
|
3251
|
+
return entries.map((entry, index) => ({
|
|
3252
|
+
...entry,
|
|
3253
|
+
index,
|
|
3254
|
+
order: entry.operationId ? orderById.get(entry.operationId) : void 0
|
|
3255
|
+
})).sort((left, right) => (left.order ?? operationOrder.length) - (right.order ?? operationOrder.length) || left.index - right.index).map(({ section }) => section);
|
|
2661
3256
|
}
|
|
2662
3257
|
async function readOpenAPISpec(filePath, contentProcessor, projectRoot) {
|
|
2663
3258
|
try {
|
|
@@ -2704,7 +3299,7 @@ function collectOpenAPIPageIntents(config) {
|
|
|
2704
3299
|
continue;
|
|
2705
3300
|
}
|
|
2706
3301
|
const intent = routeIntentFromPagesItem(item);
|
|
2707
|
-
if (intent.kind === "openapi" && (intent.path || intent.tagFilter?.length)) intents.push(intent);
|
|
3302
|
+
if (intent.kind === "openapi" && (intent.path || intent.tagFilter?.length || intent.operationOrder?.length)) intents.push(intent);
|
|
2708
3303
|
}
|
|
2709
3304
|
}
|
|
2710
3305
|
function visitPages(pages) {
|
|
@@ -2725,7 +3320,8 @@ function cloneOpenAPIRoute(route, overrides = {}) {
|
|
|
2725
3320
|
source: { ...route.source },
|
|
2726
3321
|
openapi: route.openapi ? {
|
|
2727
3322
|
...route.openapi,
|
|
2728
|
-
tagFilter: route.openapi.tagFilter ? [...route.openapi.tagFilter] : void 0
|
|
3323
|
+
tagFilter: route.openapi.tagFilter ? [...route.openapi.tagFilter] : void 0,
|
|
3324
|
+
operationOrder: route.openapi.operationOrder ? [...route.openapi.operationOrder] : void 0
|
|
2729
3325
|
} : void 0,
|
|
2730
3326
|
alternates: route.alternates ? { ...route.alternates } : void 0,
|
|
2731
3327
|
...overrides
|
|
@@ -2774,9 +3370,21 @@ function expandConfiguredOpenAPIRoutes(routes, config) {
|
|
|
2774
3370
|
const existingKeys = new Set(routes.map((route) => `${route.locale ?? ""}:${route.basePath ?? route.path}`));
|
|
2775
3371
|
for (const intent of openAPIIntents) {
|
|
2776
3372
|
const tagFilter = intent.tagFilter?.filter(Boolean);
|
|
3373
|
+
const operationOrder = intent.operationOrder?.filter(Boolean);
|
|
2777
3374
|
const sourceBasePath = openAPIPagePathFromRef(intent.ref);
|
|
2778
3375
|
const targetBasePath = openAPIPagePathFromRef(intent.ref, tagFilter, intent.path);
|
|
2779
|
-
if (sourceBasePath === targetBasePath && !tagFilter?.length)
|
|
3376
|
+
if (sourceBasePath === targetBasePath && !tagFilter?.length) {
|
|
3377
|
+
if (!operationOrder?.length) continue;
|
|
3378
|
+
for (let index = 0; index < routes.length; index++) {
|
|
3379
|
+
const route = routes[index];
|
|
3380
|
+
if (route.kind !== "openapi" || (route.basePath ?? route.path) !== sourceBasePath) continue;
|
|
3381
|
+
routes[index] = cloneOpenAPIRoute(route, { openapi: {
|
|
3382
|
+
...route.openapi,
|
|
3383
|
+
operationOrder: [...operationOrder]
|
|
3384
|
+
} });
|
|
3385
|
+
}
|
|
3386
|
+
continue;
|
|
3387
|
+
}
|
|
2780
3388
|
for (const route of routes) {
|
|
2781
3389
|
if (route.kind !== "openapi" || (route.basePath ?? route.path) !== sourceBasePath) continue;
|
|
2782
3390
|
const key = `${route.locale ?? ""}:${targetBasePath}`;
|
|
@@ -2789,7 +3397,8 @@ function expandConfiguredOpenAPIRoutes(routes, config) {
|
|
|
2789
3397
|
module: { pageVirtualModuleId: pageVirtualModuleId(path) },
|
|
2790
3398
|
openapi: {
|
|
2791
3399
|
...route.openapi,
|
|
2792
|
-
tagFilter: tagFilter?.length ? tagFilter : void 0
|
|
3400
|
+
tagFilter: tagFilter?.length ? tagFilter : void 0,
|
|
3401
|
+
operationOrder: operationOrder?.length ? [...operationOrder] : void 0
|
|
2793
3402
|
}
|
|
2794
3403
|
}));
|
|
2795
3404
|
}
|
|
@@ -2831,10 +3440,11 @@ function generateOpenAPISpecModule(spec) {
|
|
|
2831
3440
|
return `export default ${JSON.stringify(spec)};`;
|
|
2832
3441
|
}
|
|
2833
3442
|
function generateOpenAPIPageModule(opts) {
|
|
3443
|
+
const routeData = opts.operationOrder?.length ? { operationOrder: opts.operationOrder } : {};
|
|
2834
3444
|
return `import { createOpenApiRouteComponent } from '@clarify-labs/renderer';
|
|
2835
3445
|
import spec from ${JSON.stringify(opts.specModuleId)};
|
|
2836
3446
|
|
|
2837
|
-
export const routeData = {};
|
|
3447
|
+
export const routeData = ${JSON.stringify(routeData)};
|
|
2838
3448
|
|
|
2839
3449
|
export default createOpenApiRouteComponent({ ...routeData, spec });
|
|
2840
3450
|
`;
|
|
@@ -2894,7 +3504,7 @@ var OpenAPIPluginState = class {
|
|
|
2894
3504
|
route.meta.title = spec.info?.title ?? route.meta.title;
|
|
2895
3505
|
route.meta.description = spec.info?.description ?? route.meta.description;
|
|
2896
3506
|
route.meta.updatedAt = updatedAt;
|
|
2897
|
-
route.meta.sections = extractOpenAPISections(spec, route.openapi.tagFilter);
|
|
3507
|
+
route.meta.sections = extractOpenAPISections(spec, route.openapi.tagFilter, route.openapi.operationOrder);
|
|
2898
3508
|
const pageSpec = route.openapi.tagFilter?.length ? filterSpecByTags(specWithMetadata, route.openapi.tagFilter) : specWithMetadata;
|
|
2899
3509
|
const routeSpecModuleId = routeSpecModuleIdFromSourceSpecId(sourceSpecId, route.openapi.tagFilter);
|
|
2900
3510
|
route.openapi.routeSpecModuleId = routeSpecModuleId;
|
|
@@ -2928,7 +3538,10 @@ var OpenAPIPluginState = class {
|
|
|
2928
3538
|
if (!route.openapi?.sourceSpecId) continue;
|
|
2929
3539
|
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2930
3540
|
if (!this.specModules.has(routeSpecModuleId)) continue;
|
|
2931
|
-
modules.set(route.module.pageVirtualModuleId, generateOpenAPIPageModule({
|
|
3541
|
+
modules.set(route.module.pageVirtualModuleId, generateOpenAPIPageModule({
|
|
3542
|
+
specModuleId: specVirtualModuleId(routeSpecModuleId),
|
|
3543
|
+
operationOrder: route.openapi.operationOrder
|
|
3544
|
+
}));
|
|
2932
3545
|
}
|
|
2933
3546
|
return modules;
|
|
2934
3547
|
}
|
|
@@ -3418,7 +4031,7 @@ async function resolveRoutePages(routes, plugins, ctx) {
|
|
|
3418
4031
|
pageByRouteIndex.set(routeIndex, page);
|
|
3419
4032
|
}
|
|
3420
4033
|
if (pageByRouteIndex.size !== routes.length) throw new Error("[clarify] pages:resolved hooks must return exactly one page for each route");
|
|
3421
|
-
|
|
4034
|
+
const updatedRoutes = routes.map((route, routeIndex) => {
|
|
3422
4035
|
const page = pageByRouteIndex.get(routeIndex);
|
|
3423
4036
|
return {
|
|
3424
4037
|
...route,
|
|
@@ -3429,6 +4042,7 @@ async function resolveRoutePages(routes, plugins, ctx) {
|
|
|
3429
4042
|
}
|
|
3430
4043
|
};
|
|
3431
4044
|
});
|
|
4045
|
+
return Promise.all(updatedRoutes.map((route) => reanalyzeMarkdownRoute(route, ctx.projectRoot)));
|
|
3432
4046
|
}
|
|
3433
4047
|
function buildNavigationTree(routes, ctx) {
|
|
3434
4048
|
const tabs = ctx.projectConfig.navigation?.tabs;
|
|
@@ -4026,8 +4640,9 @@ var ClarifyEngine = class {
|
|
|
4026
4640
|
* - `skipModules`: skip `buildModules()` (check command).
|
|
4027
4641
|
* - `skipHints`: skip `logStartupHints()` (check command).
|
|
4028
4642
|
*
|
|
4029
|
-
*
|
|
4030
|
-
*
|
|
4643
|
+
* Configuration initialization is cached unless `force` is set. Discovery
|
|
4644
|
+
* and virtual module generation intentionally run on every call so callers
|
|
4645
|
+
* observe content changes without rebuilding the Engine instance.
|
|
4031
4646
|
*/
|
|
4032
4647
|
async prepare(env = {
|
|
4033
4648
|
command: this.runtime.command,
|
|
@@ -4147,10 +4762,11 @@ var ClarifyEngine = class {
|
|
|
4147
4762
|
assertNoContentDiagnostics(this.routes);
|
|
4148
4763
|
const outputDir = this.runtime.outputDirectory ?? this.generateOptions.outputDirectory;
|
|
4149
4764
|
if (!outputDir) throw new Error("[clarify] outputDirectory is required before SSG runs");
|
|
4150
|
-
|
|
4765
|
+
let ssrOutputDir;
|
|
4151
4766
|
let tempEntryPath;
|
|
4152
4767
|
try {
|
|
4153
4768
|
tempEntryPath = createTempEntryFile(SSR_ENTRY_CODE);
|
|
4769
|
+
ssrOutputDir = createClarifyTempDir("ssr-output");
|
|
4154
4770
|
await (this.runtime.buildSSRBundle ?? ((options) => buildSSRBundle(options.root, options.ssrEntry, options.ssrOutDir, options.plugins)))({
|
|
4155
4771
|
root: this.root,
|
|
4156
4772
|
ssrEntry: tempEntryPath,
|
|
@@ -4163,9 +4779,8 @@ var ClarifyEngine = class {
|
|
|
4163
4779
|
console.error("[clarify] SSG failed:", err);
|
|
4164
4780
|
throw err;
|
|
4165
4781
|
} finally {
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
} catch {}
|
|
4782
|
+
removeClarifyTempDir(tempEntryPath ? (0, node_path.resolve)(tempEntryPath, "..") : void 0);
|
|
4783
|
+
removeClarifyTempDir(ssrOutputDir);
|
|
4169
4784
|
}
|
|
4170
4785
|
});
|
|
4171
4786
|
await this.runBuildDone();
|
|
@@ -4276,4 +4891,4 @@ Object.defineProperty(exports, "searchMcpIndex", {
|
|
|
4276
4891
|
}
|
|
4277
4892
|
});
|
|
4278
4893
|
|
|
4279
|
-
//# sourceMappingURL=engine-
|
|
4894
|
+
//# sourceMappingURL=engine-CAis1rme.cjs.map
|