@clarify-labs/cli 0.10.0 → 0.10.1
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 +14 -9
- package/output/cli.cjs.map +1 -1
- package/output/cli.js +14 -9
- package/output/cli.js.map +1 -1
- package/output/core/engine/phases.d.ts +5 -5
- package/output/core/plugin/hooks.d.ts +2 -2
- package/output/core/runtime/ssg.d.ts +1 -1
- package/output/core/runtime/virtual-modules.d.ts +1 -0
- package/output/{engine-I04EZzjg.cjs → engine-5bdNX2Oo.cjs} +449 -247
- package/output/engine-5bdNX2Oo.cjs.map +1 -0
- package/output/{engine-3f5Tpa-6.js → engine-Ca_2MCj9.js} +449 -247
- package/output/engine-Ca_2MCj9.js.map +1 -0
- package/output/index.cjs +1 -1
- package/output/index.d.ts +1 -1
- package/output/index.js +1 -1
- package/output/parsers/routes/routes.d.ts +2 -1
- package/output/parsers/routes/routes.test-utils.d.ts +18 -1
- package/output/plugins/openapi/parser.d.ts +1 -0
- package/output/plugins/openapi/virtual-modules.d.ts +4 -3
- package/output/plugins/source-links/source-links.d.ts +2 -2
- package/output/types.d.ts +108 -44
- package/package.json +3 -3
- package/output/engine-3f5Tpa-6.js.map +0 -1
- package/output/engine-I04EZzjg.cjs.map +0 -1
|
@@ -67,14 +67,14 @@ function renderPlainCode(code) {
|
|
|
67
67
|
function isBundledLanguage(language) {
|
|
68
68
|
return language in shiki.bundledLanguages;
|
|
69
69
|
}
|
|
70
|
-
var
|
|
70
|
+
var highlighterPromise;
|
|
71
71
|
var cssVariablesTheme = (0, shiki.createCssVariablesTheme)();
|
|
72
72
|
async function getHighlighter() {
|
|
73
|
-
|
|
73
|
+
highlighterPromise = highlighterPromise ?? (0, shiki.createHighlighter)({
|
|
74
74
|
themes: [cssVariablesTheme],
|
|
75
75
|
langs: []
|
|
76
76
|
});
|
|
77
|
-
return
|
|
77
|
+
return highlighterPromise;
|
|
78
78
|
}
|
|
79
79
|
function renderHighlightedCode(html) {
|
|
80
80
|
return (/<code[^>]*>([\s\S]*?)<\/code>/.exec(html)?.[1] ?? html).split("\n").map((line) => line.trimStart().startsWith("<span") ? line : `<span>${line}</span>`).join("\n");
|
|
@@ -336,12 +336,12 @@ function extractMdxSections(content) {
|
|
|
336
336
|
return sections;
|
|
337
337
|
}
|
|
338
338
|
function navigationSections(sections) {
|
|
339
|
-
return sections.map((
|
|
340
|
-
id:
|
|
341
|
-
title:
|
|
342
|
-
level:
|
|
343
|
-
badge:
|
|
344
|
-
tags:
|
|
339
|
+
return sections.map((section) => ({
|
|
340
|
+
id: section.id,
|
|
341
|
+
title: section.title,
|
|
342
|
+
level: section.level,
|
|
343
|
+
badge: section.badge,
|
|
344
|
+
tags: section.tags
|
|
345
345
|
}));
|
|
346
346
|
}
|
|
347
347
|
function normalizePath(path) {
|
|
@@ -413,16 +413,20 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
413
413
|
}
|
|
414
414
|
routes.push({
|
|
415
415
|
kind: "mdx",
|
|
416
|
-
title,
|
|
417
416
|
path: cleanPath,
|
|
418
417
|
basePath: cleanPath,
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
418
|
+
meta: {
|
|
419
|
+
title,
|
|
420
|
+
description: typeof page.frontmatter.description === "string" ? page.frontmatter.description : void 0,
|
|
421
|
+
keywords: frontmatterKeywords(page.frontmatter),
|
|
422
|
+
sections: extractMdxSections(page.content)
|
|
423
|
+
},
|
|
424
|
+
module: { virtualModuleId: "virtual:clarify-page/" + relativePath.replace(/\.mdx?$/, "").replace(/\/+/g, "/") },
|
|
425
|
+
source: {
|
|
426
|
+
filePath: fullPath,
|
|
427
|
+
frontmatter: page.frontmatter,
|
|
428
|
+
content: page.content
|
|
429
|
+
},
|
|
426
430
|
diagnostic: mdxResult.ok ? void 0 : mdxResult.diagnostic
|
|
427
431
|
});
|
|
428
432
|
}
|
|
@@ -482,7 +486,7 @@ async function findLocalizedContentRoutes(contentRoot, i18n, options = {}) {
|
|
|
482
486
|
path: localizedRoutePath(basePath, locale.code, i18n),
|
|
483
487
|
basePath,
|
|
484
488
|
locale: locale.code,
|
|
485
|
-
virtualModuleId: virtualModuleIdForLocalizedRoute(contentRoot, route.filePath)
|
|
489
|
+
module: { virtualModuleId: virtualModuleIdForLocalizedRoute(contentRoot, route.source.filePath) }
|
|
486
490
|
});
|
|
487
491
|
}
|
|
488
492
|
}
|
|
@@ -518,12 +522,12 @@ function buildNavigation(routes) {
|
|
|
518
522
|
if (!node) {
|
|
519
523
|
node = {
|
|
520
524
|
path: pathSoFar,
|
|
521
|
-
title: i === parts.length - 1 ? route.title : kebabToTitle(parts[i]),
|
|
525
|
+
title: i === parts.length - 1 ? route.meta.title : kebabToTitle(parts[i]),
|
|
522
526
|
children: []
|
|
523
527
|
};
|
|
524
528
|
current.push(node);
|
|
525
529
|
}
|
|
526
|
-
if (i === parts.length - 1 && route.sections) node.sections = navigationSections(route.sections);
|
|
530
|
+
if (i === parts.length - 1 && route.meta.sections) node.sections = navigationSections(route.meta.sections);
|
|
527
531
|
if (i < parts.length - 1) {
|
|
528
532
|
node.children = node.children ?? [];
|
|
529
533
|
current = node.children;
|
|
@@ -532,17 +536,22 @@ function buildNavigation(routes) {
|
|
|
532
536
|
}
|
|
533
537
|
return root;
|
|
534
538
|
}
|
|
535
|
-
function
|
|
536
|
-
if (typeof item === "string") return {
|
|
539
|
+
function routeIntentFromPagesItem(item) {
|
|
540
|
+
if (typeof item === "string") return {
|
|
541
|
+
kind: "page",
|
|
542
|
+
ref: item
|
|
543
|
+
};
|
|
537
544
|
if ("openapi" in item) return {
|
|
538
|
-
|
|
539
|
-
|
|
545
|
+
kind: "openapi",
|
|
546
|
+
ref: item.openapi,
|
|
547
|
+
tagFilter: item.filter?.tags,
|
|
540
548
|
path: item.path,
|
|
541
549
|
title: item.title,
|
|
542
550
|
icon: item.icon
|
|
543
551
|
};
|
|
544
552
|
return {
|
|
545
|
-
|
|
553
|
+
kind: "page",
|
|
554
|
+
ref: item.page,
|
|
546
555
|
path: item.path,
|
|
547
556
|
redirect: item.redirect,
|
|
548
557
|
title: item.title,
|
|
@@ -557,25 +566,28 @@ function firstNavigationPath(nodes) {
|
|
|
557
566
|
}
|
|
558
567
|
return "/";
|
|
559
568
|
}
|
|
560
|
-
function
|
|
561
|
-
const
|
|
569
|
+
function collectExplicitPagePathIntents(tabs) {
|
|
570
|
+
const intents = [];
|
|
562
571
|
for (const tab of tabs ?? []) {
|
|
563
572
|
if (!tab.pages || tab.pages === "FileTree") continue;
|
|
564
|
-
for (const group of tab.pages) for (const item of group.pages) if (typeof item !== "string" && "page" in item && item.path)
|
|
565
|
-
|
|
566
|
-
path
|
|
567
|
-
|
|
573
|
+
for (const group of tab.pages) for (const item of group.pages) if (typeof item !== "string" && "page" in item && item.path) {
|
|
574
|
+
const intent = routeIntentFromPagesItem(item);
|
|
575
|
+
if (intent.kind === "page" && intent.path) intents.push({
|
|
576
|
+
...intent,
|
|
577
|
+
path: intent.path
|
|
578
|
+
});
|
|
579
|
+
}
|
|
568
580
|
}
|
|
569
|
-
return
|
|
581
|
+
return intents;
|
|
570
582
|
}
|
|
571
583
|
function applyConfiguredPageRoutePaths(routes, tabs, i18n) {
|
|
572
|
-
const
|
|
573
|
-
if (!
|
|
584
|
+
const pageIntents = collectExplicitPagePathIntents(tabs);
|
|
585
|
+
if (!pageIntents.length) return routes;
|
|
574
586
|
const additions = [];
|
|
575
587
|
const existingKeys = new Set(routes.map((route) => `${route.locale ?? ""}:${route.basePath ?? route.path}`));
|
|
576
|
-
for (const
|
|
577
|
-
const sourceBasePath = routePathFromRef(
|
|
578
|
-
const targetBasePath = routePathFromRef(
|
|
588
|
+
for (const intent of pageIntents) {
|
|
589
|
+
const sourceBasePath = routePathFromRef(intent.ref);
|
|
590
|
+
const targetBasePath = routePathFromRef(intent.ref, intent.path);
|
|
579
591
|
if (sourceBasePath === targetBasePath) continue;
|
|
580
592
|
for (const route of routes) {
|
|
581
593
|
if (route.kind !== "mdx" || (route.basePath ?? route.path) !== sourceBasePath) continue;
|
|
@@ -599,40 +611,42 @@ function buildNavigationFromPagesConfig(routes, config) {
|
|
|
599
611
|
return buildNavigationFromConfig(routes, config);
|
|
600
612
|
}
|
|
601
613
|
function buildNavigationFromTabsConfig(routes, tabs) {
|
|
602
|
-
return {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
614
|
+
return {
|
|
615
|
+
kind: "tabbed",
|
|
616
|
+
tabs: tabs.map((tab) => {
|
|
617
|
+
const children = buildNavigationFromPagesConfig(routes, tab.pages);
|
|
618
|
+
return {
|
|
619
|
+
type: "tab",
|
|
620
|
+
path: firstNavigationPath(children),
|
|
621
|
+
title: resolveLocalizedText(tab.tab, "", "") ?? "",
|
|
622
|
+
icon: tab.icon,
|
|
623
|
+
children
|
|
624
|
+
};
|
|
625
|
+
})
|
|
626
|
+
};
|
|
612
627
|
}
|
|
613
628
|
function buildNavigationFromConfig(routes, config) {
|
|
614
629
|
const routeMap = new Map(routes.map((r) => [r.path, r]));
|
|
615
630
|
return config.map((group) => {
|
|
616
631
|
const children = group.pages.map((item) => {
|
|
617
|
-
const
|
|
618
|
-
if (
|
|
619
|
-
const path = openAPIPagePathFromRef(
|
|
632
|
+
const intent = routeIntentFromPagesItem(item);
|
|
633
|
+
if (intent.kind === "openapi") {
|
|
634
|
+
const path = openAPIPagePathFromRef(intent.ref, intent.tagFilter, intent.path);
|
|
620
635
|
const route = routeMap.get(path);
|
|
621
636
|
return {
|
|
622
637
|
path,
|
|
623
|
-
title: resolveLocalizedText(title, "", "") ?? route?.title ?? kebabToTitle(path.split("/").pop() ??
|
|
624
|
-
icon,
|
|
625
|
-
sections: route?.sections ? navigationSections(route.sections) : void 0
|
|
638
|
+
title: resolveLocalizedText(intent.title, "", "") ?? route?.meta.title ?? kebabToTitle(path.split("/").pop() ?? intent.ref),
|
|
639
|
+
icon: intent.icon,
|
|
640
|
+
sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
|
|
626
641
|
};
|
|
627
642
|
}
|
|
628
|
-
const
|
|
629
|
-
const path = routePathFromRef(ref, explicitPath);
|
|
643
|
+
const path = routePathFromRef(intent.ref, intent.path);
|
|
630
644
|
const route = routeMap.get(path);
|
|
631
645
|
return {
|
|
632
|
-
path: redirect ? normalizePath(redirect) : path,
|
|
633
|
-
title: resolveLocalizedText(title, "", "") ?? route?.title ?? kebabToTitle(path.split("/").pop() ?? ref),
|
|
634
|
-
icon,
|
|
635
|
-
sections: route?.sections ? navigationSections(route.sections) : void 0
|
|
646
|
+
path: intent.redirect ? normalizePath(intent.redirect) : path,
|
|
647
|
+
title: resolveLocalizedText(intent.title, "", "") ?? route?.meta.title ?? kebabToTitle(path.split("/").pop() ?? intent.ref),
|
|
648
|
+
icon: intent.icon,
|
|
649
|
+
sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
|
|
636
650
|
};
|
|
637
651
|
});
|
|
638
652
|
return {
|
|
@@ -658,16 +672,15 @@ function buildLocalizedNavigationForLocale(routes, config, locale, i18n) {
|
|
|
658
672
|
const routeMap = new Map(routes.map((route) => [route.basePath ?? route.path, route]));
|
|
659
673
|
return config.map((group) => {
|
|
660
674
|
const children = group.pages.map((item) => {
|
|
661
|
-
const
|
|
662
|
-
const
|
|
663
|
-
const basePath = openapiRef ? openAPIPagePathFromRef(openapiRef, openapiTagFilter, explicitPath) : routePathFromRef(ref, explicitPath);
|
|
675
|
+
const intent = routeIntentFromPagesItem(item);
|
|
676
|
+
const basePath = intent.kind === "openapi" ? openAPIPagePathFromRef(intent.ref, intent.tagFilter, intent.path) : routePathFromRef(intent.ref, intent.path);
|
|
664
677
|
const route = routeMap.get(basePath);
|
|
665
678
|
const path = route?.path ?? localizedRoutePath(basePath, locale, i18n);
|
|
666
679
|
return {
|
|
667
|
-
path: (redirect ? localizedRoutePath(routePathFromRef(redirect), locale, i18n) : void 0) ?? path,
|
|
668
|
-
title: resolveLocalizedText(title, locale, i18n.defaultLocale) ?? route?.title ?? kebabToTitle(basePath.split("/").pop() ?? ref),
|
|
669
|
-
icon,
|
|
670
|
-
sections: route?.sections ? navigationSections(route.sections) : void 0
|
|
680
|
+
path: (intent.kind === "page" && intent.redirect ? localizedRoutePath(routePathFromRef(intent.redirect), locale, i18n) : void 0) ?? path,
|
|
681
|
+
title: resolveLocalizedText(intent.title, locale, i18n.defaultLocale) ?? route?.meta.title ?? kebabToTitle(basePath.split("/").pop() ?? intent.ref),
|
|
682
|
+
icon: intent.icon,
|
|
683
|
+
sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
|
|
671
684
|
};
|
|
672
685
|
});
|
|
673
686
|
return {
|
|
@@ -680,10 +693,10 @@ function buildLocalizedNavigationForLocale(routes, config, locale, i18n) {
|
|
|
680
693
|
}
|
|
681
694
|
function buildLocalizedNavigationFromTabsConfig(routes, tabs, i18n) {
|
|
682
695
|
if (!i18n) return void 0;
|
|
683
|
-
const
|
|
696
|
+
const locales = {};
|
|
684
697
|
for (const locale of i18n.locales) {
|
|
685
698
|
const localeRoutes = routes.filter((route) => route.locale === locale.code);
|
|
686
|
-
|
|
699
|
+
locales[locale.code] = { tabs: tabs.map((tab) => {
|
|
687
700
|
const children = buildLocalizedNavigationForLocale(localeRoutes, tab.pages, locale.code, i18n);
|
|
688
701
|
return {
|
|
689
702
|
type: "tab",
|
|
@@ -694,17 +707,20 @@ function buildLocalizedNavigationFromTabsConfig(routes, tabs, i18n) {
|
|
|
694
707
|
};
|
|
695
708
|
}) };
|
|
696
709
|
}
|
|
697
|
-
return
|
|
710
|
+
return {
|
|
711
|
+
kind: "localized-tabbed",
|
|
712
|
+
locales
|
|
713
|
+
};
|
|
698
714
|
}
|
|
699
715
|
/** Serialize a ContentRoute into the minimal shape exposed to tooling. */
|
|
700
716
|
function toDevRouteEntry(route) {
|
|
701
717
|
return {
|
|
702
718
|
path: route.path,
|
|
703
|
-
filePath: route.filePath,
|
|
719
|
+
filePath: route.source.filePath,
|
|
704
720
|
locale: route.locale,
|
|
705
721
|
basePath: route.basePath,
|
|
706
722
|
kind: route.kind,
|
|
707
|
-
title: route.title
|
|
723
|
+
title: route.meta.title
|
|
708
724
|
};
|
|
709
725
|
}
|
|
710
726
|
/**
|
|
@@ -718,7 +734,7 @@ function toDevRouteEntry(route) {
|
|
|
718
734
|
* or the first match.
|
|
719
735
|
*/
|
|
720
736
|
function resolveRouteForFile(filePath, routes, defaultLocale) {
|
|
721
|
-
const matches = routes.filter((r) => r.filePath === filePath);
|
|
737
|
+
const matches = routes.filter((r) => r.source.filePath === filePath);
|
|
722
738
|
if (matches.length === 0) return void 0;
|
|
723
739
|
if (matches.length === 1) return matches[0];
|
|
724
740
|
const fileLocale = inferLocaleFromPath(filePath);
|
|
@@ -1143,6 +1159,7 @@ var VIRTUAL_CONFIG = "virtual:clarify/config";
|
|
|
1143
1159
|
var VIRTUAL_ROUTES = "virtual:clarify/routes";
|
|
1144
1160
|
var VIRTUAL_SERVER_ROUTES = "virtual:clarify/routes/server";
|
|
1145
1161
|
var VIRTUAL_OPENAPI = "virtual:clarify/openapi";
|
|
1162
|
+
var VIRTUAL_OPENAPI_SERVER = "virtual:clarify/openapi/server";
|
|
1146
1163
|
var VIRTUAL_SLOTS = "virtual:clarify/slots";
|
|
1147
1164
|
var VIRTUAL_SLOT = "virtual:clarify/slot";
|
|
1148
1165
|
var VIRTUAL_CLIENT_ENTRY = "virtual:clarify/entry-client";
|
|
@@ -1163,6 +1180,7 @@ var FIXED_VIRTUAL_IDS = [
|
|
|
1163
1180
|
VIRTUAL_ROUTES,
|
|
1164
1181
|
VIRTUAL_SERVER_ROUTES,
|
|
1165
1182
|
VIRTUAL_OPENAPI,
|
|
1183
|
+
VIRTUAL_OPENAPI_SERVER,
|
|
1166
1184
|
VIRTUAL_SLOTS,
|
|
1167
1185
|
VIRTUAL_SLOT
|
|
1168
1186
|
];
|
|
@@ -1179,8 +1197,8 @@ function resolveVirtualModuleId(id, modules, routes) {
|
|
|
1179
1197
|
for (const virtualId of FIXED_VIRTUAL_IDS) if (id === virtualId || id === resolveVirtualId(virtualId)) return resolveVirtualId(virtualId);
|
|
1180
1198
|
const moduleId = stripVirtualPrefix(id);
|
|
1181
1199
|
if (modules.has(moduleId)) return resolveVirtualId(moduleId);
|
|
1182
|
-
const route = routes.find((route) => route.virtualModuleId === id || route.virtualModuleId === moduleId);
|
|
1183
|
-
if (route) return resolveVirtualId(route.virtualModuleId);
|
|
1200
|
+
const route = routes.find((route) => route.module.virtualModuleId === id || route.module.virtualModuleId === moduleId);
|
|
1201
|
+
if (route) return resolveVirtualId(route.module.virtualModuleId);
|
|
1184
1202
|
return null;
|
|
1185
1203
|
}
|
|
1186
1204
|
function generateConfigModule(projectConfig, buildOptions, version) {
|
|
@@ -1194,38 +1212,38 @@ function generateConfigModule(projectConfig, buildOptions, version) {
|
|
|
1194
1212
|
function moduleSpecifier(value) {
|
|
1195
1213
|
return JSON.stringify(value);
|
|
1196
1214
|
}
|
|
1197
|
-
function
|
|
1198
|
-
const
|
|
1215
|
+
function routeToRuntimeManifestEntry(route, component, mode) {
|
|
1216
|
+
const entry = {
|
|
1199
1217
|
path: route.path,
|
|
1200
|
-
title: route.title,
|
|
1218
|
+
title: route.meta.title,
|
|
1201
1219
|
component,
|
|
1202
1220
|
kind: route.kind
|
|
1203
1221
|
};
|
|
1204
|
-
if (mode === "client")
|
|
1205
|
-
if (route.basePath)
|
|
1206
|
-
if (route.locale)
|
|
1207
|
-
if (route.isFallback)
|
|
1208
|
-
if (route.isBareAlias)
|
|
1209
|
-
if (route.alternates)
|
|
1210
|
-
if (route.description)
|
|
1211
|
-
if (route.keywords?.length)
|
|
1212
|
-
if (route.contentArtifactUrl)
|
|
1213
|
-
if (route.
|
|
1214
|
-
if (route.sections?.length)
|
|
1215
|
-
id:
|
|
1216
|
-
title:
|
|
1217
|
-
level:
|
|
1218
|
-
badge:
|
|
1219
|
-
tags:
|
|
1222
|
+
if (mode === "client") entry.lazy = true;
|
|
1223
|
+
if (route.basePath) entry.basePath = route.basePath;
|
|
1224
|
+
if (route.locale) entry.locale = route.locale;
|
|
1225
|
+
if (route.isFallback) entry.isFallback = true;
|
|
1226
|
+
if (route.isBareAlias) entry.isBareAlias = true;
|
|
1227
|
+
if (route.alternates) entry.alternates = route.alternates;
|
|
1228
|
+
if (route.meta.description) entry.description = route.meta.description;
|
|
1229
|
+
if (route.meta.keywords?.length) entry.keywords = route.meta.keywords;
|
|
1230
|
+
if (route.artifacts?.contentArtifactUrl) entry.contentArtifactUrl = route.artifacts.contentArtifactUrl;
|
|
1231
|
+
if (route.source?.sourceEditUrl) entry.sourceEditUrl = route.source.sourceEditUrl;
|
|
1232
|
+
if (route.meta.sections?.length) entry.sections = route.meta.sections.map((section) => ({
|
|
1233
|
+
id: section.id,
|
|
1234
|
+
title: section.title,
|
|
1235
|
+
level: section.level,
|
|
1236
|
+
badge: section.badge,
|
|
1237
|
+
tags: section.tags
|
|
1220
1238
|
}));
|
|
1221
|
-
return
|
|
1239
|
+
return entry;
|
|
1222
1240
|
}
|
|
1223
1241
|
function generateRoutesModule(routes, navigation, mode = "client") {
|
|
1224
|
-
return `${mode === "server" ? routes.map((r, i) => `import Page${i} from ${moduleSpecifier(r.virtualModuleId)};`).join("\n") : `import { createContentDiagnosticComponent } from '@clarify-labs/renderer';`}\n\nexport const routes = [\n${routes.map((r, i) => {
|
|
1225
|
-
const component = mode === "server" ? `Page${i}` : `() => import(${moduleSpecifier(r.virtualModuleId)}).catch((error) => Promise.resolve({ default: createContentDiagnosticComponent({ kind: 'route-load', title: 'This page could not be loaded', message: error instanceof Error ? error.message : String(error), details: error instanceof Error ? error.stack : undefined }) }))`;
|
|
1242
|
+
return `${mode === "server" ? routes.map((r, i) => `import Page${i} from ${moduleSpecifier(r.module.virtualModuleId)};`).join("\n") : `import { createContentDiagnosticComponent } from '@clarify-labs/renderer';`}\n\nexport const routes = [\n${routes.map((r, i) => {
|
|
1243
|
+
const component = mode === "server" ? `Page${i}` : `() => import(${moduleSpecifier(r.module.virtualModuleId)}).catch((error) => Promise.resolve({ default: createContentDiagnosticComponent({ kind: 'route-load', title: 'This page could not be loaded', message: error instanceof Error ? error.message : String(error), details: error instanceof Error ? error.stack : undefined }) }))`;
|
|
1226
1244
|
const placeholder = `__COMPONENT_${i}__`;
|
|
1227
|
-
const
|
|
1228
|
-
return ` ${JSON.stringify(
|
|
1245
|
+
const manifestEntry = routeToRuntimeManifestEntry(r, placeholder, mode);
|
|
1246
|
+
return ` ${JSON.stringify(manifestEntry, null, 0).replace(`"${placeholder}"`, component).replace(/"([A-Za-z_$][\w$]*)":/g, "$1: ")}`;
|
|
1229
1247
|
}).join(",\n")}\n];\n\nexport const navigation = ${JSON.stringify(navigation, null, 2)};\n`;
|
|
1230
1248
|
}
|
|
1231
1249
|
function createClientEntryModule(options = {}) {
|
|
@@ -1234,9 +1252,15 @@ import '@clarify-labs/renderer/style.css';
|
|
|
1234
1252
|
import { render } from '@clarify-labs/renderer/client';
|
|
1235
1253
|
import { routes, navigation } from '${VIRTUAL_ROUTES}';
|
|
1236
1254
|
import { config } from '${VIRTUAL_CONFIG}';
|
|
1237
|
-
import {
|
|
1255
|
+
import { openApiSpecs } from '${VIRTUAL_OPENAPI}';
|
|
1238
1256
|
import { runtimeSlots } from '${VIRTUAL_SLOTS}';
|
|
1239
|
-
|
|
1257
|
+
const renderOptions = { config, routes, navigation, openApiSpecs, runtimeSlots, themeEditor: ${JSON.stringify(options.themeEditor ?? false)} };
|
|
1258
|
+
render(renderOptions);
|
|
1259
|
+
if (import.meta.hot) {
|
|
1260
|
+
import.meta.hot.accept('${VIRTUAL_ROUTES}', (mod) => {
|
|
1261
|
+
render({ ...renderOptions, routes: mod.routes, navigation: mod.navigation });
|
|
1262
|
+
});
|
|
1263
|
+
}`;
|
|
1240
1264
|
}
|
|
1241
1265
|
/**
|
|
1242
1266
|
* Collect every plugin's `slots` declarations into a single runtime registry
|
|
@@ -1265,7 +1289,7 @@ function createRuntimeSlotsModule(plugins = [], root = process.cwd()) {
|
|
|
1265
1289
|
}
|
|
1266
1290
|
if (!slot.component.startsWith("/")) throw new Error(`[clarify] Plugin "${plugin}" slot "${slot.name}" has an invalid component path "${slot.component}". Component paths must start with "/" to reference the project root.`);
|
|
1267
1291
|
const componentPath = root + slot.component;
|
|
1268
|
-
const entry = `{ plugin: ${JSON.stringify(plugin)},
|
|
1292
|
+
const entry = `{ plugin: ${JSON.stringify(plugin)}, loadComponent: () => import(${moduleSpecifier(componentPath)}) }`;
|
|
1269
1293
|
const list = grouped.get(slot.name) ?? [];
|
|
1270
1294
|
list.push(entry);
|
|
1271
1295
|
grouped.set(slot.name, list);
|
|
@@ -1294,18 +1318,34 @@ function buildVirtualModules(args) {
|
|
|
1294
1318
|
modules.set(VIRTUAL_ROUTES, generateRoutesModule(args.routes, args.navigation, "client"));
|
|
1295
1319
|
modules.set(VIRTUAL_SERVER_ROUTES, generateRoutesModule(args.routes, args.navigation, "server"));
|
|
1296
1320
|
modules.set(VIRTUAL_SLOTS, createRuntimeSlotsModule(allPlugins, args.generateOptions.projectRoot));
|
|
1297
|
-
modules.set(VIRTUAL_OPENAPI, "export const
|
|
1321
|
+
modules.set(VIRTUAL_OPENAPI, "export const openApiSpecs = {};");
|
|
1298
1322
|
modules.set(VIRTUAL_SLOT, "export { useSlot } from '@clarify-labs/renderer';\n");
|
|
1299
1323
|
modules.set(VIRTUAL_CLIENT_ENTRY, clientEntryModule);
|
|
1300
1324
|
modules.set(RESOLVED_CLIENT_ENTRY, clientEntryModule);
|
|
1301
1325
|
for (const route of args.routes) {
|
|
1302
|
-
const moduleContent = route.diagnostic ? generateMdxErrorModule(route.diagnostic) : `export { default } from ${moduleSpecifier(route.filePath)};`;
|
|
1303
|
-
modules.set(route.virtualModuleId, moduleContent);
|
|
1326
|
+
const moduleContent = route.diagnostic ? generateMdxErrorModule(route.diagnostic) : `export { default } from ${moduleSpecifier(route.source.filePath)};`;
|
|
1327
|
+
modules.set(route.module.virtualModuleId, moduleContent);
|
|
1304
1328
|
}
|
|
1305
1329
|
return modules;
|
|
1306
1330
|
}
|
|
1307
1331
|
//#endregion
|
|
1308
1332
|
//#region source/core/adapters.ts
|
|
1333
|
+
function routeStructure(route) {
|
|
1334
|
+
const { content: _content, ...source } = route.source;
|
|
1335
|
+
return {
|
|
1336
|
+
...route,
|
|
1337
|
+
source
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
function createDevStructureSnapshot(routes, navigation) {
|
|
1341
|
+
return {
|
|
1342
|
+
routes: JSON.stringify(routes.map(routeStructure)),
|
|
1343
|
+
navigation: JSON.stringify(navigation)
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
function hasDevStructureChanged(before, after) {
|
|
1347
|
+
return before.routes !== after.routes || before.navigation !== after.navigation;
|
|
1348
|
+
}
|
|
1309
1349
|
function invalidateVirtualModules(engine, server) {
|
|
1310
1350
|
for (const id of engine.modules.keys()) {
|
|
1311
1351
|
const moduleNode = server.moduleGraph.getModuleById(resolveVirtualId(id));
|
|
@@ -1313,8 +1353,10 @@ function invalidateVirtualModules(engine, server) {
|
|
|
1313
1353
|
}
|
|
1314
1354
|
}
|
|
1315
1355
|
async function refreshDevServer(engine, server) {
|
|
1356
|
+
const before = createDevStructureSnapshot(engine.routes, engine.navigation);
|
|
1316
1357
|
await engine.refresh();
|
|
1317
1358
|
invalidateVirtualModules(engine, server);
|
|
1359
|
+
return hasDevStructureChanged(before, createDevStructureSnapshot(engine.routes, engine.navigation));
|
|
1318
1360
|
}
|
|
1319
1361
|
function createNormalizedMdxContentPlugin(engine) {
|
|
1320
1362
|
return {
|
|
@@ -1323,10 +1365,10 @@ function createNormalizedMdxContentPlugin(engine) {
|
|
|
1323
1365
|
transform(_code, id) {
|
|
1324
1366
|
if (!/\.mdx?(?:\?|$)/.test(id)) return null;
|
|
1325
1367
|
const filePath = id.replace(/\?.*$/, "");
|
|
1326
|
-
const route = engine.routes.find((route) => route.kind === "mdx" && route.filePath === filePath);
|
|
1327
|
-
if (!route || route.content === void 0) return null;
|
|
1368
|
+
const route = engine.routes.find((route) => route.kind === "mdx" && route.source.filePath === filePath);
|
|
1369
|
+
if (!route || route.source.content === void 0) return null;
|
|
1328
1370
|
return {
|
|
1329
|
-
code: route.content,
|
|
1371
|
+
code: route.source.content,
|
|
1330
1372
|
map: null
|
|
1331
1373
|
};
|
|
1332
1374
|
}
|
|
@@ -1380,8 +1422,13 @@ function createClarifyViteCorePlugin(engine, normalizedMdxContentPlugin, mdx) {
|
|
|
1380
1422
|
}
|
|
1381
1423
|
const relativeContentFile = (0, node_path.relative)(engine.contentRoot, changedFile);
|
|
1382
1424
|
if (!(relativeContentFile && !relativeContentFile.startsWith("..") && !(0, node_path.isAbsolute)(relativeContentFile)) || !engine.hasContentRouteForFile(changedFile)) return;
|
|
1383
|
-
await refreshDevServer(engine, ctx.server)
|
|
1384
|
-
|
|
1425
|
+
if (await refreshDevServer(engine, ctx.server)) {
|
|
1426
|
+
const routesModule = ctx.server.moduleGraph.getModuleById(resolveVirtualId(VIRTUAL_ROUTES));
|
|
1427
|
+
if (routesModule) return [...ctx.modules, routesModule];
|
|
1428
|
+
ctx.server.ws.send({ type: "full-reload" });
|
|
1429
|
+
return [];
|
|
1430
|
+
}
|
|
1431
|
+
return ctx.modules;
|
|
1385
1432
|
},
|
|
1386
1433
|
resolveId(id) {
|
|
1387
1434
|
return resolveVirtualModuleId(id, engine.modules, engine.routes);
|
|
@@ -1392,18 +1439,6 @@ function createClarifyViteCorePlugin(engine, normalizedMdxContentPlugin, mdx) {
|
|
|
1392
1439
|
async configureServer(server) {
|
|
1393
1440
|
server.watcher.add(engine.contentRoot);
|
|
1394
1441
|
if (engine.configFilePath) server.watcher.add(engine.configFilePath);
|
|
1395
|
-
let reloadQueued = false;
|
|
1396
|
-
const invalidateAndReload = () => {
|
|
1397
|
-
if (reloadQueued) return;
|
|
1398
|
-
reloadQueued = true;
|
|
1399
|
-
queueMicrotask(() => {
|
|
1400
|
-
reloadQueued = false;
|
|
1401
|
-
invalidateVirtualModules(engine, server);
|
|
1402
|
-
server.ws.send({ type: "full-reload" });
|
|
1403
|
-
});
|
|
1404
|
-
};
|
|
1405
|
-
engine.ctx.onRoutesChange(invalidateAndReload);
|
|
1406
|
-
engine.ctx.onNavigationChange(invalidateAndReload);
|
|
1407
1442
|
server.middlewares.use((req, res, next) => {
|
|
1408
1443
|
if (req.url === "/dev/query-preview-route" && req.method === "POST") return handleDevRouteRequest(req, res, engine.routes, engine.runtimeContext());
|
|
1409
1444
|
if (req.url === "/dev/project-info" && (req.method === "GET" || req.method === "HEAD")) return handleProjectInfoRequest(req, res, engine.runtimeContext());
|
|
@@ -1413,7 +1448,7 @@ function createClarifyViteCorePlugin(engine, normalizedMdxContentPlugin, mdx) {
|
|
|
1413
1448
|
const changedFile = (0, node_path.isAbsolute)(filePath) ? filePath : (0, node_path.join)(engine.root, filePath);
|
|
1414
1449
|
const relativeContentFile = (0, node_path.relative)(engine.contentRoot, changedFile);
|
|
1415
1450
|
if (!(relativeContentFile && !relativeContentFile.startsWith("..") && !(0, node_path.isAbsolute)(relativeContentFile))) return;
|
|
1416
|
-
await refreshDevServer(engine, server);
|
|
1451
|
+
if (await refreshDevServer(engine, server)) server.ws.send({ type: "full-reload" });
|
|
1417
1452
|
};
|
|
1418
1453
|
server.watcher.on("add", handleContentTreeChange);
|
|
1419
1454
|
server.watcher.on("unlink", handleContentTreeChange);
|
|
@@ -1480,7 +1515,7 @@ function createViteAdapter(engine) {
|
|
|
1480
1515
|
}
|
|
1481
1516
|
var cliPackageVersion = {
|
|
1482
1517
|
name: "@clarify-labs/cli",
|
|
1483
|
-
version: "0.10.
|
|
1518
|
+
version: "0.10.1",
|
|
1484
1519
|
license: "AGPL-3.0-only",
|
|
1485
1520
|
type: "module",
|
|
1486
1521
|
repository: {
|
|
@@ -1586,11 +1621,14 @@ function routeToOpenAPIArtifactUrl(routePath) {
|
|
|
1586
1621
|
return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.openapi.json`;
|
|
1587
1622
|
}
|
|
1588
1623
|
function attachContentArtifactUrls(routes) {
|
|
1589
|
-
for (const route of routes) route.
|
|
1624
|
+
for (const route of routes) route.artifacts = {
|
|
1625
|
+
...route.artifacts,
|
|
1626
|
+
contentArtifactUrl: route.kind === "openapi" ? routeToOpenAPIArtifactUrl(route.path) : routeToMarkdownArtifactUrl(route.path)
|
|
1627
|
+
};
|
|
1590
1628
|
}
|
|
1591
1629
|
function readRouteContent(route) {
|
|
1592
|
-
if (route.content !== void 0) return route.content;
|
|
1593
|
-
throw new Error(`Route content is missing from route context: ${route.filePath}`);
|
|
1630
|
+
if (route.source.content !== void 0) return route.source.content;
|
|
1631
|
+
throw new Error(`Route content is missing from route context: ${route.source.filePath}`);
|
|
1594
1632
|
}
|
|
1595
1633
|
function readRouteArtifactContent(route) {
|
|
1596
1634
|
const content = readRouteContent(route);
|
|
@@ -1600,16 +1638,17 @@ function isLlmsTxtRoute(route) {
|
|
|
1600
1638
|
return !route.path.split("/").includes("404");
|
|
1601
1639
|
}
|
|
1602
1640
|
function llmsTxtDescription(route) {
|
|
1603
|
-
if (route.description) return route.description;
|
|
1604
|
-
const sections = route.sections?.filter((section) => section.level === 2).slice(0, 3).map((section) => section.title);
|
|
1641
|
+
if (route.meta.description) return route.meta.description;
|
|
1642
|
+
const sections = route.meta.sections?.filter((section) => section.level === 2).slice(0, 3).map((section) => section.title);
|
|
1605
1643
|
if (sections?.length) return `Covers ${sections.join(", ")}.`;
|
|
1606
|
-
if (route.keywords?.length) return `Related topics: ${route.keywords.join(", ")}.`;
|
|
1644
|
+
if (route.meta.keywords?.length) return `Related topics: ${route.meta.keywords.join(", ")}.`;
|
|
1607
1645
|
if (route.kind === "openapi") return "OpenAPI artifact for machine-readable API reference data.";
|
|
1608
1646
|
}
|
|
1609
1647
|
function llmsTxtListItem(route, basePath) {
|
|
1610
|
-
|
|
1648
|
+
const contentArtifactUrl = route.artifacts?.contentArtifactUrl;
|
|
1649
|
+
if (!contentArtifactUrl) return void 0;
|
|
1611
1650
|
const description = llmsTxtDescription(route);
|
|
1612
|
-
return description ? `- [${route.title}](${basePath}${
|
|
1651
|
+
return description ? `- [${route.meta.title}](${basePath}${contentArtifactUrl}): ${description}` : `- [${route.meta.title}](${basePath}${contentArtifactUrl})`;
|
|
1613
1652
|
}
|
|
1614
1653
|
function llmsTxtLocaleLabel(locale, projectConfig) {
|
|
1615
1654
|
return projectConfig.i18n?.locales.find((item) => item.code === locale)?.label ?? locale;
|
|
@@ -1626,7 +1665,7 @@ function groupLlmsTxtRoutesByLocale(routes, projectConfig) {
|
|
|
1626
1665
|
const routesBySource = /* @__PURE__ */ new Map();
|
|
1627
1666
|
const defaultLocale = projectConfig.i18n?.defaultLocale;
|
|
1628
1667
|
for (const route of routes) {
|
|
1629
|
-
const sourceKey = `${route.locale ?? defaultLocale ?? "default"}:${route.basePath ?? route.contentArtifactUrl ?? route.path}`;
|
|
1668
|
+
const sourceKey = `${route.locale ?? defaultLocale ?? "default"}:${route.basePath ?? route.artifacts?.contentArtifactUrl ?? route.path}`;
|
|
1630
1669
|
const previousRoute = routesBySource.get(sourceKey);
|
|
1631
1670
|
if (!previousRoute || previousRoute.locale && !route.locale) routesBySource.set(sourceKey, route);
|
|
1632
1671
|
}
|
|
@@ -1695,9 +1734,9 @@ function serveContentArtifacts(req, res, projectConfig, routes) {
|
|
|
1695
1734
|
}
|
|
1696
1735
|
if (contentPath.endsWith(".openapi.yaml")) {
|
|
1697
1736
|
const jsonPath = contentPath.replace(/\.yaml$/, ".json");
|
|
1698
|
-
const route = routes.find((
|
|
1699
|
-
if (route?.kind === "openapi" && route.content) {
|
|
1700
|
-
const spec = JSON.parse(route.content);
|
|
1737
|
+
const route = routes.find((route) => route.artifacts?.contentArtifactUrl === jsonPath);
|
|
1738
|
+
if (route?.kind === "openapi" && route.source.content) {
|
|
1739
|
+
const spec = JSON.parse(route.source.content);
|
|
1701
1740
|
res.statusCode = 200;
|
|
1702
1741
|
res.setHeader("Content-Type", "text/yaml; charset=utf-8");
|
|
1703
1742
|
res.end((0, yaml.stringify)(spec, { lineWidth: 0 }), "utf8");
|
|
@@ -1705,7 +1744,7 @@ function serveContentArtifacts(req, res, projectConfig, routes) {
|
|
|
1705
1744
|
}
|
|
1706
1745
|
return false;
|
|
1707
1746
|
}
|
|
1708
|
-
const route = routes.find((route) => route.contentArtifactUrl === contentPath);
|
|
1747
|
+
const route = routes.find((route) => route.artifacts?.contentArtifactUrl === contentPath);
|
|
1709
1748
|
if (!route) return false;
|
|
1710
1749
|
res.statusCode = 200;
|
|
1711
1750
|
res.setHeader("Content-Type", resolveContentArtifactType(route));
|
|
@@ -1731,14 +1770,15 @@ function createContentArtifactsPlugin() {
|
|
|
1731
1770
|
"build:assets": (ctx) => {
|
|
1732
1771
|
const assets = [];
|
|
1733
1772
|
for (const route of ctx.routes) {
|
|
1734
|
-
|
|
1773
|
+
const contentArtifactUrl = route.artifacts?.contentArtifactUrl;
|
|
1774
|
+
if (!contentArtifactUrl) continue;
|
|
1735
1775
|
assets.push({
|
|
1736
|
-
fileName:
|
|
1776
|
+
fileName: contentArtifactUrl.replace(/^\//, ""),
|
|
1737
1777
|
source: readRouteArtifactContent(route)
|
|
1738
1778
|
});
|
|
1739
|
-
if (route.kind === "openapi" && route.content) {
|
|
1740
|
-
const yamlFileName =
|
|
1741
|
-
const spec = JSON.parse(route.content);
|
|
1779
|
+
if (route.kind === "openapi" && route.source.content) {
|
|
1780
|
+
const yamlFileName = contentArtifactUrl.replace(/\.json$/, ".yaml");
|
|
1781
|
+
const spec = JSON.parse(route.source.content);
|
|
1742
1782
|
assets.push({
|
|
1743
1783
|
fileName: yamlFileName.replace(/^\//, ""),
|
|
1744
1784
|
source: (0, yaml.stringify)(spec, { lineWidth: 0 })
|
|
@@ -1824,6 +1864,7 @@ var OPENAPI_HTTP_METHODS = [
|
|
|
1824
1864
|
"patch",
|
|
1825
1865
|
"trace"
|
|
1826
1866
|
];
|
|
1867
|
+
var CLARIFY_OPENAPI_SECTION_ID_EXTENSION = "x-clarify-section-id";
|
|
1827
1868
|
function errorMessage(error) {
|
|
1828
1869
|
return error instanceof Error ? error.message : String(error);
|
|
1829
1870
|
}
|
|
@@ -1850,10 +1891,10 @@ function findOpenAPIRoutes(dir, base = dir) {
|
|
|
1850
1891
|
routes.push({
|
|
1851
1892
|
path: cleanPath,
|
|
1852
1893
|
basePath: cleanPath,
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1894
|
+
kind: "openapi",
|
|
1895
|
+
meta: { title: kebabToTitle(cleanPath.split("/").pop() ?? "API") },
|
|
1896
|
+
module: { virtualModuleId: virtualModuleIdFromRef(ref) },
|
|
1897
|
+
source: { filePath: fullPath }
|
|
1857
1898
|
});
|
|
1858
1899
|
}
|
|
1859
1900
|
return routes;
|
|
@@ -1862,36 +1903,80 @@ function operationMatchesTags(operationTags, filterTags) {
|
|
|
1862
1903
|
if (!filterTags?.length) return true;
|
|
1863
1904
|
return operationTags?.some((tag) => filterTags.includes(tag)) ?? false;
|
|
1864
1905
|
}
|
|
1906
|
+
function filterPathItemsByTags(items, tags) {
|
|
1907
|
+
const filteredItems = {};
|
|
1908
|
+
for (const path of Object.keys(items)) {
|
|
1909
|
+
const pathItem = items[path];
|
|
1910
|
+
if (!pathItem) continue;
|
|
1911
|
+
if (OPENAPI_HTTP_METHODS.map((method) => pathItem[method]).some((op) => op && typeof op === "object" && operationMatchesTags(op.tags, tags))) filteredItems[path] = pathItem;
|
|
1912
|
+
}
|
|
1913
|
+
return filteredItems;
|
|
1914
|
+
}
|
|
1915
|
+
function createOperationSectionId(operation, method, path, kind) {
|
|
1916
|
+
return (typeof operation.operationId === "string" ? operation.operationId.trim() : "") || (0, github_slugger.slug)(`${kind === "webhook" ? "webhook " : ""}${method.toLowerCase()} ${path}`);
|
|
1917
|
+
}
|
|
1918
|
+
function operationSectionId(operation) {
|
|
1919
|
+
const sectionId = operation[CLARIFY_OPENAPI_SECTION_ID_EXTENSION];
|
|
1920
|
+
return typeof sectionId === "string" ? sectionId : "";
|
|
1921
|
+
}
|
|
1922
|
+
function normalizeOpenAPISpecSectionIds(spec) {
|
|
1923
|
+
const items = [...Object.entries(spec.paths ?? {}).map(([path, pathItem]) => ({
|
|
1924
|
+
path,
|
|
1925
|
+
pathItem,
|
|
1926
|
+
kind: "path"
|
|
1927
|
+
})), ...Object.entries(spec.webhooks ?? {}).map(([path, pathItem]) => ({
|
|
1928
|
+
path,
|
|
1929
|
+
pathItem,
|
|
1930
|
+
kind: "webhook"
|
|
1931
|
+
}))];
|
|
1932
|
+
for (const { path, pathItem, kind } of items) {
|
|
1933
|
+
if (!pathItem || typeof pathItem !== "object") continue;
|
|
1934
|
+
const operations = pathItem;
|
|
1935
|
+
for (const method of OPENAPI_HTTP_METHODS) {
|
|
1936
|
+
const op = operations[method];
|
|
1937
|
+
if (!op || typeof op !== "object") continue;
|
|
1938
|
+
op[CLARIFY_OPENAPI_SECTION_ID_EXTENSION] = createOperationSectionId(op, method, path, kind);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
return spec;
|
|
1942
|
+
}
|
|
1865
1943
|
/** Filter an OpenAPI spec to only include paths that have at least one operation matching the given tags. */
|
|
1866
1944
|
function filterSpecByTags(spec, tags) {
|
|
1867
|
-
const filteredPaths = {};
|
|
1868
1945
|
const paths = spec.paths ?? {};
|
|
1869
|
-
|
|
1870
|
-
const pathItem = paths[path];
|
|
1871
|
-
if (!pathItem) continue;
|
|
1872
|
-
if (Object.values(pathItem).some((op) => op && typeof op === "object" && operationMatchesTags(op.tags, tags))) filteredPaths[path] = pathItem;
|
|
1873
|
-
}
|
|
1946
|
+
const webhooks = spec.webhooks ?? {};
|
|
1874
1947
|
return {
|
|
1875
1948
|
...spec,
|
|
1876
|
-
paths:
|
|
1949
|
+
paths: filterPathItemsByTags(paths, tags),
|
|
1950
|
+
webhooks: filterPathItemsByTags(webhooks, tags)
|
|
1877
1951
|
};
|
|
1878
1952
|
}
|
|
1879
1953
|
/** Extract endpoint operations from an OpenAPI spec as page sections. */
|
|
1880
1954
|
function extractOpenAPISections(spec, filterTags) {
|
|
1955
|
+
normalizeOpenAPISpecSectionIds(spec);
|
|
1881
1956
|
const sections = [];
|
|
1882
|
-
const
|
|
1883
|
-
|
|
1957
|
+
const items = [...Object.entries(spec.paths ?? {}).map(([path, pathItem]) => ({
|
|
1958
|
+
path,
|
|
1959
|
+
pathItem,
|
|
1960
|
+
kind: "path"
|
|
1961
|
+
})), ...Object.entries(spec.webhooks ?? {}).map(([path, pathItem]) => ({
|
|
1962
|
+
path,
|
|
1963
|
+
pathItem,
|
|
1964
|
+
kind: "webhook"
|
|
1965
|
+
}))];
|
|
1966
|
+
for (const { path, pathItem, kind } of items) {
|
|
1884
1967
|
if (!pathItem) continue;
|
|
1968
|
+
const operations = pathItem;
|
|
1885
1969
|
for (const method of OPENAPI_HTTP_METHODS) {
|
|
1886
|
-
const op =
|
|
1887
|
-
|
|
1888
|
-
|
|
1970
|
+
const op = operations[method];
|
|
1971
|
+
const tags = Array.isArray(op?.tags) ? op.tags : void 0;
|
|
1972
|
+
if (!op || !operationMatchesTags(tags, filterTags)) continue;
|
|
1973
|
+
const title = typeof op.summary === "string" ? op.summary : `${method.toUpperCase()} ${path}`;
|
|
1889
1974
|
sections.push({
|
|
1890
|
-
id: (
|
|
1975
|
+
id: operationSectionId(op),
|
|
1891
1976
|
title,
|
|
1892
1977
|
level: 2,
|
|
1893
|
-
badge: method.toUpperCase(),
|
|
1894
|
-
tags
|
|
1978
|
+
badge: kind === "webhook" ? "WEBHOOK" : method.toUpperCase(),
|
|
1979
|
+
tags
|
|
1895
1980
|
});
|
|
1896
1981
|
}
|
|
1897
1982
|
}
|
|
@@ -1933,12 +2018,16 @@ async function readOpenAPISpec(filePath, contentProcessor) {
|
|
|
1933
2018
|
//#endregion
|
|
1934
2019
|
//#region source/plugins/openapi/virtual-modules.ts
|
|
1935
2020
|
var openApiRegistryModuleId = VIRTUAL_OPENAPI;
|
|
2021
|
+
var openApiServerRegistryModuleId = VIRTUAL_OPENAPI_SERVER;
|
|
1936
2022
|
var OPENAPI_SPEC_PREFIX = "virtual:clarify/openapi-spec/";
|
|
1937
2023
|
function specVirtualModuleId(specKey) {
|
|
1938
2024
|
return `${OPENAPI_SPEC_PREFIX}${specKey}`;
|
|
1939
2025
|
}
|
|
1940
|
-
function generateOpenAPIRegistryModule(
|
|
1941
|
-
return `export const
|
|
2026
|
+
function generateOpenAPIRegistryModule(openApiSpecs) {
|
|
2027
|
+
return `export const openApiSpecs = {\n${Object.entries(openApiSpecs).map(([key, moduleId]) => ` ${JSON.stringify(key)}: () => import(${JSON.stringify(moduleId)}),`).join("\n")}\n};`;
|
|
2028
|
+
}
|
|
2029
|
+
function generateOpenAPIServerRegistryModule(openApiSpecs) {
|
|
2030
|
+
return `export const openApiSpecs = ${JSON.stringify(openApiSpecs)};`;
|
|
1942
2031
|
}
|
|
1943
2032
|
/** Generate a virtual module that exports a single spec as default. */
|
|
1944
2033
|
function generateOpenAPISpecModule(spec) {
|
|
@@ -1946,13 +2035,11 @@ function generateOpenAPISpecModule(spec) {
|
|
|
1946
2035
|
}
|
|
1947
2036
|
function generateOpenAPIPageModule(opts) {
|
|
1948
2037
|
return `import { createOpenApiRouteComponent } from '@clarify-labs/renderer';
|
|
2038
|
+
import spec from ${JSON.stringify(opts.specModuleId)};
|
|
1949
2039
|
|
|
1950
|
-
export const routeData =
|
|
1951
|
-
spec: opts.spec,
|
|
1952
|
-
tagFilter: opts.tagFilter
|
|
1953
|
-
})};
|
|
2040
|
+
export const routeData = {};
|
|
1954
2041
|
|
|
1955
|
-
export default createOpenApiRouteComponent(routeData);
|
|
2042
|
+
export default createOpenApiRouteComponent({ ...routeData, spec });
|
|
1956
2043
|
`;
|
|
1957
2044
|
}
|
|
1958
2045
|
function generateOpenAPIErrorModule(diagnostic) {
|
|
@@ -1971,115 +2058,212 @@ export default createContentDiagnosticComponent(contentDiagnostic);
|
|
|
1971
2058
|
}
|
|
1972
2059
|
//#endregion
|
|
1973
2060
|
//#region source/plugins/openapi/index.ts
|
|
1974
|
-
function
|
|
1975
|
-
const
|
|
2061
|
+
function collectOpenAPIPageIntents(config) {
|
|
2062
|
+
const intents = [];
|
|
1976
2063
|
function visitPages(pages) {
|
|
1977
2064
|
if (!pages || pages === "FileTree") return;
|
|
1978
|
-
for (const group of pages) for (const item of group.pages)
|
|
2065
|
+
for (const group of pages) for (const item of group.pages) {
|
|
2066
|
+
const intent = routeIntentFromPagesItem(item);
|
|
2067
|
+
if (intent.kind === "openapi" && (intent.path || intent.tagFilter?.length)) intents.push(intent);
|
|
2068
|
+
}
|
|
1979
2069
|
}
|
|
1980
2070
|
for (const tab of config.tabs ?? []) visitPages(tab.pages);
|
|
1981
|
-
return
|
|
2071
|
+
return intents;
|
|
1982
2072
|
}
|
|
1983
2073
|
function virtualModuleIdFromPath(path) {
|
|
1984
2074
|
return `virtual:clarify-page/${path.replace(/^\/+/, "") || "index"}`;
|
|
1985
2075
|
}
|
|
2076
|
+
function cloneOpenAPIRoute(route, overrides = {}) {
|
|
2077
|
+
return {
|
|
2078
|
+
...route,
|
|
2079
|
+
meta: {
|
|
2080
|
+
...route.meta,
|
|
2081
|
+
sections: route.meta.sections ? [...route.meta.sections] : void 0
|
|
2082
|
+
},
|
|
2083
|
+
module: { ...route.module },
|
|
2084
|
+
source: { ...route.source },
|
|
2085
|
+
openapi: route.openapi ? {
|
|
2086
|
+
...route.openapi,
|
|
2087
|
+
tagFilter: route.openapi.tagFilter ? [...route.openapi.tagFilter] : void 0
|
|
2088
|
+
} : void 0,
|
|
2089
|
+
alternates: route.alternates ? { ...route.alternates } : void 0,
|
|
2090
|
+
...overrides
|
|
2091
|
+
};
|
|
2092
|
+
}
|
|
2093
|
+
function findOpenAPIRoutesForContentRoot(contentRoot, i18n) {
|
|
2094
|
+
if (!i18n) return findOpenAPIRoutes(contentRoot);
|
|
2095
|
+
const localizedRoutes = [];
|
|
2096
|
+
for (const locale of i18n.locales) {
|
|
2097
|
+
const localeRoot = (0, node_path.join)(contentRoot, locale.code);
|
|
2098
|
+
for (const route of findOpenAPIRoutes(localeRoot, localeRoot)) {
|
|
2099
|
+
const basePath = route.basePath ?? route.path;
|
|
2100
|
+
const path = localizedRoutePath(basePath, locale.code, i18n);
|
|
2101
|
+
localizedRoutes.push({
|
|
2102
|
+
...route,
|
|
2103
|
+
path,
|
|
2104
|
+
basePath,
|
|
2105
|
+
locale: locale.code,
|
|
2106
|
+
module: { virtualModuleId: virtualModuleIdFromPath(path) }
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
if (i18n.missing === "fallback") {
|
|
2111
|
+
const routeByLocaleAndBase = new Map(localizedRoutes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
|
|
2112
|
+
const defaultRoutes = localizedRoutes.filter((route) => route.locale === i18n.defaultLocale);
|
|
2113
|
+
for (const sourceRoute of defaultRoutes) {
|
|
2114
|
+
const basePath = sourceRoute.basePath ?? sourceRoute.path;
|
|
2115
|
+
for (const locale of i18n.locales) {
|
|
2116
|
+
const key = `${locale.code}:${basePath}`;
|
|
2117
|
+
if (routeByLocaleAndBase.has(key)) continue;
|
|
2118
|
+
const path = localizedRoutePath(basePath, locale.code, i18n);
|
|
2119
|
+
localizedRoutes.push(cloneOpenAPIRoute(sourceRoute, {
|
|
2120
|
+
path,
|
|
2121
|
+
locale: locale.code,
|
|
2122
|
+
module: { virtualModuleId: virtualModuleIdFromPath(path) },
|
|
2123
|
+
isFallback: true
|
|
2124
|
+
}));
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
return localizedRoutes;
|
|
2129
|
+
}
|
|
1986
2130
|
function createConfiguredOpenAPIRoutes(routes, config) {
|
|
1987
|
-
const
|
|
1988
|
-
if (!
|
|
2131
|
+
const openAPIIntents = collectOpenAPIPageIntents(config);
|
|
2132
|
+
if (!openAPIIntents.length) return routes;
|
|
1989
2133
|
const additions = [];
|
|
1990
2134
|
const existingKeys = new Set(routes.map((route) => `${route.locale ?? ""}:${route.basePath ?? route.path}`));
|
|
1991
|
-
for (const
|
|
1992
|
-
const tagFilter =
|
|
1993
|
-
const sourceBasePath = openAPIPagePathFromRef(
|
|
1994
|
-
const targetBasePath = openAPIPagePathFromRef(
|
|
2135
|
+
for (const intent of openAPIIntents) {
|
|
2136
|
+
const tagFilter = intent.tagFilter?.filter(Boolean);
|
|
2137
|
+
const sourceBasePath = openAPIPagePathFromRef(intent.ref);
|
|
2138
|
+
const targetBasePath = openAPIPagePathFromRef(intent.ref, tagFilter, intent.path);
|
|
1995
2139
|
if (sourceBasePath === targetBasePath && !tagFilter?.length) continue;
|
|
1996
2140
|
for (const route of routes) {
|
|
1997
2141
|
if (route.kind !== "openapi" || (route.basePath ?? route.path) !== sourceBasePath) continue;
|
|
1998
2142
|
const key = `${route.locale ?? ""}:${targetBasePath}`;
|
|
1999
2143
|
if (existingKeys.has(key)) continue;
|
|
2000
2144
|
existingKeys.add(key);
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
path
|
|
2145
|
+
const path = route.locale && config.i18n ? localizedRoutePath(targetBasePath, route.locale, config.i18n) : targetBasePath;
|
|
2146
|
+
additions.push(cloneOpenAPIRoute(route, {
|
|
2147
|
+
path,
|
|
2004
2148
|
basePath: targetBasePath,
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2149
|
+
module: { virtualModuleId: virtualModuleIdFromPath(path) },
|
|
2150
|
+
openapi: {
|
|
2151
|
+
...route.openapi,
|
|
2152
|
+
tagFilter: tagFilter?.length ? tagFilter : void 0
|
|
2153
|
+
}
|
|
2154
|
+
}));
|
|
2009
2155
|
}
|
|
2010
2156
|
}
|
|
2011
2157
|
const nextRoutes = [...routes, ...additions];
|
|
2012
|
-
|
|
2158
|
+
if (!config.i18n) return nextRoutes;
|
|
2159
|
+
const routesWithAlternates = nextRoutes.map((route) => withAlternates(route, nextRoutes, config.i18n));
|
|
2160
|
+
const bareAliases = [];
|
|
2161
|
+
const seenBare = new Set(routesWithAlternates.map((route) => route.path));
|
|
2162
|
+
for (const route of routesWithAlternates) {
|
|
2163
|
+
if (route.locale !== config.i18n.defaultLocale) continue;
|
|
2164
|
+
const basePath = route.basePath ?? route.path;
|
|
2165
|
+
if (basePath === route.path || seenBare.has(basePath)) continue;
|
|
2166
|
+
seenBare.add(basePath);
|
|
2167
|
+
bareAliases.push(cloneOpenAPIRoute(route, {
|
|
2168
|
+
path: basePath,
|
|
2169
|
+
module: { virtualModuleId: virtualModuleIdFromPath(basePath) },
|
|
2170
|
+
isBareAlias: true
|
|
2171
|
+
}));
|
|
2172
|
+
}
|
|
2173
|
+
return [...routesWithAlternates, ...bareAliases];
|
|
2013
2174
|
}
|
|
2014
|
-
/** Derive a stable, deduplicated
|
|
2015
|
-
function
|
|
2175
|
+
/** Derive a stable, deduplicated id from an absolute spec file path. */
|
|
2176
|
+
function sourceSpecIdFromPath(filePath, projectRoot) {
|
|
2016
2177
|
return (0, node_path.relative)(projectRoot, filePath).replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
|
|
2017
2178
|
}
|
|
2179
|
+
function routeSpecModuleIdFromSourceSpecId(sourceSpecId, tagFilter) {
|
|
2180
|
+
if (!tagFilter?.length) return sourceSpecId;
|
|
2181
|
+
return `${sourceSpecId}_tags_${tagFilter.map((tag) => tag.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "") || "tag").join("_")}`;
|
|
2182
|
+
}
|
|
2183
|
+
function openAPIRegistryKeys(route) {
|
|
2184
|
+
const keys = /* @__PURE__ */ new Set();
|
|
2185
|
+
const path = (route.path || "/").replace(/^\//, "");
|
|
2186
|
+
const base = (route.basePath ?? route.path).replace(/^\//, "");
|
|
2187
|
+
if (route.locale) keys.add(`virtual:clarify-page/${route.locale}/${base}`);
|
|
2188
|
+
if (path) keys.add(`virtual:clarify-page/${path}`);
|
|
2189
|
+
else keys.add("virtual:clarify-page/index");
|
|
2190
|
+
if (!route.locale && base) keys.add(`virtual:clarify-page/${base}`);
|
|
2191
|
+
return [...keys];
|
|
2192
|
+
}
|
|
2018
2193
|
function createOpenAPIPlugin() {
|
|
2019
|
-
/** specs keyed by
|
|
2194
|
+
/** specs keyed by sourceSpecId (deduplicated per source file). */
|
|
2020
2195
|
const specs = /* @__PURE__ */ new Map();
|
|
2196
|
+
const specModules = /* @__PURE__ */ new Map();
|
|
2021
2197
|
return {
|
|
2022
2198
|
name: "clarify:openapi",
|
|
2023
2199
|
hooks: {
|
|
2024
|
-
"routes:discover": (input) => ({
|
|
2200
|
+
"routes:discover": (input, ctx) => ({
|
|
2025
2201
|
...input,
|
|
2026
|
-
routes: [...input.routes, ...
|
|
2202
|
+
routes: [...input.routes, ...findOpenAPIRoutesForContentRoot(input.contentRoot, ctx.projectConfig.i18n)]
|
|
2027
2203
|
}),
|
|
2028
2204
|
"routes:discovered": async (routes, ctx) => {
|
|
2029
2205
|
specs.clear();
|
|
2206
|
+
specModules.clear();
|
|
2030
2207
|
const nextRoutes = createConfiguredOpenAPIRoutes(routes, ctx.projectConfig);
|
|
2031
2208
|
const specByFilePath = /* @__PURE__ */ new Map();
|
|
2032
2209
|
for (const route of nextRoutes.filter((route) => route.kind === "openapi")) {
|
|
2033
|
-
const specFromCache = specByFilePath.get(route.filePath);
|
|
2210
|
+
const specFromCache = specByFilePath.get(route.source.filePath);
|
|
2034
2211
|
const processor = createProjectContentProcessor(ctx.plugins, ctx);
|
|
2035
2212
|
const result = specFromCache ? {
|
|
2036
2213
|
ok: true,
|
|
2037
2214
|
spec: specFromCache
|
|
2038
|
-
} : await readOpenAPISpec(route.filePath, processor);
|
|
2215
|
+
} : await readOpenAPISpec(route.source.filePath, processor);
|
|
2039
2216
|
if (!result.ok) {
|
|
2040
2217
|
route.diagnostic = result.diagnostic;
|
|
2041
|
-
route.title = route.title || "OpenAPI parse error";
|
|
2042
|
-
route.sections = [];
|
|
2218
|
+
route.meta.title = route.meta.title || "OpenAPI parse error";
|
|
2219
|
+
route.meta.sections = [];
|
|
2043
2220
|
continue;
|
|
2044
2221
|
}
|
|
2045
|
-
const spec = result.spec;
|
|
2046
|
-
specByFilePath.set(route.filePath, spec);
|
|
2047
|
-
const
|
|
2048
|
-
route.
|
|
2049
|
-
|
|
2222
|
+
const spec = normalizeOpenAPISpecSectionIds(result.spec);
|
|
2223
|
+
specByFilePath.set(route.source.filePath, spec);
|
|
2224
|
+
const sourceSpecId = sourceSpecIdFromPath(route.source.filePath, ctx.generateOptions.projectRoot);
|
|
2225
|
+
route.openapi = {
|
|
2226
|
+
...route.openapi,
|
|
2227
|
+
sourceSpecId
|
|
2228
|
+
};
|
|
2229
|
+
specs.set(sourceSpecId, {
|
|
2050
2230
|
spec,
|
|
2051
|
-
filePath: route.filePath
|
|
2231
|
+
filePath: route.source.filePath
|
|
2052
2232
|
});
|
|
2053
|
-
route.title = spec.info?.title ?? route.title;
|
|
2054
|
-
route.description = spec.info?.description ?? route.description;
|
|
2055
|
-
route.sections = extractOpenAPISections(spec, route.
|
|
2056
|
-
const pageSpec = route.
|
|
2057
|
-
|
|
2233
|
+
route.meta.title = spec.info?.title ?? route.meta.title;
|
|
2234
|
+
route.meta.description = spec.info?.description ?? route.meta.description;
|
|
2235
|
+
route.meta.sections = extractOpenAPISections(spec, route.openapi?.tagFilter);
|
|
2236
|
+
const pageSpec = route.openapi?.tagFilter?.length ? filterSpecByTags(spec, route.openapi.tagFilter) : spec;
|
|
2237
|
+
const routeSpecModuleId = routeSpecModuleIdFromSourceSpecId(sourceSpecId, route.openapi?.tagFilter);
|
|
2238
|
+
route.openapi.routeSpecModuleId = routeSpecModuleId;
|
|
2239
|
+
specModules.set(routeSpecModuleId, pageSpec);
|
|
2240
|
+
route.source.content = JSON.stringify(pageSpec);
|
|
2058
2241
|
}
|
|
2059
2242
|
return nextRoutes;
|
|
2060
2243
|
},
|
|
2061
2244
|
"modules:before": (modules, ctx) => {
|
|
2062
2245
|
const registryEntries = {};
|
|
2246
|
+
const serverRegistryEntries = {};
|
|
2063
2247
|
for (const route of ctx.routes) {
|
|
2064
|
-
if (route.kind !== "openapi" || !route.
|
|
2065
|
-
const
|
|
2248
|
+
if (route.kind !== "openapi" || !route.openapi?.sourceSpecId) continue;
|
|
2249
|
+
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2250
|
+
const spec = specModules.get(routeSpecModuleId) ?? specs.get(route.openapi.sourceSpecId)?.spec;
|
|
2066
2251
|
if (!spec) continue;
|
|
2067
|
-
const
|
|
2068
|
-
|
|
2069
|
-
|
|
2252
|
+
for (const registryKey of openAPIRegistryKeys(route)) {
|
|
2253
|
+
registryEntries[registryKey] = specVirtualModuleId(routeSpecModuleId);
|
|
2254
|
+
serverRegistryEntries[registryKey] = spec;
|
|
2255
|
+
}
|
|
2070
2256
|
}
|
|
2071
2257
|
modules.set(openApiRegistryModuleId, generateOpenAPIRegistryModule(registryEntries));
|
|
2072
|
-
|
|
2258
|
+
modules.set(openApiServerRegistryModuleId, generateOpenAPIServerRegistryModule(serverRegistryEntries));
|
|
2259
|
+
for (const [routeSpecModuleId, spec] of specModules) modules.set(specVirtualModuleId(routeSpecModuleId), generateOpenAPISpecModule(spec));
|
|
2073
2260
|
for (const route of ctx.routes) {
|
|
2074
2261
|
if (route.kind !== "openapi") continue;
|
|
2075
|
-
if (route.diagnostic) modules.set(route.virtualModuleId, generateOpenAPIErrorModule(route.diagnostic));
|
|
2076
|
-
else if (route.
|
|
2077
|
-
const
|
|
2078
|
-
if (!
|
|
2079
|
-
modules.set(route.virtualModuleId, generateOpenAPIPageModule({
|
|
2080
|
-
spec: entry.spec,
|
|
2081
|
-
tagFilter: route.openapiTagFilter
|
|
2082
|
-
}));
|
|
2262
|
+
if (route.diagnostic) modules.set(route.module.virtualModuleId, generateOpenAPIErrorModule(route.diagnostic));
|
|
2263
|
+
else if (route.openapi?.sourceSpecId) {
|
|
2264
|
+
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2265
|
+
if (!specModules.has(routeSpecModuleId)) continue;
|
|
2266
|
+
modules.set(route.module.virtualModuleId, generateOpenAPIPageModule({ specModuleId: specVirtualModuleId(routeSpecModuleId) }));
|
|
2083
2267
|
}
|
|
2084
2268
|
}
|
|
2085
2269
|
return modules;
|
|
@@ -2147,11 +2331,11 @@ async function generatePagefindIndex(options, pagefind) {
|
|
|
2147
2331
|
}
|
|
2148
2332
|
function routeSearchContent(route) {
|
|
2149
2333
|
return [
|
|
2150
|
-
route.title,
|
|
2151
|
-
route.description,
|
|
2152
|
-
route.keywords?.join(" "),
|
|
2153
|
-
route.sections?.map((section) => section.title).join(" "),
|
|
2154
|
-
route.content
|
|
2334
|
+
route.meta.title,
|
|
2335
|
+
route.meta.description,
|
|
2336
|
+
route.meta.keywords?.join(" "),
|
|
2337
|
+
route.meta.sections?.map((section) => section.title).join(" "),
|
|
2338
|
+
route.source.content
|
|
2155
2339
|
].filter(Boolean).join("\n\n");
|
|
2156
2340
|
}
|
|
2157
2341
|
async function generateDevSearchIndex(ctx, root, pagefind) {
|
|
@@ -2173,7 +2357,7 @@ async function generateDevSearchIndex(ctx, root, pagefind) {
|
|
|
2173
2357
|
url: route.path,
|
|
2174
2358
|
content,
|
|
2175
2359
|
language: toPagefindLanguage(route.locale ?? ctx.projectConfig.i18n?.defaultLocale),
|
|
2176
|
-
meta: { title: route.title }
|
|
2360
|
+
meta: { title: route.meta.title }
|
|
2177
2361
|
});
|
|
2178
2362
|
assertNoPagefindErrors(`addCustomRecord ${route.path}`, result.errors);
|
|
2179
2363
|
pageCount += 1;
|
|
@@ -2330,15 +2514,18 @@ function normalizeRepositoryUrl(repository) {
|
|
|
2330
2514
|
function encodePath(path) {
|
|
2331
2515
|
return path.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
|
|
2332
2516
|
}
|
|
2333
|
-
function
|
|
2517
|
+
function createSourceEditUrl(filePath, contentRoot, source) {
|
|
2334
2518
|
const repository = normalizeRepositoryUrl(source.repository);
|
|
2335
2519
|
const branch = source.branch ?? "main";
|
|
2336
2520
|
const sourcePath = [source.directory ? trimSlashes(source.directory) : "", (0, node_path.relative)(contentRoot, filePath).replaceAll("\\", "/")].filter(Boolean).join("/");
|
|
2337
2521
|
return `${repository}/edit/${encodeURIComponent(branch)}/${encodePath(sourcePath)}`;
|
|
2338
2522
|
}
|
|
2339
|
-
function
|
|
2523
|
+
function attachSourceEditUrls(routes, contentRoot, source) {
|
|
2340
2524
|
if (!source) return;
|
|
2341
|
-
for (const route of routes) route.
|
|
2525
|
+
for (const route of routes) route.source = {
|
|
2526
|
+
...route.source,
|
|
2527
|
+
sourceEditUrl: createSourceEditUrl(route.source.filePath, contentRoot, source)
|
|
2528
|
+
};
|
|
2342
2529
|
}
|
|
2343
2530
|
//#endregion
|
|
2344
2531
|
//#region source/plugins/source-links/index.ts
|
|
@@ -2346,7 +2533,7 @@ function createSourceLinksPlugin() {
|
|
|
2346
2533
|
return {
|
|
2347
2534
|
name: "clarify:source-links",
|
|
2348
2535
|
hooks: { "routes:discovered": (routes, ctx) => {
|
|
2349
|
-
|
|
2536
|
+
attachSourceEditUrls(routes, (0, node_path.join)(ctx.generateOptions.projectRoot, ctx.generateOptions.rootDirectory), ctx.projectConfig.source);
|
|
2350
2537
|
return routes;
|
|
2351
2538
|
} }
|
|
2352
2539
|
};
|
|
@@ -2503,9 +2690,9 @@ var CLARIFY_ENV_TEMPLATE = `// This file is generated by Clarify. Do not edit it
|
|
|
2503
2690
|
// plugin and slot code, so the TypeScript language service can resolve them.
|
|
2504
2691
|
|
|
2505
2692
|
declare module 'virtual:clarify/slot' {
|
|
2506
|
-
import type {
|
|
2693
|
+
import type { UISlotContext } from '@clarify-labs/cli'
|
|
2507
2694
|
|
|
2508
|
-
export function useSlot():
|
|
2695
|
+
export function useSlot(): UISlotContext
|
|
2509
2696
|
}
|
|
2510
2697
|
`;
|
|
2511
2698
|
function generateClarifyEnvDts(_plugins) {
|
|
@@ -2543,7 +2730,7 @@ function resolveProjectConfigFromContext(contextOrConfig) {
|
|
|
2543
2730
|
}
|
|
2544
2731
|
function routeTitle(contextOrConfig, route) {
|
|
2545
2732
|
const projectConfig = resolveProjectConfigFromContext(contextOrConfig);
|
|
2546
|
-
const title = route?.title
|
|
2733
|
+
const title = route?.meta.title.trim();
|
|
2547
2734
|
if (!title || title === projectConfig.title) return projectConfig.title;
|
|
2548
2735
|
return `${title} - ${projectConfig.title}`;
|
|
2549
2736
|
}
|
|
@@ -2591,8 +2778,8 @@ function injectSSRIntoTemplate(template, appHtml, contextOrConfig, route) {
|
|
|
2591
2778
|
const projectConfig = resolveProjectConfigFromContext(contextOrConfig);
|
|
2592
2779
|
let html = injectHtmlLocaleAttributes(template, contextOrConfig, route);
|
|
2593
2780
|
html = html.replace(/<title>.*?<\/title>/, `<title>${escapeHtml(routeTitle(contextOrConfig, route))}</title>`);
|
|
2594
|
-
html = setNamedMeta(html, "description", route?.description ?? projectConfig.description);
|
|
2595
|
-
html = setNamedMeta(html, "keywords", route?.keywords?.join(", "));
|
|
2781
|
+
html = setNamedMeta(html, "description", route?.meta.description ?? projectConfig.description);
|
|
2782
|
+
html = setNamedMeta(html, "keywords", route?.meta.keywords?.join(", "));
|
|
2596
2783
|
html = injectCanonicalUrl(html, contextOrConfig, route);
|
|
2597
2784
|
const dataPagefindIgnore = route?.isBareAlias ? " data-pagefind-ignore" : "";
|
|
2598
2785
|
html = html.replace(/<div id="root">([\s\S]*?)<\/div>/, `<div id="root"${dataPagefindIgnore}>${appHtml}</div>`);
|
|
@@ -2701,7 +2888,7 @@ function routeOutputFiles(outputDirectory, route) {
|
|
|
2701
2888
|
var SSR_ENTRY_CODE = `import { renderToHTML } from '@clarify-labs/renderer/server';
|
|
2702
2889
|
import { routes, navigation } from 'virtual:clarify/routes/server';
|
|
2703
2890
|
import { config } from 'virtual:clarify/config';
|
|
2704
|
-
import {
|
|
2891
|
+
import { openApiSpecs } from 'virtual:clarify/openapi/server';
|
|
2705
2892
|
import { runtimeSlots } from 'virtual:clarify/slots';
|
|
2706
2893
|
|
|
2707
2894
|
export async function render(url) {
|
|
@@ -2709,10 +2896,10 @@ export async function render(url) {
|
|
|
2709
2896
|
// them synchronously (renderToString has no Suspense support).
|
|
2710
2897
|
const entries = Object.values(runtimeSlots).flat()
|
|
2711
2898
|
await Promise.all(entries.map(async (entry) => {
|
|
2712
|
-
const mod = await entry.
|
|
2899
|
+
const mod = await entry.loadComponent()
|
|
2713
2900
|
entry._resolved = mod.default
|
|
2714
2901
|
}))
|
|
2715
|
-
return renderToHTML({ config, routes, navigation,
|
|
2902
|
+
return renderToHTML({ config, routes, navigation, openApiSpecs, runtimeSlots, url, themeEditor: config.theme.editor });
|
|
2716
2903
|
}`;
|
|
2717
2904
|
function createTempEntryFile(content) {
|
|
2718
2905
|
const entryPath = (0, node_path.join)(createClarifyTempDir("ssr"), "entry-server.ts");
|
|
@@ -2799,7 +2986,10 @@ var ClarifyContext = class {
|
|
|
2799
2986
|
this.generateOptions = init.generateOptions;
|
|
2800
2987
|
this.version = init.version;
|
|
2801
2988
|
this.currentRoutes = init.routes ?? [];
|
|
2802
|
-
this.currentNavigation = init.navigation ??
|
|
2989
|
+
this.currentNavigation = init.navigation ?? {
|
|
2990
|
+
kind: "flat",
|
|
2991
|
+
nodes: []
|
|
2992
|
+
};
|
|
2803
2993
|
this.currentPlugins = init.plugins ?? [];
|
|
2804
2994
|
}
|
|
2805
2995
|
get routes() {
|
|
@@ -2921,7 +3111,10 @@ var ClarifyEngine = class {
|
|
|
2921
3111
|
generateOptions: placeholderOptions,
|
|
2922
3112
|
version: cliPackageVersion,
|
|
2923
3113
|
routes: [],
|
|
2924
|
-
navigation:
|
|
3114
|
+
navigation: {
|
|
3115
|
+
kind: "flat",
|
|
3116
|
+
nodes: []
|
|
3117
|
+
},
|
|
2925
3118
|
plugins: []
|
|
2926
3119
|
});
|
|
2927
3120
|
}
|
|
@@ -3024,9 +3217,9 @@ var ClarifyEngine = class {
|
|
|
3024
3217
|
routes = await runPhase(plugins, "content:process", this.ctx, async () => {
|
|
3025
3218
|
const pages = routes.map((route) => ({
|
|
3026
3219
|
path: route.path,
|
|
3027
|
-
filePath: route.filePath,
|
|
3028
|
-
frontmatter: route.frontmatter ?? {},
|
|
3029
|
-
content: route.content ?? ""
|
|
3220
|
+
filePath: route.source.filePath,
|
|
3221
|
+
frontmatter: route.source.frontmatter ?? {},
|
|
3222
|
+
content: route.source.content ?? ""
|
|
3030
3223
|
}));
|
|
3031
3224
|
const resolvedPages = await runHooks(plugins, "pages:resolved", pages, this.ctx);
|
|
3032
3225
|
const pageByPath = new Map(resolvedPages.map((p) => [p.path, p]));
|
|
@@ -3035,13 +3228,22 @@ var ClarifyEngine = class {
|
|
|
3035
3228
|
if (!page) return route;
|
|
3036
3229
|
return {
|
|
3037
3230
|
...route,
|
|
3038
|
-
|
|
3039
|
-
|
|
3231
|
+
source: {
|
|
3232
|
+
...route.source,
|
|
3233
|
+
frontmatter: page.frontmatter,
|
|
3234
|
+
content: page.content
|
|
3235
|
+
}
|
|
3040
3236
|
};
|
|
3041
3237
|
});
|
|
3042
3238
|
});
|
|
3043
3239
|
routes = applyConfiguredPageRoutePaths(routes, tabs, i18n);
|
|
3044
|
-
const navigation = tabs ? i18n ? buildLocalizedNavigationFromTabsConfig(routes, tabs, i18n) ?? {
|
|
3240
|
+
const navigation = tabs ? i18n ? buildLocalizedNavigationFromTabsConfig(routes, tabs, i18n) ?? {
|
|
3241
|
+
kind: "localized-tabbed",
|
|
3242
|
+
locales: {}
|
|
3243
|
+
} : buildNavigationFromTabsConfig(routes, tabs) : {
|
|
3244
|
+
kind: "flat",
|
|
3245
|
+
nodes: buildNavigation(routes)
|
|
3246
|
+
};
|
|
3045
3247
|
const resolved = await runHooks(plugins, "routes:resolved", {
|
|
3046
3248
|
routes,
|
|
3047
3249
|
navigation
|
|
@@ -3075,7 +3277,7 @@ var ClarifyEngine = class {
|
|
|
3075
3277
|
writeClarifyEnvDts(this.root, this.plugins);
|
|
3076
3278
|
}
|
|
3077
3279
|
hasContentRouteForFile(filePath) {
|
|
3078
|
-
return this.routes.some((route) => route.filePath === filePath);
|
|
3280
|
+
return this.routes.some((route) => route.source.filePath === filePath);
|
|
3079
3281
|
}
|
|
3080
3282
|
async collectBuildAssets() {
|
|
3081
3283
|
if (!this.buildEnabled) return [];
|
|
@@ -3215,4 +3417,4 @@ Object.defineProperty(exports, "defineConfig", {
|
|
|
3215
3417
|
}
|
|
3216
3418
|
});
|
|
3217
3419
|
|
|
3218
|
-
//# sourceMappingURL=engine-
|
|
3420
|
+
//# sourceMappingURL=engine-5bdNX2Oo.cjs.map
|