@clarify-labs/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +661 -0
  2. package/bin/clarify.js +2 -0
  3. package/output/cli/commands/build.d.ts +2 -0
  4. package/output/cli/commands/dev.d.ts +2 -0
  5. package/output/cli/commands/init.d.ts +2 -0
  6. package/output/cli/index.d.ts +2 -0
  7. package/output/cli/options.d.ts +17 -0
  8. package/output/cli/package.d.ts +1 -0
  9. package/output/cli.cjs +187 -0
  10. package/output/cli.cjs.map +1 -0
  11. package/output/cli.js +187 -0
  12. package/output/cli.js.map +1 -0
  13. package/output/core/config-schema.d.ts +140 -0
  14. package/output/core/config.d.ts +3 -0
  15. package/output/core/hooks.d.ts +5 -0
  16. package/output/core/options.d.ts +24 -0
  17. package/output/core/plugin.d.ts +4 -0
  18. package/output/core/runtime-deps.d.ts +7 -0
  19. package/output/core/ssg.d.ts +8 -0
  20. package/output/core/user-config.d.ts +7 -0
  21. package/output/core/utils.d.ts +1 -0
  22. package/output/core/virtual-modules.d.ts +18 -0
  23. package/output/core/vite-config.d.ts +10 -0
  24. package/output/index.cjs +4 -0
  25. package/output/index.d.ts +5 -0
  26. package/output/index.js +2 -0
  27. package/output/parsers/frontmatter.d.ts +5 -0
  28. package/output/parsers/mdx.d.ts +14 -0
  29. package/output/parsers/routes.d.ts +13 -0
  30. package/output/plugin-BUWojlPv.js +1277 -0
  31. package/output/plugin-BUWojlPv.js.map +1 -0
  32. package/output/plugin-CEyCiD5r.cjs +1330 -0
  33. package/output/plugin-CEyCiD5r.cjs.map +1 -0
  34. package/output/plugins/content-artifacts/index.d.ts +2 -0
  35. package/output/plugins/content-artifacts/raw-content.d.ts +8 -0
  36. package/output/plugins/content-artifacts/server.d.ts +5 -0
  37. package/output/plugins/html-shell/index.d.ts +2 -0
  38. package/output/plugins/openapi/index.d.ts +2 -0
  39. package/output/plugins/openapi/parser.d.ts +5 -0
  40. package/output/plugins/openapi/virtual-modules.d.ts +4 -0
  41. package/output/types.d.ts +191 -0
  42. package/package.json +63 -0
@@ -0,0 +1,1330 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+ //#endregion
23
+ let node_fs = require("node:fs");
24
+ let node_path = require("node:path");
25
+ let _mdx_js_rollup = require("@mdx-js/rollup");
26
+ _mdx_js_rollup = __toESM(_mdx_js_rollup, 1);
27
+ let _tailwindcss_vite = require("@tailwindcss/vite");
28
+ _tailwindcss_vite = __toESM(_tailwindcss_vite, 1);
29
+ let _vitejs_plugin_react = require("@vitejs/plugin-react");
30
+ _vitejs_plugin_react = __toESM(_vitejs_plugin_react, 1);
31
+ let github_slugger = require("github-slugger");
32
+ github_slugger = __toESM(github_slugger, 1);
33
+ let mdast_util_to_string = require("mdast-util-to-string");
34
+ let remark_gfm = require("remark-gfm");
35
+ remark_gfm = __toESM(remark_gfm, 1);
36
+ let shiki = require("shiki");
37
+ shiki = __toESM(shiki, 1);
38
+ let unist_util_visit = require("unist-util-visit");
39
+ let remark = require("remark");
40
+ let gray_matter = require("gray-matter");
41
+ gray_matter = __toESM(gray_matter, 1);
42
+ let _apidevtools_swagger_parser = require("@apidevtools/swagger-parser");
43
+ _apidevtools_swagger_parser = __toESM(_apidevtools_swagger_parser, 1);
44
+ let zod = require("zod");
45
+ let node_os = require("node:os");
46
+ let node_url = require("node:url");
47
+ let vite = require("vite");
48
+ let node_module = require("node:module");
49
+ //#region source/core/utils.ts
50
+ function escapeHtml(text) {
51
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
52
+ }
53
+ //#endregion
54
+ //#region source/parsers/mdx.ts
55
+ function getLanguage(className) {
56
+ return (Array.isArray(className) ? className : typeof className === "string" ? className.split(/\s+/) : []).find((item) => typeof item === "string" && item.startsWith("language-"))?.replace(/^language-/, "") || "txt";
57
+ }
58
+ function getTextContent(node) {
59
+ if (!node) return "";
60
+ if (typeof node.value === "string") return node.value;
61
+ return node.children?.map(getTextContent).join("") ?? "";
62
+ }
63
+ function renderPlainCode(code) {
64
+ return code.split("\n").map((line) => `<span>${escapeHtml(line)}</span>`).join("\n");
65
+ }
66
+ var highlighter;
67
+ async function getHighlighter() {
68
+ highlighter = highlighter ?? await shiki.default.getHighlighter({ theme: "css-variables" });
69
+ return highlighter;
70
+ }
71
+ function rehypeParseCodeBlocks() {
72
+ return (tree) => {
73
+ (0, unist_util_visit.visit)(tree, "element", (node, _nodeIndex, parentNode) => {
74
+ if (node.tagName !== "code" || !parentNode) return;
75
+ parentNode.properties = parentNode.properties ?? {};
76
+ node.properties = node.properties ?? {};
77
+ const language = getLanguage(node.properties.className);
78
+ parentNode.properties.language = language;
79
+ node.properties.language = language;
80
+ });
81
+ };
82
+ }
83
+ function rehypeShiki() {
84
+ return async (tree) => {
85
+ const shikiHighlighter = await getHighlighter();
86
+ (0, unist_util_visit.visit)(tree, "element", (node) => {
87
+ if (node.tagName !== "pre") return;
88
+ const codeNode = node.children?.[0];
89
+ if (codeNode?.tagName !== "code") return;
90
+ node.properties = node.properties ?? {};
91
+ codeNode.properties = codeNode.properties ?? {};
92
+ const code = getTextContent(codeNode);
93
+ const language = typeof node.properties.language === "string" ? node.properties.language : getLanguage(codeNode.properties.className);
94
+ node.properties.code = code;
95
+ codeNode.properties.code = code;
96
+ codeNode.properties.language = language;
97
+ let highlighted = renderPlainCode(code);
98
+ if (language && language !== "txt") try {
99
+ const tokens = shikiHighlighter.codeToThemedTokens(code, language);
100
+ highlighted = shiki.default.renderToHtml(tokens, { elements: {
101
+ pre: ({ children }) => children,
102
+ code: ({ children }) => children,
103
+ line: ({ children }) => `<span>${children}</span>`
104
+ } });
105
+ } catch {
106
+ highlighted = renderPlainCode(code);
107
+ }
108
+ codeNode.children = [{
109
+ type: "text",
110
+ value: highlighted
111
+ }];
112
+ });
113
+ };
114
+ }
115
+ function rehypeSlugSections() {
116
+ return (tree) => {
117
+ const slugger = new github_slugger.default();
118
+ (0, unist_util_visit.visit)(tree, "element", (node) => {
119
+ if (node.tagName !== "h2" && node.tagName !== "h3") return;
120
+ node.properties = node.properties ?? {};
121
+ if (node.properties.id) return;
122
+ node.properties.id = slugger.slug((0, mdast_util_to_string.toString)(node));
123
+ });
124
+ };
125
+ }
126
+ var remarkPlugins = [remark_gfm.default];
127
+ var rehypePlugins = [
128
+ rehypeSlugSections,
129
+ rehypeParseCodeBlocks,
130
+ rehypeShiki
131
+ ];
132
+ //#endregion
133
+ //#region source/parsers/frontmatter.ts
134
+ /**
135
+ * 从 MDX 文件内容中提取 frontmatter。
136
+ * 使用 gray-matter 支持 YAML frontmatter 的标准语法和类型解析。
137
+ */
138
+ function extractFrontmatter(content) {
139
+ return (0, gray_matter.default)(content).data;
140
+ }
141
+ //#endregion
142
+ //#region source/parsers/routes.ts
143
+ function kebabToTitle$1(str) {
144
+ return str.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
145
+ }
146
+ function parseMdxTree(content) {
147
+ return remark.remark.parse(content);
148
+ }
149
+ /** 从内容中提取第一个 H1 标题 */
150
+ function extractH1(content) {
151
+ const tree = parseMdxTree(content);
152
+ let title = "";
153
+ (0, unist_util_visit.visit)(tree, "heading", (node) => {
154
+ if (!title && node.depth === 1) title = (0, mdast_util_to_string.toString)(node);
155
+ });
156
+ return title;
157
+ }
158
+ /** 从 MDX/Markdown 内容中提取 H2/H3 章节 */
159
+ function extractMdxSections(content) {
160
+ const sections = [];
161
+ const slugger = new github_slugger.default();
162
+ (0, unist_util_visit.visit)(parseMdxTree(content), "heading", (node) => {
163
+ if (node.depth !== 2 && node.depth !== 3) return;
164
+ const title = (0, mdast_util_to_string.toString)(node);
165
+ sections.push({
166
+ id: slugger.slug(title),
167
+ title,
168
+ level: node.depth
169
+ });
170
+ });
171
+ return sections;
172
+ }
173
+ function navigationSections(sections) {
174
+ return sections.map((s) => ({
175
+ id: s.id,
176
+ title: s.title,
177
+ badge: s.badge,
178
+ tags: s.tags
179
+ }));
180
+ }
181
+ function normalizePath(path) {
182
+ const clean = "/" + path.replace(/^\/+|\/+$/g, "");
183
+ return clean === "/" ? "/" : clean.replace(/\/+/g, "/");
184
+ }
185
+ function basePathFromRef(ref) {
186
+ const withoutExtension = ref.replace(/\.mdx?$/, "").replace(/\.openapi\.(json|yaml|yml)$/, "");
187
+ return normalizePath(withoutExtension === "index" ? "/" : withoutExtension.replace(/\/index$/, ""));
188
+ }
189
+ function routePathFromRef(ref) {
190
+ return basePathFromRef(ref);
191
+ }
192
+ function virtualModuleIdFromRef(ref) {
193
+ return "virtual:clarify-page/" + ref.replace(/\.mdx?$/, "").replace(/\.openapi\.(json|yaml|yml)$/, "").replace(/\/+/g, "/");
194
+ }
195
+ function localizedRoutePath(basePath, locale, i18n) {
196
+ const normalizedBasePath = normalizePath(basePath);
197
+ if (locale === i18n.defaultLocale) return normalizedBasePath;
198
+ const prefix = normalizePath(locale);
199
+ return normalizedBasePath === "/" ? prefix : normalizePath(`${prefix}${normalizedBasePath}`);
200
+ }
201
+ function resolveLocalizedText(text, locale, fallbackLocale) {
202
+ if (typeof text === "string" || text === void 0) return text;
203
+ return text[locale] ?? (fallbackLocale ? text[fallbackLocale] : void 0) ?? Object.values(text)[0];
204
+ }
205
+ function findContentRoutes(dir, base = dir) {
206
+ const routes = [];
207
+ if (!(0, node_fs.existsSync)(dir)) return routes;
208
+ const entries = (0, node_fs.readdirSync)(dir, { withFileTypes: true });
209
+ for (const entry of entries) {
210
+ const fullPath = (0, node_path.join)(dir, entry.name);
211
+ if (entry.isDirectory()) routes.push(...findContentRoutes(fullPath, base));
212
+ else if (entry.isFile() && /\.mdx?$/.test(entry.name)) {
213
+ const relativePath = (0, node_path.relative)(base, fullPath);
214
+ const pathParts = relativePath.replace(/\.mdx?$/, "").split("/");
215
+ const cleanPath = ("/" + pathParts.map((p) => p === "index" ? "" : p).filter(Boolean).join("/")).replace(/\/+/g, "/").replace(/\/$/, "") || "/";
216
+ const content = (0, node_fs.readFileSync)(fullPath, "utf-8");
217
+ const frontmatter = extractFrontmatter(content);
218
+ let title = typeof frontmatter.title === "string" ? frontmatter.title : "";
219
+ if (!title) {
220
+ const lastPart = pathParts[pathParts.length - 1] ?? "";
221
+ title = kebabToTitle$1(lastPart === "index" ? pathParts.length >= 2 ? pathParts[pathParts.length - 2] : extractH1(content) : lastPart) || "Untitled";
222
+ }
223
+ routes.push({
224
+ path: cleanPath,
225
+ basePath: cleanPath,
226
+ filePath: fullPath,
227
+ virtualModuleId: "virtual:clarify-page/" + relativePath.replace(/\.mdx?$/, "").replace(/\/+/g, "/"),
228
+ title,
229
+ kind: "mdx",
230
+ sections: extractMdxSections(content)
231
+ });
232
+ }
233
+ }
234
+ return routes;
235
+ }
236
+ function buildNavigation(routes) {
237
+ const root = [];
238
+ for (const route of routes) {
239
+ if (route.path === "/") continue;
240
+ const parts = route.path.replace(/^\//, "").split("/");
241
+ let current = root;
242
+ for (let i = 0; i < parts.length; i++) {
243
+ const pathSoFar = "/" + parts.slice(0, i + 1).join("/");
244
+ let node = current.find((n) => n.path === pathSoFar);
245
+ if (!node) {
246
+ node = {
247
+ path: pathSoFar,
248
+ title: i === parts.length - 1 ? route.title : kebabToTitle$1(parts[i]),
249
+ children: []
250
+ };
251
+ current.push(node);
252
+ }
253
+ if (i === parts.length - 1 && route.sections) node.sections = navigationSections(route.sections);
254
+ if (i < parts.length - 1) {
255
+ node.children = node.children ?? [];
256
+ current = node.children;
257
+ }
258
+ }
259
+ }
260
+ return root;
261
+ }
262
+ function resolvePageItem(item) {
263
+ if (typeof item === "string") return { pageRef: item };
264
+ if ("openapi" in item) return {
265
+ openapiRef: item.openapi,
266
+ title: item.title,
267
+ icon: item.icon
268
+ };
269
+ return {
270
+ pageRef: item.page,
271
+ redirect: item.redirect,
272
+ title: item.title,
273
+ icon: item.icon
274
+ };
275
+ }
276
+ function buildNavigationFromConfig(routes, config) {
277
+ const routeMap = new Map(routes.map((r) => [r.path, r]));
278
+ return config.map((group) => {
279
+ const children = group.pages.map((item) => {
280
+ const { pageRef, openapiRef, redirect, title, icon } = resolvePageItem(item);
281
+ if (openapiRef) {
282
+ const path = "/" + openapiRef.replace(/\.openapi\.(json|yaml|yml)$/, "");
283
+ const route = routeMap.get(path);
284
+ return {
285
+ path,
286
+ title: resolveLocalizedText(title, "", "") ?? route?.title ?? kebabToTitle$1(path.split("/").pop() ?? openapiRef),
287
+ icon,
288
+ sections: route?.sections ? navigationSections(route.sections) : void 0
289
+ };
290
+ }
291
+ const ref = pageRef ?? "";
292
+ const path = ref === "index" ? "/" : "/" + ref;
293
+ const route = routeMap.get(path);
294
+ return {
295
+ path: redirect ? redirect : path,
296
+ title: resolveLocalizedText(title, "", "") ?? route?.title ?? kebabToTitle$1(path.split("/").pop() ?? ref),
297
+ icon,
298
+ sections: route?.sections ? navigationSections(route.sections) : void 0
299
+ };
300
+ });
301
+ return {
302
+ path: children[0]?.path ?? "/",
303
+ title: resolveLocalizedText(group.group, "", "") ?? "",
304
+ icon: group.icon,
305
+ children
306
+ };
307
+ });
308
+ }
309
+ function localizeNavigationPaths(nodes, locale, i18n) {
310
+ return nodes.map((node) => ({
311
+ ...node,
312
+ path: localizedRoutePath(node.path, locale, i18n),
313
+ children: node.children ? localizeNavigationPaths(node.children, locale, i18n) : void 0
314
+ }));
315
+ }
316
+ function buildLocalizedNavigation(routes, config, i18n) {
317
+ if (!i18n) return void 0;
318
+ const result = {};
319
+ for (const locale of i18n.locales) {
320
+ const localeRoutes = routes.filter((route) => route.locale === locale.code);
321
+ if (!config || config === "FileTree") {
322
+ const baseNavigation = buildNavigation(localeRoutes.map((route) => ({
323
+ ...route,
324
+ path: route.basePath ?? route.path
325
+ })));
326
+ result[locale.code] = localizeNavigationPaths(baseNavigation, locale.code, i18n);
327
+ continue;
328
+ }
329
+ const routeMap = new Map(localeRoutes.map((route) => [route.basePath ?? route.path, route]));
330
+ result[locale.code] = config.map((group) => {
331
+ const children = group.pages.map((item) => {
332
+ const { pageRef, openapiRef, redirect, title, icon } = resolvePageItem(item);
333
+ const ref = openapiRef ?? pageRef ?? "";
334
+ const basePath = basePathFromRef(ref);
335
+ const route = routeMap.get(basePath);
336
+ const path = route?.path ?? localizedRoutePath(basePath, locale.code, i18n);
337
+ return {
338
+ path: (redirect ? localizedRoutePath(basePathFromRef(redirect), locale.code, i18n) : void 0) ?? path,
339
+ title: resolveLocalizedText(title, locale.code, i18n.defaultLocale) ?? route?.title ?? kebabToTitle$1(basePath.split("/").pop() ?? ref),
340
+ icon,
341
+ sections: route?.sections ? navigationSections(route.sections) : void 0
342
+ };
343
+ });
344
+ return {
345
+ path: children[0]?.path ?? localizedRoutePath("/", locale.code, i18n),
346
+ title: resolveLocalizedText(group.group, locale.code, i18n.defaultLocale) ?? "",
347
+ icon: group.icon,
348
+ children
349
+ };
350
+ });
351
+ }
352
+ return result;
353
+ }
354
+ //#endregion
355
+ //#region source/plugins/content-artifacts/raw-content.ts
356
+ function normalizeBasePath(basePath) {
357
+ if (!basePath || basePath === "/") return "";
358
+ return "/" + basePath.replace(/^\/+|\/+$/g, "");
359
+ }
360
+ function routeToMarkdownUrl(routePath) {
361
+ return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.md`;
362
+ }
363
+ function routeToOpenAPIUrl(routePath, filePath) {
364
+ const extension = (0, node_path.extname)(filePath).toLowerCase() || ".json";
365
+ return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.openapi${extension}`;
366
+ }
367
+ function enrichRoutesWithRawContent(routes) {
368
+ for (const route of routes) route.rawContentUrl = route.kind === "openapi" ? routeToOpenAPIUrl(route.path, route.filePath) : routeToMarkdownUrl(route.path);
369
+ }
370
+ function readRawContent(route) {
371
+ return (0, node_fs.readFileSync)(route.filePath, "utf-8");
372
+ }
373
+ function writeRawContentFiles(routes, outputDirectory) {
374
+ for (const route of routes) {
375
+ if (!route.rawContentUrl) continue;
376
+ const outFile = (0, node_path.join)(outputDirectory, route.rawContentUrl.replace(/^\//, ""));
377
+ (0, node_fs.mkdirSync)((0, node_path.dirname)(outFile), { recursive: true });
378
+ (0, node_fs.writeFileSync)(outFile, readRawContent(route), "utf-8");
379
+ }
380
+ }
381
+ function createLlmsTxt(routes, projectConfig) {
382
+ const basePath = normalizeBasePath(projectConfig.routePrefix);
383
+ const lines = [`# ${projectConfig.title}`, ""];
384
+ if (projectConfig.description) lines.push(`> ${projectConfig.description}`, "");
385
+ lines.push("## Docs");
386
+ for (const route of routes.filter((route) => route.kind === "mdx")) {
387
+ if (!route.rawContentUrl) continue;
388
+ lines.push(`- [${route.title}](${basePath}${route.rawContentUrl})`);
389
+ }
390
+ const openApiRoutes = routes.filter((route) => route.kind === "openapi");
391
+ if (openApiRoutes.length > 0) {
392
+ lines.push("", "## OpenAPI");
393
+ for (const route of openApiRoutes) {
394
+ if (!route.rawContentUrl) continue;
395
+ lines.push(`- [${route.title}](${basePath}${route.rawContentUrl})`);
396
+ }
397
+ }
398
+ return `${lines.join("\n")}\n`;
399
+ }
400
+ function writeLlmsTxt(routes, projectConfig, outputDirectory) {
401
+ (0, node_fs.mkdirSync)(outputDirectory, { recursive: true });
402
+ (0, node_fs.writeFileSync)((0, node_path.join)(outputDirectory, "llms.txt"), createLlmsTxt(routes, projectConfig), "utf-8");
403
+ }
404
+ //#endregion
405
+ //#region source/plugins/content-artifacts/server.ts
406
+ function normalizeRoutePrefix(routePrefix) {
407
+ if (!routePrefix || routePrefix === "/") return "";
408
+ return `/${routePrefix.replace(/^\/+|\/+$/g, "")}`;
409
+ }
410
+ function resolveContentArtifactPath(url, projectConfig) {
411
+ const requestPath = url?.split("?")[0] ?? "";
412
+ const basePath = normalizeRoutePrefix(projectConfig.routePrefix);
413
+ return basePath && requestPath.startsWith(basePath) ? requestPath.slice(basePath.length) || "/" : requestPath;
414
+ }
415
+ function resolveRawContentType(route) {
416
+ if (route.kind === "openapi" && /\.ya?ml$/i.test(route.rawContentUrl ?? "")) return "text/yaml; charset=utf-8";
417
+ return route.kind === "openapi" ? "application/json; charset=utf-8" : "text/markdown; charset=utf-8";
418
+ }
419
+ function serveContentArtifacts(req, res, projectConfig, routes) {
420
+ const contentPath = resolveContentArtifactPath(req.url, projectConfig);
421
+ if (contentPath === "/llms.txt") {
422
+ res.statusCode = 200;
423
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
424
+ res.end(createLlmsTxt(routes, projectConfig));
425
+ return true;
426
+ }
427
+ const route = routes.find((route) => route.rawContentUrl === contentPath);
428
+ if (!route) return false;
429
+ res.statusCode = 200;
430
+ res.setHeader("Content-Type", resolveRawContentType(route));
431
+ res.end(readRawContent(route));
432
+ return true;
433
+ }
434
+ //#endregion
435
+ //#region source/plugins/content-artifacts/index.ts
436
+ function createContentArtifactsPlugin() {
437
+ return {
438
+ name: "clarify:content-artifacts",
439
+ hooks: {
440
+ "routes:resolved": (input) => {
441
+ enrichRoutesWithRawContent(input.routes);
442
+ return input;
443
+ },
444
+ "dev:configureServer": (server, ctx) => {
445
+ server.middlewares.use((req, res, next) => {
446
+ if (serveContentArtifacts(req, res, ctx.projectConfig, ctx.routes)) return;
447
+ next();
448
+ });
449
+ },
450
+ "build:done": (ctx) => {
451
+ const outputDirectory = ctx.generateOptions.outputDirectory;
452
+ if (!outputDirectory) return;
453
+ writeRawContentFiles(ctx.routes, outputDirectory);
454
+ writeLlmsTxt(ctx.routes, ctx.projectConfig, outputDirectory);
455
+ }
456
+ }
457
+ };
458
+ }
459
+ //#endregion
460
+ //#region source/plugins/html-shell/index.ts
461
+ var THEME_BOOTSTRAP_SCRIPT = `(function(){try{var e=localStorage.getItem('clarify:theme');var t=e==='dark'||e==='light'||e==='system'?e:'system';var r=t==='system'?window.matchMedia('(prefers-color-scheme: dark)').matches:t==='dark';document.documentElement.classList.toggle('dark',r);document.documentElement.style.colorScheme=r?'dark':'light'}catch(e){}})();`;
462
+ var REL_ICON_RE = /<link\s+[^>]*rel=["'][^"']*\bicon\b[^"']*["'][^>]*>/i;
463
+ function inferIconType(href) {
464
+ const path = href.split(/[?#]/, 1)[0]?.toLowerCase() ?? href.toLowerCase();
465
+ if (path.endsWith(".svg")) return "image/svg+xml";
466
+ if (path.endsWith(".png")) return "image/png";
467
+ if (path.endsWith(".ico")) return "image/x-icon";
468
+ if (path.endsWith(".webp")) return "image/webp";
469
+ }
470
+ function createIconTag(href, media) {
471
+ return {
472
+ tag: "link",
473
+ attrs: {
474
+ rel: "icon",
475
+ ...inferIconType(href) ? { type: inferIconType(href) } : {},
476
+ href,
477
+ ...media ? { media } : {}
478
+ },
479
+ injectTo: "head"
480
+ };
481
+ }
482
+ function createIconTags(icon) {
483
+ if (!icon) return [];
484
+ if (typeof icon === "string") return [createIconTag(icon)];
485
+ const lightIcon = icon.light ?? icon.dark;
486
+ const darkIcon = icon.dark ?? icon.light;
487
+ if (!lightIcon || !darkIcon) return [];
488
+ if (lightIcon === darkIcon) return [createIconTag(lightIcon)];
489
+ return [createIconTag(lightIcon, "(prefers-color-scheme: light)"), createIconTag(darkIcon, "(prefers-color-scheme: dark)")];
490
+ }
491
+ function createHtmlShellPlugin() {
492
+ return {
493
+ name: "clarify:html-shell",
494
+ hooks: { "html:transform": (input, ctx) => ({
495
+ html: input.html,
496
+ tags: [
497
+ ...input.tags,
498
+ ...REL_ICON_RE.test(input.html) ? [] : createIconTags(ctx.projectConfig.favicon ?? ctx.projectConfig.logo),
499
+ {
500
+ tag: "script",
501
+ children: THEME_BOOTSTRAP_SCRIPT,
502
+ injectTo: "head-prepend"
503
+ },
504
+ {
505
+ tag: "script",
506
+ attrs: {
507
+ type: "module",
508
+ src: input.clientEntryId
509
+ },
510
+ injectTo: "body"
511
+ }
512
+ ],
513
+ clientEntryId: input.clientEntryId,
514
+ dev: input.dev
515
+ }) }
516
+ };
517
+ }
518
+ //#endregion
519
+ //#region source/plugins/openapi/parser.ts
520
+ var OPENAPI_HTTP_METHODS = [
521
+ "get",
522
+ "put",
523
+ "post",
524
+ "delete",
525
+ "options",
526
+ "head",
527
+ "patch",
528
+ "trace"
529
+ ];
530
+ function kebabToTitle(str) {
531
+ return str.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
532
+ }
533
+ function findOpenAPIRoutes(dir, base = dir) {
534
+ const routes = [];
535
+ if (!(0, node_fs.existsSync)(dir)) return routes;
536
+ const entries = (0, node_fs.readdirSync)(dir, { withFileTypes: true });
537
+ for (const entry of entries) {
538
+ const fullPath = (0, node_path.join)(dir, entry.name);
539
+ if (entry.isDirectory()) {
540
+ routes.push(...findOpenAPIRoutes(fullPath, base));
541
+ continue;
542
+ }
543
+ if (!entry.isFile() || !/\.openapi\.(json|yaml|yml)$/.test(entry.name)) continue;
544
+ const ref = (0, node_path.relative)(base, fullPath);
545
+ const cleanPath = routePathFromRef(ref);
546
+ routes.push({
547
+ path: cleanPath,
548
+ basePath: cleanPath,
549
+ filePath: fullPath,
550
+ virtualModuleId: virtualModuleIdFromRef(ref),
551
+ title: kebabToTitle(cleanPath.split("/").pop() ?? "API"),
552
+ kind: "openapi"
553
+ });
554
+ }
555
+ return routes;
556
+ }
557
+ /** Extract endpoint operations from an OpenAPI spec as page sections. */
558
+ function extractOpenAPISections(spec) {
559
+ const sections = [];
560
+ const paths = spec.paths ?? {};
561
+ for (const [path, pathItem] of Object.entries(paths)) {
562
+ if (!pathItem) continue;
563
+ for (const method of OPENAPI_HTTP_METHODS) {
564
+ const op = pathItem[method];
565
+ if (!op) continue;
566
+ const title = op.summary ?? `${method.toUpperCase()} ${path}`;
567
+ sections.push({
568
+ id: (0, github_slugger.slug)(`${method} ${path}`),
569
+ title,
570
+ level: 2,
571
+ badge: method.toUpperCase()
572
+ });
573
+ }
574
+ }
575
+ return sections;
576
+ }
577
+ async function readOpenAPISpec(filePath) {
578
+ try {
579
+ return await _apidevtools_swagger_parser.default.dereference(filePath);
580
+ } catch {
581
+ return null;
582
+ }
583
+ }
584
+ //#endregion
585
+ //#region source/plugins/openapi/virtual-modules.ts
586
+ var openApiRegistryModuleId = "virtual:clarify-openapi-registry";
587
+ function generateOpenAPIRegistryModule(openApis) {
588
+ return `export const openApis = ${JSON.stringify(openApis)};`;
589
+ }
590
+ function generateOpenAPIModule(spec) {
591
+ return `import { createElement } from 'react';
592
+ import { OpenApiPage } from '@clarify-labs/renderer';
593
+ const spec = ${JSON.stringify(spec)};
594
+ export default function OpenApiRoutePage() {
595
+ return createElement(OpenApiPage, { spec });
596
+ }`;
597
+ }
598
+ //#endregion
599
+ //#region source/plugins/openapi/index.ts
600
+ function createOpenAPIPlugin() {
601
+ const specs = {};
602
+ return {
603
+ name: "clarify:openapi",
604
+ hooks: {
605
+ "routes:discover": (input) => ({
606
+ ...input,
607
+ routes: [...input.routes, ...findOpenAPIRoutes(input.contentRoot)]
608
+ }),
609
+ "routes:discovered": async (routes) => {
610
+ for (const key of Object.keys(specs)) delete specs[key];
611
+ for (const route of routes.filter((route) => route.kind === "openapi")) {
612
+ const spec = await readOpenAPISpec(route.filePath);
613
+ if (!spec) throw new Error(`Failed to parse OpenAPI spec: ${route.filePath}`);
614
+ specs[route.virtualModuleId] = spec;
615
+ specs[`virtual:clarify-page/${route.path.replace(/^\//, "")}`] = spec;
616
+ route.title = spec.info?.title ?? route.title;
617
+ route.sections = extractOpenAPISections(spec);
618
+ }
619
+ return routes;
620
+ },
621
+ "modules:before": (modules) => {
622
+ modules.set(openApiRegistryModuleId, generateOpenAPIRegistryModule(specs));
623
+ for (const [moduleId, spec] of Object.entries(specs)) modules.set(moduleId, generateOpenAPIModule(spec));
624
+ return modules;
625
+ }
626
+ }
627
+ };
628
+ }
629
+ //#endregion
630
+ //#region source/core/config-schema.ts
631
+ var clarifyLogoConfigSchema = zod.z.union([zod.z.string(), zod.z.object({
632
+ light: zod.z.string().optional(),
633
+ dark: zod.z.string().optional()
634
+ })]);
635
+ var clarifyFaviconConfigSchema = zod.z.union([zod.z.string(), zod.z.object({
636
+ light: zod.z.string().optional(),
637
+ dark: zod.z.string().optional()
638
+ })]);
639
+ var clarifyLocalizedTextSchema = zod.z.union([zod.z.string(), zod.z.record(zod.z.string(), zod.z.string())]);
640
+ var clarifyLocaleConfigSchema = zod.z.object({
641
+ code: zod.z.string(),
642
+ label: zod.z.string(),
643
+ dir: zod.z.union([zod.z.literal("ltr"), zod.z.literal("rtl")]).optional()
644
+ });
645
+ var clarifyI18nConfigSchema = zod.z.object({
646
+ defaultLocale: zod.z.string().optional(),
647
+ missing: zod.z.union([
648
+ zod.z.literal("fallback"),
649
+ zod.z.literal("404"),
650
+ zod.z.literal("hide")
651
+ ]).optional(),
652
+ locales: zod.z.array(clarifyLocaleConfigSchema).min(1)
653
+ }).superRefine((config, ctx) => {
654
+ const localeCodes = /* @__PURE__ */ new Set();
655
+ for (const [index, locale] of config.locales.entries()) {
656
+ if (localeCodes.has(locale.code)) ctx.addIssue({
657
+ code: zod.z.ZodIssueCode.custom,
658
+ message: `Duplicate locale code "${locale.code}"`,
659
+ path: [
660
+ "locales",
661
+ index,
662
+ "code"
663
+ ]
664
+ });
665
+ localeCodes.add(locale.code);
666
+ }
667
+ if (config.defaultLocale && !localeCodes.has(config.defaultLocale)) ctx.addIssue({
668
+ code: zod.z.ZodIssueCode.custom,
669
+ message: "defaultLocale must be one of i18n.locales",
670
+ path: ["defaultLocale"]
671
+ });
672
+ });
673
+ var clarifyNavbarLinkSchema = zod.z.object({
674
+ label: clarifyLocalizedTextSchema,
675
+ href: zod.z.string(),
676
+ external: zod.z.boolean().optional()
677
+ });
678
+ var clarifyBannerConfigSchema = zod.z.object({
679
+ content: clarifyLocalizedTextSchema,
680
+ dismissible: zod.z.boolean().optional()
681
+ });
682
+ var clarifyFooterConfigSchema = zod.z.object({
683
+ links: zod.z.array(clarifyNavbarLinkSchema).optional(),
684
+ socials: zod.z.record(zod.z.string(), zod.z.string()).optional(),
685
+ copyright: clarifyLocalizedTextSchema.optional()
686
+ });
687
+ var clarifyPagesItemSchema = zod.z.union([
688
+ zod.z.string(),
689
+ zod.z.object({
690
+ page: zod.z.string(),
691
+ title: clarifyLocalizedTextSchema.optional(),
692
+ icon: zod.z.string().optional(),
693
+ redirect: zod.z.string().optional()
694
+ }),
695
+ zod.z.object({
696
+ openapi: zod.z.string(),
697
+ icon: zod.z.string().optional(),
698
+ title: clarifyLocalizedTextSchema.optional()
699
+ })
700
+ ]);
701
+ var clarifyPagesGroupSchema = zod.z.object({
702
+ group: clarifyLocalizedTextSchema,
703
+ icon: zod.z.string().optional(),
704
+ pages: zod.z.array(clarifyPagesItemSchema)
705
+ });
706
+ var clarifyPagesConfigSchema = zod.z.union([zod.z.array(clarifyPagesGroupSchema), zod.z.literal("FileTree")]);
707
+ var clarifyProjectConfigSchema = zod.z.object({
708
+ title: zod.z.string().optional(),
709
+ description: zod.z.string().optional(),
710
+ logo: clarifyLogoConfigSchema.optional(),
711
+ favicon: clarifyFaviconConfigSchema.optional(),
712
+ theme: zod.z.object({ primary: zod.z.string().optional() }).optional(),
713
+ routePrefix: zod.z.string().optional(),
714
+ navbar: zod.z.object({ links: zod.z.array(clarifyNavbarLinkSchema).optional() }).optional(),
715
+ banner: clarifyBannerConfigSchema.optional(),
716
+ footer: clarifyFooterConfigSchema.optional(),
717
+ i18n: clarifyI18nConfigSchema.optional(),
718
+ pages: clarifyPagesConfigSchema.optional()
719
+ });
720
+ //#endregion
721
+ //#region source/core/config.ts
722
+ function formatIssuePath(path) {
723
+ return path.reduce((result, segment) => {
724
+ if (typeof segment === "number") return `${result}[${segment}]`;
725
+ const key = String(segment);
726
+ return result ? `${result}.${key}` : key;
727
+ }, "");
728
+ }
729
+ function formatProjectConfigError(error) {
730
+ const issue = error.issues[0];
731
+ if (!issue) return "[clarify] config is invalid";
732
+ const path = formatIssuePath(issue.path);
733
+ if (!path) return `[clarify] config must contain an object: ${issue.message}`;
734
+ return `[clarify] config field "${path}" is invalid: ${issue.message}`;
735
+ }
736
+ function validateProjectConfig(value) {
737
+ const result = clarifyProjectConfigSchema.safeParse(value);
738
+ if (!result.success) throw new Error(formatProjectConfigError(result.error));
739
+ return result.data;
740
+ }
741
+ function resolveI18nConfig(i18n) {
742
+ if (!i18n) return void 0;
743
+ const firstLocale = i18n.locales[0]?.code;
744
+ const defaultLocale = i18n.defaultLocale ?? firstLocale;
745
+ if (!defaultLocale) return void 0;
746
+ return {
747
+ defaultLocale,
748
+ missing: i18n.missing ?? "fallback",
749
+ locales: i18n.locales
750
+ };
751
+ }
752
+ function resolveProjectConfig(config = {}) {
753
+ return {
754
+ title: config.title ?? "Clarify Docs",
755
+ description: config.description ?? "",
756
+ logo: config.logo,
757
+ favicon: config.favicon,
758
+ routePrefix: config.routePrefix ?? "/",
759
+ theme: config.theme ?? {},
760
+ navbar: config.navbar,
761
+ banner: config.banner,
762
+ footer: config.footer,
763
+ i18n: resolveI18nConfig(config.i18n),
764
+ pages: config.pages
765
+ };
766
+ }
767
+ //#endregion
768
+ //#region source/core/hooks.ts
769
+ async function runHooks(plugins, hookName, input, ctx) {
770
+ let result = input;
771
+ for (const plugin of plugins) {
772
+ const hook = plugin.hooks[hookName];
773
+ if (!hook) continue;
774
+ try {
775
+ result = await hook(result, ctx);
776
+ } catch (err) {
777
+ throw new Error(`[clarify] plugin "${plugin.name}" hook "${hookName}" failed: ${err}`, { cause: err });
778
+ }
779
+ }
780
+ return result;
781
+ }
782
+ async function runDevConfigureServerHooks(plugins, server, ctx) {
783
+ for (const plugin of plugins) {
784
+ const hook = plugin.hooks["dev:configureServer"];
785
+ if (!hook) continue;
786
+ try {
787
+ await hook(server, ctx);
788
+ } catch (err) {
789
+ throw new Error(`[clarify] plugin "${plugin.name}" hook "dev:configureServer" failed: ${err}`, { cause: err });
790
+ }
791
+ }
792
+ }
793
+ async function runBuildDoneHooks(plugins, ctx) {
794
+ for (const plugin of plugins) {
795
+ const hook = plugin.hooks["build:done"];
796
+ if (!hook) continue;
797
+ try {
798
+ await hook(ctx);
799
+ } catch (err) {
800
+ throw new Error(`[clarify] plugin "${plugin.name}" hook "build:done" failed: ${err}`, { cause: err });
801
+ }
802
+ }
803
+ }
804
+ //#endregion
805
+ //#region source/core/options.ts
806
+ function resolveBuildOptions(options = {}) {
807
+ return {
808
+ rootDirectory: options.rootDirectory ?? "source",
809
+ outputDirectory: options.outputDirectory,
810
+ ssg: { failOnError: options.ssg?.failOnError ?? true }
811
+ };
812
+ }
813
+ //#endregion
814
+ //#region source/core/runtime-deps.ts
815
+ var requireFromCli = (0, node_module.createRequire)({}.url);
816
+ function resolveCliDependency(id) {
817
+ return requireFromCli.resolve(id);
818
+ }
819
+ function resolvePackageExport(packageName, subpath, condition = "import") {
820
+ const packageJsonPath = requireFromCli.resolve(`${packageName}/package.json`);
821
+ const packageRoot = (0, node_path.dirname)(packageJsonPath);
822
+ const packageJson = JSON.parse((0, node_fs.readFileSync)(packageJsonPath, "utf-8"));
823
+ const exportKey = subpath ? `./${subpath}` : ".";
824
+ const exportEntry = packageJson.exports?.[exportKey];
825
+ const target = typeof exportEntry === "string" ? exportEntry : exportEntry?.[condition] ?? exportEntry?.default;
826
+ if (!target) throw new Error(`[clarify] Unable to resolve ${packageName}/${subpath}`);
827
+ return (0, node_path.join)(packageRoot, target);
828
+ }
829
+ /**
830
+ * Resolve Clarify's framework/runtime dependencies from the CLI package instead
831
+ * of from the user's documentation project. This keeps content-only projects
832
+ * free from React, renderer, and tsconfig requirements.
833
+ */
834
+ function createClarifyRuntimeAliases() {
835
+ return [
836
+ {
837
+ find: /^@clarify-labs\/renderer\/style\.css$/,
838
+ replacement: resolvePackageExport("@clarify-labs/renderer", "style.css")
839
+ },
840
+ {
841
+ find: /^@clarify-labs\/renderer\/client$/,
842
+ replacement: resolvePackageExport("@clarify-labs/renderer", "client")
843
+ },
844
+ {
845
+ find: /^@clarify-labs\/renderer\/server$/,
846
+ replacement: resolvePackageExport("@clarify-labs/renderer", "server")
847
+ },
848
+ {
849
+ find: /^@clarify-labs\/renderer$/,
850
+ replacement: resolvePackageExport("@clarify-labs/renderer", "")
851
+ },
852
+ {
853
+ find: /^react\/jsx-runtime$/,
854
+ replacement: resolveCliDependency("react/jsx-runtime")
855
+ },
856
+ {
857
+ find: /^react\/jsx-dev-runtime$/,
858
+ replacement: resolveCliDependency("react/jsx-dev-runtime")
859
+ },
860
+ {
861
+ find: /^react-dom\/client$/,
862
+ replacement: resolveCliDependency("react-dom/client")
863
+ },
864
+ {
865
+ find: /^react-dom\/server$/,
866
+ replacement: resolveCliDependency("react-dom/server")
867
+ },
868
+ {
869
+ find: /^react-dom$/,
870
+ replacement: resolveCliDependency("react-dom")
871
+ },
872
+ {
873
+ find: /^react-router-dom$/,
874
+ replacement: resolveCliDependency("react-router-dom")
875
+ },
876
+ {
877
+ find: /^react$/,
878
+ replacement: resolveCliDependency("react")
879
+ }
880
+ ];
881
+ }
882
+ //#endregion
883
+ //#region source/core/ssg.ts
884
+ function readIndexHtml(outputDirectory) {
885
+ const indexPath = (0, node_path.join)(outputDirectory, "index.html");
886
+ if (!(0, node_fs.existsSync)(indexPath)) return void 0;
887
+ try {
888
+ return (0, node_fs.readFileSync)(indexPath, "utf-8");
889
+ } catch {
890
+ return;
891
+ }
892
+ }
893
+ function injectHtmlLocaleAttributes(html, projectConfig, route) {
894
+ const localeCode = route?.locale ?? projectConfig.i18n?.defaultLocale;
895
+ if (!localeCode) return html;
896
+ const dir = (projectConfig.i18n?.locales.find((locale) => locale.code === localeCode))?.dir;
897
+ return html.replace(/<html\b([^>]*)>/i, (_match, attributes) => {
898
+ let nextAttributes = attributes.replace(/\s+lang=("[^"]*"|'[^']*'|[^\s>]*)/i, "").replace(/\s+dir=("[^"]*"|'[^']*'|[^\s>]*)/i, "");
899
+ nextAttributes = `${nextAttributes} lang="${escapeHtml(localeCode)}"`;
900
+ if (dir) nextAttributes = `${nextAttributes} dir="${escapeHtml(dir)}"`;
901
+ return `<html${nextAttributes}>`;
902
+ });
903
+ }
904
+ function injectSSRIntoTemplate(template, appHtml, projectConfig, route) {
905
+ let html = injectHtmlLocaleAttributes(template, projectConfig, route);
906
+ html = html.replace(/<title>.*?<\/title>/, `<title>${escapeHtml(projectConfig.title)}</title>`);
907
+ if (projectConfig.description && !html.includes("name=\"description\"")) {
908
+ const descriptionMeta = `<meta name="description" content="${escapeHtml(projectConfig.description)}" />`;
909
+ html = html.replace("</head>", ` ${descriptionMeta}\n </head>`);
910
+ }
911
+ html = html.replace(/<div id="root">[\s\S]*?<\/div>/, `<div id="root">${appHtml}</div>`);
912
+ return html;
913
+ }
914
+ var SSR_ENTRY_CODE = `import { renderToHTML } from '@clarify-labs/renderer/server';
915
+ import { routes, navigation } from 'virtual:clarify-routes';
916
+ import { config } from 'virtual:clarify-config';
917
+ import { openApis } from 'virtual:clarify-openapi-registry';
918
+
919
+ export function render(url) {
920
+ return renderToHTML({ config, routes, navigation, openApis, url });
921
+ }`;
922
+ function createTempEntryFile(content) {
923
+ const entryPath = (0, node_path.join)((0, node_fs.mkdtempSync)((0, node_path.join)((0, node_os.tmpdir)(), "clarify-ssr-")), "entry-server.ts");
924
+ (0, node_fs.writeFileSync)(entryPath, content, "utf-8");
925
+ return entryPath;
926
+ }
927
+ async function buildSSRBundle(root, ssrEntry, ssrOutDir, plugins) {
928
+ await (0, vite.build)({
929
+ root,
930
+ configFile: false,
931
+ resolve: { alias: createClarifyRuntimeAliases() },
932
+ plugins,
933
+ build: {
934
+ ssr: ssrEntry,
935
+ outDir: ssrOutDir,
936
+ emptyOutDir: true,
937
+ rollupOptions: { input: ssrEntry }
938
+ }
939
+ });
940
+ }
941
+ async function renderSSGRoutes(routes, projectConfig, outputDirectory, ssrBundlePath, failOnError = true) {
942
+ const { render } = await import((0, node_url.pathToFileURL)(ssrBundlePath).href);
943
+ const template = readIndexHtml(outputDirectory);
944
+ if (!template) throw new Error(`[clarify] index.html not found in outputDirectory "${outputDirectory}". Make sure Vite build produces it.`);
945
+ for (const route of routes) try {
946
+ const finalHtml = injectSSRIntoTemplate(template, render(route.path), projectConfig, route);
947
+ const outFile = (0, node_path.join)(outputDirectory, route.path, "index.html");
948
+ (0, node_fs.mkdirSync)((0, node_path.dirname)(outFile), { recursive: true });
949
+ (0, node_fs.writeFileSync)(outFile, finalHtml, "utf-8");
950
+ } catch (err) {
951
+ console.error(`[clarify] Failed to render route "${route.path}":`, err);
952
+ if (failOnError) throw err;
953
+ }
954
+ }
955
+ //#endregion
956
+ //#region source/core/user-config.ts
957
+ function defineConfig(config) {
958
+ return config;
959
+ }
960
+ var CONFIG_FILENAMES = [
961
+ "clarify.ts",
962
+ "clarify.js",
963
+ "clarify.json"
964
+ ];
965
+ function findClarifyConfigFile(root) {
966
+ for (const filename of CONFIG_FILENAMES) {
967
+ const filePath = (0, node_path.resolve)(root, filename);
968
+ if ((0, node_fs.existsSync)(filePath)) return filePath;
969
+ }
970
+ }
971
+ function assertConfigObject(config, configFile) {
972
+ if (!config || typeof config !== "object" || Array.isArray(config)) throw new Error(`[clarify] ${configFile} must export a configuration object`);
973
+ return config;
974
+ }
975
+ function loadJsonConfig(configFile) {
976
+ const config = assertConfigObject(JSON.parse((0, node_fs.readFileSync)(configFile, "utf-8")), configFile);
977
+ const projectConfig = validateProjectConfig(config);
978
+ const ssg = config.ssg && typeof config.ssg === "object" && !Array.isArray(config.ssg) ? config.ssg : void 0;
979
+ return {
980
+ ...projectConfig,
981
+ ...ssg ? { ssg } : {}
982
+ };
983
+ }
984
+ async function loadClarifyConfig(root, env) {
985
+ const configFile = findClarifyConfigFile(root);
986
+ if (!configFile) return {};
987
+ if (configFile.endsWith(".json")) return loadJsonConfig(configFile);
988
+ return assertConfigObject((await (0, vite.loadConfigFromFile)(env, configFile, root, "silent"))?.config, configFile);
989
+ }
990
+ //#endregion
991
+ //#region source/core/virtual-modules.ts
992
+ var VIRTUAL_CONFIG = "virtual:clarify-config";
993
+ var VIRTUAL_ROUTES = "virtual:clarify-routes";
994
+ var VIRTUAL_OPENAPI_REGISTRY = openApiRegistryModuleId;
995
+ var VIRTUAL_CLIENT_ENTRY = "virtual:clarify-entry-client";
996
+ var RESOLVED_CLIENT_ENTRY = "\0virtual:clarify-entry-client";
997
+ function resolveVirtualId(id) {
998
+ return "\0" + id;
999
+ }
1000
+ function stripVirtualPrefix(id) {
1001
+ return id.startsWith("\0") ? id.slice(1) : id;
1002
+ }
1003
+ function generateConfigModule(projectConfig, buildOptions) {
1004
+ return `export const config = ${JSON.stringify({
1005
+ ...projectConfig,
1006
+ ...buildOptions
1007
+ })};`;
1008
+ }
1009
+ function generateRoutesModule(routes, pagesConfig, resolvedNavigation, projectConfig) {
1010
+ const imports = routes.map((r, i) => `import Page${i} from '${r.virtualModuleId}';`).join("\n");
1011
+ const routesArray = routes.map((r, i) => {
1012
+ const sections = r.sections && r.sections.length > 0 ? `, sections: ${JSON.stringify(r.sections.map((s) => ({
1013
+ id: s.id,
1014
+ title: s.title,
1015
+ badge: s.badge,
1016
+ tags: s.tags
1017
+ })))}` : "";
1018
+ const rawContentUrl = r.rawContentUrl ? `, rawContentUrl: ${JSON.stringify(r.rawContentUrl)}` : "";
1019
+ const basePath = r.basePath ? `, basePath: ${JSON.stringify(r.basePath)}` : "";
1020
+ const locale = r.locale ? `, locale: ${JSON.stringify(r.locale)}` : "";
1021
+ const isFallback = r.isFallback ? ", isFallback: true" : "";
1022
+ const alternates = r.alternates ? `, alternates: ${JSON.stringify(r.alternates)}` : "";
1023
+ return ` { path: ${JSON.stringify(r.path)}, title: ${JSON.stringify(r.title)}, component: Page${i}, kind: '${r.kind}'${basePath}${locale}${isFallback}${alternates}${sections}${rawContentUrl} }`;
1024
+ }).join(",\n");
1025
+ const navigation = resolvedNavigation ?? (projectConfig?.i18n ? buildLocalizedNavigation(routes, pagesConfig, projectConfig.i18n) ?? {} : pagesConfig && pagesConfig !== "FileTree" ? buildNavigationFromConfig(routes, pagesConfig) : buildNavigation(routes));
1026
+ return `${imports}\n\nexport const routes = [\n${routesArray}\n];\n\nexport const navigation = ${JSON.stringify(navigation, null, 2)};\n`;
1027
+ }
1028
+ function createClientEntryModule() {
1029
+ return `
1030
+ import '@clarify-labs/renderer/style.css';
1031
+ import { render } from '@clarify-labs/renderer/client';
1032
+ import { routes, navigation } from '${VIRTUAL_ROUTES}';
1033
+ import { config } from '${VIRTUAL_CONFIG}';
1034
+ import { openApis } from '${VIRTUAL_OPENAPI_REGISTRY}';
1035
+ render({ config, routes, navigation, openApis });`;
1036
+ }
1037
+ function buildVirtualModules(args) {
1038
+ const modules = /* @__PURE__ */ new Map();
1039
+ modules.set(VIRTUAL_CONFIG, generateConfigModule(args.projectConfig, args.generateOptions));
1040
+ modules.set(VIRTUAL_ROUTES, generateRoutesModule(args.routes, args.projectConfig.pages, args.navigation, args.projectConfig));
1041
+ modules.set(VIRTUAL_OPENAPI_REGISTRY, "export const openApis = {};");
1042
+ modules.set(VIRTUAL_CLIENT_ENTRY, createClientEntryModule());
1043
+ modules.set(RESOLVED_CLIENT_ENTRY, createClientEntryModule());
1044
+ for (const route of args.routes) {
1045
+ if (route.kind === "openapi") continue;
1046
+ modules.set(route.virtualModuleId, `export { default } from '${route.filePath}';`);
1047
+ }
1048
+ return modules;
1049
+ }
1050
+ //#endregion
1051
+ //#region source/core/plugin.ts
1052
+ function loadVirtualModule(id, modules) {
1053
+ const bareId = stripVirtualPrefix(id);
1054
+ return modules.get(bareId) ?? modules.get(id) ?? null;
1055
+ }
1056
+ function clarifyPlugin(options = {}) {
1057
+ const root = (0, node_path.resolve)(options.projectRoot ?? process.cwd());
1058
+ let projectConfig = resolveProjectConfig(options);
1059
+ let generateOptions = resolveBuildOptions(options);
1060
+ const contentRoot = (0, node_path.join)(root, generateOptions.rootDirectory);
1061
+ const configFilePath = findClarifyConfigFile(root);
1062
+ let routes = [];
1063
+ const clarifyPlugins = [
1064
+ createOpenAPIPlugin(),
1065
+ createContentArtifactsPlugin(),
1066
+ createHtmlShellPlugin(),
1067
+ ...options.plugins ?? []
1068
+ ];
1069
+ const ctx = {
1070
+ projectConfig,
1071
+ generateOptions,
1072
+ routes,
1073
+ navigation: []
1074
+ };
1075
+ let viteConfig;
1076
+ let resolvedNavigation = [];
1077
+ let virtualModules = /* @__PURE__ */ new Map();
1078
+ async function reloadProjectConfig() {
1079
+ const buildOptions = {
1080
+ ...await loadClarifyConfig(root, {
1081
+ command: "serve",
1082
+ mode: viteConfig.mode
1083
+ }),
1084
+ projectRoot: options.projectRoot,
1085
+ rootDirectory: options.rootDirectory,
1086
+ outputDirectory: options.outputDirectory
1087
+ };
1088
+ projectConfig = resolveProjectConfig(buildOptions);
1089
+ generateOptions = resolveBuildOptions(buildOptions);
1090
+ ctx.projectConfig = projectConfig;
1091
+ ctx.generateOptions = generateOptions;
1092
+ }
1093
+ function withAlternates(route, routeList, i18n) {
1094
+ const basePath = route.basePath ?? route.path;
1095
+ const routeByLocaleAndBase = new Map(routeList.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
1096
+ const alternates = Object.fromEntries(i18n.locales.flatMap((locale) => {
1097
+ const alternate = routeByLocaleAndBase.get(`${locale.code}:${basePath}`);
1098
+ return alternate ? [[locale.code, alternate.path]] : [];
1099
+ }));
1100
+ return {
1101
+ ...route,
1102
+ alternates
1103
+ };
1104
+ }
1105
+ async function discoverRoutesForRoot(routeRoot, locale) {
1106
+ return (await runHooks(clarifyPlugins, "routes:discover", {
1107
+ contentRoot: routeRoot,
1108
+ locale,
1109
+ routes: findContentRoutes(routeRoot)
1110
+ }, ctx)).routes;
1111
+ }
1112
+ async function discoverRoutes() {
1113
+ const i18n = projectConfig.i18n;
1114
+ if (!i18n) return discoverRoutesForRoot(contentRoot);
1115
+ const localizedRoutes = [];
1116
+ for (const locale of i18n.locales) {
1117
+ const discovered = await discoverRoutesForRoot((0, node_path.join)(contentRoot, locale.code), locale.code);
1118
+ for (const route of discovered) {
1119
+ const basePath = route.basePath ?? route.path;
1120
+ localizedRoutes.push({
1121
+ ...route,
1122
+ path: localizedRoutePath(basePath, locale.code, i18n),
1123
+ basePath,
1124
+ locale: locale.code,
1125
+ virtualModuleId: virtualModuleIdFromRef((0, node_path.relative)(contentRoot, route.filePath))
1126
+ });
1127
+ }
1128
+ }
1129
+ if (i18n.missing === "fallback") {
1130
+ const routeByLocaleAndBase = new Map(localizedRoutes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
1131
+ const defaultRoutes = localizedRoutes.filter((route) => route.locale === i18n.defaultLocale);
1132
+ for (const sourceRoute of defaultRoutes) {
1133
+ const basePath = sourceRoute.basePath ?? sourceRoute.path;
1134
+ for (const locale of i18n.locales) {
1135
+ const key = `${locale.code}:${basePath}`;
1136
+ if (routeByLocaleAndBase.has(key)) continue;
1137
+ localizedRoutes.push({
1138
+ ...sourceRoute,
1139
+ path: localizedRoutePath(basePath, locale.code, i18n),
1140
+ locale: locale.code,
1141
+ isFallback: true
1142
+ });
1143
+ }
1144
+ }
1145
+ }
1146
+ return localizedRoutes.map((route) => withAlternates(route, localizedRoutes, i18n));
1147
+ }
1148
+ async function resolveRoutesAndSpecs() {
1149
+ routes = await discoverRoutes();
1150
+ routes = await runHooks(clarifyPlugins, "routes:discovered", routes, ctx);
1151
+ const defaultNavigation = projectConfig.i18n ? buildLocalizedNavigation(routes, projectConfig.pages, projectConfig.i18n) ?? {} : projectConfig.pages && projectConfig.pages !== "FileTree" ? buildNavigationFromConfig(routes, projectConfig.pages) : buildNavigation(routes);
1152
+ const resolved = await runHooks(clarifyPlugins, "routes:resolved", {
1153
+ routes,
1154
+ navigation: defaultNavigation
1155
+ }, ctx);
1156
+ routes = resolved.routes;
1157
+ resolvedNavigation = resolved.navigation;
1158
+ ctx.routes = routes;
1159
+ ctx.navigation = resolvedNavigation;
1160
+ }
1161
+ async function rebuildVirtualModules() {
1162
+ virtualModules = buildVirtualModules({
1163
+ projectConfig,
1164
+ generateOptions,
1165
+ routes,
1166
+ navigation: resolvedNavigation
1167
+ });
1168
+ virtualModules = await runHooks(clarifyPlugins, "modules:before", virtualModules, ctx);
1169
+ }
1170
+ function invalidateVirtualModules(server) {
1171
+ for (const id of virtualModules.keys()) {
1172
+ const moduleNode = server.moduleGraph.getModuleById(resolveVirtualId(id));
1173
+ if (moduleNode) server.moduleGraph.invalidateModule(moduleNode);
1174
+ }
1175
+ }
1176
+ async function refreshDevServer(server) {
1177
+ await resolveRoutesAndSpecs();
1178
+ await rebuildVirtualModules();
1179
+ invalidateVirtualModules(server);
1180
+ server.ws.send({ type: "full-reload" });
1181
+ }
1182
+ function hasContentRouteForFile(filePath) {
1183
+ return routes.some((route) => route.filePath === filePath);
1184
+ }
1185
+ const mdx = (0, _mdx_js_rollup.default)({
1186
+ include: ["**/*.{md,mdx}"],
1187
+ jsxImportSource: "react",
1188
+ providerImportSource: "@clarify-labs/renderer",
1189
+ remarkPlugins,
1190
+ rehypePlugins
1191
+ });
1192
+ return [
1193
+ (0, _vitejs_plugin_react.default)(),
1194
+ (0, _tailwindcss_vite.default)(),
1195
+ {
1196
+ name: "clarify:core",
1197
+ async config() {
1198
+ await resolveRoutesAndSpecs();
1199
+ return {
1200
+ base: projectConfig.routePrefix,
1201
+ build: {
1202
+ ...generateOptions.outputDirectory ? { outDir: generateOptions.outputDirectory } : {},
1203
+ manifest: true
1204
+ }
1205
+ };
1206
+ },
1207
+ configResolved(config) {
1208
+ viteConfig = config;
1209
+ if (!generateOptions.outputDirectory) generateOptions.outputDirectory = config.build.outDir;
1210
+ },
1211
+ async buildStart() {
1212
+ await rebuildVirtualModules();
1213
+ },
1214
+ async handleHotUpdate(ctx) {
1215
+ const changedFile = (0, node_path.isAbsolute)(ctx.file) ? ctx.file : (0, node_path.join)(root, ctx.file);
1216
+ if (configFilePath && changedFile === configFilePath) {
1217
+ await reloadProjectConfig();
1218
+ await refreshDevServer(ctx.server);
1219
+ return [];
1220
+ }
1221
+ const relativeContentFile = (0, node_path.relative)(contentRoot, changedFile);
1222
+ if (!(relativeContentFile && !relativeContentFile.startsWith("..") && !(0, node_path.isAbsolute)(relativeContentFile)) || !hasContentRouteForFile(changedFile)) return;
1223
+ await refreshDevServer(ctx.server);
1224
+ return [];
1225
+ },
1226
+ resolveId(id) {
1227
+ if (id === "virtual:clarify-entry-client" || id === "\0virtual:clarify-entry-client") return RESOLVED_CLIENT_ENTRY;
1228
+ if (id === "virtual:clarify-config" || id === resolveVirtualId("virtual:clarify-config")) return resolveVirtualId(VIRTUAL_CONFIG);
1229
+ if (id === "virtual:clarify-routes" || id === resolveVirtualId("virtual:clarify-routes")) return resolveVirtualId(VIRTUAL_ROUTES);
1230
+ if (id === VIRTUAL_OPENAPI_REGISTRY || id === resolveVirtualId(VIRTUAL_OPENAPI_REGISTRY)) return resolveVirtualId(VIRTUAL_OPENAPI_REGISTRY);
1231
+ const route = routes.find((r) => r.virtualModuleId === id || r.virtualModuleId === stripVirtualPrefix(id));
1232
+ if (route) return resolveVirtualId(route.virtualModuleId);
1233
+ return null;
1234
+ },
1235
+ load(id) {
1236
+ return loadVirtualModule(id, virtualModules);
1237
+ },
1238
+ async configureServer(server) {
1239
+ server.watcher.add(contentRoot);
1240
+ if (configFilePath) server.watcher.add(configFilePath);
1241
+ const handleContentTreeChange = async (filePath) => {
1242
+ const relativeContentFile = (0, node_path.relative)(contentRoot, (0, node_path.isAbsolute)(filePath) ? filePath : (0, node_path.join)(root, filePath));
1243
+ if (!(relativeContentFile && !relativeContentFile.startsWith("..") && !(0, node_path.isAbsolute)(relativeContentFile))) return;
1244
+ await refreshDevServer(server);
1245
+ };
1246
+ server.watcher.on("add", handleContentTreeChange);
1247
+ server.watcher.on("unlink", handleContentTreeChange);
1248
+ await runDevConfigureServerHooks(clarifyPlugins, server, ctx);
1249
+ },
1250
+ transformIndexHtml: {
1251
+ order: "pre",
1252
+ async handler(html, transformCtx) {
1253
+ const result = await runHooks(clarifyPlugins, "html:transform", {
1254
+ html,
1255
+ tags: [],
1256
+ clientEntryId: transformCtx.server ? `/@id/${VIRTUAL_CLIENT_ENTRY}` : VIRTUAL_CLIENT_ENTRY,
1257
+ dev: Boolean(transformCtx.server)
1258
+ }, ctx);
1259
+ return {
1260
+ html: result.html,
1261
+ tags: result.tags
1262
+ };
1263
+ }
1264
+ },
1265
+ async closeBundle() {
1266
+ if (process.env.SKIP_CLARIFY_SSG) {
1267
+ await runBuildDoneHooks(clarifyPlugins, ctx);
1268
+ return;
1269
+ }
1270
+ const outputDir = viteConfig.build.outDir;
1271
+ const ssrOutputDir = (0, node_path.join)(outputDir, ".ssr");
1272
+ let tempEntryPath;
1273
+ try {
1274
+ tempEntryPath = createTempEntryFile(SSR_ENTRY_CODE);
1275
+ await buildSSRBundle(root, tempEntryPath, ssrOutputDir, [{
1276
+ name: "clarify:virtual-ssg",
1277
+ resolveId(id) {
1278
+ if (id === "virtual:clarify-entry-client") return RESOLVED_CLIENT_ENTRY;
1279
+ if (id === "\0virtual:clarify-entry-client") return RESOLVED_CLIENT_ENTRY;
1280
+ if (id === "virtual:clarify-config") return id;
1281
+ if (id === "virtual:clarify-routes") return id;
1282
+ if (id === VIRTUAL_OPENAPI_REGISTRY) return id;
1283
+ if (routes.find((r) => r.virtualModuleId === id)) return id;
1284
+ return null;
1285
+ },
1286
+ load: (id) => loadVirtualModule(id, virtualModules)
1287
+ }, mdx]);
1288
+ const ssrBundlePath = (0, node_path.join)(ssrOutputDir, "entry-server.js");
1289
+ await renderSSGRoutes(routes, projectConfig, outputDir, ssrBundlePath, generateOptions.ssg.failOnError);
1290
+ } catch (err) {
1291
+ console.error("[clarify] SSG failed:", err);
1292
+ if (generateOptions.ssg.failOnError) throw err;
1293
+ } finally {
1294
+ if (tempEntryPath) try {
1295
+ (0, node_fs.rmSync)(tempEntryPath, { force: true });
1296
+ } catch {}
1297
+ }
1298
+ await runBuildDoneHooks(clarifyPlugins, ctx);
1299
+ }
1300
+ },
1301
+ mdx
1302
+ ].flat().filter(Boolean);
1303
+ }
1304
+ //#endregion
1305
+ Object.defineProperty(exports, "clarifyPlugin", {
1306
+ enumerable: true,
1307
+ get: function() {
1308
+ return clarifyPlugin;
1309
+ }
1310
+ });
1311
+ Object.defineProperty(exports, "createClarifyRuntimeAliases", {
1312
+ enumerable: true,
1313
+ get: function() {
1314
+ return createClarifyRuntimeAliases;
1315
+ }
1316
+ });
1317
+ Object.defineProperty(exports, "defineConfig", {
1318
+ enumerable: true,
1319
+ get: function() {
1320
+ return defineConfig;
1321
+ }
1322
+ });
1323
+ Object.defineProperty(exports, "loadClarifyConfig", {
1324
+ enumerable: true,
1325
+ get: function() {
1326
+ return loadClarifyConfig;
1327
+ }
1328
+ });
1329
+
1330
+ //# sourceMappingURL=plugin-CEyCiD5r.cjs.map