@clarify-labs/cli 0.10.1 → 0.10.3
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 +18 -32
- package/output/cli.cjs.map +1 -1
- package/output/cli.js +18 -32
- package/output/cli.js.map +1 -1
- package/output/core/adapters.d.ts +5 -0
- package/output/core/config/config-schema.d.ts +6 -110
- package/output/core/engine/engine.d.ts +1 -0
- package/output/core/runtime/virtual-modules.d.ts +1 -1
- package/output/{engine-5bdNX2Oo.cjs → engine-BDdPszs2.cjs} +212 -177
- package/output/engine-BDdPszs2.cjs.map +1 -0
- package/output/{engine-Ca_2MCj9.js → engine-CyFtr8Pz.js} +210 -178
- package/output/engine-CyFtr8Pz.js.map +1 -0
- package/output/index.cjs +1 -1
- package/output/index.js +1 -1
- package/output/parsers/content/diagnostic.d.ts +12 -0
- package/output/parsers/markdown/mdx.d.ts +12 -4
- package/output/plugins/openapi/parser.d.ts +1 -1
- package/output/plugins/openapi/virtual-modules.d.ts +1 -2
- package/output/types.d.ts +4 -9
- package/package.json +6 -3
- package/output/engine-5bdNX2Oo.cjs.map +0 -1
- package/output/engine-Ca_2MCj9.js.map +0 -1
|
@@ -2,13 +2,16 @@ import { dirname, extname, isAbsolute, join, relative, resolve } from "node:path
|
|
|
2
2
|
import mdxPlugin from "@mdx-js/rollup";
|
|
3
3
|
import tailwindcss from "@tailwindcss/vite";
|
|
4
4
|
import react from "@vitejs/plugin-react";
|
|
5
|
+
import rehypeRaw from "rehype-raw";
|
|
5
6
|
import { compile } from "@mdx-js/mdx";
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
7
|
+
import rehypeShiki from "@shikijs/rehype";
|
|
8
|
+
import rehypeSlug from "rehype-slug";
|
|
9
|
+
import { createCssVariablesTheme } from "shiki";
|
|
9
10
|
import { visit } from "unist-util-visit";
|
|
10
|
-
import { markdownRemarkPlugins, themeBootstrapScript, themePresets } from "@clarify-labs/renderer";
|
|
11
|
+
import { markdownRemarkPlugins, parseCodeMeta, themeBootstrapScript, themePresets } from "@clarify-labs/renderer";
|
|
11
12
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
13
|
+
import GithubSlugger, { slug } from "github-slugger";
|
|
14
|
+
import { toString } from "mdast-util-to-string";
|
|
12
15
|
import { remark } from "remark";
|
|
13
16
|
import matter from "gray-matter";
|
|
14
17
|
import { createReadStream, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
@@ -19,38 +22,29 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
19
22
|
import SwaggerParser from "@apidevtools/swagger-parser";
|
|
20
23
|
import { tmpdir } from "node:os";
|
|
21
24
|
import { createRequire } from "node:module";
|
|
22
|
-
//#region source/parsers/
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
+
//#region source/parsers/content/diagnostic.ts
|
|
26
|
+
function displayPath(filePath, projectRoot) {
|
|
27
|
+
if (!filePath) return void 0;
|
|
28
|
+
if (!projectRoot) return filePath.replace(/^[A-Za-z]:\//, "").replace(/^\/+/, "");
|
|
29
|
+
const normalized = relative(projectRoot, filePath);
|
|
30
|
+
return normalized && !normalized.startsWith("..") && !isAbsolute(normalized) ? normalized : filePath.replace(/^[A-Za-z]:\//, "").replace(/^\/+/, "");
|
|
31
|
+
}
|
|
32
|
+
function createContentDiagnostic(options) {
|
|
33
|
+
const { error } = options;
|
|
34
|
+
return {
|
|
35
|
+
kind: options.kind,
|
|
36
|
+
title: options.title,
|
|
37
|
+
message: options.message,
|
|
38
|
+
filePath: displayPath(options.filePath, options.projectRoot),
|
|
39
|
+
details: error instanceof Error ? options.includeStack ? error.stack ?? error.message : error.message : String(error)
|
|
40
|
+
};
|
|
25
41
|
}
|
|
26
42
|
//#endregion
|
|
27
43
|
//#region source/parsers/markdown/mdx.ts
|
|
28
44
|
function getLanguage(className) {
|
|
29
45
|
return (Array.isArray(className) ? className : typeof className === "string" ? className.split(/\s+/) : []).find((item) => typeof item === "string" && item.startsWith("language-"))?.replace(/^language-/, "") || "txt";
|
|
30
46
|
}
|
|
31
|
-
function getTextContent(node) {
|
|
32
|
-
if (!node) return "";
|
|
33
|
-
if (typeof node.value === "string") return node.value;
|
|
34
|
-
return node.children?.map(getTextContent).join("") ?? "";
|
|
35
|
-
}
|
|
36
|
-
function renderPlainCode(code) {
|
|
37
|
-
return code.split("\n").map((line) => `<span>${escapeHtml(line)}</span>`).join("\n");
|
|
38
|
-
}
|
|
39
|
-
function isBundledLanguage(language) {
|
|
40
|
-
return language in bundledLanguages;
|
|
41
|
-
}
|
|
42
|
-
var highlighterPromise;
|
|
43
47
|
var cssVariablesTheme = createCssVariablesTheme();
|
|
44
|
-
async function getHighlighter() {
|
|
45
|
-
highlighterPromise = highlighterPromise ?? createHighlighter({
|
|
46
|
-
themes: [cssVariablesTheme],
|
|
47
|
-
langs: []
|
|
48
|
-
});
|
|
49
|
-
return highlighterPromise;
|
|
50
|
-
}
|
|
51
|
-
function renderHighlightedCode(html) {
|
|
52
|
-
return (/<code[^>]*>([\s\S]*?)<\/code>/.exec(html)?.[1] ?? html).split("\n").map((line) => line.trimStart().startsWith("<span") ? line : `<span>${line}</span>`).join("\n");
|
|
53
|
-
}
|
|
54
48
|
function rehypeParseCodeBlocks() {
|
|
55
49
|
return (tree) => {
|
|
56
50
|
visit(tree, "element", (node, _nodeIndex, parentNode) => {
|
|
@@ -68,65 +62,31 @@ function rehypeParseCodeBlocks() {
|
|
|
68
62
|
});
|
|
69
63
|
};
|
|
70
64
|
}
|
|
71
|
-
function rehypeShiki() {
|
|
72
|
-
return async (tree) => {
|
|
73
|
-
const shikiHighlighter = await getHighlighter();
|
|
74
|
-
const languages = /* @__PURE__ */ new Set();
|
|
75
|
-
visit(tree, "element", (node) => {
|
|
76
|
-
if (node.tagName !== "pre") return;
|
|
77
|
-
const codeNode = node.children?.[0];
|
|
78
|
-
if (codeNode?.tagName !== "code") return;
|
|
79
|
-
const language = typeof node.properties?.language === "string" ? node.properties.language : getLanguage(codeNode.properties?.className);
|
|
80
|
-
if (language && language !== "txt" && isBundledLanguage(language)) languages.add(language);
|
|
81
|
-
});
|
|
82
|
-
if (languages.size > 0) await shikiHighlighter.loadLanguage(...languages);
|
|
83
|
-
visit(tree, "element", (node) => {
|
|
84
|
-
if (node.tagName !== "pre") return;
|
|
85
|
-
const codeNode = node.children?.[0];
|
|
86
|
-
if (codeNode?.tagName !== "code") return;
|
|
87
|
-
node.properties = node.properties ?? {};
|
|
88
|
-
codeNode.properties = codeNode.properties ?? {};
|
|
89
|
-
const code = getTextContent(codeNode);
|
|
90
|
-
const language = typeof node.properties.language === "string" ? node.properties.language : getLanguage(codeNode.properties.className);
|
|
91
|
-
node.properties.code = code;
|
|
92
|
-
codeNode.properties.code = code;
|
|
93
|
-
codeNode.properties.language = language;
|
|
94
|
-
let highlighted = renderPlainCode(code);
|
|
95
|
-
if (language && language !== "txt") try {
|
|
96
|
-
highlighted = renderHighlightedCode(shikiHighlighter.codeToHtml(code, {
|
|
97
|
-
lang: language,
|
|
98
|
-
theme: cssVariablesTheme
|
|
99
|
-
}));
|
|
100
|
-
} catch {
|
|
101
|
-
highlighted = renderPlainCode(code);
|
|
102
|
-
}
|
|
103
|
-
codeNode.children = [{
|
|
104
|
-
type: "text",
|
|
105
|
-
value: highlighted
|
|
106
|
-
}];
|
|
107
|
-
});
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
function rehypeSlugSections() {
|
|
111
|
-
return (tree) => {
|
|
112
|
-
const slugger = new GithubSlugger();
|
|
113
|
-
visit(tree, "element", (node) => {
|
|
114
|
-
if (node.tagName !== "h2" && node.tagName !== "h3") return;
|
|
115
|
-
node.properties = node.properties ?? {};
|
|
116
|
-
if (node.properties.id) return;
|
|
117
|
-
node.properties.id = slugger.slug(toString(node));
|
|
118
|
-
});
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
65
|
var remarkPlugins = markdownRemarkPlugins;
|
|
122
66
|
var rehypePlugins = [
|
|
123
|
-
|
|
67
|
+
rehypeSlug,
|
|
124
68
|
rehypeParseCodeBlocks,
|
|
125
|
-
rehypeShiki
|
|
69
|
+
[rehypeShiki, {
|
|
70
|
+
theme: cssVariablesTheme,
|
|
71
|
+
langs: [],
|
|
72
|
+
lazy: true,
|
|
73
|
+
addLanguageClass: true,
|
|
74
|
+
fallbackLanguage: "text",
|
|
75
|
+
parseMetaString: parseCodeMeta
|
|
76
|
+
}]
|
|
126
77
|
];
|
|
127
|
-
function
|
|
128
|
-
|
|
129
|
-
|
|
78
|
+
function createContentCompileDiagnostic(options) {
|
|
79
|
+
const { format, phase, error, filePath, projectRoot } = options;
|
|
80
|
+
const label = format === "mdx" ? "MDX" : "Markdown";
|
|
81
|
+
return createContentDiagnostic({
|
|
82
|
+
kind: format,
|
|
83
|
+
title: `${label} ${phase} error`,
|
|
84
|
+
message: phase === "syntax" ? "This page could not be compiled. Fix the MDX syntax or component usage above, then reload the route." : `This page could not be compiled. Fix the ${label} syntax or plugin error below, then reload the route.`,
|
|
85
|
+
error,
|
|
86
|
+
filePath,
|
|
87
|
+
projectRoot,
|
|
88
|
+
includeStack: phase === "compilation"
|
|
89
|
+
});
|
|
130
90
|
}
|
|
131
91
|
/**
|
|
132
92
|
* Validate MDX content for diagnostics WITHOUT running the build-time rehype
|
|
@@ -134,7 +94,8 @@ function formatMdxDiagnostic(error) {
|
|
|
134
94
|
*
|
|
135
95
|
* `rehypePlugins` includes `rehypeShiki`, which loads grammar/WASM assets and
|
|
136
96
|
* is expensive. The real highlighting happens at Vite build time via the
|
|
137
|
-
* `@mdx-js/rollup`
|
|
97
|
+
* `@mdx-js/rollup` plugins (see `createMarkdownPlugin` and `createMdxPlugin`
|
|
98
|
+
* in `core/adapters.ts`), which
|
|
138
99
|
* runs the full remark + rehype pipeline. Re-running it here, during route
|
|
139
100
|
* discovery (Phase 3), would compile every `.mdx` file twice and invoke Shiki
|
|
140
101
|
* once per file per `engine.refresh()` — see ARCHITECTURE.md §2.2 which states
|
|
@@ -156,13 +117,13 @@ async function compileMdxContent(content, filePath, projectRoot) {
|
|
|
156
117
|
} catch (error) {
|
|
157
118
|
return {
|
|
158
119
|
ok: false,
|
|
159
|
-
diagnostic: {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
filePath
|
|
164
|
-
|
|
165
|
-
}
|
|
120
|
+
diagnostic: createContentCompileDiagnostic({
|
|
121
|
+
format: "mdx",
|
|
122
|
+
phase: "syntax",
|
|
123
|
+
error,
|
|
124
|
+
filePath,
|
|
125
|
+
projectRoot
|
|
126
|
+
})
|
|
166
127
|
};
|
|
167
128
|
}
|
|
168
129
|
}
|
|
@@ -283,6 +244,11 @@ function kebabToTitle(str) {
|
|
|
283
244
|
function parseMdxTree(content) {
|
|
284
245
|
return remark.parse(content);
|
|
285
246
|
}
|
|
247
|
+
async function compileRouteDiagnostic(content, filePath, baseDir) {
|
|
248
|
+
if (extname(filePath).toLowerCase() !== ".mdx") return void 0;
|
|
249
|
+
const result = await compileMdxContent(content, filePath, baseDir);
|
|
250
|
+
return result.ok ? void 0 : result.diagnostic;
|
|
251
|
+
}
|
|
286
252
|
/** 从内容中提取第一个 H1 标题 */
|
|
287
253
|
function extractH1(content) {
|
|
288
254
|
const tree = parseMdxTree(content);
|
|
@@ -377,7 +343,7 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
377
343
|
frontmatter,
|
|
378
344
|
content
|
|
379
345
|
};
|
|
380
|
-
const
|
|
346
|
+
const diagnostic = await compileRouteDiagnostic(page.content, fullPath, base);
|
|
381
347
|
let title = typeof page.frontmatter.title === "string" ? page.frontmatter.title : "";
|
|
382
348
|
if (!title) {
|
|
383
349
|
const lastPart = pathParts[pathParts.length - 1] ?? "";
|
|
@@ -399,7 +365,7 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
399
365
|
frontmatter: page.frontmatter,
|
|
400
366
|
content: page.content
|
|
401
367
|
},
|
|
402
|
-
diagnostic
|
|
368
|
+
diagnostic
|
|
403
369
|
});
|
|
404
370
|
}
|
|
405
371
|
}
|
|
@@ -513,6 +479,7 @@ function routeIntentFromPagesItem(item) {
|
|
|
513
479
|
kind: "page",
|
|
514
480
|
ref: item
|
|
515
481
|
};
|
|
482
|
+
if ("group" in item) throw new Error("Navigation groups do not resolve to route intents");
|
|
516
483
|
if ("openapi" in item) return {
|
|
517
484
|
kind: "openapi",
|
|
518
485
|
ref: item.openapi,
|
|
@@ -540,15 +507,19 @@ function firstNavigationPath(nodes) {
|
|
|
540
507
|
}
|
|
541
508
|
function collectExplicitPagePathIntents(tabs) {
|
|
542
509
|
const intents = [];
|
|
543
|
-
|
|
544
|
-
if (
|
|
545
|
-
|
|
510
|
+
const collectItems = (items) => {
|
|
511
|
+
for (const item of items) if (typeof item !== "string" && "group" in item) collectItems(item.pages);
|
|
512
|
+
else if (typeof item !== "string" && "page" in item && item.path) {
|
|
546
513
|
const intent = routeIntentFromPagesItem(item);
|
|
547
514
|
if (intent.kind === "page" && intent.path) intents.push({
|
|
548
515
|
...intent,
|
|
549
516
|
path: intent.path
|
|
550
517
|
});
|
|
551
518
|
}
|
|
519
|
+
};
|
|
520
|
+
for (const tab of tabs ?? []) {
|
|
521
|
+
if (!tab.pages || tab.pages === "FileTree") continue;
|
|
522
|
+
for (const group of tab.pages) collectItems(group.pages);
|
|
552
523
|
}
|
|
553
524
|
return intents;
|
|
554
525
|
}
|
|
@@ -599,28 +570,38 @@ function buildNavigationFromTabsConfig(routes, tabs) {
|
|
|
599
570
|
}
|
|
600
571
|
function buildNavigationFromConfig(routes, config) {
|
|
601
572
|
const routeMap = new Map(routes.map((r) => [r.path, r]));
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
const path = routePathFromRef(intent.ref, intent.path);
|
|
573
|
+
const buildItem = (item) => {
|
|
574
|
+
if (typeof item !== "string" && "group" in item) {
|
|
575
|
+
const children = item.pages.map(buildItem);
|
|
576
|
+
return {
|
|
577
|
+
path: firstNavigationPath(children),
|
|
578
|
+
title: resolveLocalizedText(item.group, "", "") ?? "",
|
|
579
|
+
icon: item.icon,
|
|
580
|
+
children
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
const intent = routeIntentFromPagesItem(item);
|
|
584
|
+
if (intent.kind === "openapi") {
|
|
585
|
+
const path = openAPIPagePathFromRef(intent.ref, intent.tagFilter, intent.path);
|
|
616
586
|
const route = routeMap.get(path);
|
|
617
587
|
return {
|
|
618
|
-
path
|
|
588
|
+
path,
|
|
619
589
|
title: resolveLocalizedText(intent.title, "", "") ?? route?.meta.title ?? kebabToTitle(path.split("/").pop() ?? intent.ref),
|
|
620
590
|
icon: intent.icon,
|
|
621
591
|
sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
|
|
622
592
|
};
|
|
623
|
-
}
|
|
593
|
+
}
|
|
594
|
+
const path = routePathFromRef(intent.ref, intent.path);
|
|
595
|
+
const route = routeMap.get(path);
|
|
596
|
+
return {
|
|
597
|
+
path: intent.redirect ? normalizePath(intent.redirect) : path,
|
|
598
|
+
title: resolveLocalizedText(intent.title, "", "") ?? route?.meta.title ?? kebabToTitle(path.split("/").pop() ?? intent.ref),
|
|
599
|
+
icon: intent.icon,
|
|
600
|
+
sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
|
|
601
|
+
};
|
|
602
|
+
};
|
|
603
|
+
return config.map((group) => {
|
|
604
|
+
const children = group.pages.map(buildItem);
|
|
624
605
|
return {
|
|
625
606
|
path: children[0]?.path ?? "/",
|
|
626
607
|
title: resolveLocalizedText(group.group, "", "") ?? "",
|
|
@@ -642,19 +623,29 @@ function buildLocalizedNavigationForLocale(routes, config, locale, i18n) {
|
|
|
642
623
|
path: route.basePath ?? route.path
|
|
643
624
|
}))), locale, i18n);
|
|
644
625
|
const routeMap = new Map(routes.map((route) => [route.basePath ?? route.path, route]));
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
const
|
|
648
|
-
const basePath = intent.kind === "openapi" ? openAPIPagePathFromRef(intent.ref, intent.tagFilter, intent.path) : routePathFromRef(intent.ref, intent.path);
|
|
649
|
-
const route = routeMap.get(basePath);
|
|
650
|
-
const path = route?.path ?? localizedRoutePath(basePath, locale, i18n);
|
|
626
|
+
const buildItem = (item) => {
|
|
627
|
+
if (typeof item !== "string" && "group" in item) {
|
|
628
|
+
const children = item.pages.map(buildItem);
|
|
651
629
|
return {
|
|
652
|
-
path: (
|
|
653
|
-
title: resolveLocalizedText(
|
|
654
|
-
icon:
|
|
655
|
-
|
|
630
|
+
path: firstNavigationPath(children),
|
|
631
|
+
title: resolveLocalizedText(item.group, locale, i18n.defaultLocale) ?? "",
|
|
632
|
+
icon: item.icon,
|
|
633
|
+
children
|
|
656
634
|
};
|
|
657
|
-
}
|
|
635
|
+
}
|
|
636
|
+
const intent = routeIntentFromPagesItem(item);
|
|
637
|
+
const basePath = intent.kind === "openapi" ? openAPIPagePathFromRef(intent.ref, intent.tagFilter, intent.path) : routePathFromRef(intent.ref, intent.path);
|
|
638
|
+
const route = routeMap.get(basePath);
|
|
639
|
+
const path = route?.path ?? localizedRoutePath(basePath, locale, i18n);
|
|
640
|
+
return {
|
|
641
|
+
path: (intent.kind === "page" && intent.redirect ? localizedRoutePath(routePathFromRef(intent.redirect), locale, i18n) : void 0) ?? path,
|
|
642
|
+
title: resolveLocalizedText(intent.title, locale, i18n.defaultLocale) ?? route?.meta.title ?? kebabToTitle(basePath.split("/").pop() ?? intent.ref),
|
|
643
|
+
icon: intent.icon,
|
|
644
|
+
sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
|
|
645
|
+
};
|
|
646
|
+
};
|
|
647
|
+
return config.map((group) => {
|
|
648
|
+
const children = group.pages.map(buildItem);
|
|
658
649
|
return {
|
|
659
650
|
path: children[0]?.path ?? localizedRoutePath("/", locale, i18n),
|
|
660
651
|
title: resolveLocalizedText(group.group, locale, i18n.defaultLocale) ?? "",
|
|
@@ -899,8 +890,9 @@ var clarifySourceConfigSchema = z.object({
|
|
|
899
890
|
branch: z.string().optional(),
|
|
900
891
|
directory: z.string().optional()
|
|
901
892
|
});
|
|
902
|
-
var clarifyPagesItemSchema = z.union([
|
|
893
|
+
var clarifyPagesItemSchema = z.lazy(() => z.union([
|
|
903
894
|
z.string(),
|
|
895
|
+
clarifyPagesGroupSchema,
|
|
904
896
|
z.object({
|
|
905
897
|
page: z.string(),
|
|
906
898
|
path: z.string().optional(),
|
|
@@ -915,12 +907,12 @@ var clarifyPagesItemSchema = z.union([
|
|
|
915
907
|
title: clarifyLocalizedTextSchema.optional(),
|
|
916
908
|
filter: z.object({ tags: z.array(z.string()).optional() }).optional()
|
|
917
909
|
})
|
|
918
|
-
]);
|
|
919
|
-
var clarifyPagesGroupSchema = z.object({
|
|
910
|
+
]));
|
|
911
|
+
var clarifyPagesGroupSchema = z.lazy(() => z.object({
|
|
920
912
|
group: clarifyLocalizedTextSchema,
|
|
921
913
|
icon: z.string().optional(),
|
|
922
914
|
pages: z.array(clarifyPagesItemSchema)
|
|
923
|
-
});
|
|
915
|
+
}));
|
|
924
916
|
var clarifyPagesConfigSchema = z.union([z.array(clarifyPagesGroupSchema), z.literal("FileTree")]);
|
|
925
917
|
var clarifyTabItemSchema = z.object({
|
|
926
918
|
tab: clarifyLocalizedTextSchema,
|
|
@@ -1268,16 +1260,10 @@ function createRuntimeSlotsModule(plugins = [], root = process.cwd()) {
|
|
|
1268
1260
|
}
|
|
1269
1261
|
return `export const runtimeSlots = {\n${[...grouped.entries()].map(([name, list]) => ` ${JSON.stringify(name)}: [\n ${list.join(",\n ")}\n ]`).join(",\n")}\n};\n`;
|
|
1270
1262
|
}
|
|
1271
|
-
function
|
|
1263
|
+
function generateContentDiagnosticModule(diagnostic) {
|
|
1272
1264
|
return `import { createContentDiagnosticComponent } from '@clarify-labs/renderer';
|
|
1273
1265
|
|
|
1274
|
-
export const contentDiagnostic = ${JSON.stringify(
|
|
1275
|
-
kind: diagnostic.kind ?? "mdx",
|
|
1276
|
-
title: diagnostic.title,
|
|
1277
|
-
message: diagnostic.message,
|
|
1278
|
-
filePath: diagnostic.filePath,
|
|
1279
|
-
details: diagnostic.details
|
|
1280
|
-
})};
|
|
1266
|
+
export const contentDiagnostic = ${JSON.stringify(diagnostic)};
|
|
1281
1267
|
|
|
1282
1268
|
export default createContentDiagnosticComponent(contentDiagnostic);
|
|
1283
1269
|
`;
|
|
@@ -1295,7 +1281,7 @@ function buildVirtualModules(args) {
|
|
|
1295
1281
|
modules.set(VIRTUAL_CLIENT_ENTRY, clientEntryModule);
|
|
1296
1282
|
modules.set(RESOLVED_CLIENT_ENTRY, clientEntryModule);
|
|
1297
1283
|
for (const route of args.routes) {
|
|
1298
|
-
const moduleContent = route.diagnostic ?
|
|
1284
|
+
const moduleContent = route.diagnostic ? generateContentDiagnosticModule(route.diagnostic) : `export { default } from ${moduleSpecifier(route.source.filePath)};`;
|
|
1299
1285
|
modules.set(route.module.virtualModuleId, moduleContent);
|
|
1300
1286
|
}
|
|
1301
1287
|
return modules;
|
|
@@ -1330,9 +1316,9 @@ async function refreshDevServer(engine, server) {
|
|
|
1330
1316
|
invalidateVirtualModules(engine, server);
|
|
1331
1317
|
return hasDevStructureChanged(before, createDevStructureSnapshot(engine.routes, engine.navigation));
|
|
1332
1318
|
}
|
|
1333
|
-
function
|
|
1319
|
+
function createNormalizedContentPlugin(engine) {
|
|
1334
1320
|
return {
|
|
1335
|
-
name: "clarify:normalized-
|
|
1321
|
+
name: "clarify:normalized-content",
|
|
1336
1322
|
enforce: "pre",
|
|
1337
1323
|
transform(_code, id) {
|
|
1338
1324
|
if (!/\.mdx?(?:\?|$)/.test(id)) return null;
|
|
@@ -1346,16 +1332,44 @@ function createNormalizedMdxContentPlugin(engine) {
|
|
|
1346
1332
|
}
|
|
1347
1333
|
};
|
|
1348
1334
|
}
|
|
1349
|
-
function
|
|
1350
|
-
return
|
|
1351
|
-
|
|
1335
|
+
function createContentCompileTransform(transform, format, projectRoot, errorPolicy = () => "diagnostic") {
|
|
1336
|
+
return async function contentCompileTransform(code, id) {
|
|
1337
|
+
try {
|
|
1338
|
+
return await transform.call(this, code, id);
|
|
1339
|
+
} catch (error) {
|
|
1340
|
+
if (errorPolicy() === "throw") throw error;
|
|
1341
|
+
return {
|
|
1342
|
+
code: generateContentDiagnosticModule(createContentCompileDiagnostic({
|
|
1343
|
+
format,
|
|
1344
|
+
phase: "compilation",
|
|
1345
|
+
error,
|
|
1346
|
+
filePath: id.replace(/\?.*$/, ""),
|
|
1347
|
+
projectRoot
|
|
1348
|
+
})),
|
|
1349
|
+
map: null
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
function createContentCompilerPlugin(format, projectRoot, errorPolicy) {
|
|
1355
|
+
const markdown = format === "markdown";
|
|
1356
|
+
const plugin = mdxPlugin({
|
|
1357
|
+
include: [markdown ? "**/*.md" : "**/*.mdx"],
|
|
1358
|
+
format: markdown ? "md" : "mdx",
|
|
1352
1359
|
jsxImportSource: "react",
|
|
1353
1360
|
providerImportSource: "@clarify-labs/renderer",
|
|
1354
1361
|
remarkPlugins,
|
|
1355
|
-
|
|
1362
|
+
...markdown ? { remarkRehypeOptions: { allowDangerousHtml: true } } : {},
|
|
1363
|
+
rehypePlugins: markdown ? [rehypeRaw, ...rehypePlugins] : rehypePlugins
|
|
1356
1364
|
});
|
|
1365
|
+
const transform = typeof plugin.transform === "function" ? plugin.transform : plugin.transform?.handler;
|
|
1366
|
+
if (!transform) return plugin;
|
|
1367
|
+
return {
|
|
1368
|
+
...plugin,
|
|
1369
|
+
transform: createContentCompileTransform(transform, format, projectRoot, errorPolicy)
|
|
1370
|
+
};
|
|
1357
1371
|
}
|
|
1358
|
-
function createClarifyViteCorePlugin(engine,
|
|
1372
|
+
function createClarifyViteCorePlugin(engine, normalizedContentPlugin, markdown, mdx) {
|
|
1359
1373
|
let viteConfig;
|
|
1360
1374
|
return {
|
|
1361
1375
|
name: "clarify:core",
|
|
@@ -1457,7 +1471,8 @@ function createClarifyViteCorePlugin(engine, normalizedMdxContentPlugin, mdx) {
|
|
|
1457
1471
|
outputDirectory: viteConfig.build.outDir,
|
|
1458
1472
|
ssrPlugins: [
|
|
1459
1473
|
engine.createSSGVirtualPlugin(),
|
|
1460
|
-
|
|
1474
|
+
normalizedContentPlugin,
|
|
1475
|
+
markdown,
|
|
1461
1476
|
mdx
|
|
1462
1477
|
]
|
|
1463
1478
|
});
|
|
@@ -1474,20 +1489,31 @@ function createClarifyViteCorePlugin(engine, normalizedMdxContentPlugin, mdx) {
|
|
|
1474
1489
|
* site itself - it only bridges Vite lifecycle hooks to the engine.
|
|
1475
1490
|
*/
|
|
1476
1491
|
function createViteAdapter(engine) {
|
|
1477
|
-
|
|
1478
|
-
const
|
|
1492
|
+
let contentCompileErrorPolicy = "diagnostic";
|
|
1493
|
+
const errorPolicy = () => contentCompileErrorPolicy;
|
|
1494
|
+
const normalizedContentPlugin = createNormalizedContentPlugin(engine);
|
|
1495
|
+
const markdown = createContentCompilerPlugin("markdown", engine.root, errorPolicy);
|
|
1496
|
+
const mdx = createContentCompilerPlugin("mdx", engine.root, errorPolicy);
|
|
1497
|
+
const compilePolicyPlugin = {
|
|
1498
|
+
name: "clarify:content-compile-policy",
|
|
1499
|
+
configResolved(config) {
|
|
1500
|
+
contentCompileErrorPolicy = config.command === "build" && engine.generateOptions.ssg.failOnError ? "throw" : "diagnostic";
|
|
1501
|
+
}
|
|
1502
|
+
};
|
|
1479
1503
|
const tailwind = tailwindcss();
|
|
1480
1504
|
return [
|
|
1481
1505
|
...react(),
|
|
1482
1506
|
...Array.isArray(tailwind) ? tailwind : [tailwind],
|
|
1483
|
-
|
|
1484
|
-
|
|
1507
|
+
compilePolicyPlugin,
|
|
1508
|
+
normalizedContentPlugin,
|
|
1509
|
+
createClarifyViteCorePlugin(engine, normalizedContentPlugin, markdown, mdx),
|
|
1510
|
+
markdown,
|
|
1485
1511
|
mdx
|
|
1486
1512
|
];
|
|
1487
1513
|
}
|
|
1488
1514
|
var cliPackageVersion = {
|
|
1489
1515
|
name: "@clarify-labs/cli",
|
|
1490
|
-
version: "0.10.
|
|
1516
|
+
version: "0.10.3",
|
|
1491
1517
|
license: "AGPL-3.0-only",
|
|
1492
1518
|
type: "module",
|
|
1493
1519
|
repository: {
|
|
@@ -1528,6 +1554,7 @@ var cliPackageVersion = {
|
|
|
1528
1554
|
"@clarify-labs/templates": "workspace:*",
|
|
1529
1555
|
"@mdx-js/mdx": "^3.1.1",
|
|
1530
1556
|
"@mdx-js/rollup": "^3.1.1",
|
|
1557
|
+
"@shikijs/rehype": "^4.3.0",
|
|
1531
1558
|
"@tailwindcss/vite": "^4.3.2",
|
|
1532
1559
|
"@vitejs/plugin-react": "^6.0.3",
|
|
1533
1560
|
"cac": "^7.0.0",
|
|
@@ -1542,6 +1569,8 @@ var cliPackageVersion = {
|
|
|
1542
1569
|
"react-router": "^7.0.0",
|
|
1543
1570
|
"react-router-dom": "^7.0.0",
|
|
1544
1571
|
"react-zoom-pan-pinch": "^4.0.3",
|
|
1572
|
+
"rehype-raw": "^7.0.0",
|
|
1573
|
+
"rehype-slug": "^6.0.0",
|
|
1545
1574
|
"remark": "^15.0.1",
|
|
1546
1575
|
"remark-gfm": "^4.0.1",
|
|
1547
1576
|
"shiki": "4.3.0",
|
|
@@ -1837,9 +1866,6 @@ var OPENAPI_HTTP_METHODS = [
|
|
|
1837
1866
|
"trace"
|
|
1838
1867
|
];
|
|
1839
1868
|
var CLARIFY_OPENAPI_SECTION_ID_EXTENSION = "x-clarify-section-id";
|
|
1840
|
-
function errorMessage(error) {
|
|
1841
|
-
return error instanceof Error ? error.message : String(error);
|
|
1842
|
-
}
|
|
1843
1869
|
function isSameOpenAPIFile(url, filePath) {
|
|
1844
1870
|
try {
|
|
1845
1871
|
return resolve(fileURLToPath(url)) === resolve(filePath);
|
|
@@ -1954,7 +1980,7 @@ function extractOpenAPISections(spec, filterTags) {
|
|
|
1954
1980
|
}
|
|
1955
1981
|
return sections;
|
|
1956
1982
|
}
|
|
1957
|
-
async function readOpenAPISpec(filePath, contentProcessor) {
|
|
1983
|
+
async function readOpenAPISpec(filePath, contentProcessor, projectRoot) {
|
|
1958
1984
|
try {
|
|
1959
1985
|
if (!contentProcessor) return {
|
|
1960
1986
|
ok: true,
|
|
@@ -1977,13 +2003,14 @@ async function readOpenAPISpec(filePath, contentProcessor) {
|
|
|
1977
2003
|
} catch (error) {
|
|
1978
2004
|
return {
|
|
1979
2005
|
ok: false,
|
|
1980
|
-
diagnostic: {
|
|
2006
|
+
diagnostic: createContentDiagnostic({
|
|
1981
2007
|
kind: "openapi",
|
|
1982
2008
|
title: "OpenAPI spec parse failed",
|
|
1983
|
-
message:
|
|
2009
|
+
message: "Clarify could not parse this OpenAPI specification.",
|
|
2010
|
+
error,
|
|
1984
2011
|
filePath,
|
|
1985
|
-
|
|
1986
|
-
}
|
|
2012
|
+
projectRoot
|
|
2013
|
+
})
|
|
1987
2014
|
};
|
|
1988
2015
|
}
|
|
1989
2016
|
}
|
|
@@ -2014,31 +2041,24 @@ export const routeData = {};
|
|
|
2014
2041
|
export default createOpenApiRouteComponent({ ...routeData, spec });
|
|
2015
2042
|
`;
|
|
2016
2043
|
}
|
|
2017
|
-
function generateOpenAPIErrorModule(diagnostic) {
|
|
2018
|
-
return `import { createContentDiagnosticComponent } from '@clarify-labs/renderer';
|
|
2019
|
-
|
|
2020
|
-
export const contentDiagnostic = ${JSON.stringify({
|
|
2021
|
-
kind: diagnostic.kind ?? "openapi",
|
|
2022
|
-
title: diagnostic.title,
|
|
2023
|
-
message: diagnostic.message,
|
|
2024
|
-
filePath: diagnostic.filePath,
|
|
2025
|
-
details: diagnostic.details
|
|
2026
|
-
})};
|
|
2027
|
-
|
|
2028
|
-
export default createContentDiagnosticComponent(contentDiagnostic);
|
|
2029
|
-
`;
|
|
2030
|
-
}
|
|
2031
2044
|
//#endregion
|
|
2032
2045
|
//#region source/plugins/openapi/index.ts
|
|
2033
2046
|
function collectOpenAPIPageIntents(config) {
|
|
2034
2047
|
const intents = [];
|
|
2035
|
-
function
|
|
2036
|
-
|
|
2037
|
-
|
|
2048
|
+
function visitItems(items) {
|
|
2049
|
+
for (const item of items) {
|
|
2050
|
+
if (typeof item !== "string" && "group" in item) {
|
|
2051
|
+
visitItems(item.pages);
|
|
2052
|
+
continue;
|
|
2053
|
+
}
|
|
2038
2054
|
const intent = routeIntentFromPagesItem(item);
|
|
2039
2055
|
if (intent.kind === "openapi" && (intent.path || intent.tagFilter?.length)) intents.push(intent);
|
|
2040
2056
|
}
|
|
2041
2057
|
}
|
|
2058
|
+
function visitPages(pages) {
|
|
2059
|
+
if (!pages || pages === "FileTree") return;
|
|
2060
|
+
for (const group of pages) visitItems(group.pages);
|
|
2061
|
+
}
|
|
2042
2062
|
for (const tab of config.tabs ?? []) visitPages(tab.pages);
|
|
2043
2063
|
return intents;
|
|
2044
2064
|
}
|
|
@@ -2184,7 +2204,7 @@ function createOpenAPIPlugin() {
|
|
|
2184
2204
|
const result = specFromCache ? {
|
|
2185
2205
|
ok: true,
|
|
2186
2206
|
spec: specFromCache
|
|
2187
|
-
} : await readOpenAPISpec(route.source.filePath, processor);
|
|
2207
|
+
} : await readOpenAPISpec(route.source.filePath, processor, ctx.generateOptions.projectRoot);
|
|
2188
2208
|
if (!result.ok) {
|
|
2189
2209
|
route.diagnostic = result.diagnostic;
|
|
2190
2210
|
route.meta.title = route.meta.title || "OpenAPI parse error";
|
|
@@ -2231,7 +2251,7 @@ function createOpenAPIPlugin() {
|
|
|
2231
2251
|
for (const [routeSpecModuleId, spec] of specModules) modules.set(specVirtualModuleId(routeSpecModuleId), generateOpenAPISpecModule(spec));
|
|
2232
2252
|
for (const route of ctx.routes) {
|
|
2233
2253
|
if (route.kind !== "openapi") continue;
|
|
2234
|
-
if (route.diagnostic) modules.set(route.module.virtualModuleId,
|
|
2254
|
+
if (route.diagnostic) modules.set(route.module.virtualModuleId, generateContentDiagnosticModule(route.diagnostic));
|
|
2235
2255
|
else if (route.openapi?.sourceSpecId) {
|
|
2236
2256
|
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2237
2257
|
if (!specModules.has(routeSpecModuleId)) continue;
|
|
@@ -2686,6 +2706,11 @@ function writeClarifyEnvDts(projectRoot, plugins) {
|
|
|
2686
2706
|
writeFileSync(filePath, next, "utf-8");
|
|
2687
2707
|
}
|
|
2688
2708
|
//#endregion
|
|
2709
|
+
//#region source/parsers/markdown/utils.ts
|
|
2710
|
+
function escapeHtml(text) {
|
|
2711
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2712
|
+
}
|
|
2713
|
+
//#endregion
|
|
2689
2714
|
//#region source/core/runtime/html-template.ts
|
|
2690
2715
|
/**
|
|
2691
2716
|
* HTML template manipulation helpers used during SSG.
|
|
@@ -3058,6 +3083,12 @@ function loadVirtualModule(id, modules) {
|
|
|
3058
3083
|
const bareId = stripVirtualPrefix(id);
|
|
3059
3084
|
return modules.get(bareId) ?? modules.get(id) ?? null;
|
|
3060
3085
|
}
|
|
3086
|
+
function assertNoContentDiagnostics(routes) {
|
|
3087
|
+
const failures = routes.filter((route) => route.diagnostic);
|
|
3088
|
+
if (failures.length === 0) return;
|
|
3089
|
+
const summary = failures.map((route) => `- ${route.path}: ${route.diagnostic?.title ?? "Content error"}${route.diagnostic?.filePath ? ` (${route.diagnostic.filePath})` : ""}`).join("\n");
|
|
3090
|
+
throw new Error(`[clarify] Content diagnostics prevented the build:\n${summary}`);
|
|
3091
|
+
}
|
|
3061
3092
|
var ClarifyEngine = class {
|
|
3062
3093
|
options;
|
|
3063
3094
|
root;
|
|
@@ -3282,6 +3313,7 @@ var ClarifyEngine = class {
|
|
|
3282
3313
|
await runPhase(this.plugins, "ssg", this.ctx, async () => {
|
|
3283
3314
|
if (process.env.SKIP_CLARIFY_SSG) return;
|
|
3284
3315
|
if (!await runInterceptHooks(this.plugins, "ssg:shouldRun", this.ctx)) return;
|
|
3316
|
+
if (this.generateOptions.ssg.failOnError) assertNoContentDiagnostics(this.routes);
|
|
3285
3317
|
const outputDir = this.runtime.outputDirectory ?? this.generateOptions.outputDirectory;
|
|
3286
3318
|
if (!outputDir) throw new Error("[clarify] outputDirectory is required before SSG runs");
|
|
3287
3319
|
const ssrOutputDir = join(outputDir, ".ssr");
|
|
@@ -3342,4 +3374,4 @@ function createClarifyEngine(options = {}) {
|
|
|
3342
3374
|
//#endregion
|
|
3343
3375
|
export { cliPackageVersion as a, defineConfig as c, createClarifyTempDir as i, createClarifyEngine as n, cliPackageVersionWithCaret as o, createClarifyRuntimeAliases as r, createViteAdapter as s, ClarifyEngine as t };
|
|
3344
3376
|
|
|
3345
|
-
//# sourceMappingURL=engine-
|
|
3377
|
+
//# sourceMappingURL=engine-CyFtr8Pz.js.map
|