@clarify-labs/cli 0.11.4 → 0.11.5
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-DuiJVdO8.js} +378 -220
- package/output/engine-DuiJVdO8.js.map +1 -0
- package/output/{engine-Cgc71w13.cjs → engine-VgAP_Ms6.cjs} +383 -219
- package/output/engine-VgAP_Ms6.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
|
@@ -81,7 +81,7 @@ var rehypePlugins = [
|
|
|
81
81
|
];
|
|
82
82
|
function createContentCompileDiagnostic(options) {
|
|
83
83
|
const { format, phase, error, filePath, projectRoot } = options;
|
|
84
|
-
const label = format === "
|
|
84
|
+
const label = format === "markdown+jsx" ? "MDX" : "Markdown";
|
|
85
85
|
return createContentDiagnostic({
|
|
86
86
|
kind: format,
|
|
87
87
|
title: `${label} ${phase} error`,
|
|
@@ -127,7 +127,7 @@ async function compileMdxContent(content, options = {}) {
|
|
|
127
127
|
return {
|
|
128
128
|
ok: false,
|
|
129
129
|
diagnostic: createContentCompileDiagnostic({
|
|
130
|
-
format: "
|
|
130
|
+
format: "markdown+jsx",
|
|
131
131
|
phase: "syntax",
|
|
132
132
|
error,
|
|
133
133
|
filePath,
|
|
@@ -137,6 +137,21 @@ async function compileMdxContent(content, options = {}) {
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
//#endregion
|
|
140
|
+
//#region source/core/runtime/module-ids.ts
|
|
141
|
+
var PAGE_PREFIX = "virtual:clarify-page/";
|
|
142
|
+
var CONTENT_PREFIX = "virtual:clarify-content/";
|
|
143
|
+
function pageVirtualModuleId(path) {
|
|
144
|
+
return `${PAGE_PREFIX}${path.replace(/^\/+/, "").replace(/\/index$/, "").replace(/\/+/g, "/") || "index"}`;
|
|
145
|
+
}
|
|
146
|
+
function pageVirtualModuleIdFromRef(ref) {
|
|
147
|
+
return pageVirtualModuleId(ref.replace(/\.mdx?$/, "").replace(/\.openapi\.(json|yaml|yml)$/, ""));
|
|
148
|
+
}
|
|
149
|
+
function contentVirtualModuleId(filePath, contentRoot) {
|
|
150
|
+
const sourcePath = relative(contentRoot, filePath).replace(/\\/g, "/");
|
|
151
|
+
if (!sourcePath || sourcePath.startsWith("../") || isAbsolute(sourcePath)) throw new Error(`Content source must be inside the content root: ${filePath}`);
|
|
152
|
+
return `${CONTENT_PREFIX}${sourcePath}`;
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
140
155
|
//#region source/core/plugin/hooks.ts
|
|
141
156
|
async function runHooks(plugins, hookName, input, ctx) {
|
|
142
157
|
let result = input;
|
|
@@ -152,32 +167,43 @@ async function runHooks(plugins, hookName, input, ctx) {
|
|
|
152
167
|
return result;
|
|
153
168
|
}
|
|
154
169
|
async function runDevConfigureServerHooks(plugins, server, ctx) {
|
|
170
|
+
const postHooks = [];
|
|
155
171
|
for (const plugin of plugins) {
|
|
156
172
|
const hook = plugin.hooks?.["dev:configureServer"];
|
|
157
173
|
if (!hook) continue;
|
|
158
174
|
try {
|
|
159
|
-
await hook(server, ctx);
|
|
175
|
+
const postHook = await hook(server, ctx);
|
|
176
|
+
if (postHook) postHooks.push(postHook);
|
|
160
177
|
} catch (err) {
|
|
161
178
|
throw new Error(`[clarify] plugin "${plugin.name}" hook "dev:configureServer" failed: ${err}`, { cause: err });
|
|
162
179
|
}
|
|
163
180
|
}
|
|
181
|
+
return postHooks;
|
|
164
182
|
}
|
|
165
|
-
async function
|
|
166
|
-
const
|
|
183
|
+
async function runBuildAssetsHooks(plugins, ctx) {
|
|
184
|
+
const assets = [];
|
|
185
|
+
const producerByFileName = /* @__PURE__ */ new Map();
|
|
167
186
|
for (const plugin of plugins) {
|
|
168
|
-
const hook = plugin.hooks?.[
|
|
187
|
+
const hook = plugin.hooks?.["build:assets"];
|
|
169
188
|
if (!hook) continue;
|
|
189
|
+
let pluginAssets;
|
|
170
190
|
try {
|
|
171
|
-
|
|
172
|
-
results.push(...pluginResults);
|
|
191
|
+
pluginAssets = await hook(ctx);
|
|
173
192
|
} catch (err) {
|
|
174
|
-
throw new Error(`[clarify] plugin "${plugin.name}" hook "
|
|
193
|
+
throw new Error(`[clarify] plugin "${plugin.name}" hook "build:assets" failed: ${err}`, { cause: err });
|
|
194
|
+
}
|
|
195
|
+
for (const asset of pluginAssets) {
|
|
196
|
+
const fileName = asset.fileName.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
197
|
+
const previousProducer = producerByFileName.get(fileName);
|
|
198
|
+
if (previousProducer) throw new Error(`[clarify] build asset "${fileName}" is emitted by both plugin "${previousProducer}" and plugin "${plugin.name}"`);
|
|
199
|
+
producerByFileName.set(fileName, plugin.name);
|
|
200
|
+
assets.push({
|
|
201
|
+
...asset,
|
|
202
|
+
fileName
|
|
203
|
+
});
|
|
175
204
|
}
|
|
176
205
|
}
|
|
177
|
-
return
|
|
178
|
-
}
|
|
179
|
-
async function runBuildAssetsHooks(plugins, ctx) {
|
|
180
|
-
return runCollectorHooks(plugins, "build:assets", ctx);
|
|
206
|
+
return assets;
|
|
181
207
|
}
|
|
182
208
|
async function runBuildDoneHooks(plugins, ctx) {
|
|
183
209
|
for (const plugin of plugins) {
|
|
@@ -212,7 +238,7 @@ function createContentProcessor(transform) {
|
|
|
212
238
|
async processMdx(source, filePath) {
|
|
213
239
|
const { frontmatter, content } = parseFrontmatter(source);
|
|
214
240
|
const transformed = await transformContent(transform, {
|
|
215
|
-
kind: "
|
|
241
|
+
kind: "markdown+jsx",
|
|
216
242
|
source,
|
|
217
243
|
filePath,
|
|
218
244
|
frontmatter,
|
|
@@ -364,9 +390,6 @@ function openAPIPagePathFromRef(ref, tagFilter, explicitPath) {
|
|
|
364
390
|
function routePathFromRef(ref, explicitPath) {
|
|
365
391
|
return explicitPath ? normalizePath(explicitPath) : basePathFromRef(ref);
|
|
366
392
|
}
|
|
367
|
-
function virtualModuleIdFromRef(ref) {
|
|
368
|
-
return "virtual:clarify-page/" + ref.replace(/\.mdx?$/, "").replace(/\.openapi\.(json|yaml|yml)$/, "").replace(/\/+/g, "/");
|
|
369
|
-
}
|
|
370
393
|
function localizedRoutePath(basePath, locale, _i18n) {
|
|
371
394
|
const normalizedBasePath = normalizePath(basePath);
|
|
372
395
|
const prefix = normalizePath(locale);
|
|
@@ -409,7 +432,7 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
409
432
|
title = kebabToTitle(lastPart === "index" ? pathParts.length >= 2 ? pathParts[pathParts.length - 2] : extractH1(page.content) : lastPart) || "Untitled";
|
|
410
433
|
}
|
|
411
434
|
routes.push({
|
|
412
|
-
kind: "mdx",
|
|
435
|
+
kind: entry.name.toLowerCase().endsWith(".mdx") ? "markdown+jsx" : "markdown",
|
|
413
436
|
path: cleanPath,
|
|
414
437
|
basePath: cleanPath,
|
|
415
438
|
meta: {
|
|
@@ -418,7 +441,10 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
418
441
|
keywords: frontmatterKeywords(page.frontmatter),
|
|
419
442
|
sections: extractMdxSections(page.content)
|
|
420
443
|
},
|
|
421
|
-
module: {
|
|
444
|
+
module: {
|
|
445
|
+
pageVirtualModuleId: pageVirtualModuleId(relativePath.replace(/\.mdx?$/, "")),
|
|
446
|
+
contentVirtualModuleId: contentVirtualModuleId(fullPath, base)
|
|
447
|
+
},
|
|
422
448
|
source: {
|
|
423
449
|
filePath: fullPath,
|
|
424
450
|
frontmatter: page.frontmatter,
|
|
@@ -430,14 +456,24 @@ async function findContentRoutes(dir, base = dir, options = {}) {
|
|
|
430
456
|
}
|
|
431
457
|
return routes;
|
|
432
458
|
}
|
|
433
|
-
function
|
|
434
|
-
return
|
|
459
|
+
function pageVirtualModuleIdForLocalizedRoute(contentRoot, filePath) {
|
|
460
|
+
return pageVirtualModuleIdFromRef(relative(contentRoot, filePath));
|
|
435
461
|
}
|
|
436
462
|
function frontmatterKeywords(frontmatter) {
|
|
437
463
|
const value = frontmatter.keywords ?? frontmatter.keyword ?? frontmatter.tags;
|
|
438
464
|
const keywords = Array.isArray(value) ? value.filter((item) => typeof item === "string") : typeof value === "string" ? value.split(",").map((keyword) => keyword.trim()).filter(Boolean) : [];
|
|
439
465
|
return keywords.length > 0 ? keywords : void 0;
|
|
440
466
|
}
|
|
467
|
+
function withPageIdentity(route, path) {
|
|
468
|
+
return {
|
|
469
|
+
...route,
|
|
470
|
+
path,
|
|
471
|
+
module: {
|
|
472
|
+
...route.module,
|
|
473
|
+
pageVirtualModuleId: pageVirtualModuleId(path)
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
}
|
|
441
477
|
function withAlternates(route, routes, locales) {
|
|
442
478
|
const basePath = route.basePath ?? route.path;
|
|
443
479
|
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]));
|
|
@@ -463,8 +499,7 @@ function generateBareAliases(routes, locales) {
|
|
|
463
499
|
if (bp === route.path || seenBare.has(bp)) continue;
|
|
464
500
|
seenBare.add(bp);
|
|
465
501
|
bareRoutes.push({
|
|
466
|
-
...route,
|
|
467
|
-
path: bp,
|
|
502
|
+
...withPageIdentity(route, bp),
|
|
468
503
|
isBareAlias: true
|
|
469
504
|
});
|
|
470
505
|
}
|
|
@@ -483,7 +518,11 @@ async function findLocalizedContentRoutes(contentRoot, locales, options = {}) {
|
|
|
483
518
|
path: localizedRoutePath(basePath, locale.code, locales),
|
|
484
519
|
basePath,
|
|
485
520
|
locale: locale.code,
|
|
486
|
-
module: {
|
|
521
|
+
module: {
|
|
522
|
+
...route.module,
|
|
523
|
+
pageVirtualModuleId: pageVirtualModuleIdForLocalizedRoute(contentRoot, route.source.filePath),
|
|
524
|
+
contentVirtualModuleId: contentVirtualModuleId(route.source.filePath, contentRoot)
|
|
525
|
+
}
|
|
487
526
|
});
|
|
488
527
|
}
|
|
489
528
|
}
|
|
@@ -495,9 +534,9 @@ async function findLocalizedContentRoutes(contentRoot, locales, options = {}) {
|
|
|
495
534
|
for (const locale of locales.locales) {
|
|
496
535
|
const key = `${locale.code}:${basePath}`;
|
|
497
536
|
if (routeByLocaleAndBase.has(key)) continue;
|
|
537
|
+
const path = localizedRoutePath(basePath, locale.code, locales);
|
|
498
538
|
localizedRoutes.push({
|
|
499
|
-
...sourceRoute,
|
|
500
|
-
path: localizedRoutePath(basePath, locale.code, locales),
|
|
539
|
+
...withPageIdentity(sourceRoute, path),
|
|
501
540
|
locale: locale.code,
|
|
502
541
|
isFallback: true
|
|
503
542
|
});
|
|
@@ -592,13 +631,13 @@ function applyConfiguredPageRoutePaths(routes, tabs, locales) {
|
|
|
592
631
|
const targetBasePath = routePathFromRef(intent.ref, intent.path);
|
|
593
632
|
if (sourceBasePath === targetBasePath) continue;
|
|
594
633
|
for (const route of routes) {
|
|
595
|
-
if (route.kind
|
|
634
|
+
if (route.kind === "openapi" || (route.basePath ?? route.path) !== sourceBasePath) continue;
|
|
596
635
|
const key = `${route.locale ?? ""}:${targetBasePath}`;
|
|
597
636
|
if (existingKeys.has(key)) continue;
|
|
598
637
|
existingKeys.add(key);
|
|
638
|
+
const path = route.locale && locales ? localizedRoutePath(targetBasePath, route.locale, locales) : targetBasePath;
|
|
599
639
|
additions.push({
|
|
600
|
-
...route,
|
|
601
|
-
path: route.locale && locales ? localizedRoutePath(targetBasePath, route.locale, locales) : targetBasePath,
|
|
640
|
+
...withPageIdentity(route, path),
|
|
602
641
|
basePath: targetBasePath
|
|
603
642
|
});
|
|
604
643
|
}
|
|
@@ -606,7 +645,8 @@ function applyConfiguredPageRoutePaths(routes, tabs, locales) {
|
|
|
606
645
|
const nextRoutes = [...routes, ...additions];
|
|
607
646
|
const routesWithAlternates = locales ? nextRoutes.map((route) => withAlternates(route, nextRoutes, locales)) : nextRoutes;
|
|
608
647
|
if (!locales) return routesWithAlternates;
|
|
609
|
-
|
|
648
|
+
const markdownRoutes = routesWithAlternates.filter((route) => route.kind !== "openapi");
|
|
649
|
+
return [...routesWithAlternates, ...generateBareAliases(markdownRoutes, locales)];
|
|
610
650
|
}
|
|
611
651
|
function buildNavigationFromPagesConfig(routes, config) {
|
|
612
652
|
if (!config || config === "FileTree") return buildNavigation(routes);
|
|
@@ -800,7 +840,7 @@ function inferRouteForFile(filePath, contentRoot, defaultLocale) {
|
|
|
800
840
|
filePath,
|
|
801
841
|
locale: fileLocale,
|
|
802
842
|
basePath: normalizePath(basePath),
|
|
803
|
-
kind: isMdx ? "
|
|
843
|
+
kind: isMdx ? "markdown+jsx" : "openapi",
|
|
804
844
|
title,
|
|
805
845
|
inferred: true
|
|
806
846
|
};
|
|
@@ -1219,6 +1259,9 @@ function resolveVirtualId(id) {
|
|
|
1219
1259
|
function stripVirtualPrefix(id) {
|
|
1220
1260
|
return id.startsWith("\0") ? id.slice(1) : id;
|
|
1221
1261
|
}
|
|
1262
|
+
function virtualContentModuleId(route) {
|
|
1263
|
+
return route.module.contentVirtualModuleId;
|
|
1264
|
+
}
|
|
1222
1265
|
/**
|
|
1223
1266
|
* Fixed virtual module IDs (excluding the client entry, which has a special
|
|
1224
1267
|
* resolved form). Used to avoid hardcoding the constant list in every
|
|
@@ -1246,8 +1289,10 @@ function resolveVirtualModuleId(id, modules, routes) {
|
|
|
1246
1289
|
for (const virtualId of FIXED_VIRTUAL_IDS) if (id === virtualId || id === resolveVirtualId(virtualId)) return resolveVirtualId(virtualId);
|
|
1247
1290
|
const moduleId = stripVirtualPrefix(id);
|
|
1248
1291
|
if (modules.has(moduleId)) return resolveVirtualId(moduleId);
|
|
1249
|
-
const
|
|
1250
|
-
if (
|
|
1292
|
+
const contentRoute = routes.find((route) => (route.kind === "markdown" || route.kind === "markdown+jsx") && route.module.contentVirtualModuleId === moduleId);
|
|
1293
|
+
if (contentRoute) return contentRoute.source.filePath;
|
|
1294
|
+
const route = routes.find((route) => route.module.pageVirtualModuleId === id || route.module.pageVirtualModuleId === moduleId);
|
|
1295
|
+
if (route) return resolveVirtualId(route.module.pageVirtualModuleId);
|
|
1251
1296
|
return null;
|
|
1252
1297
|
}
|
|
1253
1298
|
function generateConfigModule(projectConfig, version) {
|
|
@@ -1301,8 +1346,8 @@ function routeToRuntimeManifestEntry(route, component, mode) {
|
|
|
1301
1346
|
return entry;
|
|
1302
1347
|
}
|
|
1303
1348
|
function generateRoutesModule(routes, navigation, mode = "client") {
|
|
1304
|
-
return `${mode === "server" ? routes.map((r, i) => `import Page${i} from ${moduleSpecifier(r.module.
|
|
1305
|
-
const component = mode === "server" ? `Page${i}` : `() => import(${moduleSpecifier(r.module.
|
|
1349
|
+
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) => {
|
|
1350
|
+
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 }) }))`;
|
|
1306
1351
|
const placeholder = `__COMPONENT_${i}__`;
|
|
1307
1352
|
const manifestEntry = routeToRuntimeManifestEntry(r, placeholder, mode);
|
|
1308
1353
|
return ` ${JSON.stringify(manifestEntry, null, 0).replace(`"${placeholder}"`, component).replace(/"([A-Za-z_$][\w$]*)":/g, "$1: ")}`;
|
|
@@ -1374,13 +1419,14 @@ function buildVirtualModules(args) {
|
|
|
1374
1419
|
modules.set(VIRTUAL_ROUTES, generateRoutesModule(args.routes, args.navigation, "client"));
|
|
1375
1420
|
modules.set(VIRTUAL_SERVER_ROUTES, generateRoutesModule(args.routes, args.navigation, "server"));
|
|
1376
1421
|
modules.set(VIRTUAL_SLOTS, createRuntimeSlotsModule(allPlugins, args.generateOptions.projectRoot));
|
|
1377
|
-
modules.set(VIRTUAL_OPENAPI, "export const openApiSpecs = {};");
|
|
1378
1422
|
modules.set(VIRTUAL_SLOT, "export { useSlot } from '@clarify-labs/renderer';\n");
|
|
1379
|
-
modules.set(
|
|
1423
|
+
modules.set(VIRTUAL_OPENAPI, "export const openApiSpecs = {};");
|
|
1380
1424
|
modules.set(RESOLVED_CLIENT_ENTRY, clientEntryModule);
|
|
1425
|
+
modules.set(VIRTUAL_CLIENT_ENTRY, clientEntryModule);
|
|
1381
1426
|
for (const route of args.routes) {
|
|
1382
|
-
const moduleContent = route.diagnostic ? generateContentDiagnosticModule(route.diagnostic) : `export { default } from ${moduleSpecifier(route
|
|
1383
|
-
|
|
1427
|
+
const moduleContent = route.diagnostic ? generateContentDiagnosticModule(route.diagnostic) : route.kind === "markdown" || route.kind === "markdown+jsx" ? `export { default } from ${moduleSpecifier(virtualContentModuleId(route))};` : null;
|
|
1428
|
+
if (moduleContent === null) continue;
|
|
1429
|
+
modules.set(route.module.pageVirtualModuleId, moduleContent);
|
|
1384
1430
|
}
|
|
1385
1431
|
return modules;
|
|
1386
1432
|
}
|
|
@@ -1421,7 +1467,7 @@ function createNormalizedContentPlugin(engine) {
|
|
|
1421
1467
|
transform(_code, id) {
|
|
1422
1468
|
if (!/\.mdx?(?:\?|$)/.test(id)) return null;
|
|
1423
1469
|
const filePath = id.replace(/\?.*$/, "");
|
|
1424
|
-
const route = engine.routes.find((route) => route.kind === "
|
|
1470
|
+
const route = engine.routes.find((route) => (route.kind === "markdown+jsx" || route.kind === "markdown") && route.source.filePath === filePath);
|
|
1425
1471
|
if (!route || route.source.content === void 0) return null;
|
|
1426
1472
|
return {
|
|
1427
1473
|
code: route.source.content,
|
|
@@ -1536,7 +1582,10 @@ function createClarifyViteCorePlugin(engine, normalizedContentPlugin, markdown,
|
|
|
1536
1582
|
};
|
|
1537
1583
|
server.watcher.on("add", handleContentTreeChange);
|
|
1538
1584
|
server.watcher.on("unlink", handleContentTreeChange);
|
|
1539
|
-
await engine.configureDevServer(server);
|
|
1585
|
+
const postHooks = await engine.configureDevServer(server);
|
|
1586
|
+
if (postHooks.length > 0) return () => {
|
|
1587
|
+
for (const postHook of postHooks) postHook();
|
|
1588
|
+
};
|
|
1540
1589
|
},
|
|
1541
1590
|
transformIndexHtml: {
|
|
1542
1591
|
order: "pre",
|
|
@@ -1591,7 +1640,7 @@ function createViteAdapter(engine) {
|
|
|
1591
1640
|
const errorPolicy = () => contentCompileErrorPolicy;
|
|
1592
1641
|
const normalizedContentPlugin = createNormalizedContentPlugin(engine);
|
|
1593
1642
|
const markdown = createContentCompilerPlugin("markdown", engine.root, errorPolicy);
|
|
1594
|
-
const mdx = createContentCompilerPlugin("
|
|
1643
|
+
const mdx = createContentCompilerPlugin("markdown+jsx", engine.root, errorPolicy);
|
|
1595
1644
|
const compilePolicyPlugin = {
|
|
1596
1645
|
name: "clarify:content-compile-policy",
|
|
1597
1646
|
configResolved(config) {
|
|
@@ -1611,7 +1660,7 @@ function createViteAdapter(engine) {
|
|
|
1611
1660
|
}
|
|
1612
1661
|
var cliPackageVersion = {
|
|
1613
1662
|
name: "@clarify-labs/cli",
|
|
1614
|
-
version: "0.11.
|
|
1663
|
+
version: "0.11.5",
|
|
1615
1664
|
license: "AGPL-3.0-only",
|
|
1616
1665
|
type: "module",
|
|
1617
1666
|
repository: {
|
|
@@ -1711,7 +1760,7 @@ function withUtf8Signature(content) {
|
|
|
1711
1760
|
return `${UTF8_SIGNATURE}${content}`;
|
|
1712
1761
|
}
|
|
1713
1762
|
function shouldUseUtf8Signature(route) {
|
|
1714
|
-
if (route.kind === "
|
|
1763
|
+
if (route.kind === "markdown+jsx" || route.kind === "markdown") return true;
|
|
1715
1764
|
return false;
|
|
1716
1765
|
}
|
|
1717
1766
|
function routeToMarkdownArtifactUrl(routePath) {
|
|
@@ -1783,9 +1832,9 @@ function createLlmsTxt(routes, projectConfig) {
|
|
|
1783
1832
|
const lines = [`# ${projectConfig.title}`, ""];
|
|
1784
1833
|
if (projectConfig.description) lines.push(`> ${projectConfig.description}`, "");
|
|
1785
1834
|
lines.push("This file lists the source-ready Markdown and OpenAPI artifacts for this documentation site.", "");
|
|
1786
|
-
const
|
|
1787
|
-
if (
|
|
1788
|
-
const localizedGroups = groupLlmsTxtRoutesByLocale(
|
|
1835
|
+
const markdownRoutes = routes.filter((route) => (route.kind === "markdown+jsx" || route.kind === "markdown") && isLlmsTxtRoute(route) && !route.isBareAlias);
|
|
1836
|
+
if (markdownRoutes.length > 0) {
|
|
1837
|
+
const localizedGroups = groupLlmsTxtRoutesByLocale(markdownRoutes, projectConfig);
|
|
1789
1838
|
for (const [locale, localeRoutes] of localizedGroups) {
|
|
1790
1839
|
lines.push(`## ${llmsTxtDocsSectionTitle(locale, localizedGroups.size, projectConfig)}`);
|
|
1791
1840
|
for (const route of localeRoutes) {
|
|
@@ -1862,10 +1911,12 @@ function createContentArtifactsPlugin() {
|
|
|
1862
1911
|
return input;
|
|
1863
1912
|
},
|
|
1864
1913
|
"dev:configureServer": (server, ctx) => {
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1914
|
+
return () => {
|
|
1915
|
+
server.middlewares.use((req, res, next) => {
|
|
1916
|
+
if (serveContentArtifacts(req, res, ctx.projectConfig, ctx.routes)) return;
|
|
1917
|
+
next();
|
|
1918
|
+
});
|
|
1919
|
+
};
|
|
1869
1920
|
},
|
|
1870
1921
|
"build:assets": (ctx) => {
|
|
1871
1922
|
const assets = [];
|
|
@@ -2356,7 +2407,7 @@ function findOpenAPIRoutes(dir, base = dir) {
|
|
|
2356
2407
|
basePath: cleanPath,
|
|
2357
2408
|
kind: "openapi",
|
|
2358
2409
|
meta: { title: kebabToTitle(cleanPath.split("/").pop() ?? "API") },
|
|
2359
|
-
module: {
|
|
2410
|
+
module: { pageVirtualModuleId: pageVirtualModuleIdFromRef(ref) },
|
|
2360
2411
|
source: { filePath: fullPath }
|
|
2361
2412
|
});
|
|
2362
2413
|
}
|
|
@@ -2480,34 +2531,7 @@ async function readOpenAPISpec(filePath, contentProcessor, projectRoot) {
|
|
|
2480
2531
|
}
|
|
2481
2532
|
}
|
|
2482
2533
|
//#endregion
|
|
2483
|
-
//#region source/plugins/openapi/
|
|
2484
|
-
var openApiRegistryModuleId = VIRTUAL_OPENAPI;
|
|
2485
|
-
var openApiServerRegistryModuleId = VIRTUAL_OPENAPI_SERVER;
|
|
2486
|
-
var OPENAPI_SPEC_PREFIX = "virtual:clarify/openapi-spec/";
|
|
2487
|
-
function specVirtualModuleId(specKey) {
|
|
2488
|
-
return `${OPENAPI_SPEC_PREFIX}${specKey}`;
|
|
2489
|
-
}
|
|
2490
|
-
function generateOpenAPIRegistryModule(openApiSpecs) {
|
|
2491
|
-
return `export const openApiSpecs = {\n${Object.entries(openApiSpecs).map(([key, moduleId]) => ` ${JSON.stringify(key)}: () => import(${JSON.stringify(moduleId)}),`).join("\n")}\n};`;
|
|
2492
|
-
}
|
|
2493
|
-
function generateOpenAPIServerRegistryModule(openApiSpecs) {
|
|
2494
|
-
return `export const openApiSpecs = ${JSON.stringify(openApiSpecs)};`;
|
|
2495
|
-
}
|
|
2496
|
-
/** Generate a virtual module that exports a single spec as default. */
|
|
2497
|
-
function generateOpenAPISpecModule(spec) {
|
|
2498
|
-
return `export default ${JSON.stringify(spec)};`;
|
|
2499
|
-
}
|
|
2500
|
-
function generateOpenAPIPageModule(opts) {
|
|
2501
|
-
return `import { createOpenApiRouteComponent } from '@clarify-labs/renderer';
|
|
2502
|
-
import spec from ${JSON.stringify(opts.specModuleId)};
|
|
2503
|
-
|
|
2504
|
-
export const routeData = {};
|
|
2505
|
-
|
|
2506
|
-
export default createOpenApiRouteComponent({ ...routeData, spec });
|
|
2507
|
-
`;
|
|
2508
|
-
}
|
|
2509
|
-
//#endregion
|
|
2510
|
-
//#region source/plugins/openapi/index.ts
|
|
2534
|
+
//#region source/plugins/openapi/routes.ts
|
|
2511
2535
|
function collectOpenAPIPageIntents(config) {
|
|
2512
2536
|
const intents = [];
|
|
2513
2537
|
function visitItems(items) {
|
|
@@ -2527,9 +2551,6 @@ function collectOpenAPIPageIntents(config) {
|
|
|
2527
2551
|
for (const tab of config.navigation?.tabs ?? []) visitPages(tab.pages);
|
|
2528
2552
|
return intents;
|
|
2529
2553
|
}
|
|
2530
|
-
function virtualModuleIdFromPath(path) {
|
|
2531
|
-
return `virtual:clarify-page/${path.replace(/^\/+/, "") || "index"}`;
|
|
2532
|
-
}
|
|
2533
2554
|
function cloneOpenAPIRoute(route, overrides = {}) {
|
|
2534
2555
|
return {
|
|
2535
2556
|
...route,
|
|
@@ -2547,7 +2568,7 @@ function cloneOpenAPIRoute(route, overrides = {}) {
|
|
|
2547
2568
|
...overrides
|
|
2548
2569
|
};
|
|
2549
2570
|
}
|
|
2550
|
-
function
|
|
2571
|
+
function discoverOpenAPIRoutes(contentRoot, locales) {
|
|
2551
2572
|
if (!locales) return findOpenAPIRoutes(contentRoot);
|
|
2552
2573
|
const localizedRoutes = [];
|
|
2553
2574
|
for (const locale of locales.locales) {
|
|
@@ -2560,31 +2581,30 @@ function findOpenAPIRoutesForContentRoot(contentRoot, locales) {
|
|
|
2560
2581
|
path,
|
|
2561
2582
|
basePath,
|
|
2562
2583
|
locale: locale.code,
|
|
2563
|
-
module: {
|
|
2584
|
+
module: { pageVirtualModuleId: pageVirtualModuleId(path) }
|
|
2564
2585
|
});
|
|
2565
2586
|
}
|
|
2566
2587
|
}
|
|
2567
|
-
if (locales.missing
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
}
|
|
2588
|
+
if (locales.missing !== "fallback") return localizedRoutes;
|
|
2589
|
+
const routeByLocaleAndBase = new Map(localizedRoutes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
|
|
2590
|
+
const defaultRoutes = localizedRoutes.filter((route) => route.locale === locales.default);
|
|
2591
|
+
for (const sourceRoute of defaultRoutes) {
|
|
2592
|
+
const basePath = sourceRoute.basePath ?? sourceRoute.path;
|
|
2593
|
+
for (const locale of locales.locales) {
|
|
2594
|
+
const key = `${locale.code}:${basePath}`;
|
|
2595
|
+
if (routeByLocaleAndBase.has(key)) continue;
|
|
2596
|
+
const path = localizedRoutePath(basePath, locale.code, locales);
|
|
2597
|
+
localizedRoutes.push(cloneOpenAPIRoute(sourceRoute, {
|
|
2598
|
+
path,
|
|
2599
|
+
locale: locale.code,
|
|
2600
|
+
module: { pageVirtualModuleId: pageVirtualModuleId(path) },
|
|
2601
|
+
isFallback: true
|
|
2602
|
+
}));
|
|
2583
2603
|
}
|
|
2584
2604
|
}
|
|
2585
2605
|
return localizedRoutes;
|
|
2586
2606
|
}
|
|
2587
|
-
function
|
|
2607
|
+
function expandConfiguredOpenAPIRoutes(routes, config) {
|
|
2588
2608
|
const openAPIIntents = collectOpenAPIPageIntents(config);
|
|
2589
2609
|
if (!openAPIIntents.length) return routes;
|
|
2590
2610
|
const additions = [];
|
|
@@ -2603,7 +2623,7 @@ function createConfiguredOpenAPIRoutes(routes, config) {
|
|
|
2603
2623
|
additions.push(cloneOpenAPIRoute(route, {
|
|
2604
2624
|
path,
|
|
2605
2625
|
basePath: targetBasePath,
|
|
2606
|
-
module: {
|
|
2626
|
+
module: { pageVirtualModuleId: pageVirtualModuleId(path) },
|
|
2607
2627
|
openapi: {
|
|
2608
2628
|
...route.openapi,
|
|
2609
2629
|
tagFilter: tagFilter?.length ? tagFilter : void 0
|
|
@@ -2617,19 +2637,47 @@ function createConfiguredOpenAPIRoutes(routes, config) {
|
|
|
2617
2637
|
const bareAliases = [];
|
|
2618
2638
|
const seenBare = new Set(routesWithAlternates.map((route) => route.path));
|
|
2619
2639
|
for (const route of routesWithAlternates) {
|
|
2620
|
-
if (route.locale !== config.locales.default) continue;
|
|
2640
|
+
if (route.kind !== "openapi" || route.locale !== config.locales.default) continue;
|
|
2621
2641
|
const basePath = route.basePath ?? route.path;
|
|
2622
2642
|
if (basePath === route.path || seenBare.has(basePath)) continue;
|
|
2623
2643
|
seenBare.add(basePath);
|
|
2624
2644
|
bareAliases.push(cloneOpenAPIRoute(route, {
|
|
2625
2645
|
path: basePath,
|
|
2626
|
-
module: {
|
|
2646
|
+
module: { pageVirtualModuleId: pageVirtualModuleId(basePath) },
|
|
2627
2647
|
isBareAlias: true
|
|
2628
2648
|
}));
|
|
2629
2649
|
}
|
|
2630
2650
|
return [...routesWithAlternates, ...bareAliases];
|
|
2631
2651
|
}
|
|
2632
|
-
|
|
2652
|
+
//#endregion
|
|
2653
|
+
//#region source/plugins/openapi/virtual-modules.ts
|
|
2654
|
+
var openApiRegistryModuleId = VIRTUAL_OPENAPI;
|
|
2655
|
+
var openApiServerRegistryModuleId = VIRTUAL_OPENAPI_SERVER;
|
|
2656
|
+
var OPENAPI_SPEC_PREFIX = "virtual:clarify/openapi-spec/";
|
|
2657
|
+
function specVirtualModuleId(specKey) {
|
|
2658
|
+
return `${OPENAPI_SPEC_PREFIX}${specKey}`;
|
|
2659
|
+
}
|
|
2660
|
+
function generateOpenAPIRegistryModule(openApiSpecs) {
|
|
2661
|
+
return `export const openApiSpecs = {\n${Object.entries(openApiSpecs).map(([key, moduleId]) => ` ${JSON.stringify(key)}: () => import(${JSON.stringify(moduleId)}),`).join("\n")}\n};`;
|
|
2662
|
+
}
|
|
2663
|
+
function generateOpenAPIServerRegistryModule(openApiSpecs) {
|
|
2664
|
+
return `export const openApiSpecs = ${JSON.stringify(openApiSpecs)};`;
|
|
2665
|
+
}
|
|
2666
|
+
/** Generate a virtual module that exports a single spec as default. */
|
|
2667
|
+
function generateOpenAPISpecModule(spec) {
|
|
2668
|
+
return `export default ${JSON.stringify(spec)};`;
|
|
2669
|
+
}
|
|
2670
|
+
function generateOpenAPIPageModule(opts) {
|
|
2671
|
+
return `import { createOpenApiRouteComponent } from '@clarify-labs/renderer';
|
|
2672
|
+
import spec from ${JSON.stringify(opts.specModuleId)};
|
|
2673
|
+
|
|
2674
|
+
export const routeData = {};
|
|
2675
|
+
|
|
2676
|
+
export default createOpenApiRouteComponent({ ...routeData, spec });
|
|
2677
|
+
`;
|
|
2678
|
+
}
|
|
2679
|
+
//#endregion
|
|
2680
|
+
//#region source/plugins/openapi/state.ts
|
|
2633
2681
|
function sourceSpecIdFromPath(filePath, projectRoot) {
|
|
2634
2682
|
return relative(projectRoot, filePath).replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
|
|
2635
2683
|
}
|
|
@@ -2641,90 +2689,94 @@ function openAPIRegistryKeys(route) {
|
|
|
2641
2689
|
const keys = /* @__PURE__ */ new Set();
|
|
2642
2690
|
const path = (route.path || "/").replace(/^\//, "");
|
|
2643
2691
|
const base = (route.basePath ?? route.path).replace(/^\//, "");
|
|
2644
|
-
if (route.locale) keys.add(
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
if (!route.locale && base) keys.add(`virtual:clarify-page/${base}`);
|
|
2692
|
+
if (route.locale) keys.add(pageVirtualModuleId(`${route.locale}/${base}`));
|
|
2693
|
+
keys.add(pageVirtualModuleId(path));
|
|
2694
|
+
if (!route.locale && base) keys.add(pageVirtualModuleId(base));
|
|
2648
2695
|
return [...keys];
|
|
2649
2696
|
}
|
|
2697
|
+
var OpenAPIPluginState = class {
|
|
2698
|
+
specModules = /* @__PURE__ */ new Map();
|
|
2699
|
+
async enrichRoutes(routes, ctx) {
|
|
2700
|
+
this.specModules.clear();
|
|
2701
|
+
const specByFilePath = /* @__PURE__ */ new Map();
|
|
2702
|
+
for (const route of routes) {
|
|
2703
|
+
if (route.kind !== "openapi") continue;
|
|
2704
|
+
const specFromCache = specByFilePath.get(route.source.filePath);
|
|
2705
|
+
const processor = createProjectContentProcessor(ctx.plugins, ctx);
|
|
2706
|
+
const result = specFromCache ? {
|
|
2707
|
+
ok: true,
|
|
2708
|
+
spec: specFromCache
|
|
2709
|
+
} : await readOpenAPISpec(route.source.filePath, processor, ctx.generateOptions.projectRoot);
|
|
2710
|
+
if (!result.ok) {
|
|
2711
|
+
route.diagnostic = result.diagnostic;
|
|
2712
|
+
route.meta.title = route.meta.title || "OpenAPI parse error";
|
|
2713
|
+
route.meta.sections = [];
|
|
2714
|
+
continue;
|
|
2715
|
+
}
|
|
2716
|
+
const spec = normalizeOpenAPISpecSectionIds(result.spec);
|
|
2717
|
+
specByFilePath.set(route.source.filePath, spec);
|
|
2718
|
+
const sourceSpecId = sourceSpecIdFromPath(route.source.filePath, ctx.generateOptions.projectRoot);
|
|
2719
|
+
route.openapi = {
|
|
2720
|
+
...route.openapi,
|
|
2721
|
+
sourceSpecId
|
|
2722
|
+
};
|
|
2723
|
+
route.meta.title = spec.info?.title ?? route.meta.title;
|
|
2724
|
+
route.meta.description = spec.info?.description ?? route.meta.description;
|
|
2725
|
+
route.meta.sections = extractOpenAPISections(spec, route.openapi.tagFilter);
|
|
2726
|
+
const pageSpec = route.openapi.tagFilter?.length ? filterSpecByTags(spec, route.openapi.tagFilter) : spec;
|
|
2727
|
+
const routeSpecModuleId = routeSpecModuleIdFromSourceSpecId(sourceSpecId, route.openapi.tagFilter);
|
|
2728
|
+
route.openapi.routeSpecModuleId = routeSpecModuleId;
|
|
2729
|
+
this.specModules.set(routeSpecModuleId, pageSpec);
|
|
2730
|
+
route.source.content = JSON.stringify(pageSpec);
|
|
2731
|
+
}
|
|
2732
|
+
return routes;
|
|
2733
|
+
}
|
|
2734
|
+
contributeModules(modules, routes) {
|
|
2735
|
+
const registryEntries = {};
|
|
2736
|
+
const serverRegistryEntries = {};
|
|
2737
|
+
for (const route of routes) {
|
|
2738
|
+
if (route.kind !== "openapi" || !route.openapi?.sourceSpecId) continue;
|
|
2739
|
+
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2740
|
+
const spec = this.specModules.get(routeSpecModuleId);
|
|
2741
|
+
if (!spec) continue;
|
|
2742
|
+
for (const registryKey of openAPIRegistryKeys(route)) {
|
|
2743
|
+
registryEntries[registryKey] = specVirtualModuleId(routeSpecModuleId);
|
|
2744
|
+
serverRegistryEntries[registryKey] = spec;
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
modules.set(openApiRegistryModuleId, generateOpenAPIRegistryModule(registryEntries));
|
|
2748
|
+
modules.set(openApiServerRegistryModuleId, generateOpenAPIServerRegistryModule(serverRegistryEntries));
|
|
2749
|
+
for (const [routeSpecModuleId, spec] of this.specModules) modules.set(specVirtualModuleId(routeSpecModuleId), generateOpenAPISpecModule(spec));
|
|
2750
|
+
for (const route of routes) {
|
|
2751
|
+
if (route.kind !== "openapi") continue;
|
|
2752
|
+
if (route.diagnostic) {
|
|
2753
|
+
modules.set(route.module.pageVirtualModuleId, generateContentDiagnosticModule(route.diagnostic));
|
|
2754
|
+
continue;
|
|
2755
|
+
}
|
|
2756
|
+
if (!route.openapi?.sourceSpecId) continue;
|
|
2757
|
+
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2758
|
+
if (!this.specModules.has(routeSpecModuleId)) continue;
|
|
2759
|
+
modules.set(route.module.pageVirtualModuleId, generateOpenAPIPageModule({ specModuleId: specVirtualModuleId(routeSpecModuleId) }));
|
|
2760
|
+
}
|
|
2761
|
+
return modules;
|
|
2762
|
+
}
|
|
2763
|
+
};
|
|
2764
|
+
//#endregion
|
|
2765
|
+
//#region source/plugins/openapi/index.ts
|
|
2650
2766
|
function createOpenAPIPlugin() {
|
|
2651
|
-
|
|
2652
|
-
const specs = /* @__PURE__ */ new Map();
|
|
2653
|
-
const specModules = /* @__PURE__ */ new Map();
|
|
2767
|
+
const state = new OpenAPIPluginState();
|
|
2654
2768
|
return {
|
|
2655
2769
|
name: "clarify:openapi",
|
|
2656
2770
|
hooks: {
|
|
2657
2771
|
"routes:discover": (input, ctx) => ({
|
|
2658
2772
|
...input,
|
|
2659
|
-
routes: [...input.routes, ...
|
|
2773
|
+
routes: [...input.routes, ...discoverOpenAPIRoutes(input.contentRoot, ctx.projectConfig.locales)]
|
|
2660
2774
|
}),
|
|
2661
2775
|
"routes:discovered": async (routes, ctx) => {
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
const nextRoutes = createConfiguredOpenAPIRoutes(routes, ctx.projectConfig);
|
|
2665
|
-
const specByFilePath = /* @__PURE__ */ new Map();
|
|
2666
|
-
for (const route of nextRoutes.filter((route) => route.kind === "openapi")) {
|
|
2667
|
-
const specFromCache = specByFilePath.get(route.source.filePath);
|
|
2668
|
-
const processor = createProjectContentProcessor(ctx.plugins, ctx);
|
|
2669
|
-
const result = specFromCache ? {
|
|
2670
|
-
ok: true,
|
|
2671
|
-
spec: specFromCache
|
|
2672
|
-
} : await readOpenAPISpec(route.source.filePath, processor, ctx.generateOptions.projectRoot);
|
|
2673
|
-
if (!result.ok) {
|
|
2674
|
-
route.diagnostic = result.diagnostic;
|
|
2675
|
-
route.meta.title = route.meta.title || "OpenAPI parse error";
|
|
2676
|
-
route.meta.sections = [];
|
|
2677
|
-
continue;
|
|
2678
|
-
}
|
|
2679
|
-
const spec = normalizeOpenAPISpecSectionIds(result.spec);
|
|
2680
|
-
specByFilePath.set(route.source.filePath, spec);
|
|
2681
|
-
const sourceSpecId = sourceSpecIdFromPath(route.source.filePath, ctx.generateOptions.projectRoot);
|
|
2682
|
-
route.openapi = {
|
|
2683
|
-
...route.openapi,
|
|
2684
|
-
sourceSpecId
|
|
2685
|
-
};
|
|
2686
|
-
specs.set(sourceSpecId, {
|
|
2687
|
-
spec,
|
|
2688
|
-
filePath: route.source.filePath
|
|
2689
|
-
});
|
|
2690
|
-
route.meta.title = spec.info?.title ?? route.meta.title;
|
|
2691
|
-
route.meta.description = spec.info?.description ?? route.meta.description;
|
|
2692
|
-
route.meta.sections = extractOpenAPISections(spec, route.openapi?.tagFilter);
|
|
2693
|
-
const pageSpec = route.openapi?.tagFilter?.length ? filterSpecByTags(spec, route.openapi.tagFilter) : spec;
|
|
2694
|
-
const routeSpecModuleId = routeSpecModuleIdFromSourceSpecId(sourceSpecId, route.openapi?.tagFilter);
|
|
2695
|
-
route.openapi.routeSpecModuleId = routeSpecModuleId;
|
|
2696
|
-
specModules.set(routeSpecModuleId, pageSpec);
|
|
2697
|
-
route.source.content = JSON.stringify(pageSpec);
|
|
2698
|
-
}
|
|
2699
|
-
return nextRoutes;
|
|
2776
|
+
const expandedRoutes = expandConfiguredOpenAPIRoutes(routes, ctx.projectConfig);
|
|
2777
|
+
return state.enrichRoutes(expandedRoutes, ctx);
|
|
2700
2778
|
},
|
|
2701
|
-
"modules:before": (modules, ctx) =>
|
|
2702
|
-
const registryEntries = {};
|
|
2703
|
-
const serverRegistryEntries = {};
|
|
2704
|
-
for (const route of ctx.routes) {
|
|
2705
|
-
if (route.kind !== "openapi" || !route.openapi?.sourceSpecId) continue;
|
|
2706
|
-
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2707
|
-
const spec = specModules.get(routeSpecModuleId) ?? specs.get(route.openapi.sourceSpecId)?.spec;
|
|
2708
|
-
if (!spec) continue;
|
|
2709
|
-
for (const registryKey of openAPIRegistryKeys(route)) {
|
|
2710
|
-
registryEntries[registryKey] = specVirtualModuleId(routeSpecModuleId);
|
|
2711
|
-
serverRegistryEntries[registryKey] = spec;
|
|
2712
|
-
}
|
|
2713
|
-
}
|
|
2714
|
-
modules.set(openApiRegistryModuleId, generateOpenAPIRegistryModule(registryEntries));
|
|
2715
|
-
modules.set(openApiServerRegistryModuleId, generateOpenAPIServerRegistryModule(serverRegistryEntries));
|
|
2716
|
-
for (const [routeSpecModuleId, spec] of specModules) modules.set(specVirtualModuleId(routeSpecModuleId), generateOpenAPISpecModule(spec));
|
|
2717
|
-
for (const route of ctx.routes) {
|
|
2718
|
-
if (route.kind !== "openapi") continue;
|
|
2719
|
-
if (route.diagnostic) modules.set(route.module.virtualModuleId, generateContentDiagnosticModule(route.diagnostic));
|
|
2720
|
-
else if (route.openapi?.sourceSpecId) {
|
|
2721
|
-
const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
|
|
2722
|
-
if (!specModules.has(routeSpecModuleId)) continue;
|
|
2723
|
-
modules.set(route.module.virtualModuleId, generateOpenAPIPageModule({ specModuleId: specVirtualModuleId(routeSpecModuleId) }));
|
|
2724
|
-
}
|
|
2725
|
-
}
|
|
2726
|
-
return modules;
|
|
2727
|
-
}
|
|
2779
|
+
"modules:before": (modules, ctx) => state.contributeModules(modules, ctx.routes)
|
|
2728
2780
|
}
|
|
2729
2781
|
};
|
|
2730
2782
|
}
|
|
@@ -3133,6 +3185,145 @@ async function resolveProjectContext(options = {}, env = {
|
|
|
3133
3185
|
};
|
|
3134
3186
|
}
|
|
3135
3187
|
//#endregion
|
|
3188
|
+
//#region source/core/routing/route-analysis.ts
|
|
3189
|
+
function routeDescription(route) {
|
|
3190
|
+
return `${route.kind} ${route.source.filePath}`;
|
|
3191
|
+
}
|
|
3192
|
+
function collectConflicts(routes, keyOf) {
|
|
3193
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
3194
|
+
for (const route of routes) {
|
|
3195
|
+
const key = keyOf(route);
|
|
3196
|
+
grouped.set(key, [...grouped.get(key) ?? [], route]);
|
|
3197
|
+
}
|
|
3198
|
+
return grouped;
|
|
3199
|
+
}
|
|
3200
|
+
function describeRouteConflict(route) {
|
|
3201
|
+
return routeDescription(route);
|
|
3202
|
+
}
|
|
3203
|
+
function findRouteConflicts(routes) {
|
|
3204
|
+
const conflicts = [];
|
|
3205
|
+
for (const [path, pathRoutes] of collectConflicts(routes, (route) => route.path)) {
|
|
3206
|
+
if (pathRoutes.length < 2) continue;
|
|
3207
|
+
conflicts.push({
|
|
3208
|
+
kind: "path",
|
|
3209
|
+
key: path,
|
|
3210
|
+
label: `path ${path}`,
|
|
3211
|
+
routes: pathRoutes
|
|
3212
|
+
});
|
|
3213
|
+
}
|
|
3214
|
+
for (const [pageModuleId, moduleRoutes] of collectConflicts(routes, (route) => route.module.pageVirtualModuleId)) {
|
|
3215
|
+
if (moduleRoutes.length < 2) continue;
|
|
3216
|
+
if (moduleRoutes.every((route) => route.path === moduleRoutes[0].path)) continue;
|
|
3217
|
+
conflicts.push({
|
|
3218
|
+
kind: "page-module",
|
|
3219
|
+
key: pageModuleId,
|
|
3220
|
+
label: `page module ${pageModuleId}`,
|
|
3221
|
+
routes: moduleRoutes
|
|
3222
|
+
});
|
|
3223
|
+
}
|
|
3224
|
+
return conflicts;
|
|
3225
|
+
}
|
|
3226
|
+
function formatRouteConflicts(conflicts) {
|
|
3227
|
+
return conflicts.map((conflict) => `- ${conflict.label}: ${conflict.routes.map(describeRouteConflict).join(", ")}`).join("\n");
|
|
3228
|
+
}
|
|
3229
|
+
//#endregion
|
|
3230
|
+
//#region source/core/routing/route-resolution.ts
|
|
3231
|
+
async function resolveRoutePages(routes, plugins, ctx) {
|
|
3232
|
+
const routeToken = Symbol("clarifyRouteToken");
|
|
3233
|
+
const resolvedPages = await runHooks(plugins, "pages:resolved", routes.map((route, routeIndex) => ({
|
|
3234
|
+
path: route.path,
|
|
3235
|
+
filePath: route.source.filePath,
|
|
3236
|
+
frontmatter: route.source.frontmatter ?? {},
|
|
3237
|
+
content: route.source.content ?? "",
|
|
3238
|
+
[routeToken]: routeIndex
|
|
3239
|
+
})), ctx);
|
|
3240
|
+
const pageByRouteIndex = /* @__PURE__ */ new Map();
|
|
3241
|
+
for (const page of resolvedPages) {
|
|
3242
|
+
const routeIndex = page[routeToken];
|
|
3243
|
+
if (!Number.isInteger(routeIndex) || routeIndex < 0 || routeIndex >= routes.length) throw new Error("[clarify] pages:resolved hooks must preserve each page route identity");
|
|
3244
|
+
if (pageByRouteIndex.has(routeIndex)) throw new Error("[clarify] pages:resolved hooks returned the same page route identity more than once");
|
|
3245
|
+
pageByRouteIndex.set(routeIndex, page);
|
|
3246
|
+
}
|
|
3247
|
+
if (pageByRouteIndex.size !== routes.length) throw new Error("[clarify] pages:resolved hooks must return exactly one page for each route");
|
|
3248
|
+
return routes.map((route, routeIndex) => {
|
|
3249
|
+
const page = pageByRouteIndex.get(routeIndex);
|
|
3250
|
+
return {
|
|
3251
|
+
...route,
|
|
3252
|
+
source: {
|
|
3253
|
+
...route.source,
|
|
3254
|
+
frontmatter: page.frontmatter,
|
|
3255
|
+
content: page.content
|
|
3256
|
+
}
|
|
3257
|
+
};
|
|
3258
|
+
});
|
|
3259
|
+
}
|
|
3260
|
+
function buildNavigationTree(routes, ctx) {
|
|
3261
|
+
const tabs = ctx.projectConfig.navigation?.tabs;
|
|
3262
|
+
if (!tabs) return {
|
|
3263
|
+
kind: "flat",
|
|
3264
|
+
nodes: buildNavigation(routes)
|
|
3265
|
+
};
|
|
3266
|
+
if (!ctx.projectConfig.locales) return buildNavigationFromTabsConfig(routes, tabs);
|
|
3267
|
+
return buildLocalizedNavigationFromTabsConfig(routes, tabs, ctx.projectConfig.locales) ?? {
|
|
3268
|
+
kind: "localized-tabbed",
|
|
3269
|
+
locales: {}
|
|
3270
|
+
};
|
|
3271
|
+
}
|
|
3272
|
+
function assertNoRouteConflicts(routes) {
|
|
3273
|
+
const conflicts = findRouteConflicts(routes);
|
|
3274
|
+
if (conflicts.length > 0) throw new Error(`[clarify] Route conflicts prevent the site from being built:\n${formatRouteConflicts(conflicts)}`);
|
|
3275
|
+
}
|
|
3276
|
+
function createRouteConflictDiagnostic(label, routes) {
|
|
3277
|
+
return {
|
|
3278
|
+
kind: "route-load",
|
|
3279
|
+
title: "Route conflict",
|
|
3280
|
+
message: `This page cannot be rendered because ${label} is used by multiple routes.`,
|
|
3281
|
+
details: routes.map(describeRouteConflict).join("\n")
|
|
3282
|
+
};
|
|
3283
|
+
}
|
|
3284
|
+
function createDevRouteConflictRoutes(routes) {
|
|
3285
|
+
const conflicts = findRouteConflicts(routes);
|
|
3286
|
+
if (conflicts.length === 0) return routes;
|
|
3287
|
+
const conflictedRoutes = /* @__PURE__ */ new Map();
|
|
3288
|
+
for (const conflict of conflicts) for (const route of conflict.routes) conflictedRoutes.set(route, conflict);
|
|
3289
|
+
function createConflictRoute(route, conflict) {
|
|
3290
|
+
return {
|
|
3291
|
+
...route,
|
|
3292
|
+
module: {
|
|
3293
|
+
...route.module,
|
|
3294
|
+
pageVirtualModuleId: `${route.module.pageVirtualModuleId}__conflict`
|
|
3295
|
+
},
|
|
3296
|
+
diagnostic: createRouteConflictDiagnostic(conflict.label, conflict.routes)
|
|
3297
|
+
};
|
|
3298
|
+
}
|
|
3299
|
+
const usedPaths = /* @__PURE__ */ new Set();
|
|
3300
|
+
return routes.flatMap((route) => {
|
|
3301
|
+
const conflict = conflictedRoutes.get(route);
|
|
3302
|
+
if (!conflict) return [route];
|
|
3303
|
+
if (usedPaths.has(route.path)) return [];
|
|
3304
|
+
usedPaths.add(route.path);
|
|
3305
|
+
return [createConflictRoute(route, conflict)];
|
|
3306
|
+
});
|
|
3307
|
+
}
|
|
3308
|
+
function handleRouteConflicts(routes, development) {
|
|
3309
|
+
if (development) return createDevRouteConflictRoutes(routes);
|
|
3310
|
+
assertNoRouteConflicts(routes);
|
|
3311
|
+
return routes;
|
|
3312
|
+
}
|
|
3313
|
+
async function resolveRouteState(routes, plugins, ctx, development) {
|
|
3314
|
+
const tabs = ctx.projectConfig.navigation?.tabs;
|
|
3315
|
+
const configuredRoutes = applyConfiguredPageRoutePaths(routes, tabs, ctx.projectConfig.locales);
|
|
3316
|
+
const resolved = await runHooks(plugins, "routes:resolved", {
|
|
3317
|
+
routes: configuredRoutes,
|
|
3318
|
+
navigation: buildNavigationTree(configuredRoutes, ctx)
|
|
3319
|
+
}, ctx);
|
|
3320
|
+
const finalRoutes = handleRouteConflicts(resolved.routes, development);
|
|
3321
|
+
return {
|
|
3322
|
+
routes: finalRoutes,
|
|
3323
|
+
navigation: finalRoutes === resolved.routes ? resolved.navigation : buildNavigationTree(finalRoutes, ctx)
|
|
3324
|
+
};
|
|
3325
|
+
}
|
|
3326
|
+
//#endregion
|
|
3136
3327
|
//#region source/core/runtime/env-types.ts
|
|
3137
3328
|
var GENERATED_FILE_NAME = "clarify-env.d.ts";
|
|
3138
3329
|
/**
|
|
@@ -3673,7 +3864,7 @@ var ClarifyEngine = class {
|
|
|
3673
3864
|
async discoverSite() {
|
|
3674
3865
|
const plugins = this.ctx.plugins;
|
|
3675
3866
|
const contentRoot = this.ctx.contentRoot;
|
|
3676
|
-
const { locales
|
|
3867
|
+
const { locales } = this.ctx.projectConfig;
|
|
3677
3868
|
let routes = await runPhase(plugins, "site:discover", this.ctx, async () => {
|
|
3678
3869
|
const options = { contentProcessor: createProjectContentProcessor(plugins, this.ctx) };
|
|
3679
3870
|
const discovered = locales ? await findLocalizedContentRoutes(contentRoot, locales, options) : await findContentRoutes(contentRoot, contentRoot, options);
|
|
@@ -3681,43 +3872,10 @@ var ClarifyEngine = class {
|
|
|
3681
3872
|
contentRoot,
|
|
3682
3873
|
routes: discovered
|
|
3683
3874
|
}, this.ctx);
|
|
3684
|
-
return runHooks(plugins, "routes:discovered", result.routes, this.ctx);
|
|
3875
|
+
return await runHooks(plugins, "routes:discovered", result.routes, this.ctx);
|
|
3685
3876
|
});
|
|
3686
|
-
routes = await runPhase(plugins, "content:process", this.ctx,
|
|
3687
|
-
|
|
3688
|
-
path: route.path,
|
|
3689
|
-
filePath: route.source.filePath,
|
|
3690
|
-
frontmatter: route.source.frontmatter ?? {},
|
|
3691
|
-
content: route.source.content ?? ""
|
|
3692
|
-
}));
|
|
3693
|
-
const resolvedPages = await runHooks(plugins, "pages:resolved", pages, this.ctx);
|
|
3694
|
-
const pageByPath = new Map(resolvedPages.map((p) => [p.path, p]));
|
|
3695
|
-
return routes.map((route) => {
|
|
3696
|
-
const page = pageByPath.get(route.path);
|
|
3697
|
-
if (!page) return route;
|
|
3698
|
-
return {
|
|
3699
|
-
...route,
|
|
3700
|
-
source: {
|
|
3701
|
-
...route.source,
|
|
3702
|
-
frontmatter: page.frontmatter,
|
|
3703
|
-
content: page.content
|
|
3704
|
-
}
|
|
3705
|
-
};
|
|
3706
|
-
});
|
|
3707
|
-
});
|
|
3708
|
-
const tabs = navigationConfig?.tabs;
|
|
3709
|
-
routes = applyConfiguredPageRoutePaths(routes, tabs, locales);
|
|
3710
|
-
const navigation = tabs ? locales ? buildLocalizedNavigationFromTabsConfig(routes, tabs, locales) ?? {
|
|
3711
|
-
kind: "localized-tabbed",
|
|
3712
|
-
locales: {}
|
|
3713
|
-
} : buildNavigationFromTabsConfig(routes, tabs) : {
|
|
3714
|
-
kind: "flat",
|
|
3715
|
-
nodes: buildNavigation(routes)
|
|
3716
|
-
};
|
|
3717
|
-
const resolved = await runHooks(plugins, "routes:resolved", {
|
|
3718
|
-
routes,
|
|
3719
|
-
navigation
|
|
3720
|
-
}, this.ctx);
|
|
3877
|
+
routes = await runPhase(plugins, "content:process", this.ctx, () => resolveRoutePages(routes, plugins, this.ctx));
|
|
3878
|
+
const resolved = await resolveRouteState(routes, plugins, this.ctx, this.runtime.command === "serve");
|
|
3721
3879
|
this.ctx.routes = resolved.routes;
|
|
3722
3880
|
this.ctx.navigation = resolved.navigation;
|
|
3723
3881
|
}
|
|
@@ -3767,8 +3925,8 @@ var ClarifyEngine = class {
|
|
|
3767
3925
|
return this.buildEnabled;
|
|
3768
3926
|
}
|
|
3769
3927
|
async configureDevServer(server) {
|
|
3770
|
-
|
|
3771
|
-
|
|
3928
|
+
return runPhase(this.plugins, "dev:server", this.ctx, () => {
|
|
3929
|
+
return runDevConfigureServerHooks(this.plugins, server, this.ctx);
|
|
3772
3930
|
});
|
|
3773
3931
|
}
|
|
3774
3932
|
async runBuildDone() {
|
|
@@ -3839,6 +3997,6 @@ function createClarifyEngine(options = {}) {
|
|
|
3839
3997
|
return new ClarifyEngine(options);
|
|
3840
3998
|
}
|
|
3841
3999
|
//#endregion
|
|
3842
|
-
export {
|
|
4000
|
+
export { createClarifyTempDir as a, cliPackageVersion as c, defineConfig as d, findRouteConflicts as i, cliPackageVersionWithCaret as l, createClarifyEngine as n, deserializeSearchIndex as o, createClarifyRuntimeAliases as r, searchMcpIndex as s, ClarifyEngine as t, createViteAdapter as u };
|
|
3843
4001
|
|
|
3844
|
-
//# sourceMappingURL=engine-
|
|
4002
|
+
//# sourceMappingURL=engine-DuiJVdO8.js.map
|