@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
|
@@ -39,14 +39,14 @@ function renderPlainCode(code) {
|
|
|
39
39
|
function isBundledLanguage(language) {
|
|
40
40
|
return language in bundledLanguages;
|
|
41
41
|
}
|
|
42
|
-
var
|
|
42
|
+
var highlighterPromise;
|
|
43
43
|
var cssVariablesTheme = createCssVariablesTheme();
|
|
44
44
|
async function getHighlighter() {
|
|
45
|
-
|
|
45
|
+
highlighterPromise = highlighterPromise ?? createHighlighter({
|
|
46
46
|
themes: [cssVariablesTheme],
|
|
47
47
|
langs: []
|
|
48
48
|
});
|
|
49
|
-
return
|
|
49
|
+
return highlighterPromise;
|
|
50
50
|
}
|
|
51
51
|
function renderHighlightedCode(html) {
|
|
52
52
|
return (/<code[^>]*>([\s\S]*?)<\/code>/.exec(html)?.[1] ?? html).split("\n").map((line) => line.trimStart().startsWith("<span") ? line : `<span>${line}</span>`).join("\n");
|
|
@@ -308,12 +308,12 @@ function extractMdxSections(content) {
|
|
|
308
308
|
return sections;
|
|
309
309
|
}
|
|
310
310
|
function navigationSections(sections) {
|
|
311
|
-
return sections.map((
|
|
312
|
-
id:
|
|
313
|
-
title:
|
|
314
|
-
level:
|
|
315
|
-
badge:
|
|
316
|
-
tags:
|
|
311
|
+
return sections.map((section) => ({
|
|
312
|
+
id: section.id,
|
|
313
|
+
title: section.title,
|
|
314
|
+
level: section.level,
|
|
315
|
+
badge: section.badge,
|
|
316
|
+
tags: section.tags
|
|
317
317
|
}));
|
|
318
318
|
}
|
|
319
319
|
function normalizePath(path) {
|
|
@@ -385,16 +385,20 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
385
385
|
}
|
|
386
386
|
routes.push({
|
|
387
387
|
kind: "mdx",
|
|
388
|
-
title,
|
|
389
388
|
path: cleanPath,
|
|
390
389
|
basePath: cleanPath,
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
390
|
+
meta: {
|
|
391
|
+
title,
|
|
392
|
+
description: typeof page.frontmatter.description === "string" ? page.frontmatter.description : void 0,
|
|
393
|
+
keywords: frontmatterKeywords(page.frontmatter),
|
|
394
|
+
sections: extractMdxSections(page.content)
|
|
395
|
+
},
|
|
396
|
+
module: { virtualModuleId: "virtual:clarify-page/" + relativePath.replace(/\.mdx?$/, "").replace(/\/+/g, "/") },
|
|
397
|
+
source: {
|
|
398
|
+
filePath: fullPath,
|
|
399
|
+
frontmatter: page.frontmatter,
|
|
400
|
+
content: page.content
|
|
401
|
+
},
|
|
398
402
|
diagnostic: mdxResult.ok ? void 0 : mdxResult.diagnostic
|
|
399
403
|
});
|
|
400
404
|
}
|
|
@@ -454,7 +458,7 @@ async function findLocalizedContentRoutes(contentRoot, i18n, options = {}) {
|
|
|
454
458
|
path: localizedRoutePath(basePath, locale.code, i18n),
|
|
455
459
|
basePath,
|
|
456
460
|
locale: locale.code,
|
|
457
|
-
virtualModuleId: virtualModuleIdForLocalizedRoute(contentRoot, route.filePath)
|
|
461
|
+
module: { virtualModuleId: virtualModuleIdForLocalizedRoute(contentRoot, route.source.filePath) }
|
|
458
462
|
});
|
|
459
463
|
}
|
|
460
464
|
}
|
|
@@ -490,12 +494,12 @@ function buildNavigation(routes) {
|
|
|
490
494
|
if (!node) {
|
|
491
495
|
node = {
|
|
492
496
|
path: pathSoFar,
|
|
493
|
-
title: i === parts.length - 1 ? route.title : kebabToTitle(parts[i]),
|
|
497
|
+
title: i === parts.length - 1 ? route.meta.title : kebabToTitle(parts[i]),
|
|
494
498
|
children: []
|
|
495
499
|
};
|
|
496
500
|
current.push(node);
|
|
497
501
|
}
|
|
498
|
-
if (i === parts.length - 1 && route.sections) node.sections = navigationSections(route.sections);
|
|
502
|
+
if (i === parts.length - 1 && route.meta.sections) node.sections = navigationSections(route.meta.sections);
|
|
499
503
|
if (i < parts.length - 1) {
|
|
500
504
|
node.children = node.children ?? [];
|
|
501
505
|
current = node.children;
|
|
@@ -504,17 +508,22 @@ function buildNavigation(routes) {
|
|
|
504
508
|
}
|
|
505
509
|
return root;
|
|
506
510
|
}
|
|
507
|
-
function
|
|
508
|
-
if (typeof item === "string") return {
|
|
511
|
+
function routeIntentFromPagesItem(item) {
|
|
512
|
+
if (typeof item === "string") return {
|
|
513
|
+
kind: "page",
|
|
514
|
+
ref: item
|
|
515
|
+
};
|
|
509
516
|
if ("openapi" in item) return {
|
|
510
|
-
|
|
511
|
-
|
|
517
|
+
kind: "openapi",
|
|
518
|
+
ref: item.openapi,
|
|
519
|
+
tagFilter: item.filter?.tags,
|
|
512
520
|
path: item.path,
|
|
513
521
|
title: item.title,
|
|
514
522
|
icon: item.icon
|
|
515
523
|
};
|
|
516
524
|
return {
|
|
517
|
-
|
|
525
|
+
kind: "page",
|
|
526
|
+
ref: item.page,
|
|
518
527
|
path: item.path,
|
|
519
528
|
redirect: item.redirect,
|
|
520
529
|
title: item.title,
|
|
@@ -529,25 +538,28 @@ function firstNavigationPath(nodes) {
|
|
|
529
538
|
}
|
|
530
539
|
return "/";
|
|
531
540
|
}
|
|
532
|
-
function
|
|
533
|
-
const
|
|
541
|
+
function collectExplicitPagePathIntents(tabs) {
|
|
542
|
+
const intents = [];
|
|
534
543
|
for (const tab of tabs ?? []) {
|
|
535
544
|
if (!tab.pages || tab.pages === "FileTree") continue;
|
|
536
|
-
for (const group of tab.pages) for (const item of group.pages) if (typeof item !== "string" && "page" in item && item.path)
|
|
537
|
-
|
|
538
|
-
path
|
|
539
|
-
|
|
545
|
+
for (const group of tab.pages) for (const item of group.pages) if (typeof item !== "string" && "page" in item && item.path) {
|
|
546
|
+
const intent = routeIntentFromPagesItem(item);
|
|
547
|
+
if (intent.kind === "page" && intent.path) intents.push({
|
|
548
|
+
...intent,
|
|
549
|
+
path: intent.path
|
|
550
|
+
});
|
|
551
|
+
}
|
|
540
552
|
}
|
|
541
|
-
return
|
|
553
|
+
return intents;
|
|
542
554
|
}
|
|
543
555
|
function applyConfiguredPageRoutePaths(routes, tabs, i18n) {
|
|
544
|
-
const
|
|
545
|
-
if (!
|
|
556
|
+
const pageIntents = collectExplicitPagePathIntents(tabs);
|
|
557
|
+
if (!pageIntents.length) return routes;
|
|
546
558
|
const additions = [];
|
|
547
559
|
const existingKeys = new Set(routes.map((route) => `${route.locale ?? ""}:${route.basePath ?? route.path}`));
|
|
548
|
-
for (const
|
|
549
|
-
const sourceBasePath = routePathFromRef(
|
|
550
|
-
const targetBasePath = routePathFromRef(
|
|
560
|
+
for (const intent of pageIntents) {
|
|
561
|
+
const sourceBasePath = routePathFromRef(intent.ref);
|
|
562
|
+
const targetBasePath = routePathFromRef(intent.ref, intent.path);
|
|
551
563
|
if (sourceBasePath === targetBasePath) continue;
|
|
552
564
|
for (const route of routes) {
|
|
553
565
|
if (route.kind !== "mdx" || (route.basePath ?? route.path) !== sourceBasePath) continue;
|
|
@@ -571,40 +583,42 @@ function buildNavigationFromPagesConfig(routes, config) {
|
|
|
571
583
|
return buildNavigationFromConfig(routes, config);
|
|
572
584
|
}
|
|
573
585
|
function buildNavigationFromTabsConfig(routes, tabs) {
|
|
574
|
-
return {
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
586
|
+
return {
|
|
587
|
+
kind: "tabbed",
|
|
588
|
+
tabs: tabs.map((tab) => {
|
|
589
|
+
const children = buildNavigationFromPagesConfig(routes, tab.pages);
|
|
590
|
+
return {
|
|
591
|
+
type: "tab",
|
|
592
|
+
path: firstNavigationPath(children),
|
|
593
|
+
title: resolveLocalizedText(tab.tab, "", "") ?? "",
|
|
594
|
+
icon: tab.icon,
|
|
595
|
+
children
|
|
596
|
+
};
|
|
597
|
+
})
|
|
598
|
+
};
|
|
584
599
|
}
|
|
585
600
|
function buildNavigationFromConfig(routes, config) {
|
|
586
601
|
const routeMap = new Map(routes.map((r) => [r.path, r]));
|
|
587
602
|
return config.map((group) => {
|
|
588
603
|
const children = group.pages.map((item) => {
|
|
589
|
-
const
|
|
590
|
-
if (
|
|
591
|
-
const path = openAPIPagePathFromRef(
|
|
604
|
+
const intent = routeIntentFromPagesItem(item);
|
|
605
|
+
if (intent.kind === "openapi") {
|
|
606
|
+
const path = openAPIPagePathFromRef(intent.ref, intent.tagFilter, intent.path);
|
|
592
607
|
const route = routeMap.get(path);
|
|
593
608
|
return {
|
|
594
609
|
path,
|
|
595
|
-
title: resolveLocalizedText(title, "", "") ?? route?.title ?? kebabToTitle(path.split("/").pop() ??
|
|
596
|
-
icon,
|
|
597
|
-
sections: route?.sections ? navigationSections(route.sections) : void 0
|
|
610
|
+
title: resolveLocalizedText(intent.title, "", "") ?? route?.meta.title ?? kebabToTitle(path.split("/").pop() ?? intent.ref),
|
|
611
|
+
icon: intent.icon,
|
|
612
|
+
sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
|
|
598
613
|
};
|
|
599
614
|
}
|
|
600
|
-
const
|
|
601
|
-
const path = routePathFromRef(ref, explicitPath);
|
|
615
|
+
const path = routePathFromRef(intent.ref, intent.path);
|
|
602
616
|
const route = routeMap.get(path);
|
|
603
617
|
return {
|
|
604
|
-
path: redirect ? normalizePath(redirect) : path,
|
|
605
|
-
title: resolveLocalizedText(title, "", "") ?? route?.title ?? kebabToTitle(path.split("/").pop() ?? ref),
|
|
606
|
-
icon,
|
|
607
|
-
sections: route?.sections ? navigationSections(route.sections) : void 0
|
|
618
|
+
path: intent.redirect ? normalizePath(intent.redirect) : path,
|
|
619
|
+
title: resolveLocalizedText(intent.title, "", "") ?? route?.meta.title ?? kebabToTitle(path.split("/").pop() ?? intent.ref),
|
|
620
|
+
icon: intent.icon,
|
|
621
|
+
sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
|
|
608
622
|
};
|
|
609
623
|
});
|
|
610
624
|
return {
|
|
@@ -630,16 +644,15 @@ function buildLocalizedNavigationForLocale(routes, config, locale, i18n) {
|
|
|
630
644
|
const routeMap = new Map(routes.map((route) => [route.basePath ?? route.path, route]));
|
|
631
645
|
return config.map((group) => {
|
|
632
646
|
const children = group.pages.map((item) => {
|
|
633
|
-
const
|
|
634
|
-
const
|
|
635
|
-
const basePath = openapiRef ? openAPIPagePathFromRef(openapiRef, openapiTagFilter, explicitPath) : routePathFromRef(ref, explicitPath);
|
|
647
|
+
const intent = routeIntentFromPagesItem(item);
|
|
648
|
+
const basePath = intent.kind === "openapi" ? openAPIPagePathFromRef(intent.ref, intent.tagFilter, intent.path) : routePathFromRef(intent.ref, intent.path);
|
|
636
649
|
const route = routeMap.get(basePath);
|
|
637
650
|
const path = route?.path ?? localizedRoutePath(basePath, locale, i18n);
|
|
638
651
|
return {
|
|
639
|
-
path: (redirect ? localizedRoutePath(routePathFromRef(redirect), locale, i18n) : void 0) ?? path,
|
|
640
|
-
title: resolveLocalizedText(title, locale, i18n.defaultLocale) ?? route?.title ?? kebabToTitle(basePath.split("/").pop() ?? ref),
|
|
641
|
-
icon,
|
|
642
|
-
sections: route?.sections ? navigationSections(route.sections) : void 0
|
|
652
|
+
path: (intent.kind === "page" && intent.redirect ? localizedRoutePath(routePathFromRef(intent.redirect), locale, i18n) : void 0) ?? path,
|
|
653
|
+
title: resolveLocalizedText(intent.title, locale, i18n.defaultLocale) ?? route?.meta.title ?? kebabToTitle(basePath.split("/").pop() ?? intent.ref),
|
|
654
|
+
icon: intent.icon,
|
|
655
|
+
sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
|
|
643
656
|
};
|
|
644
657
|
});
|
|
645
658
|
return {
|
|
@@ -652,10 +665,10 @@ function buildLocalizedNavigationForLocale(routes, config, locale, i18n) {
|
|
|
652
665
|
}
|
|
653
666
|
function buildLocalizedNavigationFromTabsConfig(routes, tabs, i18n) {
|
|
654
667
|
if (!i18n) return void 0;
|
|
655
|
-
const
|
|
668
|
+
const locales = {};
|
|
656
669
|
for (const locale of i18n.locales) {
|
|
657
670
|
const localeRoutes = routes.filter((route) => route.locale === locale.code);
|
|
658
|
-
|
|
671
|
+
locales[locale.code] = { tabs: tabs.map((tab) => {
|
|
659
672
|
const children = buildLocalizedNavigationForLocale(localeRoutes, tab.pages, locale.code, i18n);
|
|
660
673
|
return {
|
|
661
674
|
type: "tab",
|
|
@@ -666,17 +679,20 @@ function buildLocalizedNavigationFromTabsConfig(routes, tabs, i18n) {
|
|
|
666
679
|
};
|
|
667
680
|
}) };
|
|
668
681
|
}
|
|
669
|
-
return
|
|
682
|
+
return {
|
|
683
|
+
kind: "localized-tabbed",
|
|
684
|
+
locales
|
|
685
|
+
};
|
|
670
686
|
}
|
|
671
687
|
/** Serialize a ContentRoute into the minimal shape exposed to tooling. */
|
|
672
688
|
function toDevRouteEntry(route) {
|
|
673
689
|
return {
|
|
674
690
|
path: route.path,
|
|
675
|
-
filePath: route.filePath,
|
|
691
|
+
filePath: route.source.filePath,
|
|
676
692
|
locale: route.locale,
|
|
677
693
|
basePath: route.basePath,
|
|
678
694
|
kind: route.kind,
|
|
679
|
-
title: route.title
|
|
695
|
+
title: route.meta.title
|
|
680
696
|
};
|
|
681
697
|
}
|
|
682
698
|
/**
|
|
@@ -690,7 +706,7 @@ function toDevRouteEntry(route) {
|
|
|
690
706
|
* or the first match.
|
|
691
707
|
*/
|
|
692
708
|
function resolveRouteForFile(filePath, routes, defaultLocale) {
|
|
693
|
-
const matches = routes.filter((r) => r.filePath === filePath);
|
|
709
|
+
const matches = routes.filter((r) => r.source.filePath === filePath);
|
|
694
710
|
if (matches.length === 0) return void 0;
|
|
695
711
|
if (matches.length === 1) return matches[0];
|
|
696
712
|
const fileLocale = inferLocaleFromPath(filePath);
|
|
@@ -1115,6 +1131,7 @@ var VIRTUAL_CONFIG = "virtual:clarify/config";
|
|
|
1115
1131
|
var VIRTUAL_ROUTES = "virtual:clarify/routes";
|
|
1116
1132
|
var VIRTUAL_SERVER_ROUTES = "virtual:clarify/routes/server";
|
|
1117
1133
|
var VIRTUAL_OPENAPI = "virtual:clarify/openapi";
|
|
1134
|
+
var VIRTUAL_OPENAPI_SERVER = "virtual:clarify/openapi/server";
|
|
1118
1135
|
var VIRTUAL_SLOTS = "virtual:clarify/slots";
|
|
1119
1136
|
var VIRTUAL_SLOT = "virtual:clarify/slot";
|
|
1120
1137
|
var VIRTUAL_CLIENT_ENTRY = "virtual:clarify/entry-client";
|
|
@@ -1135,6 +1152,7 @@ var FIXED_VIRTUAL_IDS = [
|
|
|
1135
1152
|
VIRTUAL_ROUTES,
|
|
1136
1153
|
VIRTUAL_SERVER_ROUTES,
|
|
1137
1154
|
VIRTUAL_OPENAPI,
|
|
1155
|
+
VIRTUAL_OPENAPI_SERVER,
|
|
1138
1156
|
VIRTUAL_SLOTS,
|
|
1139
1157
|
VIRTUAL_SLOT
|
|
1140
1158
|
];
|
|
@@ -1151,8 +1169,8 @@ function resolveVirtualModuleId(id, modules, routes) {
|
|
|
1151
1169
|
for (const virtualId of FIXED_VIRTUAL_IDS) if (id === virtualId || id === resolveVirtualId(virtualId)) return resolveVirtualId(virtualId);
|
|
1152
1170
|
const moduleId = stripVirtualPrefix(id);
|
|
1153
1171
|
if (modules.has(moduleId)) return resolveVirtualId(moduleId);
|
|
1154
|
-
const route = routes.find((route) => route.virtualModuleId === id || route.virtualModuleId === moduleId);
|
|
1155
|
-
if (route) return resolveVirtualId(route.virtualModuleId);
|
|
1172
|
+
const route = routes.find((route) => route.module.virtualModuleId === id || route.module.virtualModuleId === moduleId);
|
|
1173
|
+
if (route) return resolveVirtualId(route.module.virtualModuleId);
|
|
1156
1174
|
return null;
|
|
1157
1175
|
}
|
|
1158
1176
|
function generateConfigModule(projectConfig, buildOptions, version) {
|
|
@@ -1166,38 +1184,38 @@ function generateConfigModule(projectConfig, buildOptions, version) {
|
|
|
1166
1184
|
function moduleSpecifier(value) {
|
|
1167
1185
|
return JSON.stringify(value);
|
|
1168
1186
|
}
|
|
1169
|
-
function
|
|
1170
|
-
const
|
|
1187
|
+
function routeToRuntimeManifestEntry(route, component, mode) {
|
|
1188
|
+
const entry = {
|
|
1171
1189
|
path: route.path,
|
|
1172
|
-
title: route.title,
|
|
1190
|
+
title: route.meta.title,
|
|
1173
1191
|
component,
|
|
1174
1192
|
kind: route.kind
|
|
1175
1193
|
};
|
|
1176
|
-
if (mode === "client")
|
|
1177
|
-
if (route.basePath)
|
|
1178
|
-
if (route.locale)
|
|
1179
|
-
if (route.isFallback)
|
|
1180
|
-
if (route.isBareAlias)
|
|
1181
|
-
if (route.alternates)
|
|
1182
|
-
if (route.description)
|
|
1183
|
-
if (route.keywords?.length)
|
|
1184
|
-
if (route.contentArtifactUrl)
|
|
1185
|
-
if (route.
|
|
1186
|
-
if (route.sections?.length)
|
|
1187
|
-
id:
|
|
1188
|
-
title:
|
|
1189
|
-
level:
|
|
1190
|
-
badge:
|
|
1191
|
-
tags:
|
|
1194
|
+
if (mode === "client") entry.lazy = true;
|
|
1195
|
+
if (route.basePath) entry.basePath = route.basePath;
|
|
1196
|
+
if (route.locale) entry.locale = route.locale;
|
|
1197
|
+
if (route.isFallback) entry.isFallback = true;
|
|
1198
|
+
if (route.isBareAlias) entry.isBareAlias = true;
|
|
1199
|
+
if (route.alternates) entry.alternates = route.alternates;
|
|
1200
|
+
if (route.meta.description) entry.description = route.meta.description;
|
|
1201
|
+
if (route.meta.keywords?.length) entry.keywords = route.meta.keywords;
|
|
1202
|
+
if (route.artifacts?.contentArtifactUrl) entry.contentArtifactUrl = route.artifacts.contentArtifactUrl;
|
|
1203
|
+
if (route.source?.sourceEditUrl) entry.sourceEditUrl = route.source.sourceEditUrl;
|
|
1204
|
+
if (route.meta.sections?.length) entry.sections = route.meta.sections.map((section) => ({
|
|
1205
|
+
id: section.id,
|
|
1206
|
+
title: section.title,
|
|
1207
|
+
level: section.level,
|
|
1208
|
+
badge: section.badge,
|
|
1209
|
+
tags: section.tags
|
|
1192
1210
|
}));
|
|
1193
|
-
return
|
|
1211
|
+
return entry;
|
|
1194
1212
|
}
|
|
1195
1213
|
function generateRoutesModule(routes, navigation, mode = "client") {
|
|
1196
|
-
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) => {
|
|
1197
|
-
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 }) }))`;
|
|
1214
|
+
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) => {
|
|
1215
|
+
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 }) }))`;
|
|
1198
1216
|
const placeholder = `__COMPONENT_${i}__`;
|
|
1199
|
-
const
|
|
1200
|
-
return ` ${JSON.stringify(
|
|
1217
|
+
const manifestEntry = routeToRuntimeManifestEntry(r, placeholder, mode);
|
|
1218
|
+
return ` ${JSON.stringify(manifestEntry, null, 0).replace(`"${placeholder}"`, component).replace(/"([A-Za-z_$][\w$]*)":/g, "$1: ")}`;
|
|
1201
1219
|
}).join(",\n")}\n];\n\nexport const navigation = ${JSON.stringify(navigation, null, 2)};\n`;
|
|
1202
1220
|
}
|
|
1203
1221
|
function createClientEntryModule(options = {}) {
|
|
@@ -1206,9 +1224,15 @@ import '@clarify-labs/renderer/style.css';
|
|
|
1206
1224
|
import { render } from '@clarify-labs/renderer/client';
|
|
1207
1225
|
import { routes, navigation } from '${VIRTUAL_ROUTES}';
|
|
1208
1226
|
import { config } from '${VIRTUAL_CONFIG}';
|
|
1209
|
-
import {
|
|
1227
|
+
import { openApiSpecs } from '${VIRTUAL_OPENAPI}';
|
|
1210
1228
|
import { runtimeSlots } from '${VIRTUAL_SLOTS}';
|
|
1211
|
-
|
|
1229
|
+
const renderOptions = { config, routes, navigation, openApiSpecs, runtimeSlots, themeEditor: ${JSON.stringify(options.themeEditor ?? false)} };
|
|
1230
|
+
render(renderOptions);
|
|
1231
|
+
if (import.meta.hot) {
|
|
1232
|
+
import.meta.hot.accept('${VIRTUAL_ROUTES}', (mod) => {
|
|
1233
|
+
render({ ...renderOptions, routes: mod.routes, navigation: mod.navigation });
|
|
1234
|
+
});
|
|
1235
|
+
}`;
|
|
1212
1236
|
}
|
|
1213
1237
|
/**
|
|
1214
1238
|
* Collect every plugin's `slots` declarations into a single runtime registry
|
|
@@ -1237,7 +1261,7 @@ function createRuntimeSlotsModule(plugins = [], root = process.cwd()) {
|
|
|
1237
1261
|
}
|
|
1238
1262
|
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.`);
|
|
1239
1263
|
const componentPath = root + slot.component;
|
|
1240
|
-
const entry = `{ plugin: ${JSON.stringify(plugin)},
|
|
1264
|
+
const entry = `{ plugin: ${JSON.stringify(plugin)}, loadComponent: () => import(${moduleSpecifier(componentPath)}) }`;
|
|
1241
1265
|
const list = grouped.get(slot.name) ?? [];
|
|
1242
1266
|
list.push(entry);
|
|
1243
1267
|
grouped.set(slot.name, list);
|
|
@@ -1266,18 +1290,34 @@ function buildVirtualModules(args) {
|
|
|
1266
1290
|
modules.set(VIRTUAL_ROUTES, generateRoutesModule(args.routes, args.navigation, "client"));
|
|
1267
1291
|
modules.set(VIRTUAL_SERVER_ROUTES, generateRoutesModule(args.routes, args.navigation, "server"));
|
|
1268
1292
|
modules.set(VIRTUAL_SLOTS, createRuntimeSlotsModule(allPlugins, args.generateOptions.projectRoot));
|
|
1269
|
-
modules.set(VIRTUAL_OPENAPI, "export const
|
|
1293
|
+
modules.set(VIRTUAL_OPENAPI, "export const openApiSpecs = {};");
|
|
1270
1294
|
modules.set(VIRTUAL_SLOT, "export { useSlot } from '@clarify-labs/renderer';\n");
|
|
1271
1295
|
modules.set(VIRTUAL_CLIENT_ENTRY, clientEntryModule);
|
|
1272
1296
|
modules.set(RESOLVED_CLIENT_ENTRY, clientEntryModule);
|
|
1273
1297
|
for (const route of args.routes) {
|
|
1274
|
-
const moduleContent = route.diagnostic ? generateMdxErrorModule(route.diagnostic) : `export { default } from ${moduleSpecifier(route.filePath)};`;
|
|
1275
|
-
modules.set(route.virtualModuleId, moduleContent);
|
|
1298
|
+
const moduleContent = route.diagnostic ? generateMdxErrorModule(route.diagnostic) : `export { default } from ${moduleSpecifier(route.source.filePath)};`;
|
|
1299
|
+
modules.set(route.module.virtualModuleId, moduleContent);
|
|
1276
1300
|
}
|
|
1277
1301
|
return modules;
|
|
1278
1302
|
}
|
|
1279
1303
|
//#endregion
|
|
1280
1304
|
//#region source/core/adapters.ts
|
|
1305
|
+
function routeStructure(route) {
|
|
1306
|
+
const { content: _content, ...source } = route.source;
|
|
1307
|
+
return {
|
|
1308
|
+
...route,
|
|
1309
|
+
source
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
function createDevStructureSnapshot(routes, navigation) {
|
|
1313
|
+
return {
|
|
1314
|
+
routes: JSON.stringify(routes.map(routeStructure)),
|
|
1315
|
+
navigation: JSON.stringify(navigation)
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
function hasDevStructureChanged(before, after) {
|
|
1319
|
+
return before.routes !== after.routes || before.navigation !== after.navigation;
|
|
1320
|
+
}
|
|
1281
1321
|
function invalidateVirtualModules(engine, server) {
|
|
1282
1322
|
for (const id of engine.modules.keys()) {
|
|
1283
1323
|
const moduleNode = server.moduleGraph.getModuleById(resolveVirtualId(id));
|
|
@@ -1285,8 +1325,10 @@ function invalidateVirtualModules(engine, server) {
|
|
|
1285
1325
|
}
|
|
1286
1326
|
}
|
|
1287
1327
|
async function refreshDevServer(engine, server) {
|
|
1328
|
+
const before = createDevStructureSnapshot(engine.routes, engine.navigation);
|
|
1288
1329
|
await engine.refresh();
|
|
1289
1330
|
invalidateVirtualModules(engine, server);
|
|
1331
|
+
return hasDevStructureChanged(before, createDevStructureSnapshot(engine.routes, engine.navigation));
|
|
1290
1332
|
}
|
|
1291
1333
|
function createNormalizedMdxContentPlugin(engine) {
|
|
1292
1334
|
return {
|
|
@@ -1295,10 +1337,10 @@ function createNormalizedMdxContentPlugin(engine) {
|
|
|
1295
1337
|
transform(_code, id) {
|
|
1296
1338
|
if (!/\.mdx?(?:\?|$)/.test(id)) return null;
|
|
1297
1339
|
const filePath = id.replace(/\?.*$/, "");
|
|
1298
|
-
const route = engine.routes.find((route) => route.kind === "mdx" && route.filePath === filePath);
|
|
1299
|
-
if (!route || route.content === void 0) return null;
|
|
1340
|
+
const route = engine.routes.find((route) => route.kind === "mdx" && route.source.filePath === filePath);
|
|
1341
|
+
if (!route || route.source.content === void 0) return null;
|
|
1300
1342
|
return {
|
|
1301
|
-
code: route.content,
|
|
1343
|
+
code: route.source.content,
|
|
1302
1344
|
map: null
|
|
1303
1345
|
};
|
|
1304
1346
|
}
|
|
@@ -1352,8 +1394,13 @@ function createClarifyViteCorePlugin(engine, normalizedMdxContentPlugin, mdx) {
|
|
|
1352
1394
|
}
|
|
1353
1395
|
const relativeContentFile = relative(engine.contentRoot, changedFile);
|
|
1354
1396
|
if (!(relativeContentFile && !relativeContentFile.startsWith("..") && !isAbsolute(relativeContentFile)) || !engine.hasContentRouteForFile(changedFile)) return;
|
|
1355
|
-
await refreshDevServer(engine, ctx.server)
|
|
1356
|
-
|
|
1397
|
+
if (await refreshDevServer(engine, ctx.server)) {
|
|
1398
|
+
const routesModule = ctx.server.moduleGraph.getModuleById(resolveVirtualId(VIRTUAL_ROUTES));
|
|
1399
|
+
if (routesModule) return [...ctx.modules, routesModule];
|
|
1400
|
+
ctx.server.ws.send({ type: "full-reload" });
|
|
1401
|
+
return [];
|
|
1402
|
+
}
|
|
1403
|
+
return ctx.modules;
|
|
1357
1404
|
},
|
|
1358
1405
|
resolveId(id) {
|
|
1359
1406
|
return resolveVirtualModuleId(id, engine.modules, engine.routes);
|
|
@@ -1364,18 +1411,6 @@ function createClarifyViteCorePlugin(engine, normalizedMdxContentPlugin, mdx) {
|
|
|
1364
1411
|
async configureServer(server) {
|
|
1365
1412
|
server.watcher.add(engine.contentRoot);
|
|
1366
1413
|
if (engine.configFilePath) server.watcher.add(engine.configFilePath);
|
|
1367
|
-
let reloadQueued = false;
|
|
1368
|
-
const invalidateAndReload = () => {
|
|
1369
|
-
if (reloadQueued) return;
|
|
1370
|
-
reloadQueued = true;
|
|
1371
|
-
queueMicrotask(() => {
|
|
1372
|
-
reloadQueued = false;
|
|
1373
|
-
invalidateVirtualModules(engine, server);
|
|
1374
|
-
server.ws.send({ type: "full-reload" });
|
|
1375
|
-
});
|
|
1376
|
-
};
|
|
1377
|
-
engine.ctx.onRoutesChange(invalidateAndReload);
|
|
1378
|
-
engine.ctx.onNavigationChange(invalidateAndReload);
|
|
1379
1414
|
server.middlewares.use((req, res, next) => {
|
|
1380
1415
|
if (req.url === "/dev/query-preview-route" && req.method === "POST") return handleDevRouteRequest(req, res, engine.routes, engine.runtimeContext());
|
|
1381
1416
|
if (req.url === "/dev/project-info" && (req.method === "GET" || req.method === "HEAD")) return handleProjectInfoRequest(req, res, engine.runtimeContext());
|
|
@@ -1385,7 +1420,7 @@ function createClarifyViteCorePlugin(engine, normalizedMdxContentPlugin, mdx) {
|
|
|
1385
1420
|
const changedFile = isAbsolute(filePath) ? filePath : join(engine.root, filePath);
|
|
1386
1421
|
const relativeContentFile = relative(engine.contentRoot, changedFile);
|
|
1387
1422
|
if (!(relativeContentFile && !relativeContentFile.startsWith("..") && !isAbsolute(relativeContentFile))) return;
|
|
1388
|
-
await refreshDevServer(engine, server);
|
|
1423
|
+
if (await refreshDevServer(engine, server)) server.ws.send({ type: "full-reload" });
|
|
1389
1424
|
};
|
|
1390
1425
|
server.watcher.on("add", handleContentTreeChange);
|
|
1391
1426
|
server.watcher.on("unlink", handleContentTreeChange);
|
|
@@ -1452,7 +1487,7 @@ function createViteAdapter(engine) {
|
|
|
1452
1487
|
}
|
|
1453
1488
|
var cliPackageVersion = {
|
|
1454
1489
|
name: "@clarify-labs/cli",
|
|
1455
|
-
version: "0.10.
|
|
1490
|
+
version: "0.10.1",
|
|
1456
1491
|
license: "AGPL-3.0-only",
|
|
1457
1492
|
type: "module",
|
|
1458
1493
|
repository: {
|
|
@@ -1558,11 +1593,14 @@ function routeToOpenAPIArtifactUrl(routePath) {
|
|
|
1558
1593
|
return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.openapi.json`;
|
|
1559
1594
|
}
|
|
1560
1595
|
function attachContentArtifactUrls(routes) {
|
|
1561
|
-
for (const route of routes) route.
|
|
1596
|
+
for (const route of routes) route.artifacts = {
|
|
1597
|
+
...route.artifacts,
|
|
1598
|
+
contentArtifactUrl: route.kind === "openapi" ? routeToOpenAPIArtifactUrl(route.path) : routeToMarkdownArtifactUrl(route.path)
|
|
1599
|
+
};
|
|
1562
1600
|
}
|
|
1563
1601
|
function readRouteContent(route) {
|
|
1564
|
-
if (route.content !== void 0) return route.content;
|
|
1565
|
-
throw new Error(`Route content is missing from route context: ${route.filePath}`);
|
|
1602
|
+
if (route.source.content !== void 0) return route.source.content;
|
|
1603
|
+
throw new Error(`Route content is missing from route context: ${route.source.filePath}`);
|
|
1566
1604
|
}
|
|
1567
1605
|
function readRouteArtifactContent(route) {
|
|
1568
1606
|
const content = readRouteContent(route);
|
|
@@ -1572,16 +1610,17 @@ function isLlmsTxtRoute(route) {
|
|
|
1572
1610
|
return !route.path.split("/").includes("404");
|
|
1573
1611
|
}
|
|
1574
1612
|
function llmsTxtDescription(route) {
|
|
1575
|
-
if (route.description) return route.description;
|
|
1576
|
-
const sections = route.sections?.filter((section) => section.level === 2).slice(0, 3).map((section) => section.title);
|
|
1613
|
+
if (route.meta.description) return route.meta.description;
|
|
1614
|
+
const sections = route.meta.sections?.filter((section) => section.level === 2).slice(0, 3).map((section) => section.title);
|
|
1577
1615
|
if (sections?.length) return `Covers ${sections.join(", ")}.`;
|
|
1578
|
-
if (route.keywords?.length) return `Related topics: ${route.keywords.join(", ")}.`;
|
|
1616
|
+
if (route.meta.keywords?.length) return `Related topics: ${route.meta.keywords.join(", ")}.`;
|
|
1579
1617
|
if (route.kind === "openapi") return "OpenAPI artifact for machine-readable API reference data.";
|
|
1580
1618
|
}
|
|
1581
1619
|
function llmsTxtListItem(route, basePath) {
|
|
1582
|
-
|
|
1620
|
+
const contentArtifactUrl = route.artifacts?.contentArtifactUrl;
|
|
1621
|
+
if (!contentArtifactUrl) return void 0;
|
|
1583
1622
|
const description = llmsTxtDescription(route);
|
|
1584
|
-
return description ? `- [${route.title}](${basePath}${
|
|
1623
|
+
return description ? `- [${route.meta.title}](${basePath}${contentArtifactUrl}): ${description}` : `- [${route.meta.title}](${basePath}${contentArtifactUrl})`;
|
|
1585
1624
|
}
|
|
1586
1625
|
function llmsTxtLocaleLabel(locale, projectConfig) {
|
|
1587
1626
|
return projectConfig.i18n?.locales.find((item) => item.code === locale)?.label ?? locale;
|
|
@@ -1598,7 +1637,7 @@ function groupLlmsTxtRoutesByLocale(routes, projectConfig) {
|
|
|
1598
1637
|
const routesBySource = /* @__PURE__ */ new Map();
|
|
1599
1638
|
const defaultLocale = projectConfig.i18n?.defaultLocale;
|
|
1600
1639
|
for (const route of routes) {
|
|
1601
|
-
const sourceKey = `${route.locale ?? defaultLocale ?? "default"}:${route.basePath ?? route.contentArtifactUrl ?? route.path}`;
|
|
1640
|
+
const sourceKey = `${route.locale ?? defaultLocale ?? "default"}:${route.basePath ?? route.artifacts?.contentArtifactUrl ?? route.path}`;
|
|
1602
1641
|
const previousRoute = routesBySource.get(sourceKey);
|
|
1603
1642
|
if (!previousRoute || previousRoute.locale && !route.locale) routesBySource.set(sourceKey, route);
|
|
1604
1643
|
}
|
|
@@ -1667,9 +1706,9 @@ function serveContentArtifacts(req, res, projectConfig, routes) {
|
|
|
1667
1706
|
}
|
|
1668
1707
|
if (contentPath.endsWith(".openapi.yaml")) {
|
|
1669
1708
|
const jsonPath = contentPath.replace(/\.yaml$/, ".json");
|
|
1670
|
-
const route = routes.find((
|
|
1671
|
-
if (route?.kind === "openapi" && route.content) {
|
|
1672
|
-
const spec = JSON.parse(route.content);
|
|
1709
|
+
const route = routes.find((route) => route.artifacts?.contentArtifactUrl === jsonPath);
|
|
1710
|
+
if (route?.kind === "openapi" && route.source.content) {
|
|
1711
|
+
const spec = JSON.parse(route.source.content);
|
|
1673
1712
|
res.statusCode = 200;
|
|
1674
1713
|
res.setHeader("Content-Type", "text/yaml; charset=utf-8");
|
|
1675
1714
|
res.end(stringify(spec, { lineWidth: 0 }), "utf8");
|
|
@@ -1677,7 +1716,7 @@ function serveContentArtifacts(req, res, projectConfig, routes) {
|
|
|
1677
1716
|
}
|
|
1678
1717
|
return false;
|
|
1679
1718
|
}
|
|
1680
|
-
const route = routes.find((route) => route.contentArtifactUrl === contentPath);
|
|
1719
|
+
const route = routes.find((route) => route.artifacts?.contentArtifactUrl === contentPath);
|
|
1681
1720
|
if (!route) return false;
|
|
1682
1721
|
res.statusCode = 200;
|
|
1683
1722
|
res.setHeader("Content-Type", resolveContentArtifactType(route));
|
|
@@ -1703,14 +1742,15 @@ function createContentArtifactsPlugin() {
|
|
|
1703
1742
|
"build:assets": (ctx) => {
|
|
1704
1743
|
const assets = [];
|
|
1705
1744
|
for (const route of ctx.routes) {
|
|
1706
|
-
|
|
1745
|
+
const contentArtifactUrl = route.artifacts?.contentArtifactUrl;
|
|
1746
|
+
if (!contentArtifactUrl) continue;
|
|
1707
1747
|
assets.push({
|
|
1708
|
-
fileName:
|
|
1748
|
+
fileName: contentArtifactUrl.replace(/^\//, ""),
|
|
1709
1749
|
source: readRouteArtifactContent(route)
|
|
1710
1750
|
});
|
|
1711
|
-
if (route.kind === "openapi" && route.content) {
|
|
1712
|
-
const yamlFileName =
|
|
1713
|
-
const spec = JSON.parse(route.content);
|
|
1751
|
+
if (route.kind === "openapi" && route.source.content) {
|
|
1752
|
+
const yamlFileName = contentArtifactUrl.replace(/\.json$/, ".yaml");
|
|
1753
|
+
const spec = JSON.parse(route.source.content);
|
|
1714
1754
|
assets.push({
|
|
1715
1755
|
fileName: yamlFileName.replace(/^\//, ""),
|
|
1716
1756
|
source: stringify(spec, { lineWidth: 0 })
|
|
@@ -1796,6 +1836,7 @@ var OPENAPI_HTTP_METHODS = [
|
|
|
1796
1836
|
"patch",
|
|
1797
1837
|
"trace"
|
|
1798
1838
|
];
|
|
1839
|
+
var CLARIFY_OPENAPI_SECTION_ID_EXTENSION = "x-clarify-section-id";
|
|
1799
1840
|
function errorMessage(error) {
|
|
1800
1841
|
return error instanceof Error ? error.message : String(error);
|
|
1801
1842
|
}
|
|
@@ -1822,10 +1863,10 @@ function findOpenAPIRoutes(dir, base = dir) {
|
|
|
1822
1863
|
routes.push({
|
|
1823
1864
|
path: cleanPath,
|
|
1824
1865
|
basePath: cleanPath,
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1866
|
+
kind: "openapi",
|
|
1867
|
+
meta: { title: kebabToTitle(cleanPath.split("/").pop() ?? "API") },
|
|
1868
|
+
module: { virtualModuleId: virtualModuleIdFromRef(ref) },
|
|
1869
|
+
source: { filePath: fullPath }
|
|
1829
1870
|
});
|
|
1830
1871
|
}
|
|
1831
1872
|
return routes;
|
|
@@ -1834,36 +1875,80 @@ function operationMatchesTags(operationTags, filterTags) {
|
|
|
1834
1875
|
if (!filterTags?.length) return true;
|
|
1835
1876
|
return operationTags?.some((tag) => filterTags.includes(tag)) ?? false;
|
|
1836
1877
|
}
|
|
1878
|
+
function filterPathItemsByTags(items, tags) {
|
|
1879
|
+
const filteredItems = {};
|
|
1880
|
+
for (const path of Object.keys(items)) {
|
|
1881
|
+
const pathItem = items[path];
|
|
1882
|
+
if (!pathItem) continue;
|
|
1883
|
+
if (OPENAPI_HTTP_METHODS.map((method) => pathItem[method]).some((op) => op && typeof op === "object" && operationMatchesTags(op.tags, tags))) filteredItems[path] = pathItem;
|
|
1884
|
+
}
|
|
1885
|
+
return filteredItems;
|
|
1886
|
+
}
|
|
1887
|
+
function createOperationSectionId(operation, method, path, kind) {
|
|
1888
|
+
return (typeof operation.operationId === "string" ? operation.operationId.trim() : "") || slug(`${kind === "webhook" ? "webhook " : ""}${method.toLowerCase()} ${path}`);
|
|
1889
|
+
}
|
|
1890
|
+
function operationSectionId(operation) {
|
|
1891
|
+
const sectionId = operation[CLARIFY_OPENAPI_SECTION_ID_EXTENSION];
|
|
1892
|
+
return typeof sectionId === "string" ? sectionId : "";
|
|
1893
|
+
}
|
|
1894
|
+
function normalizeOpenAPISpecSectionIds(spec) {
|
|
1895
|
+
const items = [...Object.entries(spec.paths ?? {}).map(([path, pathItem]) => ({
|
|
1896
|
+
path,
|
|
1897
|
+
pathItem,
|
|
1898
|
+
kind: "path"
|
|
1899
|
+
})), ...Object.entries(spec.webhooks ?? {}).map(([path, pathItem]) => ({
|
|
1900
|
+
path,
|
|
1901
|
+
pathItem,
|
|
1902
|
+
kind: "webhook"
|
|
1903
|
+
}))];
|
|
1904
|
+
for (const { path, pathItem, kind } of items) {
|
|
1905
|
+
if (!pathItem || typeof pathItem !== "object") continue;
|
|
1906
|
+
const operations = pathItem;
|
|
1907
|
+
for (const method of OPENAPI_HTTP_METHODS) {
|
|
1908
|
+
const op = operations[method];
|
|
1909
|
+
if (!op || typeof op !== "object") continue;
|
|
1910
|
+
op[CLARIFY_OPENAPI_SECTION_ID_EXTENSION] = createOperationSectionId(op, method, path, kind);
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
return spec;
|
|
1914
|
+
}
|
|
1837
1915
|
/** Filter an OpenAPI spec to only include paths that have at least one operation matching the given tags. */
|
|
1838
1916
|
function filterSpecByTags(spec, tags) {
|
|
1839
|
-
const filteredPaths = {};
|
|
1840
1917
|
const paths = spec.paths ?? {};
|
|
1841
|
-
|
|
1842
|
-
const pathItem = paths[path];
|
|
1843
|
-
if (!pathItem) continue;
|
|
1844
|
-
if (Object.values(pathItem).some((op) => op && typeof op === "object" && operationMatchesTags(op.tags, tags))) filteredPaths[path] = pathItem;
|
|
1845
|
-
}
|
|
1918
|
+
const webhooks = spec.webhooks ?? {};
|
|
1846
1919
|
return {
|
|
1847
1920
|
...spec,
|
|
1848
|
-
paths:
|
|
1921
|
+
paths: filterPathItemsByTags(paths, tags),
|
|
1922
|
+
webhooks: filterPathItemsByTags(webhooks, tags)
|
|
1849
1923
|
};
|
|
1850
1924
|
}
|
|
1851
1925
|
/** Extract endpoint operations from an OpenAPI spec as page sections. */
|
|
1852
1926
|
function extractOpenAPISections(spec, filterTags) {
|
|
1927
|
+
normalizeOpenAPISpecSectionIds(spec);
|
|
1853
1928
|
const sections = [];
|
|
1854
|
-
const
|
|
1855
|
-
|
|
1929
|
+
const items = [...Object.entries(spec.paths ?? {}).map(([path, pathItem]) => ({
|
|
1930
|
+
path,
|
|
1931
|
+
pathItem,
|
|
1932
|
+
kind: "path"
|
|
1933
|
+
})), ...Object.entries(spec.webhooks ?? {}).map(([path, pathItem]) => ({
|
|
1934
|
+
path,
|
|
1935
|
+
pathItem,
|
|
1936
|
+
kind: "webhook"
|
|
1937
|
+
}))];
|
|
1938
|
+
for (const { path, pathItem, kind } of items) {
|
|
1856
1939
|
if (!pathItem) continue;
|
|
1940
|
+
const operations = pathItem;
|
|
1857
1941
|
for (const method of OPENAPI_HTTP_METHODS) {
|
|
1858
|
-
const op =
|
|
1859
|
-
|
|
1860
|
-
|
|
1942
|
+
const op = operations[method];
|
|
1943
|
+
const tags = Array.isArray(op?.tags) ? op.tags : void 0;
|
|
1944
|
+
if (!op || !operationMatchesTags(tags, filterTags)) continue;
|
|
1945
|
+
const title = typeof op.summary === "string" ? op.summary : `${method.toUpperCase()} ${path}`;
|
|
1861
1946
|
sections.push({
|
|
1862
|
-
id:
|
|
1947
|
+
id: operationSectionId(op),
|
|
1863
1948
|
title,
|
|
1864
1949
|
level: 2,
|
|
1865
|
-
badge: method.toUpperCase(),
|
|
1866
|
-
tags
|
|
1950
|
+
badge: kind === "webhook" ? "WEBHOOK" : method.toUpperCase(),
|
|
1951
|
+
tags
|
|
1867
1952
|
});
|
|
1868
1953
|
}
|
|
1869
1954
|
}
|
|
@@ -1905,12 +1990,16 @@ async function readOpenAPISpec(filePath, contentProcessor) {
|
|
|
1905
1990
|
//#endregion
|
|
1906
1991
|
//#region source/plugins/openapi/virtual-modules.ts
|
|
1907
1992
|
var openApiRegistryModuleId = VIRTUAL_OPENAPI;
|
|
1993
|
+
var openApiServerRegistryModuleId = VIRTUAL_OPENAPI_SERVER;
|
|
1908
1994
|
var OPENAPI_SPEC_PREFIX = "virtual:clarify/openapi-spec/";
|
|
1909
1995
|
function specVirtualModuleId(specKey) {
|
|
1910
1996
|
return `${OPENAPI_SPEC_PREFIX}${specKey}`;
|
|
1911
1997
|
}
|
|
1912
|
-
function generateOpenAPIRegistryModule(
|
|
1913
|
-
return `export const
|
|
1998
|
+
function generateOpenAPIRegistryModule(openApiSpecs) {
|
|
1999
|
+
return `export const openApiSpecs = {\n${Object.entries(openApiSpecs).map(([key, moduleId]) => ` ${JSON.stringify(key)}: () => import(${JSON.stringify(moduleId)}),`).join("\n")}\n};`;
|
|
2000
|
+
}
|
|
2001
|
+
function generateOpenAPIServerRegistryModule(openApiSpecs) {
|
|
2002
|
+
return `export const openApiSpecs = ${JSON.stringify(openApiSpecs)};`;
|
|
1914
2003
|
}
|
|
1915
2004
|
/** Generate a virtual module that exports a single spec as default. */
|
|
1916
2005
|
function generateOpenAPISpecModule(spec) {
|
|
@@ -1918,13 +2007,11 @@ function generateOpenAPISpecModule(spec) {
|
|
|
1918
2007
|
}
|
|
1919
2008
|
function generateOpenAPIPageModule(opts) {
|
|
1920
2009
|
return `import { createOpenApiRouteComponent } from '@clarify-labs/renderer';
|
|
2010
|
+
import spec from ${JSON.stringify(opts.specModuleId)};
|
|
1921
2011
|
|
|
1922
|
-
export const routeData =
|
|
1923
|
-
spec: opts.spec,
|
|
1924
|
-
tagFilter: opts.tagFilter
|
|
1925
|
-
})};
|
|
2012
|
+
export const routeData = {};
|
|
1926
2013
|
|
|
1927
|
-
export default createOpenApiRouteComponent(routeData);
|
|
2014
|
+
export default createOpenApiRouteComponent({ ...routeData, spec });
|
|
1928
2015
|
`;
|
|
1929
2016
|
}
|
|
1930
2017
|
function generateOpenAPIErrorModule(diagnostic) {
|
|
@@ -1943,115 +2030,212 @@ export default createContentDiagnosticComponent(contentDiagnostic);
|
|
|
1943
2030
|
}
|
|
1944
2031
|
//#endregion
|
|
1945
2032
|
//#region source/plugins/openapi/index.ts
|
|
1946
|
-
function
|
|
1947
|
-
const
|
|
2033
|
+
function collectOpenAPIPageIntents(config) {
|
|
2034
|
+
const intents = [];
|
|
1948
2035
|
function visitPages(pages) {
|
|
1949
2036
|
if (!pages || pages === "FileTree") return;
|
|
1950
|
-
for (const group of pages) for (const item of group.pages)
|
|
2037
|
+
for (const group of pages) for (const item of group.pages) {
|
|
2038
|
+
const intent = routeIntentFromPagesItem(item);
|
|
2039
|
+
if (intent.kind === "openapi" && (intent.path || intent.tagFilter?.length)) intents.push(intent);
|
|
2040
|
+
}
|
|
1951
2041
|
}
|
|
1952
2042
|
for (const tab of config.tabs ?? []) visitPages(tab.pages);
|
|
1953
|
-
return
|
|
2043
|
+
return intents;
|
|
1954
2044
|
}
|
|
1955
2045
|
function virtualModuleIdFromPath(path) {
|
|
1956
2046
|
return `virtual:clarify-page/${path.replace(/^\/+/, "") || "index"}`;
|
|
1957
2047
|
}
|
|
2048
|
+
function cloneOpenAPIRoute(route, overrides = {}) {
|
|
2049
|
+
return {
|
|
2050
|
+
...route,
|
|
2051
|
+
meta: {
|
|
2052
|
+
...route.meta,
|
|
2053
|
+
sections: route.meta.sections ? [...route.meta.sections] : void 0
|
|
2054
|
+
},
|
|
2055
|
+
module: { ...route.module },
|
|
2056
|
+
source: { ...route.source },
|
|
2057
|
+
openapi: route.openapi ? {
|
|
2058
|
+
...route.openapi,
|
|
2059
|
+
tagFilter: route.openapi.tagFilter ? [...route.openapi.tagFilter] : void 0
|
|
2060
|
+
} : void 0,
|
|
2061
|
+
alternates: route.alternates ? { ...route.alternates } : void 0,
|
|
2062
|
+
...overrides
|
|
2063
|
+
};
|
|
2064
|
+
}
|
|
2065
|
+
function findOpenAPIRoutesForContentRoot(contentRoot, i18n) {
|
|
2066
|
+
if (!i18n) return findOpenAPIRoutes(contentRoot);
|
|
2067
|
+
const localizedRoutes = [];
|
|
2068
|
+
for (const locale of i18n.locales) {
|
|
2069
|
+
const localeRoot = join(contentRoot, locale.code);
|
|
2070
|
+
for (const route of findOpenAPIRoutes(localeRoot, localeRoot)) {
|
|
2071
|
+
const basePath = route.basePath ?? route.path;
|
|
2072
|
+
const path = localizedRoutePath(basePath, locale.code, i18n);
|
|
2073
|
+
localizedRoutes.push({
|
|
2074
|
+
...route,
|
|
2075
|
+
path,
|
|
2076
|
+
basePath,
|
|
2077
|
+
locale: locale.code,
|
|
2078
|
+
module: { virtualModuleId: virtualModuleIdFromPath(path) }
|
|
2079
|
+
});
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
if (i18n.missing === "fallback") {
|
|
2083
|
+
const routeByLocaleAndBase = new Map(localizedRoutes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
|
|
2084
|
+
const defaultRoutes = localizedRoutes.filter((route) => route.locale === i18n.defaultLocale);
|
|
2085
|
+
for (const sourceRoute of defaultRoutes) {
|
|
2086
|
+
const basePath = sourceRoute.basePath ?? sourceRoute.path;
|
|
2087
|
+
for (const locale of i18n.locales) {
|
|
2088
|
+
const key = `${locale.code}:${basePath}`;
|
|
2089
|
+
if (routeByLocaleAndBase.has(key)) continue;
|
|
2090
|
+
const path = localizedRoutePath(basePath, locale.code, i18n);
|
|
2091
|
+
localizedRoutes.push(cloneOpenAPIRoute(sourceRoute, {
|
|
2092
|
+
path,
|
|
2093
|
+
locale: locale.code,
|
|
2094
|
+
module: { virtualModuleId: virtualModuleIdFromPath(path) },
|
|
2095
|
+
isFallback: true
|
|
2096
|
+
}));
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
return localizedRoutes;
|
|
2101
|
+
}
|
|
1958
2102
|
function createConfiguredOpenAPIRoutes(routes, config) {
|
|
1959
|
-
const
|
|
1960
|
-
if (!
|
|
2103
|
+
const openAPIIntents = collectOpenAPIPageIntents(config);
|
|
2104
|
+
if (!openAPIIntents.length) return routes;
|
|
1961
2105
|
const additions = [];
|
|
1962
2106
|
const existingKeys = new Set(routes.map((route) => `${route.locale ?? ""}:${route.basePath ?? route.path}`));
|
|
1963
|
-
for (const
|
|
1964
|
-
const tagFilter =
|
|
1965
|
-
const sourceBasePath = openAPIPagePathFromRef(
|
|
1966
|
-
const targetBasePath = openAPIPagePathFromRef(
|
|
2107
|
+
for (const intent of openAPIIntents) {
|
|
2108
|
+
const tagFilter = intent.tagFilter?.filter(Boolean);
|
|
2109
|
+
const sourceBasePath = openAPIPagePathFromRef(intent.ref);
|
|
2110
|
+
const targetBasePath = openAPIPagePathFromRef(intent.ref, tagFilter, intent.path);
|
|
1967
2111
|
if (sourceBasePath === targetBasePath && !tagFilter?.length) continue;
|
|
1968
2112
|
for (const route of routes) {
|
|
1969
2113
|
if (route.kind !== "openapi" || (route.basePath ?? route.path) !== sourceBasePath) continue;
|
|
1970
2114
|
const key = `${route.locale ?? ""}:${targetBasePath}`;
|
|
1971
2115
|
if (existingKeys.has(key)) continue;
|
|
1972
2116
|
existingKeys.add(key);
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
path
|
|
2117
|
+
const path = route.locale && config.i18n ? localizedRoutePath(targetBasePath, route.locale, config.i18n) : targetBasePath;
|
|
2118
|
+
additions.push(cloneOpenAPIRoute(route, {
|
|
2119
|
+
path,
|
|
1976
2120
|
basePath: targetBasePath,
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
2121
|
+
module: { virtualModuleId: virtualModuleIdFromPath(path) },
|
|
2122
|
+
openapi: {
|
|
2123
|
+
...route.openapi,
|
|
2124
|
+
tagFilter: tagFilter?.length ? tagFilter : void 0
|
|
2125
|
+
}
|
|
2126
|
+
}));
|
|
1981
2127
|
}
|
|
1982
2128
|
}
|
|
1983
2129
|
const nextRoutes = [...routes, ...additions];
|
|
1984
|
-
|
|
2130
|
+
if (!config.i18n) return nextRoutes;
|
|
2131
|
+
const routesWithAlternates = nextRoutes.map((route) => withAlternates(route, nextRoutes, config.i18n));
|
|
2132
|
+
const bareAliases = [];
|
|
2133
|
+
const seenBare = new Set(routesWithAlternates.map((route) => route.path));
|
|
2134
|
+
for (const route of routesWithAlternates) {
|
|
2135
|
+
if (route.locale !== config.i18n.defaultLocale) continue;
|
|
2136
|
+
const basePath = route.basePath ?? route.path;
|
|
2137
|
+
if (basePath === route.path || seenBare.has(basePath)) continue;
|
|
2138
|
+
seenBare.add(basePath);
|
|
2139
|
+
bareAliases.push(cloneOpenAPIRoute(route, {
|
|
2140
|
+
path: basePath,
|
|
2141
|
+
module: { virtualModuleId: virtualModuleIdFromPath(basePath) },
|
|
2142
|
+
isBareAlias: true
|
|
2143
|
+
}));
|
|
2144
|
+
}
|
|
2145
|
+
return [...routesWithAlternates, ...bareAliases];
|
|
1985
2146
|
}
|
|
1986
|
-
/** Derive a stable, deduplicated
|
|
1987
|
-
function
|
|
2147
|
+
/** Derive a stable, deduplicated id from an absolute spec file path. */
|
|
2148
|
+
function sourceSpecIdFromPath(filePath, projectRoot) {
|
|
1988
2149
|
return relative(projectRoot, filePath).replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
|
|
1989
2150
|
}
|
|
2151
|
+
function routeSpecModuleIdFromSourceSpecId(sourceSpecId, tagFilter) {
|
|
2152
|
+
if (!tagFilter?.length) return sourceSpecId;
|
|
2153
|
+
return `${sourceSpecId}_tags_${tagFilter.map((tag) => tag.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "") || "tag").join("_")}`;
|
|
2154
|
+
}
|
|
2155
|
+
function openAPIRegistryKeys(route) {
|
|
2156
|
+
const keys = /* @__PURE__ */ new Set();
|
|
2157
|
+
const path = (route.path || "/").replace(/^\//, "");
|
|
2158
|
+
const base = (route.basePath ?? route.path).replace(/^\//, "");
|
|
2159
|
+
if (route.locale) keys.add(`virtual:clarify-page/${route.locale}/${base}`);
|
|
2160
|
+
if (path) keys.add(`virtual:clarify-page/${path}`);
|
|
2161
|
+
else keys.add("virtual:clarify-page/index");
|
|
2162
|
+
if (!route.locale && base) keys.add(`virtual:clarify-page/${base}`);
|
|
2163
|
+
return [...keys];
|
|
2164
|
+
}
|
|
1990
2165
|
function createOpenAPIPlugin() {
|
|
1991
|
-
/** specs keyed by
|
|
2166
|
+
/** specs keyed by sourceSpecId (deduplicated per source file). */
|
|
1992
2167
|
const specs = /* @__PURE__ */ new Map();
|
|
2168
|
+
const specModules = /* @__PURE__ */ new Map();
|
|
1993
2169
|
return {
|
|
1994
2170
|
name: "clarify:openapi",
|
|
1995
2171
|
hooks: {
|
|
1996
|
-
"routes:discover": (input) => ({
|
|
2172
|
+
"routes:discover": (input, ctx) => ({
|
|
1997
2173
|
...input,
|
|
1998
|
-
routes: [...input.routes, ...
|
|
2174
|
+
routes: [...input.routes, ...findOpenAPIRoutesForContentRoot(input.contentRoot, ctx.projectConfig.i18n)]
|
|
1999
2175
|
}),
|
|
2000
2176
|
"routes:discovered": async (routes, ctx) => {
|
|
2001
2177
|
specs.clear();
|
|
2178
|
+
specModules.clear();
|
|
2002
2179
|
const nextRoutes = createConfiguredOpenAPIRoutes(routes, ctx.projectConfig);
|
|
2003
2180
|
const specByFilePath = /* @__PURE__ */ new Map();
|
|
2004
2181
|
for (const route of nextRoutes.filter((route) => route.kind === "openapi")) {
|
|
2005
|
-
const specFromCache = specByFilePath.get(route.filePath);
|
|
2182
|
+
const specFromCache = specByFilePath.get(route.source.filePath);
|
|
2006
2183
|
const processor = createProjectContentProcessor(ctx.plugins, ctx);
|
|
2007
2184
|
const result = specFromCache ? {
|
|
2008
2185
|
ok: true,
|
|
2009
2186
|
spec: specFromCache
|
|
2010
|
-
} : await readOpenAPISpec(route.filePath, processor);
|
|
2187
|
+
} : await readOpenAPISpec(route.source.filePath, processor);
|
|
2011
2188
|
if (!result.ok) {
|
|
2012
2189
|
route.diagnostic = result.diagnostic;
|
|
2013
|
-
route.title = route.title || "OpenAPI parse error";
|
|
2014
|
-
route.sections = [];
|
|
2190
|
+
route.meta.title = route.meta.title || "OpenAPI parse error";
|
|
2191
|
+
route.meta.sections = [];
|
|
2015
2192
|
continue;
|
|
2016
2193
|
}
|
|
2017
|
-
const spec = result.spec;
|
|
2018
|
-
specByFilePath.set(route.filePath, spec);
|
|
2019
|
-
const
|
|
2020
|
-
route.
|
|
2021
|
-
|
|
2194
|
+
const spec = normalizeOpenAPISpecSectionIds(result.spec);
|
|
2195
|
+
specByFilePath.set(route.source.filePath, spec);
|
|
2196
|
+
const sourceSpecId = sourceSpecIdFromPath(route.source.filePath, ctx.generateOptions.projectRoot);
|
|
2197
|
+
route.openapi = {
|
|
2198
|
+
...route.openapi,
|
|
2199
|
+
sourceSpecId
|
|
2200
|
+
};
|
|
2201
|
+
specs.set(sourceSpecId, {
|
|
2022
2202
|
spec,
|
|
2023
|
-
filePath: route.filePath
|
|
2203
|
+
filePath: route.source.filePath
|
|
2024
2204
|
});
|
|
2025
|
-
route.title = spec.info?.title ?? route.title;
|
|
2026
|
-
route.description = spec.info?.description ?? route.description;
|
|
2027
|
-
route.sections = extractOpenAPISections(spec, route.
|
|
2028
|
-
const pageSpec = route.
|
|
2029
|
-
|
|
2205
|
+
route.meta.title = spec.info?.title ?? route.meta.title;
|
|
2206
|
+
route.meta.description = spec.info?.description ?? route.meta.description;
|
|
2207
|
+
route.meta.sections = extractOpenAPISections(spec, route.openapi?.tagFilter);
|
|
2208
|
+
const pageSpec = route.openapi?.tagFilter?.length ? filterSpecByTags(spec, route.openapi.tagFilter) : spec;
|
|
2209
|
+
const routeSpecModuleId = routeSpecModuleIdFromSourceSpecId(sourceSpecId, route.openapi?.tagFilter);
|
|
2210
|
+
route.openapi.routeSpecModuleId = routeSpecModuleId;
|
|
2211
|
+
specModules.set(routeSpecModuleId, pageSpec);
|
|
2212
|
+
route.source.content = JSON.stringify(pageSpec);
|
|
2030
2213
|
}
|
|
2031
2214
|
return nextRoutes;
|
|
2032
2215
|
},
|
|
2033
2216
|
"modules:before": (modules, ctx) => {
|
|
2034
2217
|
const registryEntries = {};
|
|
2218
|
+
const serverRegistryEntries = {};
|
|
2035
2219
|
for (const route of ctx.routes) {
|
|
2036
|
-
if (route.kind !== "openapi" || !route.
|
|
2037
|
-
const
|
|
2220
|
+
if (route.kind !== "openapi" || !route.openapi?.sourceSpecId) continue;
|
|
2221
|
+
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2222
|
+
const spec = specModules.get(routeSpecModuleId) ?? specs.get(route.openapi.sourceSpecId)?.spec;
|
|
2038
2223
|
if (!spec) continue;
|
|
2039
|
-
const
|
|
2040
|
-
|
|
2041
|
-
|
|
2224
|
+
for (const registryKey of openAPIRegistryKeys(route)) {
|
|
2225
|
+
registryEntries[registryKey] = specVirtualModuleId(routeSpecModuleId);
|
|
2226
|
+
serverRegistryEntries[registryKey] = spec;
|
|
2227
|
+
}
|
|
2042
2228
|
}
|
|
2043
2229
|
modules.set(openApiRegistryModuleId, generateOpenAPIRegistryModule(registryEntries));
|
|
2044
|
-
|
|
2230
|
+
modules.set(openApiServerRegistryModuleId, generateOpenAPIServerRegistryModule(serverRegistryEntries));
|
|
2231
|
+
for (const [routeSpecModuleId, spec] of specModules) modules.set(specVirtualModuleId(routeSpecModuleId), generateOpenAPISpecModule(spec));
|
|
2045
2232
|
for (const route of ctx.routes) {
|
|
2046
2233
|
if (route.kind !== "openapi") continue;
|
|
2047
|
-
if (route.diagnostic) modules.set(route.virtualModuleId, generateOpenAPIErrorModule(route.diagnostic));
|
|
2048
|
-
else if (route.
|
|
2049
|
-
const
|
|
2050
|
-
if (!
|
|
2051
|
-
modules.set(route.virtualModuleId, generateOpenAPIPageModule({
|
|
2052
|
-
spec: entry.spec,
|
|
2053
|
-
tagFilter: route.openapiTagFilter
|
|
2054
|
-
}));
|
|
2234
|
+
if (route.diagnostic) modules.set(route.module.virtualModuleId, generateOpenAPIErrorModule(route.diagnostic));
|
|
2235
|
+
else if (route.openapi?.sourceSpecId) {
|
|
2236
|
+
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2237
|
+
if (!specModules.has(routeSpecModuleId)) continue;
|
|
2238
|
+
modules.set(route.module.virtualModuleId, generateOpenAPIPageModule({ specModuleId: specVirtualModuleId(routeSpecModuleId) }));
|
|
2055
2239
|
}
|
|
2056
2240
|
}
|
|
2057
2241
|
return modules;
|
|
@@ -2119,11 +2303,11 @@ async function generatePagefindIndex(options, pagefind) {
|
|
|
2119
2303
|
}
|
|
2120
2304
|
function routeSearchContent(route) {
|
|
2121
2305
|
return [
|
|
2122
|
-
route.title,
|
|
2123
|
-
route.description,
|
|
2124
|
-
route.keywords?.join(" "),
|
|
2125
|
-
route.sections?.map((section) => section.title).join(" "),
|
|
2126
|
-
route.content
|
|
2306
|
+
route.meta.title,
|
|
2307
|
+
route.meta.description,
|
|
2308
|
+
route.meta.keywords?.join(" "),
|
|
2309
|
+
route.meta.sections?.map((section) => section.title).join(" "),
|
|
2310
|
+
route.source.content
|
|
2127
2311
|
].filter(Boolean).join("\n\n");
|
|
2128
2312
|
}
|
|
2129
2313
|
async function generateDevSearchIndex(ctx, root, pagefind) {
|
|
@@ -2145,7 +2329,7 @@ async function generateDevSearchIndex(ctx, root, pagefind) {
|
|
|
2145
2329
|
url: route.path,
|
|
2146
2330
|
content,
|
|
2147
2331
|
language: toPagefindLanguage(route.locale ?? ctx.projectConfig.i18n?.defaultLocale),
|
|
2148
|
-
meta: { title: route.title }
|
|
2332
|
+
meta: { title: route.meta.title }
|
|
2149
2333
|
});
|
|
2150
2334
|
assertNoPagefindErrors(`addCustomRecord ${route.path}`, result.errors);
|
|
2151
2335
|
pageCount += 1;
|
|
@@ -2302,15 +2486,18 @@ function normalizeRepositoryUrl(repository) {
|
|
|
2302
2486
|
function encodePath(path) {
|
|
2303
2487
|
return path.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
|
|
2304
2488
|
}
|
|
2305
|
-
function
|
|
2489
|
+
function createSourceEditUrl(filePath, contentRoot, source) {
|
|
2306
2490
|
const repository = normalizeRepositoryUrl(source.repository);
|
|
2307
2491
|
const branch = source.branch ?? "main";
|
|
2308
2492
|
const sourcePath = [source.directory ? trimSlashes(source.directory) : "", relative(contentRoot, filePath).replaceAll("\\", "/")].filter(Boolean).join("/");
|
|
2309
2493
|
return `${repository}/edit/${encodeURIComponent(branch)}/${encodePath(sourcePath)}`;
|
|
2310
2494
|
}
|
|
2311
|
-
function
|
|
2495
|
+
function attachSourceEditUrls(routes, contentRoot, source) {
|
|
2312
2496
|
if (!source) return;
|
|
2313
|
-
for (const route of routes) route.
|
|
2497
|
+
for (const route of routes) route.source = {
|
|
2498
|
+
...route.source,
|
|
2499
|
+
sourceEditUrl: createSourceEditUrl(route.source.filePath, contentRoot, source)
|
|
2500
|
+
};
|
|
2314
2501
|
}
|
|
2315
2502
|
//#endregion
|
|
2316
2503
|
//#region source/plugins/source-links/index.ts
|
|
@@ -2318,7 +2505,7 @@ function createSourceLinksPlugin() {
|
|
|
2318
2505
|
return {
|
|
2319
2506
|
name: "clarify:source-links",
|
|
2320
2507
|
hooks: { "routes:discovered": (routes, ctx) => {
|
|
2321
|
-
|
|
2508
|
+
attachSourceEditUrls(routes, join(ctx.generateOptions.projectRoot, ctx.generateOptions.rootDirectory), ctx.projectConfig.source);
|
|
2322
2509
|
return routes;
|
|
2323
2510
|
} }
|
|
2324
2511
|
};
|
|
@@ -2475,9 +2662,9 @@ var CLARIFY_ENV_TEMPLATE = `// This file is generated by Clarify. Do not edit it
|
|
|
2475
2662
|
// plugin and slot code, so the TypeScript language service can resolve them.
|
|
2476
2663
|
|
|
2477
2664
|
declare module 'virtual:clarify/slot' {
|
|
2478
|
-
import type {
|
|
2665
|
+
import type { UISlotContext } from '@clarify-labs/cli'
|
|
2479
2666
|
|
|
2480
|
-
export function useSlot():
|
|
2667
|
+
export function useSlot(): UISlotContext
|
|
2481
2668
|
}
|
|
2482
2669
|
`;
|
|
2483
2670
|
function generateClarifyEnvDts(_plugins) {
|
|
@@ -2515,7 +2702,7 @@ function resolveProjectConfigFromContext(contextOrConfig) {
|
|
|
2515
2702
|
}
|
|
2516
2703
|
function routeTitle(contextOrConfig, route) {
|
|
2517
2704
|
const projectConfig = resolveProjectConfigFromContext(contextOrConfig);
|
|
2518
|
-
const title = route?.title
|
|
2705
|
+
const title = route?.meta.title.trim();
|
|
2519
2706
|
if (!title || title === projectConfig.title) return projectConfig.title;
|
|
2520
2707
|
return `${title} - ${projectConfig.title}`;
|
|
2521
2708
|
}
|
|
@@ -2563,8 +2750,8 @@ function injectSSRIntoTemplate(template, appHtml, contextOrConfig, route) {
|
|
|
2563
2750
|
const projectConfig = resolveProjectConfigFromContext(contextOrConfig);
|
|
2564
2751
|
let html = injectHtmlLocaleAttributes(template, contextOrConfig, route);
|
|
2565
2752
|
html = html.replace(/<title>.*?<\/title>/, `<title>${escapeHtml(routeTitle(contextOrConfig, route))}</title>`);
|
|
2566
|
-
html = setNamedMeta(html, "description", route?.description ?? projectConfig.description);
|
|
2567
|
-
html = setNamedMeta(html, "keywords", route?.keywords?.join(", "));
|
|
2753
|
+
html = setNamedMeta(html, "description", route?.meta.description ?? projectConfig.description);
|
|
2754
|
+
html = setNamedMeta(html, "keywords", route?.meta.keywords?.join(", "));
|
|
2568
2755
|
html = injectCanonicalUrl(html, contextOrConfig, route);
|
|
2569
2756
|
const dataPagefindIgnore = route?.isBareAlias ? " data-pagefind-ignore" : "";
|
|
2570
2757
|
html = html.replace(/<div id="root">([\s\S]*?)<\/div>/, `<div id="root"${dataPagefindIgnore}>${appHtml}</div>`);
|
|
@@ -2673,7 +2860,7 @@ function routeOutputFiles(outputDirectory, route) {
|
|
|
2673
2860
|
var SSR_ENTRY_CODE = `import { renderToHTML } from '@clarify-labs/renderer/server';
|
|
2674
2861
|
import { routes, navigation } from 'virtual:clarify/routes/server';
|
|
2675
2862
|
import { config } from 'virtual:clarify/config';
|
|
2676
|
-
import {
|
|
2863
|
+
import { openApiSpecs } from 'virtual:clarify/openapi/server';
|
|
2677
2864
|
import { runtimeSlots } from 'virtual:clarify/slots';
|
|
2678
2865
|
|
|
2679
2866
|
export async function render(url) {
|
|
@@ -2681,10 +2868,10 @@ export async function render(url) {
|
|
|
2681
2868
|
// them synchronously (renderToString has no Suspense support).
|
|
2682
2869
|
const entries = Object.values(runtimeSlots).flat()
|
|
2683
2870
|
await Promise.all(entries.map(async (entry) => {
|
|
2684
|
-
const mod = await entry.
|
|
2871
|
+
const mod = await entry.loadComponent()
|
|
2685
2872
|
entry._resolved = mod.default
|
|
2686
2873
|
}))
|
|
2687
|
-
return renderToHTML({ config, routes, navigation,
|
|
2874
|
+
return renderToHTML({ config, routes, navigation, openApiSpecs, runtimeSlots, url, themeEditor: config.theme.editor });
|
|
2688
2875
|
}`;
|
|
2689
2876
|
function createTempEntryFile(content) {
|
|
2690
2877
|
const entryPath = join(createClarifyTempDir("ssr"), "entry-server.ts");
|
|
@@ -2771,7 +2958,10 @@ var ClarifyContext = class {
|
|
|
2771
2958
|
this.generateOptions = init.generateOptions;
|
|
2772
2959
|
this.version = init.version;
|
|
2773
2960
|
this.currentRoutes = init.routes ?? [];
|
|
2774
|
-
this.currentNavigation = init.navigation ??
|
|
2961
|
+
this.currentNavigation = init.navigation ?? {
|
|
2962
|
+
kind: "flat",
|
|
2963
|
+
nodes: []
|
|
2964
|
+
};
|
|
2775
2965
|
this.currentPlugins = init.plugins ?? [];
|
|
2776
2966
|
}
|
|
2777
2967
|
get routes() {
|
|
@@ -2893,7 +3083,10 @@ var ClarifyEngine = class {
|
|
|
2893
3083
|
generateOptions: placeholderOptions,
|
|
2894
3084
|
version: cliPackageVersion,
|
|
2895
3085
|
routes: [],
|
|
2896
|
-
navigation:
|
|
3086
|
+
navigation: {
|
|
3087
|
+
kind: "flat",
|
|
3088
|
+
nodes: []
|
|
3089
|
+
},
|
|
2897
3090
|
plugins: []
|
|
2898
3091
|
});
|
|
2899
3092
|
}
|
|
@@ -2996,9 +3189,9 @@ var ClarifyEngine = class {
|
|
|
2996
3189
|
routes = await runPhase(plugins, "content:process", this.ctx, async () => {
|
|
2997
3190
|
const pages = routes.map((route) => ({
|
|
2998
3191
|
path: route.path,
|
|
2999
|
-
filePath: route.filePath,
|
|
3000
|
-
frontmatter: route.frontmatter ?? {},
|
|
3001
|
-
content: route.content ?? ""
|
|
3192
|
+
filePath: route.source.filePath,
|
|
3193
|
+
frontmatter: route.source.frontmatter ?? {},
|
|
3194
|
+
content: route.source.content ?? ""
|
|
3002
3195
|
}));
|
|
3003
3196
|
const resolvedPages = await runHooks(plugins, "pages:resolved", pages, this.ctx);
|
|
3004
3197
|
const pageByPath = new Map(resolvedPages.map((p) => [p.path, p]));
|
|
@@ -3007,13 +3200,22 @@ var ClarifyEngine = class {
|
|
|
3007
3200
|
if (!page) return route;
|
|
3008
3201
|
return {
|
|
3009
3202
|
...route,
|
|
3010
|
-
|
|
3011
|
-
|
|
3203
|
+
source: {
|
|
3204
|
+
...route.source,
|
|
3205
|
+
frontmatter: page.frontmatter,
|
|
3206
|
+
content: page.content
|
|
3207
|
+
}
|
|
3012
3208
|
};
|
|
3013
3209
|
});
|
|
3014
3210
|
});
|
|
3015
3211
|
routes = applyConfiguredPageRoutePaths(routes, tabs, i18n);
|
|
3016
|
-
const navigation = tabs ? i18n ? buildLocalizedNavigationFromTabsConfig(routes, tabs, i18n) ?? {
|
|
3212
|
+
const navigation = tabs ? i18n ? buildLocalizedNavigationFromTabsConfig(routes, tabs, i18n) ?? {
|
|
3213
|
+
kind: "localized-tabbed",
|
|
3214
|
+
locales: {}
|
|
3215
|
+
} : buildNavigationFromTabsConfig(routes, tabs) : {
|
|
3216
|
+
kind: "flat",
|
|
3217
|
+
nodes: buildNavigation(routes)
|
|
3218
|
+
};
|
|
3017
3219
|
const resolved = await runHooks(plugins, "routes:resolved", {
|
|
3018
3220
|
routes,
|
|
3019
3221
|
navigation
|
|
@@ -3047,7 +3249,7 @@ var ClarifyEngine = class {
|
|
|
3047
3249
|
writeClarifyEnvDts(this.root, this.plugins);
|
|
3048
3250
|
}
|
|
3049
3251
|
hasContentRouteForFile(filePath) {
|
|
3050
|
-
return this.routes.some((route) => route.filePath === filePath);
|
|
3252
|
+
return this.routes.some((route) => route.source.filePath === filePath);
|
|
3051
3253
|
}
|
|
3052
3254
|
async collectBuildAssets() {
|
|
3053
3255
|
if (!this.buildEnabled) return [];
|
|
@@ -3140,4 +3342,4 @@ function createClarifyEngine(options = {}) {
|
|
|
3140
3342
|
//#endregion
|
|
3141
3343
|
export { cliPackageVersion as a, defineConfig as c, createClarifyTempDir as i, createClarifyEngine as n, cliPackageVersionWithCaret as o, createClarifyRuntimeAliases as r, createViteAdapter as s, ClarifyEngine as t };
|
|
3142
3344
|
|
|
3143
|
-
//# sourceMappingURL=engine-
|
|
3345
|
+
//# sourceMappingURL=engine-Ca_2MCj9.js.map
|