@clarify-labs/cli 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +196 -201
- package/output/core/config/config.d.ts +2 -1
- package/output/core/config/options.d.ts +0 -8
- package/output/core/config/user-config.d.ts +4 -3
- package/output/core/engine/engine.d.ts +1 -0
- package/output/core/project/project-info.d.ts +3 -3
- package/output/core/runtime/ssg.d.ts +1 -1
- package/output/core/runtime/virtual-modules.d.ts +2 -2
- package/output/{engine-qafLfnXQ.cjs → engine-BfkoyVNo.cjs} +353 -236
- package/output/engine-BfkoyVNo.cjs.map +1 -0
- package/output/{engine-CSSL_gGd.js → engine-DfF5RJjC.js} +353 -236
- package/output/engine-DfF5RJjC.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 +8 -0
- package/output/parsers/routes/routes.d.ts +7 -7
- package/output/parsers/routes/routes.test-utils.d.ts +2 -2
- package/output/plugins/openapi/parser.d.ts +1 -1
- package/output/plugins/openapi/virtual-modules.d.ts +1 -2
- package/output/plugins/source-links/source-links.d.ts +3 -3
- package/output/types.d.ts +67 -42
- package/package.json +3 -3
- package/output/engine-CSSL_gGd.js.map +0 -1
- package/output/engine-qafLfnXQ.cjs.map +0 -1
|
@@ -53,6 +53,24 @@ let _apidevtools_swagger_parser = require("@apidevtools/swagger-parser");
|
|
|
53
53
|
_apidevtools_swagger_parser = __toESM(_apidevtools_swagger_parser, 1);
|
|
54
54
|
let node_os = require("node:os");
|
|
55
55
|
let node_module = require("node:module");
|
|
56
|
+
//#region source/parsers/content/diagnostic.ts
|
|
57
|
+
function displayPath(filePath, projectRoot) {
|
|
58
|
+
if (!filePath) return void 0;
|
|
59
|
+
if (!projectRoot) return filePath.replace(/^[A-Za-z]:\//, "").replace(/^\/+/, "");
|
|
60
|
+
const normalized = (0, node_path.relative)(projectRoot, filePath);
|
|
61
|
+
return normalized && !normalized.startsWith("..") && !(0, node_path.isAbsolute)(normalized) ? normalized : filePath.replace(/^[A-Za-z]:\//, "").replace(/^\/+/, "");
|
|
62
|
+
}
|
|
63
|
+
function createContentDiagnostic(options) {
|
|
64
|
+
const { error } = options;
|
|
65
|
+
return {
|
|
66
|
+
kind: options.kind,
|
|
67
|
+
title: options.title,
|
|
68
|
+
message: options.message,
|
|
69
|
+
filePath: displayPath(options.filePath, options.projectRoot),
|
|
70
|
+
details: error instanceof Error ? options.includeStack ? error.stack ?? error.message : error.message : String(error)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
56
74
|
//#region source/parsers/markdown/mdx.ts
|
|
57
75
|
function getLanguage(className) {
|
|
58
76
|
return (Array.isArray(className) ? className : typeof className === "string" ? className.split(/\s+/) : []).find((item) => typeof item === "string" && item.startsWith("language-"))?.replace(/^language-/, "") || "txt";
|
|
@@ -88,9 +106,18 @@ var rehypePlugins = [
|
|
|
88
106
|
parseMetaString: _clarify_labs_renderer.parseCodeMeta
|
|
89
107
|
}]
|
|
90
108
|
];
|
|
91
|
-
function
|
|
92
|
-
|
|
93
|
-
|
|
109
|
+
function createContentCompileDiagnostic(options) {
|
|
110
|
+
const { format, phase, error, filePath, projectRoot } = options;
|
|
111
|
+
const label = format === "mdx" ? "MDX" : "Markdown";
|
|
112
|
+
return createContentDiagnostic({
|
|
113
|
+
kind: format,
|
|
114
|
+
title: `${label} ${phase} error`,
|
|
115
|
+
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.`,
|
|
116
|
+
error,
|
|
117
|
+
filePath,
|
|
118
|
+
projectRoot,
|
|
119
|
+
includeStack: phase === "compilation"
|
|
120
|
+
});
|
|
94
121
|
}
|
|
95
122
|
/**
|
|
96
123
|
* Validate MDX content for diagnostics WITHOUT running the build-time rehype
|
|
@@ -121,13 +148,13 @@ async function compileMdxContent(content, filePath, projectRoot) {
|
|
|
121
148
|
} catch (error) {
|
|
122
149
|
return {
|
|
123
150
|
ok: false,
|
|
124
|
-
diagnostic: {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
filePath
|
|
129
|
-
|
|
130
|
-
}
|
|
151
|
+
diagnostic: createContentCompileDiagnostic({
|
|
152
|
+
format: "mdx",
|
|
153
|
+
phase: "syntax",
|
|
154
|
+
error,
|
|
155
|
+
filePath,
|
|
156
|
+
projectRoot
|
|
157
|
+
})
|
|
131
158
|
};
|
|
132
159
|
}
|
|
133
160
|
}
|
|
@@ -383,10 +410,10 @@ function frontmatterKeywords(frontmatter) {
|
|
|
383
410
|
const keywords = Array.isArray(value) ? value.filter((item) => typeof item === "string") : typeof value === "string" ? value.split(",").map((keyword) => keyword.trim()).filter(Boolean) : [];
|
|
384
411
|
return keywords.length > 0 ? keywords : void 0;
|
|
385
412
|
}
|
|
386
|
-
function withAlternates(route, routes,
|
|
413
|
+
function withAlternates(route, routes, locales) {
|
|
387
414
|
const basePath = route.basePath ?? route.path;
|
|
388
415
|
const routeByLocaleAndBase = new Map(routes.filter((r) => !r.locale || r.path === `/${r.locale}` || r.path.startsWith(`/${r.locale}/`)).map((r) => [`${r.locale ?? ""}:${r.basePath ?? r.path}`, r]));
|
|
389
|
-
const alternates = Object.fromEntries(
|
|
416
|
+
const alternates = Object.fromEntries(locales.locales.flatMap((locale) => {
|
|
390
417
|
const alternate = routeByLocaleAndBase.get(`${locale.code}:${basePath}`);
|
|
391
418
|
return alternate ? [[locale.code, alternate.path]] : [];
|
|
392
419
|
}));
|
|
@@ -399,11 +426,11 @@ function withAlternates(route, routes, i18n) {
|
|
|
399
426
|
* 为默认语言的 locale-prefixed 路由生成无前缀的"裸路径别名",方便不带语言
|
|
400
427
|
* 前缀的 URL 也能访问。标记 `isBareAlias` 以便搜索索引生成时过滤重复索引。
|
|
401
428
|
*/
|
|
402
|
-
function generateBareAliases(routes,
|
|
429
|
+
function generateBareAliases(routes, locales) {
|
|
403
430
|
const bareRoutes = [];
|
|
404
431
|
const seenBare = new Set(routes.map((r) => r.path));
|
|
405
432
|
for (const route of routes) {
|
|
406
|
-
if (route.locale !==
|
|
433
|
+
if (route.locale !== locales.default) continue;
|
|
407
434
|
const bp = route.basePath ?? route.path;
|
|
408
435
|
if (bp === route.path || seenBare.has(bp)) continue;
|
|
409
436
|
seenBare.add(bp);
|
|
@@ -415,42 +442,42 @@ function generateBareAliases(routes, i18n) {
|
|
|
415
442
|
}
|
|
416
443
|
return bareRoutes;
|
|
417
444
|
}
|
|
418
|
-
async function findLocalizedContentRoutes(contentRoot,
|
|
419
|
-
if (!
|
|
445
|
+
async function findLocalizedContentRoutes(contentRoot, locales, options = {}) {
|
|
446
|
+
if (!locales) return findContentRoutes(contentRoot, contentRoot, options);
|
|
420
447
|
const localizedRoutes = [];
|
|
421
|
-
for (const locale of
|
|
448
|
+
for (const locale of locales.locales) {
|
|
422
449
|
const localeRoot = (0, node_path.join)(contentRoot, locale.code);
|
|
423
450
|
const discovered = await findContentRoutes(localeRoot, localeRoot, options);
|
|
424
451
|
for (const route of discovered) {
|
|
425
452
|
const basePath = route.basePath ?? route.path;
|
|
426
453
|
localizedRoutes.push({
|
|
427
454
|
...route,
|
|
428
|
-
path: localizedRoutePath(basePath, locale.code,
|
|
455
|
+
path: localizedRoutePath(basePath, locale.code, locales),
|
|
429
456
|
basePath,
|
|
430
457
|
locale: locale.code,
|
|
431
458
|
module: { virtualModuleId: virtualModuleIdForLocalizedRoute(contentRoot, route.source.filePath) }
|
|
432
459
|
});
|
|
433
460
|
}
|
|
434
461
|
}
|
|
435
|
-
if (
|
|
462
|
+
if (locales.missing === "fallback") {
|
|
436
463
|
const routeByLocaleAndBase = new Map(localizedRoutes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
|
|
437
|
-
const defaultRoutes = localizedRoutes.filter((route) => route.locale ===
|
|
464
|
+
const defaultRoutes = localizedRoutes.filter((route) => route.locale === locales.default);
|
|
438
465
|
for (const sourceRoute of defaultRoutes) {
|
|
439
466
|
const basePath = sourceRoute.basePath ?? sourceRoute.path;
|
|
440
|
-
for (const locale of
|
|
467
|
+
for (const locale of locales.locales) {
|
|
441
468
|
const key = `${locale.code}:${basePath}`;
|
|
442
469
|
if (routeByLocaleAndBase.has(key)) continue;
|
|
443
470
|
localizedRoutes.push({
|
|
444
471
|
...sourceRoute,
|
|
445
|
-
path: localizedRoutePath(basePath, locale.code,
|
|
472
|
+
path: localizedRoutePath(basePath, locale.code, locales),
|
|
446
473
|
locale: locale.code,
|
|
447
474
|
isFallback: true
|
|
448
475
|
});
|
|
449
476
|
}
|
|
450
477
|
}
|
|
451
478
|
}
|
|
452
|
-
const routesWithAlternates = localizedRoutes.map((route) => withAlternates(route, localizedRoutes,
|
|
453
|
-
return [...routesWithAlternates, ...generateBareAliases(routesWithAlternates,
|
|
479
|
+
const routesWithAlternates = localizedRoutes.map((route) => withAlternates(route, localizedRoutes, locales));
|
|
480
|
+
return [...routesWithAlternates, ...generateBareAliases(routesWithAlternates, locales)];
|
|
454
481
|
}
|
|
455
482
|
function buildNavigation(routes) {
|
|
456
483
|
const root = [];
|
|
@@ -483,6 +510,7 @@ function routeIntentFromPagesItem(item) {
|
|
|
483
510
|
kind: "page",
|
|
484
511
|
ref: item
|
|
485
512
|
};
|
|
513
|
+
if ("group" in item) throw new Error("Navigation groups do not resolve to route intents");
|
|
486
514
|
if ("openapi" in item) return {
|
|
487
515
|
kind: "openapi",
|
|
488
516
|
ref: item.openapi,
|
|
@@ -510,19 +538,23 @@ function firstNavigationPath(nodes) {
|
|
|
510
538
|
}
|
|
511
539
|
function collectExplicitPagePathIntents(tabs) {
|
|
512
540
|
const intents = [];
|
|
513
|
-
|
|
514
|
-
if (
|
|
515
|
-
|
|
541
|
+
const collectItems = (items) => {
|
|
542
|
+
for (const item of items) if (typeof item !== "string" && "group" in item) collectItems(item.pages);
|
|
543
|
+
else if (typeof item !== "string" && "page" in item && item.path) {
|
|
516
544
|
const intent = routeIntentFromPagesItem(item);
|
|
517
545
|
if (intent.kind === "page" && intent.path) intents.push({
|
|
518
546
|
...intent,
|
|
519
547
|
path: intent.path
|
|
520
548
|
});
|
|
521
549
|
}
|
|
550
|
+
};
|
|
551
|
+
for (const tab of tabs ?? []) {
|
|
552
|
+
if (!tab.pages || tab.pages === "FileTree") continue;
|
|
553
|
+
for (const group of tab.pages) collectItems(group.pages);
|
|
522
554
|
}
|
|
523
555
|
return intents;
|
|
524
556
|
}
|
|
525
|
-
function applyConfiguredPageRoutePaths(routes, tabs,
|
|
557
|
+
function applyConfiguredPageRoutePaths(routes, tabs, locales) {
|
|
526
558
|
const pageIntents = collectExplicitPagePathIntents(tabs);
|
|
527
559
|
if (!pageIntents.length) return routes;
|
|
528
560
|
const additions = [];
|
|
@@ -538,15 +570,15 @@ function applyConfiguredPageRoutePaths(routes, tabs, i18n) {
|
|
|
538
570
|
existingKeys.add(key);
|
|
539
571
|
additions.push({
|
|
540
572
|
...route,
|
|
541
|
-
path: route.locale &&
|
|
573
|
+
path: route.locale && locales ? localizedRoutePath(targetBasePath, route.locale, locales) : targetBasePath,
|
|
542
574
|
basePath: targetBasePath
|
|
543
575
|
});
|
|
544
576
|
}
|
|
545
577
|
}
|
|
546
578
|
const nextRoutes = [...routes, ...additions];
|
|
547
|
-
const routesWithAlternates =
|
|
548
|
-
if (!
|
|
549
|
-
return [...routesWithAlternates, ...generateBareAliases(routesWithAlternates,
|
|
579
|
+
const routesWithAlternates = locales ? nextRoutes.map((route) => withAlternates(route, nextRoutes, locales)) : nextRoutes;
|
|
580
|
+
if (!locales) return routesWithAlternates;
|
|
581
|
+
return [...routesWithAlternates, ...generateBareAliases(routesWithAlternates, locales)];
|
|
550
582
|
}
|
|
551
583
|
function buildNavigationFromPagesConfig(routes, config) {
|
|
552
584
|
if (!config || config === "FileTree") return buildNavigation(routes);
|
|
@@ -569,28 +601,38 @@ function buildNavigationFromTabsConfig(routes, tabs) {
|
|
|
569
601
|
}
|
|
570
602
|
function buildNavigationFromConfig(routes, config) {
|
|
571
603
|
const routeMap = new Map(routes.map((r) => [r.path, r]));
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
const path = routePathFromRef(intent.ref, intent.path);
|
|
604
|
+
const buildItem = (item) => {
|
|
605
|
+
if (typeof item !== "string" && "group" in item) {
|
|
606
|
+
const children = item.pages.map(buildItem);
|
|
607
|
+
return {
|
|
608
|
+
path: firstNavigationPath(children),
|
|
609
|
+
title: resolveLocalizedText(item.group, "", "") ?? "",
|
|
610
|
+
icon: item.icon,
|
|
611
|
+
children
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
const intent = routeIntentFromPagesItem(item);
|
|
615
|
+
if (intent.kind === "openapi") {
|
|
616
|
+
const path = openAPIPagePathFromRef(intent.ref, intent.tagFilter, intent.path);
|
|
586
617
|
const route = routeMap.get(path);
|
|
587
618
|
return {
|
|
588
|
-
path
|
|
619
|
+
path,
|
|
589
620
|
title: resolveLocalizedText(intent.title, "", "") ?? route?.meta.title ?? kebabToTitle(path.split("/").pop() ?? intent.ref),
|
|
590
621
|
icon: intent.icon,
|
|
591
622
|
sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
|
|
592
623
|
};
|
|
593
|
-
}
|
|
624
|
+
}
|
|
625
|
+
const path = routePathFromRef(intent.ref, intent.path);
|
|
626
|
+
const route = routeMap.get(path);
|
|
627
|
+
return {
|
|
628
|
+
path: intent.redirect ? normalizePath(intent.redirect) : path,
|
|
629
|
+
title: resolveLocalizedText(intent.title, "", "") ?? route?.meta.title ?? kebabToTitle(path.split("/").pop() ?? intent.ref),
|
|
630
|
+
icon: intent.icon,
|
|
631
|
+
sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
|
|
632
|
+
};
|
|
633
|
+
};
|
|
634
|
+
return config.map((group) => {
|
|
635
|
+
const children = group.pages.map(buildItem);
|
|
594
636
|
return {
|
|
595
637
|
path: children[0]?.path ?? "/",
|
|
596
638
|
title: resolveLocalizedText(group.group, "", "") ?? "",
|
|
@@ -599,51 +641,61 @@ function buildNavigationFromConfig(routes, config) {
|
|
|
599
641
|
};
|
|
600
642
|
});
|
|
601
643
|
}
|
|
602
|
-
function localizeNavigationPaths(nodes, locale,
|
|
644
|
+
function localizeNavigationPaths(nodes, locale, locales) {
|
|
603
645
|
return nodes.map((node) => ({
|
|
604
646
|
...node,
|
|
605
|
-
path: localizedRoutePath(node.path, locale,
|
|
606
|
-
children: node.children ? localizeNavigationPaths(node.children, locale,
|
|
647
|
+
path: localizedRoutePath(node.path, locale, locales),
|
|
648
|
+
children: node.children ? localizeNavigationPaths(node.children, locale, locales) : void 0
|
|
607
649
|
}));
|
|
608
650
|
}
|
|
609
|
-
function buildLocalizedNavigationForLocale(routes, config, locale,
|
|
651
|
+
function buildLocalizedNavigationForLocale(routes, config, locale, locales) {
|
|
610
652
|
if (!config || config === "FileTree") return localizeNavigationPaths(buildNavigation(routes.map((route) => ({
|
|
611
653
|
...route,
|
|
612
654
|
path: route.basePath ?? route.path
|
|
613
|
-
}))), locale,
|
|
655
|
+
}))), locale, locales);
|
|
614
656
|
const routeMap = new Map(routes.map((route) => [route.basePath ?? route.path, route]));
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
const
|
|
618
|
-
const basePath = intent.kind === "openapi" ? openAPIPagePathFromRef(intent.ref, intent.tagFilter, intent.path) : routePathFromRef(intent.ref, intent.path);
|
|
619
|
-
const route = routeMap.get(basePath);
|
|
620
|
-
const path = route?.path ?? localizedRoutePath(basePath, locale, i18n);
|
|
657
|
+
const buildItem = (item) => {
|
|
658
|
+
if (typeof item !== "string" && "group" in item) {
|
|
659
|
+
const children = item.pages.map(buildItem);
|
|
621
660
|
return {
|
|
622
|
-
path: (
|
|
623
|
-
title: resolveLocalizedText(
|
|
624
|
-
icon:
|
|
625
|
-
|
|
661
|
+
path: firstNavigationPath(children),
|
|
662
|
+
title: resolveLocalizedText(item.group, locale, locales.default) ?? "",
|
|
663
|
+
icon: item.icon,
|
|
664
|
+
children
|
|
626
665
|
};
|
|
627
|
-
}
|
|
666
|
+
}
|
|
667
|
+
const intent = routeIntentFromPagesItem(item);
|
|
668
|
+
const basePath = intent.kind === "openapi" ? openAPIPagePathFromRef(intent.ref, intent.tagFilter, intent.path) : routePathFromRef(intent.ref, intent.path);
|
|
669
|
+
const route = routeMap.get(basePath);
|
|
670
|
+
const path = route?.path ?? localizedRoutePath(basePath, locale, locales);
|
|
671
|
+
return {
|
|
672
|
+
path: (intent.kind === "page" && intent.redirect ? localizedRoutePath(routePathFromRef(intent.redirect), locale, locales) : void 0) ?? path,
|
|
673
|
+
title: resolveLocalizedText(intent.title, locale, locales.default) ?? route?.meta.title ?? kebabToTitle(basePath.split("/").pop() ?? intent.ref),
|
|
674
|
+
icon: intent.icon,
|
|
675
|
+
sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
|
|
676
|
+
};
|
|
677
|
+
};
|
|
678
|
+
return config.map((group) => {
|
|
679
|
+
const children = group.pages.map(buildItem);
|
|
628
680
|
return {
|
|
629
|
-
path: children[0]?.path ?? localizedRoutePath("/", locale,
|
|
630
|
-
title: resolveLocalizedText(group.group, locale,
|
|
681
|
+
path: children[0]?.path ?? localizedRoutePath("/", locale, locales),
|
|
682
|
+
title: resolveLocalizedText(group.group, locale, locales.default) ?? "",
|
|
631
683
|
icon: group.icon,
|
|
632
684
|
children
|
|
633
685
|
};
|
|
634
686
|
});
|
|
635
687
|
}
|
|
636
|
-
function buildLocalizedNavigationFromTabsConfig(routes, tabs,
|
|
637
|
-
if (!
|
|
688
|
+
function buildLocalizedNavigationFromTabsConfig(routes, tabs, localesConfig) {
|
|
689
|
+
if (!localesConfig) return void 0;
|
|
638
690
|
const locales = {};
|
|
639
|
-
for (const locale of
|
|
691
|
+
for (const locale of localesConfig.locales) {
|
|
640
692
|
const localeRoutes = routes.filter((route) => route.locale === locale.code);
|
|
641
693
|
locales[locale.code] = { tabs: tabs.map((tab) => {
|
|
642
|
-
const children = buildLocalizedNavigationForLocale(localeRoutes, tab.pages, locale.code,
|
|
694
|
+
const children = buildLocalizedNavigationForLocale(localeRoutes, tab.pages, locale.code, localesConfig);
|
|
643
695
|
return {
|
|
644
696
|
type: "tab",
|
|
645
697
|
path: firstNavigationPath(children),
|
|
646
|
-
title: resolveLocalizedText(tab.tab, locale.code,
|
|
698
|
+
title: resolveLocalizedText(tab.tab, locale.code, localesConfig.default) ?? "",
|
|
647
699
|
icon: tab.icon,
|
|
648
700
|
children
|
|
649
701
|
};
|
|
@@ -710,7 +762,7 @@ function inferRouteForFile(filePath, contentRoot, defaultLocale) {
|
|
|
710
762
|
const localeSegmentIndex = fileLocale ? segments.indexOf(fileLocale) : -1;
|
|
711
763
|
const basePath = basePathFromRef(localeSegmentIndex >= 0 ? segments.slice(localeSegmentIndex + 1).join("/") : ref);
|
|
712
764
|
const path = fileLocale && defaultLocale ? localizedRoutePath(basePath, fileLocale, {
|
|
713
|
-
defaultLocale,
|
|
765
|
+
default: defaultLocale,
|
|
714
766
|
missing: "fallback",
|
|
715
767
|
locales: []
|
|
716
768
|
}) : basePath;
|
|
@@ -763,12 +815,12 @@ async function handleDevRouteRequest(req, res, routes, context) {
|
|
|
763
815
|
}
|
|
764
816
|
const file = typeof body === "object" && body !== null ? body.file : void 0;
|
|
765
817
|
if (typeof file === "string" && file.length > 0) {
|
|
766
|
-
const route = resolveRouteForFile(file, routes, context.projectConfig.
|
|
818
|
+
const route = resolveRouteForFile(file, routes, context.projectConfig.locales?.default);
|
|
767
819
|
if (route) {
|
|
768
820
|
res.end(JSON.stringify(toDevRouteEntry(route)));
|
|
769
821
|
return;
|
|
770
822
|
}
|
|
771
|
-
const inferred = inferRouteForFile(file, context.contentRoot, context.projectConfig.
|
|
823
|
+
const inferred = inferRouteForFile(file, context.contentRoot, context.projectConfig.locales?.default);
|
|
772
824
|
res.end(JSON.stringify(inferred));
|
|
773
825
|
return;
|
|
774
826
|
}
|
|
@@ -794,8 +846,7 @@ function resolveThemeConfig(theme = {}) {
|
|
|
794
846
|
layout: {
|
|
795
847
|
...presetTheme.layout,
|
|
796
848
|
...theme.layout
|
|
797
|
-
}
|
|
798
|
-
editor: theme.editor ?? presetTheme.editor
|
|
849
|
+
}
|
|
799
850
|
};
|
|
800
851
|
}
|
|
801
852
|
//#endregion
|
|
@@ -803,26 +854,26 @@ function resolveThemeConfig(theme = {}) {
|
|
|
803
854
|
var clarifyLogoConfigSchema = zod.z.union([zod.z.string(), zod.z.object({
|
|
804
855
|
light: zod.z.string().optional(),
|
|
805
856
|
dark: zod.z.string().optional()
|
|
806
|
-
})]);
|
|
857
|
+
}).strict()]);
|
|
807
858
|
var clarifyFaviconConfigSchema = zod.z.union([zod.z.string(), zod.z.object({
|
|
808
859
|
light: zod.z.string().optional(),
|
|
809
860
|
dark: zod.z.string().optional()
|
|
810
|
-
})]);
|
|
861
|
+
}).strict()]);
|
|
811
862
|
var clarifyLocalizedTextSchema = zod.z.union([zod.z.string(), zod.z.record(zod.z.string(), zod.z.string())]);
|
|
812
863
|
var clarifyLocaleConfigSchema = zod.z.object({
|
|
813
864
|
code: zod.z.string(),
|
|
814
865
|
label: zod.z.string(),
|
|
815
866
|
dir: zod.z.union([zod.z.literal("ltr"), zod.z.literal("rtl")]).optional()
|
|
816
|
-
});
|
|
817
|
-
var
|
|
818
|
-
|
|
867
|
+
}).strict();
|
|
868
|
+
var clarifyLocalesConfigSchema = zod.z.object({
|
|
869
|
+
default: zod.z.string().optional(),
|
|
819
870
|
missing: zod.z.union([
|
|
820
871
|
zod.z.literal("fallback"),
|
|
821
872
|
zod.z.literal("404"),
|
|
822
873
|
zod.z.literal("hide")
|
|
823
874
|
]).optional(),
|
|
824
875
|
locales: zod.z.array(clarifyLocaleConfigSchema).min(1)
|
|
825
|
-
}).superRefine((config, ctx) => {
|
|
876
|
+
}).strict().superRefine((config, ctx) => {
|
|
826
877
|
const localeCodes = /* @__PURE__ */ new Set();
|
|
827
878
|
for (const [index, locale] of config.locales.entries()) {
|
|
828
879
|
if (localeCodes.has(locale.code)) ctx.addIssue({
|
|
@@ -836,17 +887,17 @@ var clarifyI18nConfigSchema = zod.z.object({
|
|
|
836
887
|
});
|
|
837
888
|
localeCodes.add(locale.code);
|
|
838
889
|
}
|
|
839
|
-
if (config.
|
|
890
|
+
if (config.default && !localeCodes.has(config.default)) ctx.addIssue({
|
|
840
891
|
code: zod.z.ZodIssueCode.custom,
|
|
841
|
-
message: "
|
|
842
|
-
path: ["
|
|
892
|
+
message: "default must be one of locales.locales",
|
|
893
|
+
path: ["default"]
|
|
843
894
|
});
|
|
844
895
|
});
|
|
845
896
|
var clarifyNavbarLinkSchema = zod.z.object({
|
|
846
897
|
label: clarifyLocalizedTextSchema,
|
|
847
898
|
href: zod.z.string(),
|
|
848
899
|
external: zod.z.boolean().optional()
|
|
849
|
-
});
|
|
900
|
+
}).strict();
|
|
850
901
|
var clarifyBannerConfigOptionsSchema = zod.z.object({
|
|
851
902
|
content: clarifyLocalizedTextSchema,
|
|
852
903
|
dismissible: zod.z.boolean().optional(),
|
|
@@ -864,45 +915,46 @@ var clarifyVariableValueSchema = zod.z.lazy(() => zod.z.union([
|
|
|
864
915
|
zod.z.record(zod.z.string(), clarifyVariableValueSchema)
|
|
865
916
|
]));
|
|
866
917
|
var clarifyVariablesConfigSchema = zod.z.record(zod.z.string(), clarifyVariableValueSchema);
|
|
867
|
-
var
|
|
868
|
-
|
|
918
|
+
var clarifyRepositoryConfigSchema = zod.z.object({
|
|
919
|
+
url: zod.z.string().optional(),
|
|
869
920
|
branch: zod.z.string().optional(),
|
|
870
921
|
directory: zod.z.string().optional()
|
|
871
|
-
});
|
|
872
|
-
var clarifyPagesItemSchema = zod.z.union([
|
|
922
|
+
}).strict();
|
|
923
|
+
var clarifyPagesItemSchema = zod.z.lazy(() => zod.z.union([
|
|
873
924
|
zod.z.string(),
|
|
925
|
+
clarifyPagesGroupSchema,
|
|
874
926
|
zod.z.object({
|
|
875
927
|
page: zod.z.string(),
|
|
876
928
|
path: zod.z.string().optional(),
|
|
877
929
|
title: clarifyLocalizedTextSchema.optional(),
|
|
878
930
|
icon: zod.z.string().optional(),
|
|
879
931
|
redirect: zod.z.string().optional()
|
|
880
|
-
}),
|
|
932
|
+
}).strict(),
|
|
881
933
|
zod.z.object({
|
|
882
934
|
openapi: zod.z.string(),
|
|
883
935
|
path: zod.z.string().optional(),
|
|
884
936
|
icon: zod.z.string().optional(),
|
|
885
937
|
title: clarifyLocalizedTextSchema.optional(),
|
|
886
|
-
filter: zod.z.object({ tags: zod.z.array(zod.z.string()).optional() }).optional()
|
|
887
|
-
})
|
|
888
|
-
]);
|
|
889
|
-
var clarifyPagesGroupSchema = zod.z.object({
|
|
938
|
+
filter: zod.z.object({ tags: zod.z.array(zod.z.string()).optional() }).strict().optional()
|
|
939
|
+
}).strict()
|
|
940
|
+
]));
|
|
941
|
+
var clarifyPagesGroupSchema = zod.z.lazy(() => zod.z.object({
|
|
890
942
|
group: clarifyLocalizedTextSchema,
|
|
891
943
|
icon: zod.z.string().optional(),
|
|
892
944
|
pages: zod.z.array(clarifyPagesItemSchema)
|
|
893
|
-
});
|
|
945
|
+
}).strict());
|
|
894
946
|
var clarifyPagesConfigSchema = zod.z.union([zod.z.array(clarifyPagesGroupSchema), zod.z.literal("FileTree")]);
|
|
895
947
|
var clarifyTabItemSchema = zod.z.object({
|
|
896
948
|
tab: clarifyLocalizedTextSchema,
|
|
897
949
|
icon: zod.z.string().optional(),
|
|
898
950
|
pages: clarifyPagesConfigSchema.optional()
|
|
899
|
-
});
|
|
951
|
+
}).strict();
|
|
900
952
|
var clarifyTabsConfigSchema = zod.z.array(clarifyTabItemSchema);
|
|
901
953
|
var clarifyThemePresetSchema = zod.z.enum(["default", "base"]);
|
|
902
954
|
var clarifyThemeModeColorValueSchema = zod.z.object({
|
|
903
955
|
light: zod.z.string().optional(),
|
|
904
956
|
dark: zod.z.string().optional()
|
|
905
|
-
});
|
|
957
|
+
}).strict();
|
|
906
958
|
var clarifyThemeColorValueSchema = zod.z.union([zod.z.string(), clarifyThemeModeColorValueSchema]);
|
|
907
959
|
var clarifyThemeColorTokensConfigSchema = zod.z.object({
|
|
908
960
|
primary: clarifyThemeColorValueSchema.optional(),
|
|
@@ -913,42 +965,65 @@ var clarifyThemeColorTokensConfigSchema = zod.z.object({
|
|
|
913
965
|
muted: clarifyThemeColorValueSchema.optional(),
|
|
914
966
|
border: clarifyThemeColorValueSchema.optional(),
|
|
915
967
|
codeBackground: clarifyThemeColorValueSchema.optional()
|
|
916
|
-
});
|
|
968
|
+
}).strict();
|
|
917
969
|
var clarifyThemeRadiusTokensConfigSchema = zod.z.object({
|
|
918
970
|
sm: zod.z.string().optional(),
|
|
919
971
|
md: zod.z.string().optional(),
|
|
920
972
|
lg: zod.z.string().optional(),
|
|
921
973
|
xl: zod.z.string().optional()
|
|
922
|
-
});
|
|
974
|
+
}).strict();
|
|
923
975
|
var clarifyThemeTokensConfigSchema = zod.z.object({
|
|
924
976
|
colors: clarifyThemeColorTokensConfigSchema.optional(),
|
|
925
977
|
radius: clarifyThemeRadiusTokensConfigSchema.optional()
|
|
926
|
-
});
|
|
927
|
-
var clarifyThemeLayoutConfigSchema = zod.z.object({ maxWidth: zod.z.string().optional() });
|
|
978
|
+
}).strict();
|
|
979
|
+
var clarifyThemeLayoutConfigSchema = zod.z.object({ maxWidth: zod.z.string().optional() }).strict();
|
|
928
980
|
var clarifyThemeConfigSchema = zod.z.object({
|
|
929
981
|
preset: clarifyThemePresetSchema.optional(),
|
|
930
982
|
tokens: clarifyThemeTokensConfigSchema.optional(),
|
|
931
|
-
layout: clarifyThemeLayoutConfigSchema.optional()
|
|
932
|
-
|
|
933
|
-
|
|
983
|
+
layout: clarifyThemeLayoutConfigSchema.optional()
|
|
984
|
+
}).strict();
|
|
985
|
+
function featureSchema(shape, defaults) {
|
|
986
|
+
const objectSchema = zod.z.object({
|
|
987
|
+
enabled: zod.z.boolean().default(true),
|
|
988
|
+
...shape
|
|
989
|
+
}).strict().default({
|
|
990
|
+
enabled: true,
|
|
991
|
+
...defaults
|
|
992
|
+
});
|
|
993
|
+
return zod.z.union([zod.z.boolean(), objectSchema]).default({
|
|
994
|
+
enabled: true,
|
|
995
|
+
...defaults
|
|
996
|
+
}).transform((value) => typeof value === "boolean" ? {
|
|
997
|
+
enabled: value,
|
|
998
|
+
...defaults
|
|
999
|
+
} : value);
|
|
1000
|
+
}
|
|
1001
|
+
var clarifyFeaturesConfigSchema = zod.z.object({
|
|
1002
|
+
search: featureSchema({ provider: zod.z.literal("pagefind").default("pagefind") }, { provider: "pagefind" }),
|
|
1003
|
+
repository: featureSchema(clarifyRepositoryConfigSchema.shape, {}),
|
|
1004
|
+
themeEditor: featureSchema({}, {}),
|
|
1005
|
+
openapi: featureSchema({ playground: zod.z.boolean().default(true) }, { playground: true })
|
|
1006
|
+
}).strict();
|
|
934
1007
|
var clarifyProjectConfigSchema = zod.z.object({
|
|
935
1008
|
title: zod.z.string().optional(),
|
|
936
1009
|
description: zod.z.string().optional(),
|
|
937
1010
|
siteUrl: zod.z.string().optional(),
|
|
938
|
-
|
|
1011
|
+
routePrefix: zod.z.string().default("/"),
|
|
1012
|
+
assetPrefix: zod.z.string().optional(),
|
|
939
1013
|
logo: clarifyLogoConfigSchema.optional(),
|
|
940
1014
|
homeUrl: zod.z.string().optional(),
|
|
941
1015
|
favicon: clarifyFaviconConfigSchema.optional(),
|
|
942
1016
|
theme: clarifyThemeConfigSchema.optional(),
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1017
|
+
navigation: zod.z.object({
|
|
1018
|
+
links: zod.z.array(clarifyNavbarLinkSchema).optional(),
|
|
1019
|
+
tabs: clarifyTabsConfigSchema.optional()
|
|
1020
|
+
}).strict().optional(),
|
|
946
1021
|
banner: clarifyBannerConfigOptionsSchema.optional(),
|
|
947
1022
|
footer: clarifyFooterConfigSchema.optional(),
|
|
1023
|
+
locales: clarifyLocalesConfigSchema.optional(),
|
|
948
1024
|
variables: clarifyVariablesConfigSchema.optional(),
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
});
|
|
1025
|
+
features: clarifyFeaturesConfigSchema.optional()
|
|
1026
|
+
}).strict();
|
|
952
1027
|
//#endregion
|
|
953
1028
|
//#region source/core/config/config.ts
|
|
954
1029
|
function formatIssuePath(path) {
|
|
@@ -970,17 +1045,20 @@ function validateProjectConfig(value) {
|
|
|
970
1045
|
if (!result.success) throw new Error(formatProjectConfigError(result.error));
|
|
971
1046
|
return result.data;
|
|
972
1047
|
}
|
|
973
|
-
function
|
|
974
|
-
if (!
|
|
975
|
-
const firstLocale =
|
|
976
|
-
const defaultLocale =
|
|
1048
|
+
function resolveLocalesConfig(locales) {
|
|
1049
|
+
if (!locales) return void 0;
|
|
1050
|
+
const firstLocale = locales.locales[0]?.code;
|
|
1051
|
+
const defaultLocale = locales.default ?? firstLocale;
|
|
977
1052
|
if (!defaultLocale) return void 0;
|
|
978
1053
|
return {
|
|
979
|
-
defaultLocale,
|
|
980
|
-
missing:
|
|
981
|
-
locales:
|
|
1054
|
+
default: defaultLocale,
|
|
1055
|
+
missing: locales.missing ?? "fallback",
|
|
1056
|
+
locales: locales.locales
|
|
982
1057
|
};
|
|
983
1058
|
}
|
|
1059
|
+
function resolveFeaturesConfig(features = {}) {
|
|
1060
|
+
return clarifyFeaturesConfigSchema.parse(features);
|
|
1061
|
+
}
|
|
984
1062
|
function resolveRoutePrefix(routePrefix) {
|
|
985
1063
|
const trimmed = routePrefix?.trim();
|
|
986
1064
|
if (!trimmed || trimmed === "/") return "/";
|
|
@@ -1001,26 +1079,24 @@ function resolveProjectConfig(config = {}) {
|
|
|
1001
1079
|
title: config.title ?? "Clarify Docs",
|
|
1002
1080
|
description: config.description ?? "",
|
|
1003
1081
|
siteUrl: config.siteUrl,
|
|
1004
|
-
|
|
1082
|
+
routePrefix,
|
|
1083
|
+
assetPrefix: resolveAssetPrefix(config.assetPrefix, routePrefix),
|
|
1005
1084
|
logo: config.logo,
|
|
1006
1085
|
homeUrl: config.homeUrl,
|
|
1007
1086
|
favicon: config.favicon,
|
|
1008
|
-
routePrefix,
|
|
1009
|
-
assetPrefix: resolveAssetPrefix(config.assetPrefix, routePrefix),
|
|
1010
1087
|
theme: resolveThemeConfig(config.theme),
|
|
1011
|
-
|
|
1088
|
+
navigation: config.navigation,
|
|
1012
1089
|
banner: config.banner,
|
|
1013
1090
|
footer: config.footer,
|
|
1091
|
+
locales: resolveLocalesConfig(config.locales),
|
|
1014
1092
|
variables: config.variables ?? {},
|
|
1015
|
-
|
|
1016
|
-
tabs: config.tabs
|
|
1093
|
+
features: resolveFeaturesConfig(config.features)
|
|
1017
1094
|
};
|
|
1018
1095
|
}
|
|
1019
1096
|
//#endregion
|
|
1020
1097
|
//#region source/core/config/user-config.ts
|
|
1021
1098
|
function defineConfig(config) {
|
|
1022
|
-
|
|
1023
|
-
return config;
|
|
1099
|
+
return validateClarifyConfig(config);
|
|
1024
1100
|
}
|
|
1025
1101
|
/**
|
|
1026
1102
|
* Config filenames Clarify CLI looks for at the project root, in resolution
|
|
@@ -1044,18 +1120,23 @@ function assertConfigObject(config, configFile) {
|
|
|
1044
1120
|
}
|
|
1045
1121
|
function loadJsonConfig(configFile) {
|
|
1046
1122
|
const config = assertConfigObject(JSON.parse((0, node_fs.readFileSync)(configFile, "utf-8")), configFile);
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1123
|
+
if ("plugins" in config) throw new Error("[clarify] clarify.json does not support plugins; use clarify.ts or clarify.js");
|
|
1124
|
+
return validateClarifyConfig(config);
|
|
1125
|
+
}
|
|
1126
|
+
function validateClarifyConfig(config) {
|
|
1127
|
+
const { plugins, ...projectConfigInput } = config;
|
|
1128
|
+
if (plugins !== void 0 && !Array.isArray(plugins)) throw new Error("[clarify] config field \"plugins\" is invalid: Expected an array");
|
|
1129
|
+
const projectConfig = validateProjectConfig(projectConfigInput);
|
|
1130
|
+
return plugins ? {
|
|
1050
1131
|
...projectConfig,
|
|
1051
|
-
|
|
1052
|
-
};
|
|
1132
|
+
plugins
|
|
1133
|
+
} : projectConfig;
|
|
1053
1134
|
}
|
|
1054
1135
|
async function loadClarifyConfig(root, env) {
|
|
1055
1136
|
const configFile = findClarifyConfigFile(root);
|
|
1056
1137
|
if (!configFile) return {};
|
|
1057
1138
|
if (configFile.endsWith(".json")) return loadJsonConfig(configFile);
|
|
1058
|
-
return assertConfigObject((await (0, vite.loadConfigFromFile)(env, configFile, root, "silent"))?.config, configFile);
|
|
1139
|
+
return validateClarifyConfig(assertConfigObject((await (0, vite.loadConfigFromFile)(env, configFile, root, "silent"))?.config, configFile));
|
|
1059
1140
|
}
|
|
1060
1141
|
/**
|
|
1061
1142
|
* File extensions Clarify treats as content files. Kept in sync with the
|
|
@@ -1081,9 +1162,9 @@ function buildProjectInfo(context) {
|
|
|
1081
1162
|
contentRoot: (contentRoot.startsWith(projectRoot) ? contentRoot.slice(projectRoot.length).replace(/^[/\\]+/, "") : contentRoot) || "source",
|
|
1082
1163
|
projectRoot
|
|
1083
1164
|
};
|
|
1084
|
-
if (projectConfig.
|
|
1085
|
-
|
|
1086
|
-
locales: projectConfig.
|
|
1165
|
+
if (projectConfig.locales) response.locales = {
|
|
1166
|
+
default: projectConfig.locales.default,
|
|
1167
|
+
locales: projectConfig.locales.locales.map((l) => l.code)
|
|
1087
1168
|
};
|
|
1088
1169
|
return response;
|
|
1089
1170
|
}
|
|
@@ -1143,10 +1224,23 @@ function resolveVirtualModuleId(id, modules, routes) {
|
|
|
1143
1224
|
if (route) return resolveVirtualId(route.module.virtualModuleId);
|
|
1144
1225
|
return null;
|
|
1145
1226
|
}
|
|
1146
|
-
function generateConfigModule(projectConfig,
|
|
1227
|
+
function generateConfigModule(projectConfig, version) {
|
|
1147
1228
|
const runtimeConfig = {
|
|
1148
|
-
|
|
1149
|
-
|
|
1229
|
+
title: projectConfig.title,
|
|
1230
|
+
description: projectConfig.description,
|
|
1231
|
+
siteUrl: projectConfig.siteUrl,
|
|
1232
|
+
routePrefix: projectConfig.routePrefix,
|
|
1233
|
+
assetPrefix: projectConfig.assetPrefix,
|
|
1234
|
+
logo: projectConfig.logo,
|
|
1235
|
+
homeUrl: projectConfig.homeUrl,
|
|
1236
|
+
favicon: projectConfig.favicon,
|
|
1237
|
+
theme: projectConfig.theme,
|
|
1238
|
+
navigation: projectConfig.navigation,
|
|
1239
|
+
banner: projectConfig.banner,
|
|
1240
|
+
footer: projectConfig.footer,
|
|
1241
|
+
locales: projectConfig.locales,
|
|
1242
|
+
variables: projectConfig.variables,
|
|
1243
|
+
features: projectConfig.features
|
|
1150
1244
|
};
|
|
1151
1245
|
if (version) runtimeConfig.version = version;
|
|
1152
1246
|
return `export const config = ${JSON.stringify(runtimeConfig)};`;
|
|
@@ -1238,16 +1332,10 @@ function createRuntimeSlotsModule(plugins = [], root = process.cwd()) {
|
|
|
1238
1332
|
}
|
|
1239
1333
|
return `export const runtimeSlots = {\n${[...grouped.entries()].map(([name, list]) => ` ${JSON.stringify(name)}: [\n ${list.join(",\n ")}\n ]`).join(",\n")}\n};\n`;
|
|
1240
1334
|
}
|
|
1241
|
-
function
|
|
1335
|
+
function generateContentDiagnosticModule(diagnostic) {
|
|
1242
1336
|
return `import { createContentDiagnosticComponent } from '@clarify-labs/renderer';
|
|
1243
1337
|
|
|
1244
|
-
export const contentDiagnostic = ${JSON.stringify(
|
|
1245
|
-
kind: diagnostic.kind ?? "mdx",
|
|
1246
|
-
title: diagnostic.title,
|
|
1247
|
-
message: diagnostic.message,
|
|
1248
|
-
filePath: diagnostic.filePath,
|
|
1249
|
-
details: diagnostic.details
|
|
1250
|
-
})};
|
|
1338
|
+
export const contentDiagnostic = ${JSON.stringify(diagnostic)};
|
|
1251
1339
|
|
|
1252
1340
|
export default createContentDiagnosticComponent(contentDiagnostic);
|
|
1253
1341
|
`;
|
|
@@ -1256,7 +1344,7 @@ function buildVirtualModules(args) {
|
|
|
1256
1344
|
const modules = /* @__PURE__ */ new Map();
|
|
1257
1345
|
const clientEntryModule = createClientEntryModule({ themeEditor: args.themeEditor });
|
|
1258
1346
|
const allPlugins = [...args.plugins ?? []];
|
|
1259
|
-
modules.set(VIRTUAL_CONFIG, generateConfigModule(args.projectConfig, args.
|
|
1347
|
+
modules.set(VIRTUAL_CONFIG, generateConfigModule(args.projectConfig, args.version));
|
|
1260
1348
|
modules.set(VIRTUAL_ROUTES, generateRoutesModule(args.routes, args.navigation, "client"));
|
|
1261
1349
|
modules.set(VIRTUAL_SERVER_ROUTES, generateRoutesModule(args.routes, args.navigation, "server"));
|
|
1262
1350
|
modules.set(VIRTUAL_SLOTS, createRuntimeSlotsModule(allPlugins, args.generateOptions.projectRoot));
|
|
@@ -1265,7 +1353,7 @@ function buildVirtualModules(args) {
|
|
|
1265
1353
|
modules.set(VIRTUAL_CLIENT_ENTRY, clientEntryModule);
|
|
1266
1354
|
modules.set(RESOLVED_CLIENT_ENTRY, clientEntryModule);
|
|
1267
1355
|
for (const route of args.routes) {
|
|
1268
|
-
const moduleContent = route.diagnostic ?
|
|
1356
|
+
const moduleContent = route.diagnostic ? generateContentDiagnosticModule(route.diagnostic) : `export { default } from ${moduleSpecifier(route.source.filePath)};`;
|
|
1269
1357
|
modules.set(route.module.virtualModuleId, moduleContent);
|
|
1270
1358
|
}
|
|
1271
1359
|
return modules;
|
|
@@ -1316,26 +1404,42 @@ function createNormalizedContentPlugin(engine) {
|
|
|
1316
1404
|
}
|
|
1317
1405
|
};
|
|
1318
1406
|
}
|
|
1319
|
-
function
|
|
1320
|
-
return (
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1407
|
+
function createContentCompileTransform(transform, format, projectRoot, errorPolicy = () => "diagnostic") {
|
|
1408
|
+
return async function contentCompileTransform(code, id) {
|
|
1409
|
+
try {
|
|
1410
|
+
return await transform.call(this, code, id);
|
|
1411
|
+
} catch (error) {
|
|
1412
|
+
if (errorPolicy() === "throw") throw error;
|
|
1413
|
+
return {
|
|
1414
|
+
code: generateContentDiagnosticModule(createContentCompileDiagnostic({
|
|
1415
|
+
format,
|
|
1416
|
+
phase: "compilation",
|
|
1417
|
+
error,
|
|
1418
|
+
filePath: id.replace(/\?.*$/, ""),
|
|
1419
|
+
projectRoot
|
|
1420
|
+
})),
|
|
1421
|
+
map: null
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
};
|
|
1328
1425
|
}
|
|
1329
|
-
function
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1426
|
+
function createContentCompilerPlugin(format, projectRoot, errorPolicy) {
|
|
1427
|
+
const markdown = format === "markdown";
|
|
1428
|
+
const plugin = (0, _mdx_js_rollup.default)({
|
|
1429
|
+
include: [markdown ? "**/*.md" : "**/*.mdx"],
|
|
1430
|
+
format: markdown ? "md" : "mdx",
|
|
1333
1431
|
jsxImportSource: "react",
|
|
1334
1432
|
providerImportSource: "@clarify-labs/renderer",
|
|
1335
1433
|
remarkPlugins,
|
|
1336
|
-
remarkRehypeOptions: { allowDangerousHtml: true },
|
|
1337
|
-
rehypePlugins: [rehype_raw.default, ...rehypePlugins]
|
|
1434
|
+
...markdown ? { remarkRehypeOptions: { allowDangerousHtml: true } } : {},
|
|
1435
|
+
rehypePlugins: markdown ? [rehype_raw.default, ...rehypePlugins] : rehypePlugins
|
|
1338
1436
|
});
|
|
1437
|
+
const transform = typeof plugin.transform === "function" ? plugin.transform : plugin.transform?.handler;
|
|
1438
|
+
if (!transform) return plugin;
|
|
1439
|
+
return {
|
|
1440
|
+
...plugin,
|
|
1441
|
+
transform: createContentCompileTransform(transform, format, projectRoot, errorPolicy)
|
|
1442
|
+
};
|
|
1339
1443
|
}
|
|
1340
1444
|
function createClarifyViteCorePlugin(engine, normalizedContentPlugin, markdown, mdx) {
|
|
1341
1445
|
let viteConfig;
|
|
@@ -1457,13 +1561,22 @@ function createClarifyViteCorePlugin(engine, normalizedContentPlugin, markdown,
|
|
|
1457
1561
|
* site itself - it only bridges Vite lifecycle hooks to the engine.
|
|
1458
1562
|
*/
|
|
1459
1563
|
function createViteAdapter(engine) {
|
|
1564
|
+
let contentCompileErrorPolicy = "diagnostic";
|
|
1565
|
+
const errorPolicy = () => contentCompileErrorPolicy;
|
|
1460
1566
|
const normalizedContentPlugin = createNormalizedContentPlugin(engine);
|
|
1461
|
-
const markdown =
|
|
1462
|
-
const mdx =
|
|
1567
|
+
const markdown = createContentCompilerPlugin("markdown", engine.root, errorPolicy);
|
|
1568
|
+
const mdx = createContentCompilerPlugin("mdx", engine.root, errorPolicy);
|
|
1569
|
+
const compilePolicyPlugin = {
|
|
1570
|
+
name: "clarify:content-compile-policy",
|
|
1571
|
+
configResolved(config) {
|
|
1572
|
+
contentCompileErrorPolicy = config.command === "build" ? "throw" : "diagnostic";
|
|
1573
|
+
}
|
|
1574
|
+
};
|
|
1463
1575
|
const tailwind = (0, _tailwindcss_vite.default)();
|
|
1464
1576
|
return [
|
|
1465
1577
|
...(0, _vitejs_plugin_react.default)(),
|
|
1466
1578
|
...Array.isArray(tailwind) ? tailwind : [tailwind],
|
|
1579
|
+
compilePolicyPlugin,
|
|
1467
1580
|
normalizedContentPlugin,
|
|
1468
1581
|
createClarifyViteCorePlugin(engine, normalizedContentPlugin, markdown, mdx),
|
|
1469
1582
|
markdown,
|
|
@@ -1472,7 +1585,7 @@ function createViteAdapter(engine) {
|
|
|
1472
1585
|
}
|
|
1473
1586
|
var cliPackageVersion = {
|
|
1474
1587
|
name: "@clarify-labs/cli",
|
|
1475
|
-
version: "0.
|
|
1588
|
+
version: "0.11.0",
|
|
1476
1589
|
license: "AGPL-3.0-only",
|
|
1477
1590
|
type: "module",
|
|
1478
1591
|
repository: {
|
|
@@ -1552,8 +1665,7 @@ function resolveBuildOptions(options = {}) {
|
|
|
1552
1665
|
return {
|
|
1553
1666
|
projectRoot: options.projectRoot ?? process.cwd(),
|
|
1554
1667
|
rootDirectory: options.rootDirectory ?? "source",
|
|
1555
|
-
outputDirectory: options.outputDirectory
|
|
1556
|
-
ssg: { failOnError: options.ssg?.failOnError ?? true }
|
|
1668
|
+
outputDirectory: options.outputDirectory
|
|
1557
1669
|
};
|
|
1558
1670
|
}
|
|
1559
1671
|
//#endregion
|
|
@@ -1611,7 +1723,7 @@ function llmsTxtListItem(route, basePath) {
|
|
|
1611
1723
|
return description ? `- [${route.meta.title}](${basePath}${contentArtifactUrl}): ${description}` : `- [${route.meta.title}](${basePath}${contentArtifactUrl})`;
|
|
1612
1724
|
}
|
|
1613
1725
|
function llmsTxtLocaleLabel(locale, projectConfig) {
|
|
1614
|
-
return projectConfig.
|
|
1726
|
+
return projectConfig.locales?.locales.find((item) => item.code === locale)?.label ?? locale;
|
|
1615
1727
|
}
|
|
1616
1728
|
function groupRoutesByLocale(routes) {
|
|
1617
1729
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -1623,7 +1735,7 @@ function groupRoutesByLocale(routes) {
|
|
|
1623
1735
|
}
|
|
1624
1736
|
function groupLlmsTxtRoutesByLocale(routes, projectConfig) {
|
|
1625
1737
|
const routesBySource = /* @__PURE__ */ new Map();
|
|
1626
|
-
const defaultLocale = projectConfig.
|
|
1738
|
+
const defaultLocale = projectConfig.locales?.default;
|
|
1627
1739
|
for (const route of routes) {
|
|
1628
1740
|
const sourceKey = `${route.locale ?? defaultLocale ?? "default"}:${route.basePath ?? route.artifacts?.contentArtifactUrl ?? route.path}`;
|
|
1629
1741
|
const previousRoute = routesBySource.get(sourceKey);
|
|
@@ -1825,9 +1937,6 @@ var OPENAPI_HTTP_METHODS = [
|
|
|
1825
1937
|
"trace"
|
|
1826
1938
|
];
|
|
1827
1939
|
var CLARIFY_OPENAPI_SECTION_ID_EXTENSION = "x-clarify-section-id";
|
|
1828
|
-
function errorMessage(error) {
|
|
1829
|
-
return error instanceof Error ? error.message : String(error);
|
|
1830
|
-
}
|
|
1831
1940
|
function isSameOpenAPIFile(url, filePath) {
|
|
1832
1941
|
try {
|
|
1833
1942
|
return (0, node_path.resolve)((0, node_url.fileURLToPath)(url)) === (0, node_path.resolve)(filePath);
|
|
@@ -1942,7 +2051,7 @@ function extractOpenAPISections(spec, filterTags) {
|
|
|
1942
2051
|
}
|
|
1943
2052
|
return sections;
|
|
1944
2053
|
}
|
|
1945
|
-
async function readOpenAPISpec(filePath, contentProcessor) {
|
|
2054
|
+
async function readOpenAPISpec(filePath, contentProcessor, projectRoot) {
|
|
1946
2055
|
try {
|
|
1947
2056
|
if (!contentProcessor) return {
|
|
1948
2057
|
ok: true,
|
|
@@ -1965,13 +2074,14 @@ async function readOpenAPISpec(filePath, contentProcessor) {
|
|
|
1965
2074
|
} catch (error) {
|
|
1966
2075
|
return {
|
|
1967
2076
|
ok: false,
|
|
1968
|
-
diagnostic: {
|
|
2077
|
+
diagnostic: createContentDiagnostic({
|
|
1969
2078
|
kind: "openapi",
|
|
1970
2079
|
title: "OpenAPI spec parse failed",
|
|
1971
|
-
message:
|
|
2080
|
+
message: "Clarify could not parse this OpenAPI specification.",
|
|
2081
|
+
error,
|
|
1972
2082
|
filePath,
|
|
1973
|
-
|
|
1974
|
-
}
|
|
2083
|
+
projectRoot
|
|
2084
|
+
})
|
|
1975
2085
|
};
|
|
1976
2086
|
}
|
|
1977
2087
|
}
|
|
@@ -2002,32 +2112,25 @@ export const routeData = {};
|
|
|
2002
2112
|
export default createOpenApiRouteComponent({ ...routeData, spec });
|
|
2003
2113
|
`;
|
|
2004
2114
|
}
|
|
2005
|
-
function generateOpenAPIErrorModule(diagnostic) {
|
|
2006
|
-
return `import { createContentDiagnosticComponent } from '@clarify-labs/renderer';
|
|
2007
|
-
|
|
2008
|
-
export const contentDiagnostic = ${JSON.stringify({
|
|
2009
|
-
kind: diagnostic.kind ?? "openapi",
|
|
2010
|
-
title: diagnostic.title,
|
|
2011
|
-
message: diagnostic.message,
|
|
2012
|
-
filePath: diagnostic.filePath,
|
|
2013
|
-
details: diagnostic.details
|
|
2014
|
-
})};
|
|
2015
|
-
|
|
2016
|
-
export default createContentDiagnosticComponent(contentDiagnostic);
|
|
2017
|
-
`;
|
|
2018
|
-
}
|
|
2019
2115
|
//#endregion
|
|
2020
2116
|
//#region source/plugins/openapi/index.ts
|
|
2021
2117
|
function collectOpenAPIPageIntents(config) {
|
|
2022
2118
|
const intents = [];
|
|
2023
|
-
function
|
|
2024
|
-
|
|
2025
|
-
|
|
2119
|
+
function visitItems(items) {
|
|
2120
|
+
for (const item of items) {
|
|
2121
|
+
if (typeof item !== "string" && "group" in item) {
|
|
2122
|
+
visitItems(item.pages);
|
|
2123
|
+
continue;
|
|
2124
|
+
}
|
|
2026
2125
|
const intent = routeIntentFromPagesItem(item);
|
|
2027
2126
|
if (intent.kind === "openapi" && (intent.path || intent.tagFilter?.length)) intents.push(intent);
|
|
2028
2127
|
}
|
|
2029
2128
|
}
|
|
2030
|
-
|
|
2129
|
+
function visitPages(pages) {
|
|
2130
|
+
if (!pages || pages === "FileTree") return;
|
|
2131
|
+
for (const group of pages) visitItems(group.pages);
|
|
2132
|
+
}
|
|
2133
|
+
for (const tab of config.navigation?.tabs ?? []) visitPages(tab.pages);
|
|
2031
2134
|
return intents;
|
|
2032
2135
|
}
|
|
2033
2136
|
function virtualModuleIdFromPath(path) {
|
|
@@ -2050,14 +2153,14 @@ function cloneOpenAPIRoute(route, overrides = {}) {
|
|
|
2050
2153
|
...overrides
|
|
2051
2154
|
};
|
|
2052
2155
|
}
|
|
2053
|
-
function findOpenAPIRoutesForContentRoot(contentRoot,
|
|
2054
|
-
if (!
|
|
2156
|
+
function findOpenAPIRoutesForContentRoot(contentRoot, locales) {
|
|
2157
|
+
if (!locales) return findOpenAPIRoutes(contentRoot);
|
|
2055
2158
|
const localizedRoutes = [];
|
|
2056
|
-
for (const locale of
|
|
2159
|
+
for (const locale of locales.locales) {
|
|
2057
2160
|
const localeRoot = (0, node_path.join)(contentRoot, locale.code);
|
|
2058
2161
|
for (const route of findOpenAPIRoutes(localeRoot, localeRoot)) {
|
|
2059
2162
|
const basePath = route.basePath ?? route.path;
|
|
2060
|
-
const path = localizedRoutePath(basePath, locale.code,
|
|
2163
|
+
const path = localizedRoutePath(basePath, locale.code, locales);
|
|
2061
2164
|
localizedRoutes.push({
|
|
2062
2165
|
...route,
|
|
2063
2166
|
path,
|
|
@@ -2067,15 +2170,15 @@ function findOpenAPIRoutesForContentRoot(contentRoot, i18n) {
|
|
|
2067
2170
|
});
|
|
2068
2171
|
}
|
|
2069
2172
|
}
|
|
2070
|
-
if (
|
|
2173
|
+
if (locales.missing === "fallback") {
|
|
2071
2174
|
const routeByLocaleAndBase = new Map(localizedRoutes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
|
|
2072
|
-
const defaultRoutes = localizedRoutes.filter((route) => route.locale ===
|
|
2175
|
+
const defaultRoutes = localizedRoutes.filter((route) => route.locale === locales.default);
|
|
2073
2176
|
for (const sourceRoute of defaultRoutes) {
|
|
2074
2177
|
const basePath = sourceRoute.basePath ?? sourceRoute.path;
|
|
2075
|
-
for (const locale of
|
|
2178
|
+
for (const locale of locales.locales) {
|
|
2076
2179
|
const key = `${locale.code}:${basePath}`;
|
|
2077
2180
|
if (routeByLocaleAndBase.has(key)) continue;
|
|
2078
|
-
const path = localizedRoutePath(basePath, locale.code,
|
|
2181
|
+
const path = localizedRoutePath(basePath, locale.code, locales);
|
|
2079
2182
|
localizedRoutes.push(cloneOpenAPIRoute(sourceRoute, {
|
|
2080
2183
|
path,
|
|
2081
2184
|
locale: locale.code,
|
|
@@ -2102,7 +2205,7 @@ function createConfiguredOpenAPIRoutes(routes, config) {
|
|
|
2102
2205
|
const key = `${route.locale ?? ""}:${targetBasePath}`;
|
|
2103
2206
|
if (existingKeys.has(key)) continue;
|
|
2104
2207
|
existingKeys.add(key);
|
|
2105
|
-
const path = route.locale && config.
|
|
2208
|
+
const path = route.locale && config.locales ? localizedRoutePath(targetBasePath, route.locale, config.locales) : targetBasePath;
|
|
2106
2209
|
additions.push(cloneOpenAPIRoute(route, {
|
|
2107
2210
|
path,
|
|
2108
2211
|
basePath: targetBasePath,
|
|
@@ -2115,12 +2218,12 @@ function createConfiguredOpenAPIRoutes(routes, config) {
|
|
|
2115
2218
|
}
|
|
2116
2219
|
}
|
|
2117
2220
|
const nextRoutes = [...routes, ...additions];
|
|
2118
|
-
if (!config.
|
|
2119
|
-
const routesWithAlternates = nextRoutes.map((route) => withAlternates(route, nextRoutes, config.
|
|
2221
|
+
if (!config.locales) return nextRoutes;
|
|
2222
|
+
const routesWithAlternates = nextRoutes.map((route) => withAlternates(route, nextRoutes, config.locales));
|
|
2120
2223
|
const bareAliases = [];
|
|
2121
2224
|
const seenBare = new Set(routesWithAlternates.map((route) => route.path));
|
|
2122
2225
|
for (const route of routesWithAlternates) {
|
|
2123
|
-
if (route.locale !== config.
|
|
2226
|
+
if (route.locale !== config.locales.default) continue;
|
|
2124
2227
|
const basePath = route.basePath ?? route.path;
|
|
2125
2228
|
if (basePath === route.path || seenBare.has(basePath)) continue;
|
|
2126
2229
|
seenBare.add(basePath);
|
|
@@ -2159,7 +2262,7 @@ function createOpenAPIPlugin() {
|
|
|
2159
2262
|
hooks: {
|
|
2160
2263
|
"routes:discover": (input, ctx) => ({
|
|
2161
2264
|
...input,
|
|
2162
|
-
routes: [...input.routes, ...findOpenAPIRoutesForContentRoot(input.contentRoot, ctx.projectConfig.
|
|
2265
|
+
routes: [...input.routes, ...findOpenAPIRoutesForContentRoot(input.contentRoot, ctx.projectConfig.locales)]
|
|
2163
2266
|
}),
|
|
2164
2267
|
"routes:discovered": async (routes, ctx) => {
|
|
2165
2268
|
specs.clear();
|
|
@@ -2172,7 +2275,7 @@ function createOpenAPIPlugin() {
|
|
|
2172
2275
|
const result = specFromCache ? {
|
|
2173
2276
|
ok: true,
|
|
2174
2277
|
spec: specFromCache
|
|
2175
|
-
} : await readOpenAPISpec(route.source.filePath, processor);
|
|
2278
|
+
} : await readOpenAPISpec(route.source.filePath, processor, ctx.generateOptions.projectRoot);
|
|
2176
2279
|
if (!result.ok) {
|
|
2177
2280
|
route.diagnostic = result.diagnostic;
|
|
2178
2281
|
route.meta.title = route.meta.title || "OpenAPI parse error";
|
|
@@ -2219,7 +2322,7 @@ function createOpenAPIPlugin() {
|
|
|
2219
2322
|
for (const [routeSpecModuleId, spec] of specModules) modules.set(specVirtualModuleId(routeSpecModuleId), generateOpenAPISpecModule(spec));
|
|
2220
2323
|
for (const route of ctx.routes) {
|
|
2221
2324
|
if (route.kind !== "openapi") continue;
|
|
2222
|
-
if (route.diagnostic) modules.set(route.module.virtualModuleId,
|
|
2325
|
+
if (route.diagnostic) modules.set(route.module.virtualModuleId, generateContentDiagnosticModule(route.diagnostic));
|
|
2223
2326
|
else if (route.openapi?.sourceSpecId) {
|
|
2224
2327
|
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2225
2328
|
if (!specModules.has(routeSpecModuleId)) continue;
|
|
@@ -2316,7 +2419,7 @@ async function generateDevSearchIndex(ctx, root, pagefind) {
|
|
|
2316
2419
|
const result = await index.addCustomRecord({
|
|
2317
2420
|
url: route.path,
|
|
2318
2421
|
content,
|
|
2319
|
-
language: toPagefindLanguage(route.locale ?? ctx.projectConfig.
|
|
2422
|
+
language: toPagefindLanguage(route.locale ?? ctx.projectConfig.locales?.default),
|
|
2320
2423
|
meta: { title: route.meta.title }
|
|
2321
2424
|
});
|
|
2322
2425
|
assertNoPagefindErrors(`addCustomRecord ${route.path}`, result.errors);
|
|
@@ -2359,6 +2462,7 @@ function createSearchIndexPlugin() {
|
|
|
2359
2462
|
name: "clarify:search-index",
|
|
2360
2463
|
hooks: {
|
|
2361
2464
|
async "dev:configureServer"(server, ctx) {
|
|
2465
|
+
if (!ctx.projectConfig.features.search.enabled) return;
|
|
2362
2466
|
const pagefind = await import("pagefind");
|
|
2363
2467
|
let currentRoot;
|
|
2364
2468
|
const staleRoots = /* @__PURE__ */ new Set();
|
|
@@ -2398,6 +2502,7 @@ function createSearchIndexPlugin() {
|
|
|
2398
2502
|
});
|
|
2399
2503
|
},
|
|
2400
2504
|
async "build:done"(ctx) {
|
|
2505
|
+
if (!ctx.projectConfig.features.search.enabled) return;
|
|
2401
2506
|
const outputDirectory = ctx.generateOptions.outputDirectory;
|
|
2402
2507
|
if (!outputDirectory) return;
|
|
2403
2508
|
const pageCount = await generatePagefindIndex({ outputRoot: (0, node_path.resolve)(ctx.generateOptions.projectRoot, outputDirectory) });
|
|
@@ -2450,16 +2555,18 @@ function createSeoPlugin() {
|
|
|
2450
2555
|
return {
|
|
2451
2556
|
name: "clarify:seo",
|
|
2452
2557
|
hooks: { "build:assets"(ctx) {
|
|
2558
|
+
const assets = [];
|
|
2453
2559
|
const sitemap = createSitemapXml(ctx.routes, ctx.projectConfig);
|
|
2454
2560
|
const robots = createRobotsTxt(ctx.projectConfig);
|
|
2455
|
-
if (
|
|
2456
|
-
return [{
|
|
2561
|
+
if (sitemap) assets.push({
|
|
2457
2562
|
fileName: "sitemap.xml",
|
|
2458
2563
|
source: sitemap
|
|
2459
|
-
}
|
|
2564
|
+
});
|
|
2565
|
+
if (robots) assets.push({
|
|
2460
2566
|
fileName: "robots.txt",
|
|
2461
2567
|
source: robots
|
|
2462
|
-
}
|
|
2568
|
+
});
|
|
2569
|
+
return assets;
|
|
2463
2570
|
} }
|
|
2464
2571
|
};
|
|
2465
2572
|
}
|
|
@@ -2475,7 +2582,8 @@ function encodePath(path) {
|
|
|
2475
2582
|
return path.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
|
|
2476
2583
|
}
|
|
2477
2584
|
function createSourceEditUrl(filePath, contentRoot, source) {
|
|
2478
|
-
|
|
2585
|
+
if (!source.url) return "";
|
|
2586
|
+
const repository = normalizeRepositoryUrl(source.url);
|
|
2479
2587
|
const branch = source.branch ?? "main";
|
|
2480
2588
|
const sourcePath = [source.directory ? trimSlashes(source.directory) : "", (0, node_path.relative)(contentRoot, filePath).replaceAll("\\", "/")].filter(Boolean).join("/");
|
|
2481
2589
|
return `${repository}/edit/${encodeURIComponent(branch)}/${encodePath(sourcePath)}`;
|
|
@@ -2493,7 +2601,8 @@ function createSourceLinksPlugin() {
|
|
|
2493
2601
|
return {
|
|
2494
2602
|
name: "clarify:source-links",
|
|
2495
2603
|
hooks: { "routes:discovered": (routes, ctx) => {
|
|
2496
|
-
|
|
2604
|
+
if (!ctx.projectConfig.features.repository.enabled) return routes;
|
|
2605
|
+
attachSourceEditUrls(routes, (0, node_path.join)(ctx.generateOptions.projectRoot, ctx.generateOptions.rootDirectory), ctx.projectConfig.features.repository);
|
|
2497
2606
|
return routes;
|
|
2498
2607
|
} }
|
|
2499
2608
|
};
|
|
@@ -2608,18 +2717,18 @@ async function resolveProjectContext(options = {}, env = {
|
|
|
2608
2717
|
}) {
|
|
2609
2718
|
const projectRoot = (0, node_path.resolve)(options.projectRoot ?? process.cwd());
|
|
2610
2719
|
const initialBuildOptions = resolveBuildOptions(options);
|
|
2611
|
-
const contentRoot = (0, node_path.join)(projectRoot, initialBuildOptions.rootDirectory);
|
|
2612
2720
|
const configFilePath = findClarifyConfigFile(projectRoot);
|
|
2613
2721
|
const config = configFilePath && (0, node_fs.existsSync)(configFilePath) ? await loadClarifyConfig(projectRoot, env) : {};
|
|
2614
2722
|
const mergedOptions = {
|
|
2615
2723
|
...config,
|
|
2616
2724
|
...options,
|
|
2617
2725
|
projectRoot,
|
|
2618
|
-
rootDirectory: initialBuildOptions.rootDirectory,
|
|
2619
|
-
outputDirectory: initialBuildOptions.outputDirectory
|
|
2726
|
+
rootDirectory: options.rootDirectory ?? initialBuildOptions.rootDirectory,
|
|
2727
|
+
outputDirectory: options.outputDirectory ?? initialBuildOptions.outputDirectory
|
|
2620
2728
|
};
|
|
2621
2729
|
const resolvedProjectConfig = resolveProjectConfig(mergedOptions);
|
|
2622
2730
|
const resolvedBuildOptions = resolveBuildOptions(mergedOptions);
|
|
2731
|
+
const contentRoot = (0, node_path.join)(projectRoot, resolvedBuildOptions.rootDirectory);
|
|
2623
2732
|
return {
|
|
2624
2733
|
projectRoot,
|
|
2625
2734
|
contentRoot,
|
|
@@ -2709,9 +2818,9 @@ function canonicalUrl(contextOrConfig, route) {
|
|
|
2709
2818
|
/** Inject `lang` (and optional `dir`) onto the `<html>` tag for the route locale. */
|
|
2710
2819
|
function injectHtmlLocaleAttributes(html, contextOrConfig, route) {
|
|
2711
2820
|
const projectConfig = resolveProjectConfigFromContext(contextOrConfig);
|
|
2712
|
-
const localeCode = route?.locale ?? projectConfig.
|
|
2821
|
+
const localeCode = route?.locale ?? projectConfig.locales?.default;
|
|
2713
2822
|
if (!localeCode) return html;
|
|
2714
|
-
const dir = (projectConfig.
|
|
2823
|
+
const dir = (projectConfig.locales?.locales.find((locale) => locale.code === localeCode))?.dir;
|
|
2715
2824
|
return html.replace(/<html\b([^>]*)>/i, (_match, attributes) => {
|
|
2716
2825
|
let nextAttributes = attributes.replace(/\s+lang=("[^"]*"|'[^']*'|[^\s>]*)/i, "").replace(/\s+dir=("[^"]*"|'[^']*'|[^\s>]*)/i, "");
|
|
2717
2826
|
nextAttributes = `${nextAttributes} lang="${escapeHtml(localeCode)}"`;
|
|
@@ -2889,7 +2998,7 @@ async function buildSSRBundle(root, ssrEntry, ssrOutDir, plugins) {
|
|
|
2889
2998
|
}
|
|
2890
2999
|
});
|
|
2891
3000
|
}
|
|
2892
|
-
async function renderSSGRoutes(routes, contextOrConfig, outputDirectory, ssrBundlePath
|
|
3001
|
+
async function renderSSGRoutes(routes, contextOrConfig, outputDirectory, ssrBundlePath) {
|
|
2893
3002
|
const { render } = await import((0, node_url.pathToFileURL)(ssrBundlePath).href);
|
|
2894
3003
|
const template = readIndexHtml(outputDirectory);
|
|
2895
3004
|
if (!template) throw new Error(`[clarify] index.html not found in outputDirectory "${outputDirectory}". Make sure Vite build produces it.`);
|
|
@@ -2901,7 +3010,7 @@ async function renderSSGRoutes(routes, contextOrConfig, outputDirectory, ssrBund
|
|
|
2901
3010
|
}
|
|
2902
3011
|
} catch (err) {
|
|
2903
3012
|
console.error(`[clarify] Failed to render route "${route.path}":`, err);
|
|
2904
|
-
|
|
3013
|
+
throw err;
|
|
2905
3014
|
}
|
|
2906
3015
|
}
|
|
2907
3016
|
//#endregion
|
|
@@ -2978,10 +3087,10 @@ var ClarifyContext = class {
|
|
|
2978
3087
|
this.currentPlugins = plugins;
|
|
2979
3088
|
}
|
|
2980
3089
|
get isI18n() {
|
|
2981
|
-
return Boolean(this.projectConfig.
|
|
3090
|
+
return Boolean(this.projectConfig.locales);
|
|
2982
3091
|
}
|
|
2983
3092
|
get defaultLocale() {
|
|
2984
|
-
return this.projectConfig.
|
|
3093
|
+
return this.projectConfig.locales?.default;
|
|
2985
3094
|
}
|
|
2986
3095
|
get(key) {
|
|
2987
3096
|
return this.values.get(key);
|
|
@@ -3051,6 +3160,12 @@ function loadVirtualModule(id, modules) {
|
|
|
3051
3160
|
const bareId = stripVirtualPrefix(id);
|
|
3052
3161
|
return modules.get(bareId) ?? modules.get(id) ?? null;
|
|
3053
3162
|
}
|
|
3163
|
+
function assertNoContentDiagnostics(routes) {
|
|
3164
|
+
const failures = routes.filter((route) => route.diagnostic);
|
|
3165
|
+
if (failures.length === 0) return;
|
|
3166
|
+
const summary = failures.map((route) => `- ${route.path}: ${route.diagnostic?.title ?? "Content error"}${route.diagnostic?.filePath ? ` (${route.diagnostic.filePath})` : ""}`).join("\n");
|
|
3167
|
+
throw new Error(`[clarify] Content diagnostics prevented the build:\n${summary}`);
|
|
3168
|
+
}
|
|
3054
3169
|
var ClarifyEngine = class {
|
|
3055
3170
|
options;
|
|
3056
3171
|
root;
|
|
@@ -3169,10 +3284,10 @@ var ClarifyEngine = class {
|
|
|
3169
3284
|
async discoverSite() {
|
|
3170
3285
|
const plugins = this.ctx.plugins;
|
|
3171
3286
|
const contentRoot = this.ctx.contentRoot;
|
|
3172
|
-
const {
|
|
3287
|
+
const { locales, navigation: navigationConfig } = this.ctx.projectConfig;
|
|
3173
3288
|
let routes = await runPhase(plugins, "site:discover", this.ctx, async () => {
|
|
3174
3289
|
const options = { contentProcessor: createProjectContentProcessor(plugins, this.ctx) };
|
|
3175
|
-
const discovered =
|
|
3290
|
+
const discovered = locales ? await findLocalizedContentRoutes(contentRoot, locales, options) : await findContentRoutes(contentRoot, contentRoot, options);
|
|
3176
3291
|
const result = await runHooks(plugins, "routes:discover", {
|
|
3177
3292
|
contentRoot,
|
|
3178
3293
|
routes: discovered
|
|
@@ -3201,8 +3316,9 @@ var ClarifyEngine = class {
|
|
|
3201
3316
|
};
|
|
3202
3317
|
});
|
|
3203
3318
|
});
|
|
3204
|
-
|
|
3205
|
-
|
|
3319
|
+
const tabs = navigationConfig?.tabs;
|
|
3320
|
+
routes = applyConfiguredPageRoutePaths(routes, tabs, locales);
|
|
3321
|
+
const navigation = tabs ? locales ? buildLocalizedNavigationFromTabsConfig(routes, tabs, locales) ?? {
|
|
3206
3322
|
kind: "localized-tabbed",
|
|
3207
3323
|
locales: {}
|
|
3208
3324
|
} : buildNavigationFromTabsConfig(routes, tabs) : {
|
|
@@ -3232,7 +3348,7 @@ var ClarifyEngine = class {
|
|
|
3232
3348
|
routes: this.routes,
|
|
3233
3349
|
navigation: this.navigation,
|
|
3234
3350
|
plugins: this.plugins,
|
|
3235
|
-
themeEditor: this.runtime.command === "serve" || this.runtime.themeEditor === true || this.projectConfig.
|
|
3351
|
+
themeEditor: this.runtime.command === "serve" || this.runtime.themeEditor === true || this.projectConfig.features.themeEditor.enabled,
|
|
3236
3352
|
version: this.ctx.version
|
|
3237
3353
|
});
|
|
3238
3354
|
this.virtualModules = await runHooks(this.plugins, "modules:before", this.virtualModules, this.ctx);
|
|
@@ -3275,6 +3391,7 @@ var ClarifyEngine = class {
|
|
|
3275
3391
|
await runPhase(this.plugins, "ssg", this.ctx, async () => {
|
|
3276
3392
|
if (process.env.SKIP_CLARIFY_SSG) return;
|
|
3277
3393
|
if (!await runInterceptHooks(this.plugins, "ssg:shouldRun", this.ctx)) return;
|
|
3394
|
+
assertNoContentDiagnostics(this.routes);
|
|
3278
3395
|
const outputDir = this.runtime.outputDirectory ?? this.generateOptions.outputDirectory;
|
|
3279
3396
|
if (!outputDir) throw new Error("[clarify] outputDirectory is required before SSG runs");
|
|
3280
3397
|
const ssrOutputDir = (0, node_path.join)(outputDir, ".ssr");
|
|
@@ -3288,10 +3405,10 @@ var ClarifyEngine = class {
|
|
|
3288
3405
|
plugins: this.runtime.ssrPlugins ?? []
|
|
3289
3406
|
});
|
|
3290
3407
|
const ssrBundlePath = (0, node_path.join)(ssrOutputDir, "entry-server.js");
|
|
3291
|
-
await renderSSGRoutes(this.routes, this.runtimeContext(), outputDir, ssrBundlePath
|
|
3408
|
+
await renderSSGRoutes(this.routes, this.runtimeContext(), outputDir, ssrBundlePath);
|
|
3292
3409
|
} catch (err) {
|
|
3293
3410
|
console.error("[clarify] SSG failed:", err);
|
|
3294
|
-
|
|
3411
|
+
throw err;
|
|
3295
3412
|
} finally {
|
|
3296
3413
|
if (tempEntryPath) try {
|
|
3297
3414
|
(0, node_fs.rmSync)(tempEntryPath, { force: true });
|
|
@@ -3382,4 +3499,4 @@ Object.defineProperty(exports, "defineConfig", {
|
|
|
3382
3499
|
}
|
|
3383
3500
|
});
|
|
3384
3501
|
|
|
3385
|
-
//# sourceMappingURL=engine-
|
|
3502
|
+
//# sourceMappingURL=engine-BfkoyVNo.cjs.map
|