@clarify-labs/cli 0.11.4 → 0.11.6
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 +9 -17
- package/output/cli.cjs.map +1 -1
- package/output/cli.js +9 -17
- package/output/cli.js.map +1 -1
- package/output/core/adapters.d.ts +1 -1
- package/output/core/engine/engine.d.ts +2 -1
- package/output/core/plugin/hooks.d.ts +2 -2
- package/output/core/routing/route-analysis.d.ts +11 -0
- package/output/core/routing/route-resolution.d.ts +8 -0
- package/output/core/runtime/module-ids.d.ts +5 -0
- package/output/core/runtime/virtual-modules.d.ts +2 -1
- package/output/{engine-CJmXVTo5.js → engine-Cuwx3aJ2.js} +378 -220
- package/output/engine-Cuwx3aJ2.js.map +1 -0
- package/output/{engine-Cgc71w13.cjs → engine-DVw8CV5R.cjs} +383 -219
- package/output/engine-DVw8CV5R.cjs.map +1 -0
- package/output/index.cjs +1 -1
- package/output/index.js +1 -1
- package/output/parsers/content/content.d.ts +1 -1
- package/output/parsers/markdown/mdx.d.ts +1 -1
- package/output/parsers/routes/routes.d.ts +4 -5
- package/output/parsers/routes/routes.test-utils.d.ts +5 -3
- package/output/plugins/openapi/parser.d.ts +2 -2
- package/output/plugins/openapi/routes.d.ts +3 -0
- package/output/plugins/openapi/state.d.ts +7 -0
- package/output/types.d.ts +24 -9
- package/package.json +3 -3
- package/output/engine-CJmXVTo5.js.map +0 -1
- package/output/engine-Cgc71w13.cjs.map +0 -1
|
@@ -112,7 +112,7 @@ var rehypePlugins = [
|
|
|
112
112
|
];
|
|
113
113
|
function createContentCompileDiagnostic(options) {
|
|
114
114
|
const { format, phase, error, filePath, projectRoot } = options;
|
|
115
|
-
const label = format === "
|
|
115
|
+
const label = format === "markdown+jsx" ? "MDX" : "Markdown";
|
|
116
116
|
return createContentDiagnostic({
|
|
117
117
|
kind: format,
|
|
118
118
|
title: `${label} ${phase} error`,
|
|
@@ -158,7 +158,7 @@ async function compileMdxContent(content, options = {}) {
|
|
|
158
158
|
return {
|
|
159
159
|
ok: false,
|
|
160
160
|
diagnostic: createContentCompileDiagnostic({
|
|
161
|
-
format: "
|
|
161
|
+
format: "markdown+jsx",
|
|
162
162
|
phase: "syntax",
|
|
163
163
|
error,
|
|
164
164
|
filePath,
|
|
@@ -168,6 +168,21 @@ async function compileMdxContent(content, options = {}) {
|
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
//#endregion
|
|
171
|
+
//#region source/core/runtime/module-ids.ts
|
|
172
|
+
var PAGE_PREFIX = "virtual:clarify-page/";
|
|
173
|
+
var CONTENT_PREFIX = "virtual:clarify-content/";
|
|
174
|
+
function pageVirtualModuleId(path) {
|
|
175
|
+
return `${PAGE_PREFIX}${path.replace(/^\/+/, "").replace(/\/index$/, "").replace(/\/+/g, "/") || "index"}`;
|
|
176
|
+
}
|
|
177
|
+
function pageVirtualModuleIdFromRef(ref) {
|
|
178
|
+
return pageVirtualModuleId(ref.replace(/\.mdx?$/, "").replace(/\.openapi\.(json|yaml|yml)$/, ""));
|
|
179
|
+
}
|
|
180
|
+
function contentVirtualModuleId(filePath, contentRoot) {
|
|
181
|
+
const sourcePath = (0, node_path.relative)(contentRoot, filePath).replace(/\\/g, "/");
|
|
182
|
+
if (!sourcePath || sourcePath.startsWith("../") || (0, node_path.isAbsolute)(sourcePath)) throw new Error(`Content source must be inside the content root: ${filePath}`);
|
|
183
|
+
return `${CONTENT_PREFIX}${sourcePath}`;
|
|
184
|
+
}
|
|
185
|
+
//#endregion
|
|
171
186
|
//#region source/core/plugin/hooks.ts
|
|
172
187
|
async function runHooks(plugins, hookName, input, ctx) {
|
|
173
188
|
let result = input;
|
|
@@ -183,32 +198,43 @@ async function runHooks(plugins, hookName, input, ctx) {
|
|
|
183
198
|
return result;
|
|
184
199
|
}
|
|
185
200
|
async function runDevConfigureServerHooks(plugins, server, ctx) {
|
|
201
|
+
const postHooks = [];
|
|
186
202
|
for (const plugin of plugins) {
|
|
187
203
|
const hook = plugin.hooks?.["dev:configureServer"];
|
|
188
204
|
if (!hook) continue;
|
|
189
205
|
try {
|
|
190
|
-
await hook(server, ctx);
|
|
206
|
+
const postHook = await hook(server, ctx);
|
|
207
|
+
if (postHook) postHooks.push(postHook);
|
|
191
208
|
} catch (err) {
|
|
192
209
|
throw new Error(`[clarify] plugin "${plugin.name}" hook "dev:configureServer" failed: ${err}`, { cause: err });
|
|
193
210
|
}
|
|
194
211
|
}
|
|
212
|
+
return postHooks;
|
|
195
213
|
}
|
|
196
|
-
async function
|
|
197
|
-
const
|
|
214
|
+
async function runBuildAssetsHooks(plugins, ctx) {
|
|
215
|
+
const assets = [];
|
|
216
|
+
const producerByFileName = /* @__PURE__ */ new Map();
|
|
198
217
|
for (const plugin of plugins) {
|
|
199
|
-
const hook = plugin.hooks?.[
|
|
218
|
+
const hook = plugin.hooks?.["build:assets"];
|
|
200
219
|
if (!hook) continue;
|
|
220
|
+
let pluginAssets;
|
|
201
221
|
try {
|
|
202
|
-
|
|
203
|
-
results.push(...pluginResults);
|
|
222
|
+
pluginAssets = await hook(ctx);
|
|
204
223
|
} catch (err) {
|
|
205
|
-
throw new Error(`[clarify] plugin "${plugin.name}" hook "
|
|
224
|
+
throw new Error(`[clarify] plugin "${plugin.name}" hook "build:assets" failed: ${err}`, { cause: err });
|
|
225
|
+
}
|
|
226
|
+
for (const asset of pluginAssets) {
|
|
227
|
+
const fileName = asset.fileName.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
228
|
+
const previousProducer = producerByFileName.get(fileName);
|
|
229
|
+
if (previousProducer) throw new Error(`[clarify] build asset "${fileName}" is emitted by both plugin "${previousProducer}" and plugin "${plugin.name}"`);
|
|
230
|
+
producerByFileName.set(fileName, plugin.name);
|
|
231
|
+
assets.push({
|
|
232
|
+
...asset,
|
|
233
|
+
fileName
|
|
234
|
+
});
|
|
206
235
|
}
|
|
207
236
|
}
|
|
208
|
-
return
|
|
209
|
-
}
|
|
210
|
-
async function runBuildAssetsHooks(plugins, ctx) {
|
|
211
|
-
return runCollectorHooks(plugins, "build:assets", ctx);
|
|
237
|
+
return assets;
|
|
212
238
|
}
|
|
213
239
|
async function runBuildDoneHooks(plugins, ctx) {
|
|
214
240
|
for (const plugin of plugins) {
|
|
@@ -243,7 +269,7 @@ function createContentProcessor(transform) {
|
|
|
243
269
|
async processMdx(source, filePath) {
|
|
244
270
|
const { frontmatter, content } = parseFrontmatter(source);
|
|
245
271
|
const transformed = await transformContent(transform, {
|
|
246
|
-
kind: "
|
|
272
|
+
kind: "markdown+jsx",
|
|
247
273
|
source,
|
|
248
274
|
filePath,
|
|
249
275
|
frontmatter,
|
|
@@ -395,9 +421,6 @@ function openAPIPagePathFromRef(ref, tagFilter, explicitPath) {
|
|
|
395
421
|
function routePathFromRef(ref, explicitPath) {
|
|
396
422
|
return explicitPath ? normalizePath(explicitPath) : basePathFromRef(ref);
|
|
397
423
|
}
|
|
398
|
-
function virtualModuleIdFromRef(ref) {
|
|
399
|
-
return "virtual:clarify-page/" + ref.replace(/\.mdx?$/, "").replace(/\.openapi\.(json|yaml|yml)$/, "").replace(/\/+/g, "/");
|
|
400
|
-
}
|
|
401
424
|
function localizedRoutePath(basePath, locale, _i18n) {
|
|
402
425
|
const normalizedBasePath = normalizePath(basePath);
|
|
403
426
|
const prefix = normalizePath(locale);
|
|
@@ -440,7 +463,7 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
440
463
|
title = kebabToTitle(lastPart === "index" ? pathParts.length >= 2 ? pathParts[pathParts.length - 2] : extractH1(page.content) : lastPart) || "Untitled";
|
|
441
464
|
}
|
|
442
465
|
routes.push({
|
|
443
|
-
kind: "mdx",
|
|
466
|
+
kind: entry.name.toLowerCase().endsWith(".mdx") ? "markdown+jsx" : "markdown",
|
|
444
467
|
path: cleanPath,
|
|
445
468
|
basePath: cleanPath,
|
|
446
469
|
meta: {
|
|
@@ -449,7 +472,10 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
449
472
|
keywords: frontmatterKeywords(page.frontmatter),
|
|
450
473
|
sections: extractMdxSections(page.content)
|
|
451
474
|
},
|
|
452
|
-
module: {
|
|
475
|
+
module: {
|
|
476
|
+
pageVirtualModuleId: pageVirtualModuleId(relativePath.replace(/\.mdx?$/, "")),
|
|
477
|
+
contentVirtualModuleId: contentVirtualModuleId(fullPath, base)
|
|
478
|
+
},
|
|
453
479
|
source: {
|
|
454
480
|
filePath: fullPath,
|
|
455
481
|
frontmatter: page.frontmatter,
|
|
@@ -461,14 +487,24 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
461
487
|
}
|
|
462
488
|
return routes;
|
|
463
489
|
}
|
|
464
|
-
function
|
|
465
|
-
return
|
|
490
|
+
function pageVirtualModuleIdForLocalizedRoute(contentRoot, filePath) {
|
|
491
|
+
return pageVirtualModuleIdFromRef((0, node_path.relative)(contentRoot, filePath));
|
|
466
492
|
}
|
|
467
493
|
function frontmatterKeywords(frontmatter) {
|
|
468
494
|
const value = frontmatter.keywords ?? frontmatter.keyword ?? frontmatter.tags;
|
|
469
495
|
const keywords = Array.isArray(value) ? value.filter((item) => typeof item === "string") : typeof value === "string" ? value.split(",").map((keyword) => keyword.trim()).filter(Boolean) : [];
|
|
470
496
|
return keywords.length > 0 ? keywords : void 0;
|
|
471
497
|
}
|
|
498
|
+
function withPageIdentity(route, path) {
|
|
499
|
+
return {
|
|
500
|
+
...route,
|
|
501
|
+
path,
|
|
502
|
+
module: {
|
|
503
|
+
...route.module,
|
|
504
|
+
pageVirtualModuleId: pageVirtualModuleId(path)
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
}
|
|
472
508
|
function withAlternates(route, routes, locales) {
|
|
473
509
|
const basePath = route.basePath ?? route.path;
|
|
474
510
|
const routeByLocaleAndBase = new Map(routes.filter((r) => !r.locale || r.path === `/${r.locale}` || r.path.startsWith(`/${r.locale}/`)).map((r) => [`${r.locale ?? ""}:${r.basePath ?? r.path}`, r]));
|
|
@@ -494,8 +530,7 @@ function generateBareAliases(routes, locales) {
|
|
|
494
530
|
if (bp === route.path || seenBare.has(bp)) continue;
|
|
495
531
|
seenBare.add(bp);
|
|
496
532
|
bareRoutes.push({
|
|
497
|
-
...route,
|
|
498
|
-
path: bp,
|
|
533
|
+
...withPageIdentity(route, bp),
|
|
499
534
|
isBareAlias: true
|
|
500
535
|
});
|
|
501
536
|
}
|
|
@@ -514,7 +549,11 @@ async function findLocalizedContentRoutes(contentRoot, locales, options = {}) {
|
|
|
514
549
|
path: localizedRoutePath(basePath, locale.code, locales),
|
|
515
550
|
basePath,
|
|
516
551
|
locale: locale.code,
|
|
517
|
-
module: {
|
|
552
|
+
module: {
|
|
553
|
+
...route.module,
|
|
554
|
+
pageVirtualModuleId: pageVirtualModuleIdForLocalizedRoute(contentRoot, route.source.filePath),
|
|
555
|
+
contentVirtualModuleId: contentVirtualModuleId(route.source.filePath, contentRoot)
|
|
556
|
+
}
|
|
518
557
|
});
|
|
519
558
|
}
|
|
520
559
|
}
|
|
@@ -526,9 +565,9 @@ async function findLocalizedContentRoutes(contentRoot, locales, options = {}) {
|
|
|
526
565
|
for (const locale of locales.locales) {
|
|
527
566
|
const key = `${locale.code}:${basePath}`;
|
|
528
567
|
if (routeByLocaleAndBase.has(key)) continue;
|
|
568
|
+
const path = localizedRoutePath(basePath, locale.code, locales);
|
|
529
569
|
localizedRoutes.push({
|
|
530
|
-
...sourceRoute,
|
|
531
|
-
path: localizedRoutePath(basePath, locale.code, locales),
|
|
570
|
+
...withPageIdentity(sourceRoute, path),
|
|
532
571
|
locale: locale.code,
|
|
533
572
|
isFallback: true
|
|
534
573
|
});
|
|
@@ -623,13 +662,13 @@ function applyConfiguredPageRoutePaths(routes, tabs, locales) {
|
|
|
623
662
|
const targetBasePath = routePathFromRef(intent.ref, intent.path);
|
|
624
663
|
if (sourceBasePath === targetBasePath) continue;
|
|
625
664
|
for (const route of routes) {
|
|
626
|
-
if (route.kind
|
|
665
|
+
if (route.kind === "openapi" || (route.basePath ?? route.path) !== sourceBasePath) continue;
|
|
627
666
|
const key = `${route.locale ?? ""}:${targetBasePath}`;
|
|
628
667
|
if (existingKeys.has(key)) continue;
|
|
629
668
|
existingKeys.add(key);
|
|
669
|
+
const path = route.locale && locales ? localizedRoutePath(targetBasePath, route.locale, locales) : targetBasePath;
|
|
630
670
|
additions.push({
|
|
631
|
-
...route,
|
|
632
|
-
path: route.locale && locales ? localizedRoutePath(targetBasePath, route.locale, locales) : targetBasePath,
|
|
671
|
+
...withPageIdentity(route, path),
|
|
633
672
|
basePath: targetBasePath
|
|
634
673
|
});
|
|
635
674
|
}
|
|
@@ -637,7 +676,8 @@ function applyConfiguredPageRoutePaths(routes, tabs, locales) {
|
|
|
637
676
|
const nextRoutes = [...routes, ...additions];
|
|
638
677
|
const routesWithAlternates = locales ? nextRoutes.map((route) => withAlternates(route, nextRoutes, locales)) : nextRoutes;
|
|
639
678
|
if (!locales) return routesWithAlternates;
|
|
640
|
-
|
|
679
|
+
const markdownRoutes = routesWithAlternates.filter((route) => route.kind !== "openapi");
|
|
680
|
+
return [...routesWithAlternates, ...generateBareAliases(markdownRoutes, locales)];
|
|
641
681
|
}
|
|
642
682
|
function buildNavigationFromPagesConfig(routes, config) {
|
|
643
683
|
if (!config || config === "FileTree") return buildNavigation(routes);
|
|
@@ -831,7 +871,7 @@ function inferRouteForFile(filePath, contentRoot, defaultLocale) {
|
|
|
831
871
|
filePath,
|
|
832
872
|
locale: fileLocale,
|
|
833
873
|
basePath: normalizePath(basePath),
|
|
834
|
-
kind: isMdx ? "
|
|
874
|
+
kind: isMdx ? "markdown+jsx" : "openapi",
|
|
835
875
|
title,
|
|
836
876
|
inferred: true
|
|
837
877
|
};
|
|
@@ -1250,6 +1290,9 @@ function resolveVirtualId(id) {
|
|
|
1250
1290
|
function stripVirtualPrefix(id) {
|
|
1251
1291
|
return id.startsWith("\0") ? id.slice(1) : id;
|
|
1252
1292
|
}
|
|
1293
|
+
function virtualContentModuleId(route) {
|
|
1294
|
+
return route.module.contentVirtualModuleId;
|
|
1295
|
+
}
|
|
1253
1296
|
/**
|
|
1254
1297
|
* Fixed virtual module IDs (excluding the client entry, which has a special
|
|
1255
1298
|
* resolved form). Used to avoid hardcoding the constant list in every
|
|
@@ -1277,8 +1320,10 @@ function resolveVirtualModuleId(id, modules, routes) {
|
|
|
1277
1320
|
for (const virtualId of FIXED_VIRTUAL_IDS) if (id === virtualId || id === resolveVirtualId(virtualId)) return resolveVirtualId(virtualId);
|
|
1278
1321
|
const moduleId = stripVirtualPrefix(id);
|
|
1279
1322
|
if (modules.has(moduleId)) return resolveVirtualId(moduleId);
|
|
1280
|
-
const
|
|
1281
|
-
if (
|
|
1323
|
+
const contentRoute = routes.find((route) => (route.kind === "markdown" || route.kind === "markdown+jsx") && route.module.contentVirtualModuleId === moduleId);
|
|
1324
|
+
if (contentRoute) return contentRoute.source.filePath;
|
|
1325
|
+
const route = routes.find((route) => route.module.pageVirtualModuleId === id || route.module.pageVirtualModuleId === moduleId);
|
|
1326
|
+
if (route) return resolveVirtualId(route.module.pageVirtualModuleId);
|
|
1282
1327
|
return null;
|
|
1283
1328
|
}
|
|
1284
1329
|
function generateConfigModule(projectConfig, version) {
|
|
@@ -1332,8 +1377,8 @@ function routeToRuntimeManifestEntry(route, component, mode) {
|
|
|
1332
1377
|
return entry;
|
|
1333
1378
|
}
|
|
1334
1379
|
function generateRoutesModule(routes, navigation, mode = "client") {
|
|
1335
|
-
return `${mode === "server" ? routes.map((r, i) => `import Page${i} from ${moduleSpecifier(r.module.
|
|
1336
|
-
const component = mode === "server" ? `Page${i}` : `() => import(${moduleSpecifier(r.module.
|
|
1380
|
+
return `${mode === "server" ? routes.map((r, i) => `import Page${i} from ${moduleSpecifier(r.module.pageVirtualModuleId)};`).join("\n") : `import { createContentDiagnosticComponent } from '@clarify-labs/renderer';`}\n\nexport const routes = [\n${routes.map((r, i) => {
|
|
1381
|
+
const component = mode === "server" ? `Page${i}` : `() => import(${moduleSpecifier(r.module.pageVirtualModuleId)}).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 }) }))`;
|
|
1337
1382
|
const placeholder = `__COMPONENT_${i}__`;
|
|
1338
1383
|
const manifestEntry = routeToRuntimeManifestEntry(r, placeholder, mode);
|
|
1339
1384
|
return ` ${JSON.stringify(manifestEntry, null, 0).replace(`"${placeholder}"`, component).replace(/"([A-Za-z_$][\w$]*)":/g, "$1: ")}`;
|
|
@@ -1405,13 +1450,14 @@ function buildVirtualModules(args) {
|
|
|
1405
1450
|
modules.set(VIRTUAL_ROUTES, generateRoutesModule(args.routes, args.navigation, "client"));
|
|
1406
1451
|
modules.set(VIRTUAL_SERVER_ROUTES, generateRoutesModule(args.routes, args.navigation, "server"));
|
|
1407
1452
|
modules.set(VIRTUAL_SLOTS, createRuntimeSlotsModule(allPlugins, args.generateOptions.projectRoot));
|
|
1408
|
-
modules.set(VIRTUAL_OPENAPI, "export const openApiSpecs = {};");
|
|
1409
1453
|
modules.set(VIRTUAL_SLOT, "export { useSlot } from '@clarify-labs/renderer';\n");
|
|
1410
|
-
modules.set(
|
|
1454
|
+
modules.set(VIRTUAL_OPENAPI, "export const openApiSpecs = {};");
|
|
1411
1455
|
modules.set(RESOLVED_CLIENT_ENTRY, clientEntryModule);
|
|
1456
|
+
modules.set(VIRTUAL_CLIENT_ENTRY, clientEntryModule);
|
|
1412
1457
|
for (const route of args.routes) {
|
|
1413
|
-
const moduleContent = route.diagnostic ? generateContentDiagnosticModule(route.diagnostic) : `export { default } from ${moduleSpecifier(route
|
|
1414
|
-
|
|
1458
|
+
const moduleContent = route.diagnostic ? generateContentDiagnosticModule(route.diagnostic) : route.kind === "markdown" || route.kind === "markdown+jsx" ? `export { default } from ${moduleSpecifier(virtualContentModuleId(route))};` : null;
|
|
1459
|
+
if (moduleContent === null) continue;
|
|
1460
|
+
modules.set(route.module.pageVirtualModuleId, moduleContent);
|
|
1415
1461
|
}
|
|
1416
1462
|
return modules;
|
|
1417
1463
|
}
|
|
@@ -1452,7 +1498,7 @@ function createNormalizedContentPlugin(engine) {
|
|
|
1452
1498
|
transform(_code, id) {
|
|
1453
1499
|
if (!/\.mdx?(?:\?|$)/.test(id)) return null;
|
|
1454
1500
|
const filePath = id.replace(/\?.*$/, "");
|
|
1455
|
-
const route = engine.routes.find((route) => route.kind === "
|
|
1501
|
+
const route = engine.routes.find((route) => (route.kind === "markdown+jsx" || route.kind === "markdown") && route.source.filePath === filePath);
|
|
1456
1502
|
if (!route || route.source.content === void 0) return null;
|
|
1457
1503
|
return {
|
|
1458
1504
|
code: route.source.content,
|
|
@@ -1567,7 +1613,10 @@ function createClarifyViteCorePlugin(engine, normalizedContentPlugin, markdown,
|
|
|
1567
1613
|
};
|
|
1568
1614
|
server.watcher.on("add", handleContentTreeChange);
|
|
1569
1615
|
server.watcher.on("unlink", handleContentTreeChange);
|
|
1570
|
-
await engine.configureDevServer(server);
|
|
1616
|
+
const postHooks = await engine.configureDevServer(server);
|
|
1617
|
+
if (postHooks.length > 0) return () => {
|
|
1618
|
+
for (const postHook of postHooks) postHook();
|
|
1619
|
+
};
|
|
1571
1620
|
},
|
|
1572
1621
|
transformIndexHtml: {
|
|
1573
1622
|
order: "pre",
|
|
@@ -1622,7 +1671,7 @@ function createViteAdapter(engine) {
|
|
|
1622
1671
|
const errorPolicy = () => contentCompileErrorPolicy;
|
|
1623
1672
|
const normalizedContentPlugin = createNormalizedContentPlugin(engine);
|
|
1624
1673
|
const markdown = createContentCompilerPlugin("markdown", engine.root, errorPolicy);
|
|
1625
|
-
const mdx = createContentCompilerPlugin("
|
|
1674
|
+
const mdx = createContentCompilerPlugin("markdown+jsx", engine.root, errorPolicy);
|
|
1626
1675
|
const compilePolicyPlugin = {
|
|
1627
1676
|
name: "clarify:content-compile-policy",
|
|
1628
1677
|
configResolved(config) {
|
|
@@ -1642,7 +1691,7 @@ function createViteAdapter(engine) {
|
|
|
1642
1691
|
}
|
|
1643
1692
|
var cliPackageVersion = {
|
|
1644
1693
|
name: "@clarify-labs/cli",
|
|
1645
|
-
version: "0.11.
|
|
1694
|
+
version: "0.11.6",
|
|
1646
1695
|
license: "AGPL-3.0-only",
|
|
1647
1696
|
type: "module",
|
|
1648
1697
|
repository: {
|
|
@@ -1742,7 +1791,7 @@ function withUtf8Signature(content) {
|
|
|
1742
1791
|
return `${UTF8_SIGNATURE}${content}`;
|
|
1743
1792
|
}
|
|
1744
1793
|
function shouldUseUtf8Signature(route) {
|
|
1745
|
-
if (route.kind === "
|
|
1794
|
+
if (route.kind === "markdown+jsx" || route.kind === "markdown") return true;
|
|
1746
1795
|
return false;
|
|
1747
1796
|
}
|
|
1748
1797
|
function routeToMarkdownArtifactUrl(routePath) {
|
|
@@ -1814,9 +1863,9 @@ function createLlmsTxt(routes, projectConfig) {
|
|
|
1814
1863
|
const lines = [`# ${projectConfig.title}`, ""];
|
|
1815
1864
|
if (projectConfig.description) lines.push(`> ${projectConfig.description}`, "");
|
|
1816
1865
|
lines.push("This file lists the source-ready Markdown and OpenAPI artifacts for this documentation site.", "");
|
|
1817
|
-
const
|
|
1818
|
-
if (
|
|
1819
|
-
const localizedGroups = groupLlmsTxtRoutesByLocale(
|
|
1866
|
+
const markdownRoutes = routes.filter((route) => (route.kind === "markdown+jsx" || route.kind === "markdown") && isLlmsTxtRoute(route) && !route.isBareAlias);
|
|
1867
|
+
if (markdownRoutes.length > 0) {
|
|
1868
|
+
const localizedGroups = groupLlmsTxtRoutesByLocale(markdownRoutes, projectConfig);
|
|
1820
1869
|
for (const [locale, localeRoutes] of localizedGroups) {
|
|
1821
1870
|
lines.push(`## ${llmsTxtDocsSectionTitle(locale, localizedGroups.size, projectConfig)}`);
|
|
1822
1871
|
for (const route of localeRoutes) {
|
|
@@ -1893,10 +1942,12 @@ function createContentArtifactsPlugin() {
|
|
|
1893
1942
|
return input;
|
|
1894
1943
|
},
|
|
1895
1944
|
"dev:configureServer": (server, ctx) => {
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1945
|
+
return () => {
|
|
1946
|
+
server.middlewares.use((req, res, next) => {
|
|
1947
|
+
if (serveContentArtifacts(req, res, ctx.projectConfig, ctx.routes)) return;
|
|
1948
|
+
next();
|
|
1949
|
+
});
|
|
1950
|
+
};
|
|
1900
1951
|
},
|
|
1901
1952
|
"build:assets": (ctx) => {
|
|
1902
1953
|
const assets = [];
|
|
@@ -2387,7 +2438,7 @@ function findOpenAPIRoutes(dir, base = dir) {
|
|
|
2387
2438
|
basePath: cleanPath,
|
|
2388
2439
|
kind: "openapi",
|
|
2389
2440
|
meta: { title: kebabToTitle(cleanPath.split("/").pop() ?? "API") },
|
|
2390
|
-
module: {
|
|
2441
|
+
module: { pageVirtualModuleId: pageVirtualModuleIdFromRef(ref) },
|
|
2391
2442
|
source: { filePath: fullPath }
|
|
2392
2443
|
});
|
|
2393
2444
|
}
|
|
@@ -2511,34 +2562,7 @@ async function readOpenAPISpec(filePath, contentProcessor, projectRoot) {
|
|
|
2511
2562
|
}
|
|
2512
2563
|
}
|
|
2513
2564
|
//#endregion
|
|
2514
|
-
//#region source/plugins/openapi/
|
|
2515
|
-
var openApiRegistryModuleId = VIRTUAL_OPENAPI;
|
|
2516
|
-
var openApiServerRegistryModuleId = VIRTUAL_OPENAPI_SERVER;
|
|
2517
|
-
var OPENAPI_SPEC_PREFIX = "virtual:clarify/openapi-spec/";
|
|
2518
|
-
function specVirtualModuleId(specKey) {
|
|
2519
|
-
return `${OPENAPI_SPEC_PREFIX}${specKey}`;
|
|
2520
|
-
}
|
|
2521
|
-
function generateOpenAPIRegistryModule(openApiSpecs) {
|
|
2522
|
-
return `export const openApiSpecs = {\n${Object.entries(openApiSpecs).map(([key, moduleId]) => ` ${JSON.stringify(key)}: () => import(${JSON.stringify(moduleId)}),`).join("\n")}\n};`;
|
|
2523
|
-
}
|
|
2524
|
-
function generateOpenAPIServerRegistryModule(openApiSpecs) {
|
|
2525
|
-
return `export const openApiSpecs = ${JSON.stringify(openApiSpecs)};`;
|
|
2526
|
-
}
|
|
2527
|
-
/** Generate a virtual module that exports a single spec as default. */
|
|
2528
|
-
function generateOpenAPISpecModule(spec) {
|
|
2529
|
-
return `export default ${JSON.stringify(spec)};`;
|
|
2530
|
-
}
|
|
2531
|
-
function generateOpenAPIPageModule(opts) {
|
|
2532
|
-
return `import { createOpenApiRouteComponent } from '@clarify-labs/renderer';
|
|
2533
|
-
import spec from ${JSON.stringify(opts.specModuleId)};
|
|
2534
|
-
|
|
2535
|
-
export const routeData = {};
|
|
2536
|
-
|
|
2537
|
-
export default createOpenApiRouteComponent({ ...routeData, spec });
|
|
2538
|
-
`;
|
|
2539
|
-
}
|
|
2540
|
-
//#endregion
|
|
2541
|
-
//#region source/plugins/openapi/index.ts
|
|
2565
|
+
//#region source/plugins/openapi/routes.ts
|
|
2542
2566
|
function collectOpenAPIPageIntents(config) {
|
|
2543
2567
|
const intents = [];
|
|
2544
2568
|
function visitItems(items) {
|
|
@@ -2558,9 +2582,6 @@ function collectOpenAPIPageIntents(config) {
|
|
|
2558
2582
|
for (const tab of config.navigation?.tabs ?? []) visitPages(tab.pages);
|
|
2559
2583
|
return intents;
|
|
2560
2584
|
}
|
|
2561
|
-
function virtualModuleIdFromPath(path) {
|
|
2562
|
-
return `virtual:clarify-page/${path.replace(/^\/+/, "") || "index"}`;
|
|
2563
|
-
}
|
|
2564
2585
|
function cloneOpenAPIRoute(route, overrides = {}) {
|
|
2565
2586
|
return {
|
|
2566
2587
|
...route,
|
|
@@ -2578,7 +2599,7 @@ function cloneOpenAPIRoute(route, overrides = {}) {
|
|
|
2578
2599
|
...overrides
|
|
2579
2600
|
};
|
|
2580
2601
|
}
|
|
2581
|
-
function
|
|
2602
|
+
function discoverOpenAPIRoutes(contentRoot, locales) {
|
|
2582
2603
|
if (!locales) return findOpenAPIRoutes(contentRoot);
|
|
2583
2604
|
const localizedRoutes = [];
|
|
2584
2605
|
for (const locale of locales.locales) {
|
|
@@ -2591,31 +2612,30 @@ function findOpenAPIRoutesForContentRoot(contentRoot, locales) {
|
|
|
2591
2612
|
path,
|
|
2592
2613
|
basePath,
|
|
2593
2614
|
locale: locale.code,
|
|
2594
|
-
module: {
|
|
2615
|
+
module: { pageVirtualModuleId: pageVirtualModuleId(path) }
|
|
2595
2616
|
});
|
|
2596
2617
|
}
|
|
2597
2618
|
}
|
|
2598
|
-
if (locales.missing
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
}
|
|
2619
|
+
if (locales.missing !== "fallback") return localizedRoutes;
|
|
2620
|
+
const routeByLocaleAndBase = new Map(localizedRoutes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
|
|
2621
|
+
const defaultRoutes = localizedRoutes.filter((route) => route.locale === locales.default);
|
|
2622
|
+
for (const sourceRoute of defaultRoutes) {
|
|
2623
|
+
const basePath = sourceRoute.basePath ?? sourceRoute.path;
|
|
2624
|
+
for (const locale of locales.locales) {
|
|
2625
|
+
const key = `${locale.code}:${basePath}`;
|
|
2626
|
+
if (routeByLocaleAndBase.has(key)) continue;
|
|
2627
|
+
const path = localizedRoutePath(basePath, locale.code, locales);
|
|
2628
|
+
localizedRoutes.push(cloneOpenAPIRoute(sourceRoute, {
|
|
2629
|
+
path,
|
|
2630
|
+
locale: locale.code,
|
|
2631
|
+
module: { pageVirtualModuleId: pageVirtualModuleId(path) },
|
|
2632
|
+
isFallback: true
|
|
2633
|
+
}));
|
|
2614
2634
|
}
|
|
2615
2635
|
}
|
|
2616
2636
|
return localizedRoutes;
|
|
2617
2637
|
}
|
|
2618
|
-
function
|
|
2638
|
+
function expandConfiguredOpenAPIRoutes(routes, config) {
|
|
2619
2639
|
const openAPIIntents = collectOpenAPIPageIntents(config);
|
|
2620
2640
|
if (!openAPIIntents.length) return routes;
|
|
2621
2641
|
const additions = [];
|
|
@@ -2634,7 +2654,7 @@ function createConfiguredOpenAPIRoutes(routes, config) {
|
|
|
2634
2654
|
additions.push(cloneOpenAPIRoute(route, {
|
|
2635
2655
|
path,
|
|
2636
2656
|
basePath: targetBasePath,
|
|
2637
|
-
module: {
|
|
2657
|
+
module: { pageVirtualModuleId: pageVirtualModuleId(path) },
|
|
2638
2658
|
openapi: {
|
|
2639
2659
|
...route.openapi,
|
|
2640
2660
|
tagFilter: tagFilter?.length ? tagFilter : void 0
|
|
@@ -2648,19 +2668,47 @@ function createConfiguredOpenAPIRoutes(routes, config) {
|
|
|
2648
2668
|
const bareAliases = [];
|
|
2649
2669
|
const seenBare = new Set(routesWithAlternates.map((route) => route.path));
|
|
2650
2670
|
for (const route of routesWithAlternates) {
|
|
2651
|
-
if (route.locale !== config.locales.default) continue;
|
|
2671
|
+
if (route.kind !== "openapi" || route.locale !== config.locales.default) continue;
|
|
2652
2672
|
const basePath = route.basePath ?? route.path;
|
|
2653
2673
|
if (basePath === route.path || seenBare.has(basePath)) continue;
|
|
2654
2674
|
seenBare.add(basePath);
|
|
2655
2675
|
bareAliases.push(cloneOpenAPIRoute(route, {
|
|
2656
2676
|
path: basePath,
|
|
2657
|
-
module: {
|
|
2677
|
+
module: { pageVirtualModuleId: pageVirtualModuleId(basePath) },
|
|
2658
2678
|
isBareAlias: true
|
|
2659
2679
|
}));
|
|
2660
2680
|
}
|
|
2661
2681
|
return [...routesWithAlternates, ...bareAliases];
|
|
2662
2682
|
}
|
|
2663
|
-
|
|
2683
|
+
//#endregion
|
|
2684
|
+
//#region source/plugins/openapi/virtual-modules.ts
|
|
2685
|
+
var openApiRegistryModuleId = VIRTUAL_OPENAPI;
|
|
2686
|
+
var openApiServerRegistryModuleId = VIRTUAL_OPENAPI_SERVER;
|
|
2687
|
+
var OPENAPI_SPEC_PREFIX = "virtual:clarify/openapi-spec/";
|
|
2688
|
+
function specVirtualModuleId(specKey) {
|
|
2689
|
+
return `${OPENAPI_SPEC_PREFIX}${specKey}`;
|
|
2690
|
+
}
|
|
2691
|
+
function generateOpenAPIRegistryModule(openApiSpecs) {
|
|
2692
|
+
return `export const openApiSpecs = {\n${Object.entries(openApiSpecs).map(([key, moduleId]) => ` ${JSON.stringify(key)}: () => import(${JSON.stringify(moduleId)}),`).join("\n")}\n};`;
|
|
2693
|
+
}
|
|
2694
|
+
function generateOpenAPIServerRegistryModule(openApiSpecs) {
|
|
2695
|
+
return `export const openApiSpecs = ${JSON.stringify(openApiSpecs)};`;
|
|
2696
|
+
}
|
|
2697
|
+
/** Generate a virtual module that exports a single spec as default. */
|
|
2698
|
+
function generateOpenAPISpecModule(spec) {
|
|
2699
|
+
return `export default ${JSON.stringify(spec)};`;
|
|
2700
|
+
}
|
|
2701
|
+
function generateOpenAPIPageModule(opts) {
|
|
2702
|
+
return `import { createOpenApiRouteComponent } from '@clarify-labs/renderer';
|
|
2703
|
+
import spec from ${JSON.stringify(opts.specModuleId)};
|
|
2704
|
+
|
|
2705
|
+
export const routeData = {};
|
|
2706
|
+
|
|
2707
|
+
export default createOpenApiRouteComponent({ ...routeData, spec });
|
|
2708
|
+
`;
|
|
2709
|
+
}
|
|
2710
|
+
//#endregion
|
|
2711
|
+
//#region source/plugins/openapi/state.ts
|
|
2664
2712
|
function sourceSpecIdFromPath(filePath, projectRoot) {
|
|
2665
2713
|
return (0, node_path.relative)(projectRoot, filePath).replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
|
|
2666
2714
|
}
|
|
@@ -2672,90 +2720,94 @@ function openAPIRegistryKeys(route) {
|
|
|
2672
2720
|
const keys = /* @__PURE__ */ new Set();
|
|
2673
2721
|
const path = (route.path || "/").replace(/^\//, "");
|
|
2674
2722
|
const base = (route.basePath ?? route.path).replace(/^\//, "");
|
|
2675
|
-
if (route.locale) keys.add(
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
if (!route.locale && base) keys.add(`virtual:clarify-page/${base}`);
|
|
2723
|
+
if (route.locale) keys.add(pageVirtualModuleId(`${route.locale}/${base}`));
|
|
2724
|
+
keys.add(pageVirtualModuleId(path));
|
|
2725
|
+
if (!route.locale && base) keys.add(pageVirtualModuleId(base));
|
|
2679
2726
|
return [...keys];
|
|
2680
2727
|
}
|
|
2728
|
+
var OpenAPIPluginState = class {
|
|
2729
|
+
specModules = /* @__PURE__ */ new Map();
|
|
2730
|
+
async enrichRoutes(routes, ctx) {
|
|
2731
|
+
this.specModules.clear();
|
|
2732
|
+
const specByFilePath = /* @__PURE__ */ new Map();
|
|
2733
|
+
for (const route of routes) {
|
|
2734
|
+
if (route.kind !== "openapi") continue;
|
|
2735
|
+
const specFromCache = specByFilePath.get(route.source.filePath);
|
|
2736
|
+
const processor = createProjectContentProcessor(ctx.plugins, ctx);
|
|
2737
|
+
const result = specFromCache ? {
|
|
2738
|
+
ok: true,
|
|
2739
|
+
spec: specFromCache
|
|
2740
|
+
} : await readOpenAPISpec(route.source.filePath, processor, ctx.generateOptions.projectRoot);
|
|
2741
|
+
if (!result.ok) {
|
|
2742
|
+
route.diagnostic = result.diagnostic;
|
|
2743
|
+
route.meta.title = route.meta.title || "OpenAPI parse error";
|
|
2744
|
+
route.meta.sections = [];
|
|
2745
|
+
continue;
|
|
2746
|
+
}
|
|
2747
|
+
const spec = normalizeOpenAPISpecSectionIds(result.spec);
|
|
2748
|
+
specByFilePath.set(route.source.filePath, spec);
|
|
2749
|
+
const sourceSpecId = sourceSpecIdFromPath(route.source.filePath, ctx.generateOptions.projectRoot);
|
|
2750
|
+
route.openapi = {
|
|
2751
|
+
...route.openapi,
|
|
2752
|
+
sourceSpecId
|
|
2753
|
+
};
|
|
2754
|
+
route.meta.title = spec.info?.title ?? route.meta.title;
|
|
2755
|
+
route.meta.description = spec.info?.description ?? route.meta.description;
|
|
2756
|
+
route.meta.sections = extractOpenAPISections(spec, route.openapi.tagFilter);
|
|
2757
|
+
const pageSpec = route.openapi.tagFilter?.length ? filterSpecByTags(spec, route.openapi.tagFilter) : spec;
|
|
2758
|
+
const routeSpecModuleId = routeSpecModuleIdFromSourceSpecId(sourceSpecId, route.openapi.tagFilter);
|
|
2759
|
+
route.openapi.routeSpecModuleId = routeSpecModuleId;
|
|
2760
|
+
this.specModules.set(routeSpecModuleId, pageSpec);
|
|
2761
|
+
route.source.content = JSON.stringify(pageSpec);
|
|
2762
|
+
}
|
|
2763
|
+
return routes;
|
|
2764
|
+
}
|
|
2765
|
+
contributeModules(modules, routes) {
|
|
2766
|
+
const registryEntries = {};
|
|
2767
|
+
const serverRegistryEntries = {};
|
|
2768
|
+
for (const route of routes) {
|
|
2769
|
+
if (route.kind !== "openapi" || !route.openapi?.sourceSpecId) continue;
|
|
2770
|
+
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2771
|
+
const spec = this.specModules.get(routeSpecModuleId);
|
|
2772
|
+
if (!spec) continue;
|
|
2773
|
+
for (const registryKey of openAPIRegistryKeys(route)) {
|
|
2774
|
+
registryEntries[registryKey] = specVirtualModuleId(routeSpecModuleId);
|
|
2775
|
+
serverRegistryEntries[registryKey] = spec;
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
modules.set(openApiRegistryModuleId, generateOpenAPIRegistryModule(registryEntries));
|
|
2779
|
+
modules.set(openApiServerRegistryModuleId, generateOpenAPIServerRegistryModule(serverRegistryEntries));
|
|
2780
|
+
for (const [routeSpecModuleId, spec] of this.specModules) modules.set(specVirtualModuleId(routeSpecModuleId), generateOpenAPISpecModule(spec));
|
|
2781
|
+
for (const route of routes) {
|
|
2782
|
+
if (route.kind !== "openapi") continue;
|
|
2783
|
+
if (route.diagnostic) {
|
|
2784
|
+
modules.set(route.module.pageVirtualModuleId, generateContentDiagnosticModule(route.diagnostic));
|
|
2785
|
+
continue;
|
|
2786
|
+
}
|
|
2787
|
+
if (!route.openapi?.sourceSpecId) continue;
|
|
2788
|
+
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2789
|
+
if (!this.specModules.has(routeSpecModuleId)) continue;
|
|
2790
|
+
modules.set(route.module.pageVirtualModuleId, generateOpenAPIPageModule({ specModuleId: specVirtualModuleId(routeSpecModuleId) }));
|
|
2791
|
+
}
|
|
2792
|
+
return modules;
|
|
2793
|
+
}
|
|
2794
|
+
};
|
|
2795
|
+
//#endregion
|
|
2796
|
+
//#region source/plugins/openapi/index.ts
|
|
2681
2797
|
function createOpenAPIPlugin() {
|
|
2682
|
-
|
|
2683
|
-
const specs = /* @__PURE__ */ new Map();
|
|
2684
|
-
const specModules = /* @__PURE__ */ new Map();
|
|
2798
|
+
const state = new OpenAPIPluginState();
|
|
2685
2799
|
return {
|
|
2686
2800
|
name: "clarify:openapi",
|
|
2687
2801
|
hooks: {
|
|
2688
2802
|
"routes:discover": (input, ctx) => ({
|
|
2689
2803
|
...input,
|
|
2690
|
-
routes: [...input.routes, ...
|
|
2804
|
+
routes: [...input.routes, ...discoverOpenAPIRoutes(input.contentRoot, ctx.projectConfig.locales)]
|
|
2691
2805
|
}),
|
|
2692
2806
|
"routes:discovered": async (routes, ctx) => {
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
const nextRoutes = createConfiguredOpenAPIRoutes(routes, ctx.projectConfig);
|
|
2696
|
-
const specByFilePath = /* @__PURE__ */ new Map();
|
|
2697
|
-
for (const route of nextRoutes.filter((route) => route.kind === "openapi")) {
|
|
2698
|
-
const specFromCache = specByFilePath.get(route.source.filePath);
|
|
2699
|
-
const processor = createProjectContentProcessor(ctx.plugins, ctx);
|
|
2700
|
-
const result = specFromCache ? {
|
|
2701
|
-
ok: true,
|
|
2702
|
-
spec: specFromCache
|
|
2703
|
-
} : await readOpenAPISpec(route.source.filePath, processor, ctx.generateOptions.projectRoot);
|
|
2704
|
-
if (!result.ok) {
|
|
2705
|
-
route.diagnostic = result.diagnostic;
|
|
2706
|
-
route.meta.title = route.meta.title || "OpenAPI parse error";
|
|
2707
|
-
route.meta.sections = [];
|
|
2708
|
-
continue;
|
|
2709
|
-
}
|
|
2710
|
-
const spec = normalizeOpenAPISpecSectionIds(result.spec);
|
|
2711
|
-
specByFilePath.set(route.source.filePath, spec);
|
|
2712
|
-
const sourceSpecId = sourceSpecIdFromPath(route.source.filePath, ctx.generateOptions.projectRoot);
|
|
2713
|
-
route.openapi = {
|
|
2714
|
-
...route.openapi,
|
|
2715
|
-
sourceSpecId
|
|
2716
|
-
};
|
|
2717
|
-
specs.set(sourceSpecId, {
|
|
2718
|
-
spec,
|
|
2719
|
-
filePath: route.source.filePath
|
|
2720
|
-
});
|
|
2721
|
-
route.meta.title = spec.info?.title ?? route.meta.title;
|
|
2722
|
-
route.meta.description = spec.info?.description ?? route.meta.description;
|
|
2723
|
-
route.meta.sections = extractOpenAPISections(spec, route.openapi?.tagFilter);
|
|
2724
|
-
const pageSpec = route.openapi?.tagFilter?.length ? filterSpecByTags(spec, route.openapi.tagFilter) : spec;
|
|
2725
|
-
const routeSpecModuleId = routeSpecModuleIdFromSourceSpecId(sourceSpecId, route.openapi?.tagFilter);
|
|
2726
|
-
route.openapi.routeSpecModuleId = routeSpecModuleId;
|
|
2727
|
-
specModules.set(routeSpecModuleId, pageSpec);
|
|
2728
|
-
route.source.content = JSON.stringify(pageSpec);
|
|
2729
|
-
}
|
|
2730
|
-
return nextRoutes;
|
|
2807
|
+
const expandedRoutes = expandConfiguredOpenAPIRoutes(routes, ctx.projectConfig);
|
|
2808
|
+
return state.enrichRoutes(expandedRoutes, ctx);
|
|
2731
2809
|
},
|
|
2732
|
-
"modules:before": (modules, ctx) =>
|
|
2733
|
-
const registryEntries = {};
|
|
2734
|
-
const serverRegistryEntries = {};
|
|
2735
|
-
for (const route of ctx.routes) {
|
|
2736
|
-
if (route.kind !== "openapi" || !route.openapi?.sourceSpecId) continue;
|
|
2737
|
-
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2738
|
-
const spec = specModules.get(routeSpecModuleId) ?? specs.get(route.openapi.sourceSpecId)?.spec;
|
|
2739
|
-
if (!spec) continue;
|
|
2740
|
-
for (const registryKey of openAPIRegistryKeys(route)) {
|
|
2741
|
-
registryEntries[registryKey] = specVirtualModuleId(routeSpecModuleId);
|
|
2742
|
-
serverRegistryEntries[registryKey] = spec;
|
|
2743
|
-
}
|
|
2744
|
-
}
|
|
2745
|
-
modules.set(openApiRegistryModuleId, generateOpenAPIRegistryModule(registryEntries));
|
|
2746
|
-
modules.set(openApiServerRegistryModuleId, generateOpenAPIServerRegistryModule(serverRegistryEntries));
|
|
2747
|
-
for (const [routeSpecModuleId, spec] of specModules) modules.set(specVirtualModuleId(routeSpecModuleId), generateOpenAPISpecModule(spec));
|
|
2748
|
-
for (const route of ctx.routes) {
|
|
2749
|
-
if (route.kind !== "openapi") continue;
|
|
2750
|
-
if (route.diagnostic) modules.set(route.module.virtualModuleId, generateContentDiagnosticModule(route.diagnostic));
|
|
2751
|
-
else if (route.openapi?.sourceSpecId) {
|
|
2752
|
-
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2753
|
-
if (!specModules.has(routeSpecModuleId)) continue;
|
|
2754
|
-
modules.set(route.module.virtualModuleId, generateOpenAPIPageModule({ specModuleId: specVirtualModuleId(routeSpecModuleId) }));
|
|
2755
|
-
}
|
|
2756
|
-
}
|
|
2757
|
-
return modules;
|
|
2758
|
-
}
|
|
2810
|
+
"modules:before": (modules, ctx) => state.contributeModules(modules, ctx.routes)
|
|
2759
2811
|
}
|
|
2760
2812
|
};
|
|
2761
2813
|
}
|
|
@@ -3164,6 +3216,145 @@ async function resolveProjectContext(options = {}, env = {
|
|
|
3164
3216
|
};
|
|
3165
3217
|
}
|
|
3166
3218
|
//#endregion
|
|
3219
|
+
//#region source/core/routing/route-analysis.ts
|
|
3220
|
+
function routeDescription(route) {
|
|
3221
|
+
return `${route.kind} ${route.source.filePath}`;
|
|
3222
|
+
}
|
|
3223
|
+
function collectConflicts(routes, keyOf) {
|
|
3224
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
3225
|
+
for (const route of routes) {
|
|
3226
|
+
const key = keyOf(route);
|
|
3227
|
+
grouped.set(key, [...grouped.get(key) ?? [], route]);
|
|
3228
|
+
}
|
|
3229
|
+
return grouped;
|
|
3230
|
+
}
|
|
3231
|
+
function describeRouteConflict(route) {
|
|
3232
|
+
return routeDescription(route);
|
|
3233
|
+
}
|
|
3234
|
+
function findRouteConflicts(routes) {
|
|
3235
|
+
const conflicts = [];
|
|
3236
|
+
for (const [path, pathRoutes] of collectConflicts(routes, (route) => route.path)) {
|
|
3237
|
+
if (pathRoutes.length < 2) continue;
|
|
3238
|
+
conflicts.push({
|
|
3239
|
+
kind: "path",
|
|
3240
|
+
key: path,
|
|
3241
|
+
label: `path ${path}`,
|
|
3242
|
+
routes: pathRoutes
|
|
3243
|
+
});
|
|
3244
|
+
}
|
|
3245
|
+
for (const [pageModuleId, moduleRoutes] of collectConflicts(routes, (route) => route.module.pageVirtualModuleId)) {
|
|
3246
|
+
if (moduleRoutes.length < 2) continue;
|
|
3247
|
+
if (moduleRoutes.every((route) => route.path === moduleRoutes[0].path)) continue;
|
|
3248
|
+
conflicts.push({
|
|
3249
|
+
kind: "page-module",
|
|
3250
|
+
key: pageModuleId,
|
|
3251
|
+
label: `page module ${pageModuleId}`,
|
|
3252
|
+
routes: moduleRoutes
|
|
3253
|
+
});
|
|
3254
|
+
}
|
|
3255
|
+
return conflicts;
|
|
3256
|
+
}
|
|
3257
|
+
function formatRouteConflicts(conflicts) {
|
|
3258
|
+
return conflicts.map((conflict) => `- ${conflict.label}: ${conflict.routes.map(describeRouteConflict).join(", ")}`).join("\n");
|
|
3259
|
+
}
|
|
3260
|
+
//#endregion
|
|
3261
|
+
//#region source/core/routing/route-resolution.ts
|
|
3262
|
+
async function resolveRoutePages(routes, plugins, ctx) {
|
|
3263
|
+
const routeToken = Symbol("clarifyRouteToken");
|
|
3264
|
+
const resolvedPages = await runHooks(plugins, "pages:resolved", routes.map((route, routeIndex) => ({
|
|
3265
|
+
path: route.path,
|
|
3266
|
+
filePath: route.source.filePath,
|
|
3267
|
+
frontmatter: route.source.frontmatter ?? {},
|
|
3268
|
+
content: route.source.content ?? "",
|
|
3269
|
+
[routeToken]: routeIndex
|
|
3270
|
+
})), ctx);
|
|
3271
|
+
const pageByRouteIndex = /* @__PURE__ */ new Map();
|
|
3272
|
+
for (const page of resolvedPages) {
|
|
3273
|
+
const routeIndex = page[routeToken];
|
|
3274
|
+
if (!Number.isInteger(routeIndex) || routeIndex < 0 || routeIndex >= routes.length) throw new Error("[clarify] pages:resolved hooks must preserve each page route identity");
|
|
3275
|
+
if (pageByRouteIndex.has(routeIndex)) throw new Error("[clarify] pages:resolved hooks returned the same page route identity more than once");
|
|
3276
|
+
pageByRouteIndex.set(routeIndex, page);
|
|
3277
|
+
}
|
|
3278
|
+
if (pageByRouteIndex.size !== routes.length) throw new Error("[clarify] pages:resolved hooks must return exactly one page for each route");
|
|
3279
|
+
return routes.map((route, routeIndex) => {
|
|
3280
|
+
const page = pageByRouteIndex.get(routeIndex);
|
|
3281
|
+
return {
|
|
3282
|
+
...route,
|
|
3283
|
+
source: {
|
|
3284
|
+
...route.source,
|
|
3285
|
+
frontmatter: page.frontmatter,
|
|
3286
|
+
content: page.content
|
|
3287
|
+
}
|
|
3288
|
+
};
|
|
3289
|
+
});
|
|
3290
|
+
}
|
|
3291
|
+
function buildNavigationTree(routes, ctx) {
|
|
3292
|
+
const tabs = ctx.projectConfig.navigation?.tabs;
|
|
3293
|
+
if (!tabs) return {
|
|
3294
|
+
kind: "flat",
|
|
3295
|
+
nodes: buildNavigation(routes)
|
|
3296
|
+
};
|
|
3297
|
+
if (!ctx.projectConfig.locales) return buildNavigationFromTabsConfig(routes, tabs);
|
|
3298
|
+
return buildLocalizedNavigationFromTabsConfig(routes, tabs, ctx.projectConfig.locales) ?? {
|
|
3299
|
+
kind: "localized-tabbed",
|
|
3300
|
+
locales: {}
|
|
3301
|
+
};
|
|
3302
|
+
}
|
|
3303
|
+
function assertNoRouteConflicts(routes) {
|
|
3304
|
+
const conflicts = findRouteConflicts(routes);
|
|
3305
|
+
if (conflicts.length > 0) throw new Error(`[clarify] Route conflicts prevent the site from being built:\n${formatRouteConflicts(conflicts)}`);
|
|
3306
|
+
}
|
|
3307
|
+
function createRouteConflictDiagnostic(label, routes) {
|
|
3308
|
+
return {
|
|
3309
|
+
kind: "route-load",
|
|
3310
|
+
title: "Route conflict",
|
|
3311
|
+
message: `This page cannot be rendered because ${label} is used by multiple routes.`,
|
|
3312
|
+
details: routes.map(describeRouteConflict).join("\n")
|
|
3313
|
+
};
|
|
3314
|
+
}
|
|
3315
|
+
function createDevRouteConflictRoutes(routes) {
|
|
3316
|
+
const conflicts = findRouteConflicts(routes);
|
|
3317
|
+
if (conflicts.length === 0) return routes;
|
|
3318
|
+
const conflictedRoutes = /* @__PURE__ */ new Map();
|
|
3319
|
+
for (const conflict of conflicts) for (const route of conflict.routes) conflictedRoutes.set(route, conflict);
|
|
3320
|
+
function createConflictRoute(route, conflict) {
|
|
3321
|
+
return {
|
|
3322
|
+
...route,
|
|
3323
|
+
module: {
|
|
3324
|
+
...route.module,
|
|
3325
|
+
pageVirtualModuleId: `${route.module.pageVirtualModuleId}__conflict`
|
|
3326
|
+
},
|
|
3327
|
+
diagnostic: createRouteConflictDiagnostic(conflict.label, conflict.routes)
|
|
3328
|
+
};
|
|
3329
|
+
}
|
|
3330
|
+
const usedPaths = /* @__PURE__ */ new Set();
|
|
3331
|
+
return routes.flatMap((route) => {
|
|
3332
|
+
const conflict = conflictedRoutes.get(route);
|
|
3333
|
+
if (!conflict) return [route];
|
|
3334
|
+
if (usedPaths.has(route.path)) return [];
|
|
3335
|
+
usedPaths.add(route.path);
|
|
3336
|
+
return [createConflictRoute(route, conflict)];
|
|
3337
|
+
});
|
|
3338
|
+
}
|
|
3339
|
+
function handleRouteConflicts(routes, development) {
|
|
3340
|
+
if (development) return createDevRouteConflictRoutes(routes);
|
|
3341
|
+
assertNoRouteConflicts(routes);
|
|
3342
|
+
return routes;
|
|
3343
|
+
}
|
|
3344
|
+
async function resolveRouteState(routes, plugins, ctx, development) {
|
|
3345
|
+
const tabs = ctx.projectConfig.navigation?.tabs;
|
|
3346
|
+
const configuredRoutes = applyConfiguredPageRoutePaths(routes, tabs, ctx.projectConfig.locales);
|
|
3347
|
+
const resolved = await runHooks(plugins, "routes:resolved", {
|
|
3348
|
+
routes: configuredRoutes,
|
|
3349
|
+
navigation: buildNavigationTree(configuredRoutes, ctx)
|
|
3350
|
+
}, ctx);
|
|
3351
|
+
const finalRoutes = handleRouteConflicts(resolved.routes, development);
|
|
3352
|
+
return {
|
|
3353
|
+
routes: finalRoutes,
|
|
3354
|
+
navigation: finalRoutes === resolved.routes ? resolved.navigation : buildNavigationTree(finalRoutes, ctx)
|
|
3355
|
+
};
|
|
3356
|
+
}
|
|
3357
|
+
//#endregion
|
|
3167
3358
|
//#region source/core/runtime/env-types.ts
|
|
3168
3359
|
var GENERATED_FILE_NAME = "clarify-env.d.ts";
|
|
3169
3360
|
/**
|
|
@@ -3704,7 +3895,7 @@ var ClarifyEngine = class {
|
|
|
3704
3895
|
async discoverSite() {
|
|
3705
3896
|
const plugins = this.ctx.plugins;
|
|
3706
3897
|
const contentRoot = this.ctx.contentRoot;
|
|
3707
|
-
const { locales
|
|
3898
|
+
const { locales } = this.ctx.projectConfig;
|
|
3708
3899
|
let routes = await runPhase(plugins, "site:discover", this.ctx, async () => {
|
|
3709
3900
|
const options = { contentProcessor: createProjectContentProcessor(plugins, this.ctx) };
|
|
3710
3901
|
const discovered = locales ? await findLocalizedContentRoutes(contentRoot, locales, options) : await findContentRoutes(contentRoot, contentRoot, options);
|
|
@@ -3712,43 +3903,10 @@ var ClarifyEngine = class {
|
|
|
3712
3903
|
contentRoot,
|
|
3713
3904
|
routes: discovered
|
|
3714
3905
|
}, this.ctx);
|
|
3715
|
-
return runHooks(plugins, "routes:discovered", result.routes, this.ctx);
|
|
3906
|
+
return await runHooks(plugins, "routes:discovered", result.routes, this.ctx);
|
|
3716
3907
|
});
|
|
3717
|
-
routes = await runPhase(plugins, "content:process", this.ctx,
|
|
3718
|
-
|
|
3719
|
-
path: route.path,
|
|
3720
|
-
filePath: route.source.filePath,
|
|
3721
|
-
frontmatter: route.source.frontmatter ?? {},
|
|
3722
|
-
content: route.source.content ?? ""
|
|
3723
|
-
}));
|
|
3724
|
-
const resolvedPages = await runHooks(plugins, "pages:resolved", pages, this.ctx);
|
|
3725
|
-
const pageByPath = new Map(resolvedPages.map((p) => [p.path, p]));
|
|
3726
|
-
return routes.map((route) => {
|
|
3727
|
-
const page = pageByPath.get(route.path);
|
|
3728
|
-
if (!page) return route;
|
|
3729
|
-
return {
|
|
3730
|
-
...route,
|
|
3731
|
-
source: {
|
|
3732
|
-
...route.source,
|
|
3733
|
-
frontmatter: page.frontmatter,
|
|
3734
|
-
content: page.content
|
|
3735
|
-
}
|
|
3736
|
-
};
|
|
3737
|
-
});
|
|
3738
|
-
});
|
|
3739
|
-
const tabs = navigationConfig?.tabs;
|
|
3740
|
-
routes = applyConfiguredPageRoutePaths(routes, tabs, locales);
|
|
3741
|
-
const navigation = tabs ? locales ? buildLocalizedNavigationFromTabsConfig(routes, tabs, locales) ?? {
|
|
3742
|
-
kind: "localized-tabbed",
|
|
3743
|
-
locales: {}
|
|
3744
|
-
} : buildNavigationFromTabsConfig(routes, tabs) : {
|
|
3745
|
-
kind: "flat",
|
|
3746
|
-
nodes: buildNavigation(routes)
|
|
3747
|
-
};
|
|
3748
|
-
const resolved = await runHooks(plugins, "routes:resolved", {
|
|
3749
|
-
routes,
|
|
3750
|
-
navigation
|
|
3751
|
-
}, this.ctx);
|
|
3908
|
+
routes = await runPhase(plugins, "content:process", this.ctx, () => resolveRoutePages(routes, plugins, this.ctx));
|
|
3909
|
+
const resolved = await resolveRouteState(routes, plugins, this.ctx, this.runtime.command === "serve");
|
|
3752
3910
|
this.ctx.routes = resolved.routes;
|
|
3753
3911
|
this.ctx.navigation = resolved.navigation;
|
|
3754
3912
|
}
|
|
@@ -3798,8 +3956,8 @@ var ClarifyEngine = class {
|
|
|
3798
3956
|
return this.buildEnabled;
|
|
3799
3957
|
}
|
|
3800
3958
|
async configureDevServer(server) {
|
|
3801
|
-
|
|
3802
|
-
|
|
3959
|
+
return runPhase(this.plugins, "dev:server", this.ctx, () => {
|
|
3960
|
+
return runDevConfigureServerHooks(this.plugins, server, this.ctx);
|
|
3803
3961
|
});
|
|
3804
3962
|
}
|
|
3805
3963
|
async runBuildDone() {
|
|
@@ -3924,6 +4082,12 @@ Object.defineProperty(exports, "deserializeSearchIndex", {
|
|
|
3924
4082
|
return deserializeSearchIndex;
|
|
3925
4083
|
}
|
|
3926
4084
|
});
|
|
4085
|
+
Object.defineProperty(exports, "findRouteConflicts", {
|
|
4086
|
+
enumerable: true,
|
|
4087
|
+
get: function() {
|
|
4088
|
+
return findRouteConflicts;
|
|
4089
|
+
}
|
|
4090
|
+
});
|
|
3927
4091
|
Object.defineProperty(exports, "searchMcpIndex", {
|
|
3928
4092
|
enumerable: true,
|
|
3929
4093
|
get: function() {
|
|
@@ -3931,4 +4095,4 @@ Object.defineProperty(exports, "searchMcpIndex", {
|
|
|
3931
4095
|
}
|
|
3932
4096
|
});
|
|
3933
4097
|
|
|
3934
|
-
//# sourceMappingURL=engine-
|
|
4098
|
+
//# sourceMappingURL=engine-DVw8CV5R.cjs.map
|