@clarify-labs/cli 0.6.5 → 0.6.7

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.
@@ -35,13 +35,13 @@ let shiki = require("shiki");
35
35
  shiki = __toESM(shiki, 1);
36
36
  let unist_util_visit = require("unist-util-visit");
37
37
  let _clarify_labs_renderer = require("@clarify-labs/renderer");
38
- let zod = require("zod");
39
38
  let remark = require("remark");
40
39
  let gray_matter = require("gray-matter");
41
40
  gray_matter = __toESM(gray_matter, 1);
42
41
  let _apidevtools_swagger_parser = require("@apidevtools/swagger-parser");
43
42
  _apidevtools_swagger_parser = __toESM(_apidevtools_swagger_parser, 1);
44
43
  let node_fs_promises = require("node:fs/promises");
44
+ let zod = require("zod");
45
45
  let node_os = require("node:os");
46
46
  let node_url = require("node:url");
47
47
  let vite = require("vite");
@@ -77,6 +77,11 @@ function rehypeParseCodeBlocks() {
77
77
  const language = getLanguage(node.properties.className);
78
78
  parentNode.properties.language = language;
79
79
  node.properties.language = language;
80
+ for (const key of [
81
+ "title",
82
+ "tag",
83
+ "label"
84
+ ]) if (node.properties[key] !== void 0) parentNode.properties[key] = node.properties[key];
80
85
  });
81
86
  };
82
87
  }
@@ -130,284 +135,25 @@ var rehypePlugins = [
130
135
  rehypeShiki
131
136
  ];
132
137
  //#endregion
133
- //#region source/core/config-schema.ts
134
- var clarifyLogoConfigSchema = zod.z.union([zod.z.string(), zod.z.object({
135
- light: zod.z.string().optional(),
136
- dark: zod.z.string().optional()
137
- })]);
138
- var clarifyFaviconConfigSchema = zod.z.union([zod.z.string(), zod.z.object({
139
- light: zod.z.string().optional(),
140
- dark: zod.z.string().optional()
141
- })]);
142
- var clarifyLocalizedTextSchema = zod.z.union([zod.z.string(), zod.z.record(zod.z.string(), zod.z.string())]);
143
- var clarifyLocaleConfigSchema = zod.z.object({
144
- code: zod.z.string(),
145
- label: zod.z.string(),
146
- dir: zod.z.union([zod.z.literal("ltr"), zod.z.literal("rtl")]).optional()
147
- });
148
- var clarifyI18nConfigSchema = zod.z.object({
149
- defaultLocale: zod.z.string().optional(),
150
- missing: zod.z.union([
151
- zod.z.literal("fallback"),
152
- zod.z.literal("404"),
153
- zod.z.literal("hide")
154
- ]).optional(),
155
- locales: zod.z.array(clarifyLocaleConfigSchema).min(1)
156
- }).superRefine((config, ctx) => {
157
- const localeCodes = /* @__PURE__ */ new Set();
158
- for (const [index, locale] of config.locales.entries()) {
159
- if (localeCodes.has(locale.code)) ctx.addIssue({
160
- code: zod.z.ZodIssueCode.custom,
161
- message: `Duplicate locale code "${locale.code}"`,
162
- path: [
163
- "locales",
164
- index,
165
- "code"
166
- ]
167
- });
168
- localeCodes.add(locale.code);
169
- }
170
- if (config.defaultLocale && !localeCodes.has(config.defaultLocale)) ctx.addIssue({
171
- code: zod.z.ZodIssueCode.custom,
172
- message: "defaultLocale must be one of i18n.locales",
173
- path: ["defaultLocale"]
174
- });
175
- });
176
- var clarifyNavbarLinkSchema = zod.z.object({
177
- label: clarifyLocalizedTextSchema,
178
- href: zod.z.string(),
179
- external: zod.z.boolean().optional()
180
- });
181
- var clarifyBannerConfigSchema = zod.z.object({
182
- content: clarifyLocalizedTextSchema,
183
- dismissible: zod.z.boolean().optional()
184
- });
185
- var clarifyFooterConfigSchema = zod.z.object({
186
- links: zod.z.array(clarifyNavbarLinkSchema).optional(),
187
- socials: zod.z.record(zod.z.string(), zod.z.string()).optional(),
188
- copyright: clarifyLocalizedTextSchema.optional()
189
- });
190
- var clarifySourceConfigSchema = zod.z.object({
191
- repository: zod.z.string(),
192
- branch: zod.z.string().optional(),
193
- directory: zod.z.string().optional()
194
- });
195
- var clarifyPagesItemSchema = zod.z.union([
196
- zod.z.string(),
197
- zod.z.object({
198
- page: zod.z.string(),
199
- title: clarifyLocalizedTextSchema.optional(),
200
- icon: zod.z.string().optional(),
201
- redirect: zod.z.string().optional()
202
- }),
203
- zod.z.object({
204
- openapi: zod.z.string(),
205
- icon: zod.z.string().optional(),
206
- title: clarifyLocalizedTextSchema.optional(),
207
- filter: zod.z.object({ tags: zod.z.array(zod.z.string()).optional() }).optional()
208
- })
209
- ]);
210
- var clarifyPagesGroupSchema = zod.z.object({
211
- group: clarifyLocalizedTextSchema,
212
- icon: zod.z.string().optional(),
213
- pages: zod.z.array(clarifyPagesItemSchema)
214
- });
215
- var clarifyPagesConfigSchema = zod.z.union([zod.z.array(clarifyPagesGroupSchema), zod.z.literal("FileTree")]);
216
- var clarifyTabItemSchema = zod.z.object({
217
- tab: clarifyLocalizedTextSchema,
218
- icon: zod.z.string().optional(),
219
- pages: clarifyPagesConfigSchema.optional()
220
- });
221
- var clarifyTabsConfigSchema = zod.z.array(clarifyTabItemSchema);
222
- var clarifyThemePresetSchema = zod.z.enum(["default", "base"]);
223
- var clarifyThemeModeColorValueSchema = zod.z.object({
224
- light: zod.z.string().optional(),
225
- dark: zod.z.string().optional()
226
- });
227
- var clarifyThemeColorValueSchema = zod.z.union([zod.z.string(), clarifyThemeModeColorValueSchema]);
228
- var clarifyThemeColorTokensConfigSchema = zod.z.object({
229
- primary: clarifyThemeColorValueSchema.optional(),
230
- accent: clarifyThemeColorValueSchema.optional(),
231
- background: clarifyThemeColorValueSchema.optional(),
232
- foreground: clarifyThemeColorValueSchema.optional(),
233
- surface: clarifyThemeColorValueSchema.optional(),
234
- muted: clarifyThemeColorValueSchema.optional(),
235
- border: clarifyThemeColorValueSchema.optional(),
236
- codeBackground: clarifyThemeColorValueSchema.optional()
237
- });
238
- var clarifyThemeRadiusTokensConfigSchema = zod.z.object({
239
- sm: zod.z.string().optional(),
240
- md: zod.z.string().optional(),
241
- lg: zod.z.string().optional(),
242
- xl: zod.z.string().optional()
243
- });
244
- var clarifyThemeTokensConfigSchema = zod.z.object({
245
- colors: clarifyThemeColorTokensConfigSchema.optional(),
246
- radius: clarifyThemeRadiusTokensConfigSchema.optional()
247
- });
248
- var clarifyThemeLayoutConfigSchema = zod.z.object({ maxWidth: zod.z.string().optional() });
249
- var clarifyThemeConfigSchema = zod.z.object({
250
- preset: clarifyThemePresetSchema.optional(),
251
- tokens: clarifyThemeTokensConfigSchema.optional(),
252
- layout: clarifyThemeLayoutConfigSchema.optional(),
253
- editor: zod.z.boolean().optional()
254
- });
255
- var clarifyProjectConfigSchema = zod.z.object({
256
- title: zod.z.string().optional(),
257
- description: zod.z.string().optional(),
258
- siteUrl: zod.z.string().optional(),
259
- source: clarifySourceConfigSchema.optional(),
260
- logo: clarifyLogoConfigSchema.optional(),
261
- favicon: clarifyFaviconConfigSchema.optional(),
262
- theme: clarifyThemeConfigSchema.optional(),
263
- routePrefix: zod.z.string().optional(),
264
- navbar: zod.z.object({ links: zod.z.array(clarifyNavbarLinkSchema).optional() }).optional(),
265
- banner: clarifyBannerConfigSchema.optional(),
266
- footer: clarifyFooterConfigSchema.optional(),
267
- i18n: clarifyI18nConfigSchema.optional(),
268
- tabs: clarifyTabsConfigSchema.optional()
269
- });
270
- //#endregion
271
- //#region source/core/theme.ts
272
- var clarifyThemePresets = {
273
- default: {
274
- preset: "default",
275
- tokens: {
276
- colors: {
277
- primary: "#047857",
278
- accent: "#0D9488",
279
- background: "#ffffff",
280
- foreground: "#111827",
281
- surface: "#ffffff",
282
- muted: "#64748b",
283
- border: "rgb(15 23 42 / 0.12)",
284
- codeBackground: "#f6fbf8"
285
- },
286
- radius: {
287
- sm: "6px",
288
- md: "10px",
289
- lg: "14px",
290
- xl: "18px"
291
- }
292
- },
293
- layout: { maxWidth: "82rem" },
294
- editor: false
295
- },
296
- base: {
297
- preset: "base",
298
- tokens: {
299
- colors: {
300
- primary: "#18181b",
301
- accent: "#52525b",
302
- background: "#ffffff",
303
- foreground: "#18181b",
304
- surface: "#ffffff",
305
- muted: "#71717a",
306
- border: "rgb(24 24 27 / 0.12)",
307
- codeBackground: "#f4f4f5"
308
- },
309
- radius: {
310
- sm: "6px",
311
- md: "8px",
312
- lg: "12px",
313
- xl: "16px"
314
- }
315
- },
316
- layout: { maxWidth: "80rem" },
317
- editor: false
318
- }
319
- };
320
- function resolveThemeConfig(theme = {}) {
321
- const preset = theme.preset ?? "default";
322
- const presetTheme = clarifyThemePresets[preset];
323
- return {
324
- preset,
325
- tokens: {
326
- colors: {
327
- ...presetTheme.tokens.colors,
328
- ...theme.tokens?.colors
329
- },
330
- radius: {
331
- ...presetTheme.tokens.radius,
332
- ...theme.tokens?.radius
333
- }
334
- },
335
- layout: {
336
- ...presetTheme.layout,
337
- ...theme.layout
338
- },
339
- editor: theme.editor ?? presetTheme.editor
340
- };
138
+ //#region source/plugins/content-artifacts/artifacts.ts
139
+ var UTF8_SIGNATURE = "";
140
+ function normalizeBasePath(basePath) {
141
+ if (!basePath || basePath === "/") return "";
142
+ return "/" + basePath.replace(/^\/+|\/+$/g, "");
341
143
  }
342
- //#endregion
343
- //#region source/core/config.ts
344
- function formatIssuePath(path) {
345
- return path.reduce((result, segment) => {
346
- if (typeof segment === "number") return `${result}[${segment}]`;
347
- const key = String(segment);
348
- return result ? `${result}.${key}` : key;
349
- }, "");
144
+ function containsNonAscii(content) {
145
+ return [...content].some((character) => character.codePointAt(0) > 127);
350
146
  }
351
- function formatProjectConfigError(error) {
352
- const issue = error.issues[0];
353
- if (!issue) return "[clarify] config is invalid";
354
- const path = formatIssuePath(issue.path);
355
- if (!path) return `[clarify] config must contain an object: ${issue.message}`;
356
- return `[clarify] config field "${path}" is invalid: ${issue.message}`;
147
+ function withUtf8Signature(content) {
148
+ if (!containsNonAscii(content) || content.startsWith(UTF8_SIGNATURE)) return content;
149
+ return `${UTF8_SIGNATURE}${content}`;
357
150
  }
358
- function validateProjectConfig(value) {
359
- const result = clarifyProjectConfigSchema.safeParse(value);
360
- if (!result.success) throw new Error(formatProjectConfigError(result.error));
361
- return result.data;
151
+ function shouldUseUtf8Signature(route) {
152
+ if (route.kind === "mdx") return true;
153
+ return /\.ya?ml$/i.test(route.contentArtifactUrl ?? "");
362
154
  }
363
- function resolveI18nConfig(i18n) {
364
- if (!i18n) return void 0;
365
- const firstLocale = i18n.locales[0]?.code;
366
- const defaultLocale = i18n.defaultLocale ?? firstLocale;
367
- if (!defaultLocale) return void 0;
368
- return {
369
- defaultLocale,
370
- missing: i18n.missing ?? "fallback",
371
- locales: i18n.locales
372
- };
373
- }
374
- function resolveProjectConfig(config = {}) {
375
- return {
376
- title: config.title ?? "Clarify Docs",
377
- description: config.description ?? "",
378
- siteUrl: config.siteUrl,
379
- source: config.source,
380
- logo: config.logo,
381
- favicon: config.favicon,
382
- routePrefix: config.routePrefix ?? "/",
383
- theme: resolveThemeConfig(config.theme),
384
- navbar: config.navbar,
385
- banner: config.banner,
386
- footer: config.footer,
387
- i18n: resolveI18nConfig(config.i18n),
388
- tabs: config.tabs
389
- };
390
- }
391
- //#endregion
392
- //#region source/plugins/content-artifacts/artifacts.ts
393
- var UTF8_SIGNATURE = "";
394
- function normalizeBasePath(basePath) {
395
- if (!basePath || basePath === "/") return "";
396
- return "/" + basePath.replace(/^\/+|\/+$/g, "");
397
- }
398
- function containsNonAscii(content) {
399
- return [...content].some((character) => character.codePointAt(0) > 127);
400
- }
401
- function withUtf8Signature(content) {
402
- if (!containsNonAscii(content) || content.startsWith(UTF8_SIGNATURE)) return content;
403
- return `${UTF8_SIGNATURE}${content}`;
404
- }
405
- function shouldUseUtf8Signature(route) {
406
- if (route.kind === "mdx") return true;
407
- return /\.ya?ml$/i.test(route.contentArtifactUrl ?? "");
408
- }
409
- function routeToMarkdownArtifactUrl(routePath) {
410
- return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.md`;
155
+ function routeToMarkdownArtifactUrl(routePath) {
156
+ return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.md`;
411
157
  }
412
158
  function routeToOpenAPIArtifactUrl(routePath, filePath) {
413
159
  const extension = (0, node_path.extname)(filePath).toLowerCase() || ".json";
@@ -639,13 +385,14 @@ function basePathFromRef(ref) {
639
385
  const withoutExtension = ref.replace(/\.mdx?$/, "").replace(/\.openapi\.(json|yaml|yml)$/, "");
640
386
  return normalizePath(withoutExtension === "index" ? "/" : withoutExtension.replace(/\/index$/, ""));
641
387
  }
642
- function openAPIPagePathFromRef(ref, tagFilter) {
388
+ function openAPIPagePathFromRef(ref, tagFilter, explicitPath) {
389
+ if (explicitPath) return normalizePath(explicitPath);
643
390
  const basePath = basePathFromRef(ref);
644
391
  if (!tagFilter?.length) return basePath;
645
392
  return normalizePath(`${basePath}/${openAPITagsPathSegment(tagFilter)}`);
646
393
  }
647
- function routePathFromRef(ref) {
648
- return basePathFromRef(ref);
394
+ function routePathFromRef(ref, explicitPath) {
395
+ return explicitPath ? normalizePath(explicitPath) : basePathFromRef(ref);
649
396
  }
650
397
  function virtualModuleIdFromRef(ref) {
651
398
  return "virtual:clarify-page/" + ref.replace(/\.mdx?$/, "").replace(/\.openapi\.(json|yaml|yml)$/, "").replace(/\/+/g, "/");
@@ -699,6 +446,18 @@ function frontmatterKeywords(frontmatter) {
699
446
  const keywords = Array.isArray(value) ? value.filter((item) => typeof item === "string") : typeof value === "string" ? value.split(",").map((keyword) => keyword.trim()).filter(Boolean) : [];
700
447
  return keywords.length > 0 ? keywords : void 0;
701
448
  }
449
+ function withAlternates$2(route, routes, i18n) {
450
+ const basePath = route.basePath ?? route.path;
451
+ const routeByLocaleAndBase = new Map(routes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
452
+ const alternates = Object.fromEntries(i18n.locales.flatMap((locale) => {
453
+ const alternate = routeByLocaleAndBase.get(`${locale.code}:${basePath}`);
454
+ return alternate ? [[locale.code, alternate.path]] : [];
455
+ }));
456
+ return {
457
+ ...route,
458
+ alternates
459
+ };
460
+ }
702
461
  function buildNavigation(routes) {
703
462
  const root = [];
704
463
  for (const route of routes) {
@@ -730,11 +489,13 @@ function resolvePageItem(item) {
730
489
  if ("openapi" in item) return {
731
490
  openapiRef: item.openapi,
732
491
  openapiTagFilter: item.filter?.tags,
492
+ path: item.path,
733
493
  title: item.title,
734
494
  icon: item.icon
735
495
  };
736
496
  return {
737
497
  pageRef: item.page,
498
+ path: item.path,
738
499
  redirect: item.redirect,
739
500
  title: item.title,
740
501
  icon: item.icon
@@ -748,6 +509,41 @@ function firstNavigationPath(nodes) {
748
509
  }
749
510
  return "/";
750
511
  }
512
+ function collectExplicitPagePathItems(tabs) {
513
+ const items = [];
514
+ for (const tab of tabs ?? []) {
515
+ if (!tab.pages || tab.pages === "FileTree") continue;
516
+ for (const group of tab.pages) for (const item of group.pages) if (typeof item !== "string" && "page" in item && item.path) items.push({
517
+ pageRef: item.page,
518
+ path: item.path
519
+ });
520
+ }
521
+ return items;
522
+ }
523
+ function applyConfiguredPageRoutePaths(routes, tabs, i18n) {
524
+ const pageItems = collectExplicitPagePathItems(tabs);
525
+ if (!pageItems.length) return routes;
526
+ const additions = [];
527
+ const existingKeys = new Set(routes.map((route) => `${route.locale ?? ""}:${route.basePath ?? route.path}`));
528
+ for (const item of pageItems) {
529
+ const sourceBasePath = routePathFromRef(item.pageRef);
530
+ const targetBasePath = routePathFromRef(item.pageRef, item.path);
531
+ if (sourceBasePath === targetBasePath) continue;
532
+ for (const route of routes) {
533
+ if (route.kind !== "mdx" || (route.basePath ?? route.path) !== sourceBasePath) continue;
534
+ const key = `${route.locale ?? ""}:${targetBasePath}`;
535
+ if (existingKeys.has(key)) continue;
536
+ existingKeys.add(key);
537
+ additions.push({
538
+ ...route,
539
+ path: route.locale && i18n ? localizedRoutePath(targetBasePath, route.locale, i18n) : targetBasePath,
540
+ basePath: targetBasePath
541
+ });
542
+ }
543
+ }
544
+ const nextRoutes = [...routes, ...additions];
545
+ return i18n ? nextRoutes.map((route) => withAlternates$2(route, nextRoutes, i18n)) : nextRoutes;
546
+ }
751
547
  function buildNavigationFromPagesConfig(routes, config) {
752
548
  if (!config || config === "FileTree") return buildNavigation(routes);
753
549
  return buildNavigationFromConfig(routes, config);
@@ -768,9 +564,9 @@ function buildNavigationFromConfig(routes, config) {
768
564
  const routeMap = new Map(routes.map((r) => [r.path, r]));
769
565
  return config.map((group) => {
770
566
  const children = group.pages.map((item) => {
771
- const { pageRef, openapiRef, openapiTagFilter, redirect, title, icon } = resolvePageItem(item);
567
+ const { pageRef, openapiRef, openapiTagFilter, path: explicitPath, redirect, title, icon } = resolvePageItem(item);
772
568
  if (openapiRef) {
773
- const path = openAPIPagePathFromRef(openapiRef, openapiTagFilter);
569
+ const path = openAPIPagePathFromRef(openapiRef, openapiTagFilter, explicitPath);
774
570
  const route = routeMap.get(path);
775
571
  return {
776
572
  path,
@@ -780,10 +576,10 @@ function buildNavigationFromConfig(routes, config) {
780
576
  };
781
577
  }
782
578
  const ref = pageRef ?? "";
783
- const path = ref === "index" ? "/" : "/" + ref;
579
+ const path = routePathFromRef(ref, explicitPath);
784
580
  const route = routeMap.get(path);
785
581
  return {
786
- path: redirect ? redirect : path,
582
+ path: redirect ? normalizePath(redirect) : path,
787
583
  title: resolveLocalizedText(title, "", "") ?? route?.title ?? kebabToTitle$1(path.split("/").pop() ?? ref),
788
584
  icon,
789
585
  sections: route?.sections ? navigationSections(route.sections) : void 0
@@ -812,13 +608,13 @@ function buildLocalizedNavigationForLocale(routes, config, locale, i18n) {
812
608
  const routeMap = new Map(routes.map((route) => [route.basePath ?? route.path, route]));
813
609
  return config.map((group) => {
814
610
  const children = group.pages.map((item) => {
815
- const { pageRef, openapiRef, openapiTagFilter, redirect, title, icon } = resolvePageItem(item);
611
+ const { pageRef, openapiRef, openapiTagFilter, path: explicitPath, redirect, title, icon } = resolvePageItem(item);
816
612
  const ref = openapiRef ?? pageRef ?? "";
817
- const basePath = openapiRef ? openAPIPagePathFromRef(openapiRef, openapiTagFilter) : basePathFromRef(ref);
613
+ const basePath = openapiRef ? openAPIPagePathFromRef(openapiRef, openapiTagFilter, explicitPath) : routePathFromRef(ref, explicitPath);
818
614
  const route = routeMap.get(basePath);
819
615
  const path = route?.path ?? localizedRoutePath(basePath, locale, i18n);
820
616
  return {
821
- path: (redirect ? localizedRoutePath(basePathFromRef(redirect), locale, i18n) : void 0) ?? path,
617
+ path: (redirect ? localizedRoutePath(routePathFromRef(redirect), locale, i18n) : void 0) ?? path,
822
618
  title: resolveLocalizedText(title, locale, i18n.defaultLocale) ?? route?.title ?? kebabToTitle$1(basePath.split("/").pop() ?? ref),
823
619
  icon,
824
620
  sections: route?.sections ? navigationSections(route.sections) : void 0
@@ -1049,7 +845,7 @@ function collectOpenAPIPageItems(config) {
1049
845
  const items = [];
1050
846
  function visitPages(pages) {
1051
847
  if (!pages || pages === "FileTree") return;
1052
- for (const group of pages) for (const item of group.pages) if (typeof item !== "string" && "openapi" in item && item.filter?.tags?.length) items.push(item);
848
+ for (const group of pages) for (const item of group.pages) if (typeof item !== "string" && "openapi" in item && (item.path || item.filter?.tags?.length)) items.push(item);
1053
849
  }
1054
850
  for (const tab of config.tabs ?? []) visitPages(tab.pages);
1055
851
  return items;
@@ -1066,28 +862,31 @@ function withAlternates$1(route, routes, i18n) {
1066
862
  alternates
1067
863
  };
1068
864
  }
1069
- function createTaggedOpenAPIRoutes(routes, config) {
865
+ function virtualModuleIdFromPath(path) {
866
+ return `virtual:clarify-page/${path.replace(/^\/+/, "") || "index"}`;
867
+ }
868
+ function createConfiguredOpenAPIRoutes(routes, config) {
1070
869
  const pageItems = collectOpenAPIPageItems(config);
1071
870
  if (!pageItems.length) return routes;
1072
871
  const additions = [];
1073
872
  const existingKeys = new Set(routes.map((route) => `${route.locale ?? ""}:${route.basePath ?? route.path}`));
1074
873
  for (const item of pageItems) {
1075
874
  const tagFilter = item.filter?.tags?.filter(Boolean);
1076
- if (!tagFilter?.length) continue;
1077
875
  const sourceBasePath = openAPIPagePathFromRef(item.openapi);
1078
- const filteredBasePath = openAPIPagePathFromRef(item.openapi, tagFilter);
876
+ const targetBasePath = openAPIPagePathFromRef(item.openapi, tagFilter, item.path);
877
+ if (sourceBasePath === targetBasePath && !tagFilter?.length) continue;
1079
878
  for (const route of routes) {
1080
879
  if (route.kind !== "openapi" || (route.basePath ?? route.path) !== sourceBasePath) continue;
1081
- const key = `${route.locale ?? ""}:${filteredBasePath}`;
880
+ const key = `${route.locale ?? ""}:${targetBasePath}`;
1082
881
  if (existingKeys.has(key)) continue;
1083
882
  existingKeys.add(key);
1084
883
  additions.push({
1085
884
  ...route,
1086
- path: route.locale && config.i18n ? localizedRoutePath(filteredBasePath, route.locale, config.i18n) : filteredBasePath,
1087
- basePath: filteredBasePath,
885
+ path: route.locale && config.i18n ? localizedRoutePath(targetBasePath, route.locale, config.i18n) : targetBasePath,
886
+ basePath: targetBasePath,
1088
887
  title: route.title,
1089
- virtualModuleId: `${route.virtualModuleId}/${openAPITagsPathSegment(tagFilter)}`,
1090
- openapiTagFilter: tagFilter
888
+ virtualModuleId: virtualModuleIdFromPath(targetBasePath),
889
+ openapiTagFilter: tagFilter?.length ? tagFilter : void 0
1091
890
  });
1092
891
  }
1093
892
  }
@@ -1105,7 +904,7 @@ function createOpenAPIPlugin() {
1105
904
  }),
1106
905
  "routes:discovered": async (routes, ctx) => {
1107
906
  for (const key of Object.keys(specs)) delete specs[key];
1108
- const nextRoutes = createTaggedOpenAPIRoutes(routes, ctx.projectConfig);
907
+ const nextRoutes = createConfiguredOpenAPIRoutes(routes, ctx.projectConfig);
1109
908
  const specByFilePath = /* @__PURE__ */ new Map();
1110
909
  for (const route of nextRoutes.filter((route) => route.kind === "openapi")) {
1111
910
  const specFromCache = specByFilePath.get(route.filePath);
@@ -1246,6 +1045,267 @@ function createBuiltinPlugins(options = {}) {
1246
1045
  return plugins;
1247
1046
  }
1248
1047
  //#endregion
1048
+ //#region source/core/config-schema.ts
1049
+ var clarifyLogoConfigSchema = zod.z.union([zod.z.string(), zod.z.object({
1050
+ light: zod.z.string().optional(),
1051
+ dark: zod.z.string().optional()
1052
+ })]);
1053
+ var clarifyFaviconConfigSchema = zod.z.union([zod.z.string(), zod.z.object({
1054
+ light: zod.z.string().optional(),
1055
+ dark: zod.z.string().optional()
1056
+ })]);
1057
+ var clarifyLocalizedTextSchema = zod.z.union([zod.z.string(), zod.z.record(zod.z.string(), zod.z.string())]);
1058
+ var clarifyLocaleConfigSchema = zod.z.object({
1059
+ code: zod.z.string(),
1060
+ label: zod.z.string(),
1061
+ dir: zod.z.union([zod.z.literal("ltr"), zod.z.literal("rtl")]).optional()
1062
+ });
1063
+ var clarifyI18nConfigSchema = zod.z.object({
1064
+ defaultLocale: zod.z.string().optional(),
1065
+ missing: zod.z.union([
1066
+ zod.z.literal("fallback"),
1067
+ zod.z.literal("404"),
1068
+ zod.z.literal("hide")
1069
+ ]).optional(),
1070
+ locales: zod.z.array(clarifyLocaleConfigSchema).min(1)
1071
+ }).superRefine((config, ctx) => {
1072
+ const localeCodes = /* @__PURE__ */ new Set();
1073
+ for (const [index, locale] of config.locales.entries()) {
1074
+ if (localeCodes.has(locale.code)) ctx.addIssue({
1075
+ code: zod.z.ZodIssueCode.custom,
1076
+ message: `Duplicate locale code "${locale.code}"`,
1077
+ path: [
1078
+ "locales",
1079
+ index,
1080
+ "code"
1081
+ ]
1082
+ });
1083
+ localeCodes.add(locale.code);
1084
+ }
1085
+ if (config.defaultLocale && !localeCodes.has(config.defaultLocale)) ctx.addIssue({
1086
+ code: zod.z.ZodIssueCode.custom,
1087
+ message: "defaultLocale must be one of i18n.locales",
1088
+ path: ["defaultLocale"]
1089
+ });
1090
+ });
1091
+ var clarifyNavbarLinkSchema = zod.z.object({
1092
+ label: clarifyLocalizedTextSchema,
1093
+ href: zod.z.string(),
1094
+ external: zod.z.boolean().optional()
1095
+ });
1096
+ var clarifyBannerConfigSchema = zod.z.object({
1097
+ content: clarifyLocalizedTextSchema,
1098
+ dismissible: zod.z.boolean().optional()
1099
+ });
1100
+ var clarifyFooterConfigSchema = zod.z.object({
1101
+ links: zod.z.array(clarifyNavbarLinkSchema).optional(),
1102
+ socials: zod.z.record(zod.z.string(), zod.z.string()).optional(),
1103
+ copyright: clarifyLocalizedTextSchema.optional()
1104
+ });
1105
+ var clarifySourceConfigSchema = zod.z.object({
1106
+ repository: zod.z.string(),
1107
+ branch: zod.z.string().optional(),
1108
+ directory: zod.z.string().optional()
1109
+ });
1110
+ var clarifyPagesItemSchema = zod.z.union([
1111
+ zod.z.string(),
1112
+ zod.z.object({
1113
+ page: zod.z.string(),
1114
+ path: zod.z.string().optional(),
1115
+ title: clarifyLocalizedTextSchema.optional(),
1116
+ icon: zod.z.string().optional(),
1117
+ redirect: zod.z.string().optional()
1118
+ }),
1119
+ zod.z.object({
1120
+ openapi: zod.z.string(),
1121
+ path: zod.z.string().optional(),
1122
+ icon: zod.z.string().optional(),
1123
+ title: clarifyLocalizedTextSchema.optional(),
1124
+ filter: zod.z.object({ tags: zod.z.array(zod.z.string()).optional() }).optional()
1125
+ })
1126
+ ]);
1127
+ var clarifyPagesGroupSchema = zod.z.object({
1128
+ group: clarifyLocalizedTextSchema,
1129
+ icon: zod.z.string().optional(),
1130
+ pages: zod.z.array(clarifyPagesItemSchema)
1131
+ });
1132
+ var clarifyPagesConfigSchema = zod.z.union([zod.z.array(clarifyPagesGroupSchema), zod.z.literal("FileTree")]);
1133
+ var clarifyTabItemSchema = zod.z.object({
1134
+ tab: clarifyLocalizedTextSchema,
1135
+ icon: zod.z.string().optional(),
1136
+ pages: clarifyPagesConfigSchema.optional()
1137
+ });
1138
+ var clarifyTabsConfigSchema = zod.z.array(clarifyTabItemSchema);
1139
+ var clarifyThemePresetSchema = zod.z.enum(["default", "base"]);
1140
+ var clarifyThemeModeColorValueSchema = zod.z.object({
1141
+ light: zod.z.string().optional(),
1142
+ dark: zod.z.string().optional()
1143
+ });
1144
+ var clarifyThemeColorValueSchema = zod.z.union([zod.z.string(), clarifyThemeModeColorValueSchema]);
1145
+ var clarifyThemeColorTokensConfigSchema = zod.z.object({
1146
+ primary: clarifyThemeColorValueSchema.optional(),
1147
+ accent: clarifyThemeColorValueSchema.optional(),
1148
+ background: clarifyThemeColorValueSchema.optional(),
1149
+ foreground: clarifyThemeColorValueSchema.optional(),
1150
+ surface: clarifyThemeColorValueSchema.optional(),
1151
+ muted: clarifyThemeColorValueSchema.optional(),
1152
+ border: clarifyThemeColorValueSchema.optional(),
1153
+ codeBackground: clarifyThemeColorValueSchema.optional()
1154
+ });
1155
+ var clarifyThemeRadiusTokensConfigSchema = zod.z.object({
1156
+ sm: zod.z.string().optional(),
1157
+ md: zod.z.string().optional(),
1158
+ lg: zod.z.string().optional(),
1159
+ xl: zod.z.string().optional()
1160
+ });
1161
+ var clarifyThemeTokensConfigSchema = zod.z.object({
1162
+ colors: clarifyThemeColorTokensConfigSchema.optional(),
1163
+ radius: clarifyThemeRadiusTokensConfigSchema.optional()
1164
+ });
1165
+ var clarifyThemeLayoutConfigSchema = zod.z.object({ maxWidth: zod.z.string().optional() });
1166
+ var clarifyThemeConfigSchema = zod.z.object({
1167
+ preset: clarifyThemePresetSchema.optional(),
1168
+ tokens: clarifyThemeTokensConfigSchema.optional(),
1169
+ layout: clarifyThemeLayoutConfigSchema.optional(),
1170
+ editor: zod.z.boolean().optional()
1171
+ });
1172
+ var clarifyProjectConfigSchema = zod.z.object({
1173
+ title: zod.z.string().optional(),
1174
+ description: zod.z.string().optional(),
1175
+ siteUrl: zod.z.string().optional(),
1176
+ source: clarifySourceConfigSchema.optional(),
1177
+ logo: clarifyLogoConfigSchema.optional(),
1178
+ favicon: clarifyFaviconConfigSchema.optional(),
1179
+ theme: clarifyThemeConfigSchema.optional(),
1180
+ routePrefix: zod.z.string().optional(),
1181
+ navbar: zod.z.object({ links: zod.z.array(clarifyNavbarLinkSchema).optional() }).optional(),
1182
+ banner: clarifyBannerConfigSchema.optional(),
1183
+ footer: clarifyFooterConfigSchema.optional(),
1184
+ i18n: clarifyI18nConfigSchema.optional(),
1185
+ tabs: clarifyTabsConfigSchema.optional()
1186
+ });
1187
+ //#endregion
1188
+ //#region source/core/theme.ts
1189
+ var clarifyThemePresets = {
1190
+ default: {
1191
+ preset: "default",
1192
+ tokens: {
1193
+ colors: {
1194
+ primary: "#047857",
1195
+ accent: "#0D9488",
1196
+ background: "#ffffff",
1197
+ foreground: "#111827",
1198
+ surface: "#ffffff",
1199
+ muted: "#64748b",
1200
+ border: "rgb(15 23 42 / 0.12)",
1201
+ codeBackground: "#f6fbf8"
1202
+ },
1203
+ radius: {
1204
+ sm: "6px",
1205
+ md: "10px",
1206
+ lg: "14px",
1207
+ xl: "18px"
1208
+ }
1209
+ },
1210
+ layout: { maxWidth: "82rem" },
1211
+ editor: false
1212
+ },
1213
+ base: {
1214
+ preset: "base",
1215
+ tokens: {
1216
+ colors: {
1217
+ primary: "#18181b",
1218
+ accent: "#52525b",
1219
+ background: "#ffffff",
1220
+ foreground: "#18181b",
1221
+ surface: "#ffffff",
1222
+ muted: "#71717a",
1223
+ border: "rgb(24 24 27 / 0.12)",
1224
+ codeBackground: "#f4f4f5"
1225
+ },
1226
+ radius: {
1227
+ sm: "6px",
1228
+ md: "8px",
1229
+ lg: "12px",
1230
+ xl: "16px"
1231
+ }
1232
+ },
1233
+ layout: { maxWidth: "80rem" },
1234
+ editor: false
1235
+ }
1236
+ };
1237
+ function resolveThemeConfig(theme = {}) {
1238
+ const preset = theme.preset ?? "default";
1239
+ const presetTheme = clarifyThemePresets[preset];
1240
+ return {
1241
+ preset,
1242
+ tokens: {
1243
+ colors: {
1244
+ ...presetTheme.tokens.colors,
1245
+ ...theme.tokens?.colors
1246
+ },
1247
+ radius: {
1248
+ ...presetTheme.tokens.radius,
1249
+ ...theme.tokens?.radius
1250
+ }
1251
+ },
1252
+ layout: {
1253
+ ...presetTheme.layout,
1254
+ ...theme.layout
1255
+ },
1256
+ editor: theme.editor ?? presetTheme.editor
1257
+ };
1258
+ }
1259
+ //#endregion
1260
+ //#region source/core/config.ts
1261
+ function formatIssuePath(path) {
1262
+ return path.reduce((result, segment) => {
1263
+ if (typeof segment === "number") return `${result}[${segment}]`;
1264
+ const key = String(segment);
1265
+ return result ? `${result}.${key}` : key;
1266
+ }, "");
1267
+ }
1268
+ function formatProjectConfigError(error) {
1269
+ const issue = error.issues[0];
1270
+ if (!issue) return "[clarify] config is invalid";
1271
+ const path = formatIssuePath(issue.path);
1272
+ if (!path) return `[clarify] config must contain an object: ${issue.message}`;
1273
+ return `[clarify] config field "${path}" is invalid: ${issue.message}`;
1274
+ }
1275
+ function validateProjectConfig(value) {
1276
+ const result = clarifyProjectConfigSchema.safeParse(value);
1277
+ if (!result.success) throw new Error(formatProjectConfigError(result.error));
1278
+ return result.data;
1279
+ }
1280
+ function resolveI18nConfig(i18n) {
1281
+ if (!i18n) return void 0;
1282
+ const firstLocale = i18n.locales[0]?.code;
1283
+ const defaultLocale = i18n.defaultLocale ?? firstLocale;
1284
+ if (!defaultLocale) return void 0;
1285
+ return {
1286
+ defaultLocale,
1287
+ missing: i18n.missing ?? "fallback",
1288
+ locales: i18n.locales
1289
+ };
1290
+ }
1291
+ function resolveProjectConfig(config = {}) {
1292
+ return {
1293
+ title: config.title ?? "Clarify Docs",
1294
+ description: config.description ?? "",
1295
+ siteUrl: config.siteUrl,
1296
+ source: config.source,
1297
+ logo: config.logo,
1298
+ favicon: config.favicon,
1299
+ routePrefix: config.routePrefix ?? "/",
1300
+ theme: resolveThemeConfig(config.theme),
1301
+ navbar: config.navbar,
1302
+ banner: config.banner,
1303
+ footer: config.footer,
1304
+ i18n: resolveI18nConfig(config.i18n),
1305
+ tabs: config.tabs
1306
+ };
1307
+ }
1308
+ //#endregion
1249
1309
  //#region source/core/hooks.ts
1250
1310
  async function runHooks(plugins, hookName, input, ctx) {
1251
1311
  let result = input;
@@ -1363,6 +1423,7 @@ async function resolveClarifySite(options = {}, resolveOptions = {}) {
1363
1423
  };
1364
1424
  let routes = await discoverRoutes(root, contentRoot, plugins, ctx);
1365
1425
  routes = await runHooks(plugins, "routes:discovered", routes, ctx);
1426
+ routes = applyConfiguredPageRoutePaths(routes, projectConfig.tabs, projectConfig.i18n);
1366
1427
  const defaultNavigation = projectConfig.tabs ? projectConfig.i18n ? buildLocalizedNavigationFromTabsConfig(routes, projectConfig.tabs, projectConfig.i18n) ?? {} : buildNavigationFromTabsConfig(routes, projectConfig.tabs) : buildNavigation(routes);
1367
1428
  const resolved = await runHooks(plugins, "routes:resolved", {
1368
1429
  routes,
@@ -1816,4 +1877,4 @@ Object.defineProperty(exports, "resolveClarifySite", {
1816
1877
  }
1817
1878
  });
1818
1879
 
1819
- //# sourceMappingURL=plugin-Dq1zHXan.cjs.map
1880
+ //# sourceMappingURL=plugin-hJcy-YdO.cjs.map