@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.
@@ -8,11 +8,11 @@ import { toString } from "mdast-util-to-string";
8
8
  import shiki from "shiki";
9
9
  import { visit } from "unist-util-visit";
10
10
  import { markdownRemarkPlugins } from "@clarify-labs/renderer";
11
- import { z } from "zod";
12
11
  import { remark } from "remark";
13
12
  import matter from "gray-matter";
14
13
  import SwaggerParser from "@apidevtools/swagger-parser";
15
14
  import { mkdir, writeFile } from "node:fs/promises";
15
+ import { z } from "zod";
16
16
  import { tmpdir } from "node:os";
17
17
  import { pathToFileURL } from "node:url";
18
18
  import { build, loadConfigFromFile } from "vite";
@@ -48,6 +48,11 @@ function rehypeParseCodeBlocks() {
48
48
  const language = getLanguage(node.properties.className);
49
49
  parentNode.properties.language = language;
50
50
  node.properties.language = language;
51
+ for (const key of [
52
+ "title",
53
+ "tag",
54
+ "label"
55
+ ]) if (node.properties[key] !== void 0) parentNode.properties[key] = node.properties[key];
51
56
  });
52
57
  };
53
58
  }
@@ -101,284 +106,25 @@ var rehypePlugins = [
101
106
  rehypeShiki
102
107
  ];
103
108
  //#endregion
104
- //#region source/core/config-schema.ts
105
- var clarifyLogoConfigSchema = z.union([z.string(), z.object({
106
- light: z.string().optional(),
107
- dark: z.string().optional()
108
- })]);
109
- var clarifyFaviconConfigSchema = z.union([z.string(), z.object({
110
- light: z.string().optional(),
111
- dark: z.string().optional()
112
- })]);
113
- var clarifyLocalizedTextSchema = z.union([z.string(), z.record(z.string(), z.string())]);
114
- var clarifyLocaleConfigSchema = z.object({
115
- code: z.string(),
116
- label: z.string(),
117
- dir: z.union([z.literal("ltr"), z.literal("rtl")]).optional()
118
- });
119
- var clarifyI18nConfigSchema = z.object({
120
- defaultLocale: z.string().optional(),
121
- missing: z.union([
122
- z.literal("fallback"),
123
- z.literal("404"),
124
- z.literal("hide")
125
- ]).optional(),
126
- locales: z.array(clarifyLocaleConfigSchema).min(1)
127
- }).superRefine((config, ctx) => {
128
- const localeCodes = /* @__PURE__ */ new Set();
129
- for (const [index, locale] of config.locales.entries()) {
130
- if (localeCodes.has(locale.code)) ctx.addIssue({
131
- code: z.ZodIssueCode.custom,
132
- message: `Duplicate locale code "${locale.code}"`,
133
- path: [
134
- "locales",
135
- index,
136
- "code"
137
- ]
138
- });
139
- localeCodes.add(locale.code);
140
- }
141
- if (config.defaultLocale && !localeCodes.has(config.defaultLocale)) ctx.addIssue({
142
- code: z.ZodIssueCode.custom,
143
- message: "defaultLocale must be one of i18n.locales",
144
- path: ["defaultLocale"]
145
- });
146
- });
147
- var clarifyNavbarLinkSchema = z.object({
148
- label: clarifyLocalizedTextSchema,
149
- href: z.string(),
150
- external: z.boolean().optional()
151
- });
152
- var clarifyBannerConfigSchema = z.object({
153
- content: clarifyLocalizedTextSchema,
154
- dismissible: z.boolean().optional()
155
- });
156
- var clarifyFooterConfigSchema = z.object({
157
- links: z.array(clarifyNavbarLinkSchema).optional(),
158
- socials: z.record(z.string(), z.string()).optional(),
159
- copyright: clarifyLocalizedTextSchema.optional()
160
- });
161
- var clarifySourceConfigSchema = z.object({
162
- repository: z.string(),
163
- branch: z.string().optional(),
164
- directory: z.string().optional()
165
- });
166
- var clarifyPagesItemSchema = z.union([
167
- z.string(),
168
- z.object({
169
- page: z.string(),
170
- title: clarifyLocalizedTextSchema.optional(),
171
- icon: z.string().optional(),
172
- redirect: z.string().optional()
173
- }),
174
- z.object({
175
- openapi: z.string(),
176
- icon: z.string().optional(),
177
- title: clarifyLocalizedTextSchema.optional(),
178
- filter: z.object({ tags: z.array(z.string()).optional() }).optional()
179
- })
180
- ]);
181
- var clarifyPagesGroupSchema = z.object({
182
- group: clarifyLocalizedTextSchema,
183
- icon: z.string().optional(),
184
- pages: z.array(clarifyPagesItemSchema)
185
- });
186
- var clarifyPagesConfigSchema = z.union([z.array(clarifyPagesGroupSchema), z.literal("FileTree")]);
187
- var clarifyTabItemSchema = z.object({
188
- tab: clarifyLocalizedTextSchema,
189
- icon: z.string().optional(),
190
- pages: clarifyPagesConfigSchema.optional()
191
- });
192
- var clarifyTabsConfigSchema = z.array(clarifyTabItemSchema);
193
- var clarifyThemePresetSchema = z.enum(["default", "base"]);
194
- var clarifyThemeModeColorValueSchema = z.object({
195
- light: z.string().optional(),
196
- dark: z.string().optional()
197
- });
198
- var clarifyThemeColorValueSchema = z.union([z.string(), clarifyThemeModeColorValueSchema]);
199
- var clarifyThemeColorTokensConfigSchema = z.object({
200
- primary: clarifyThemeColorValueSchema.optional(),
201
- accent: clarifyThemeColorValueSchema.optional(),
202
- background: clarifyThemeColorValueSchema.optional(),
203
- foreground: clarifyThemeColorValueSchema.optional(),
204
- surface: clarifyThemeColorValueSchema.optional(),
205
- muted: clarifyThemeColorValueSchema.optional(),
206
- border: clarifyThemeColorValueSchema.optional(),
207
- codeBackground: clarifyThemeColorValueSchema.optional()
208
- });
209
- var clarifyThemeRadiusTokensConfigSchema = z.object({
210
- sm: z.string().optional(),
211
- md: z.string().optional(),
212
- lg: z.string().optional(),
213
- xl: z.string().optional()
214
- });
215
- var clarifyThemeTokensConfigSchema = z.object({
216
- colors: clarifyThemeColorTokensConfigSchema.optional(),
217
- radius: clarifyThemeRadiusTokensConfigSchema.optional()
218
- });
219
- var clarifyThemeLayoutConfigSchema = z.object({ maxWidth: z.string().optional() });
220
- var clarifyThemeConfigSchema = z.object({
221
- preset: clarifyThemePresetSchema.optional(),
222
- tokens: clarifyThemeTokensConfigSchema.optional(),
223
- layout: clarifyThemeLayoutConfigSchema.optional(),
224
- editor: z.boolean().optional()
225
- });
226
- var clarifyProjectConfigSchema = z.object({
227
- title: z.string().optional(),
228
- description: z.string().optional(),
229
- siteUrl: z.string().optional(),
230
- source: clarifySourceConfigSchema.optional(),
231
- logo: clarifyLogoConfigSchema.optional(),
232
- favicon: clarifyFaviconConfigSchema.optional(),
233
- theme: clarifyThemeConfigSchema.optional(),
234
- routePrefix: z.string().optional(),
235
- navbar: z.object({ links: z.array(clarifyNavbarLinkSchema).optional() }).optional(),
236
- banner: clarifyBannerConfigSchema.optional(),
237
- footer: clarifyFooterConfigSchema.optional(),
238
- i18n: clarifyI18nConfigSchema.optional(),
239
- tabs: clarifyTabsConfigSchema.optional()
240
- });
241
- //#endregion
242
- //#region source/core/theme.ts
243
- var clarifyThemePresets = {
244
- default: {
245
- preset: "default",
246
- tokens: {
247
- colors: {
248
- primary: "#047857",
249
- accent: "#0D9488",
250
- background: "#ffffff",
251
- foreground: "#111827",
252
- surface: "#ffffff",
253
- muted: "#64748b",
254
- border: "rgb(15 23 42 / 0.12)",
255
- codeBackground: "#f6fbf8"
256
- },
257
- radius: {
258
- sm: "6px",
259
- md: "10px",
260
- lg: "14px",
261
- xl: "18px"
262
- }
263
- },
264
- layout: { maxWidth: "82rem" },
265
- editor: false
266
- },
267
- base: {
268
- preset: "base",
269
- tokens: {
270
- colors: {
271
- primary: "#18181b",
272
- accent: "#52525b",
273
- background: "#ffffff",
274
- foreground: "#18181b",
275
- surface: "#ffffff",
276
- muted: "#71717a",
277
- border: "rgb(24 24 27 / 0.12)",
278
- codeBackground: "#f4f4f5"
279
- },
280
- radius: {
281
- sm: "6px",
282
- md: "8px",
283
- lg: "12px",
284
- xl: "16px"
285
- }
286
- },
287
- layout: { maxWidth: "80rem" },
288
- editor: false
289
- }
290
- };
291
- function resolveThemeConfig(theme = {}) {
292
- const preset = theme.preset ?? "default";
293
- const presetTheme = clarifyThemePresets[preset];
294
- return {
295
- preset,
296
- tokens: {
297
- colors: {
298
- ...presetTheme.tokens.colors,
299
- ...theme.tokens?.colors
300
- },
301
- radius: {
302
- ...presetTheme.tokens.radius,
303
- ...theme.tokens?.radius
304
- }
305
- },
306
- layout: {
307
- ...presetTheme.layout,
308
- ...theme.layout
309
- },
310
- editor: theme.editor ?? presetTheme.editor
311
- };
109
+ //#region source/plugins/content-artifacts/artifacts.ts
110
+ var UTF8_SIGNATURE = "";
111
+ function normalizeBasePath(basePath) {
112
+ if (!basePath || basePath === "/") return "";
113
+ return "/" + basePath.replace(/^\/+|\/+$/g, "");
312
114
  }
313
- //#endregion
314
- //#region source/core/config.ts
315
- function formatIssuePath(path) {
316
- return path.reduce((result, segment) => {
317
- if (typeof segment === "number") return `${result}[${segment}]`;
318
- const key = String(segment);
319
- return result ? `${result}.${key}` : key;
320
- }, "");
115
+ function containsNonAscii(content) {
116
+ return [...content].some((character) => character.codePointAt(0) > 127);
321
117
  }
322
- function formatProjectConfigError(error) {
323
- const issue = error.issues[0];
324
- if (!issue) return "[clarify] config is invalid";
325
- const path = formatIssuePath(issue.path);
326
- if (!path) return `[clarify] config must contain an object: ${issue.message}`;
327
- return `[clarify] config field "${path}" is invalid: ${issue.message}`;
118
+ function withUtf8Signature(content) {
119
+ if (!containsNonAscii(content) || content.startsWith(UTF8_SIGNATURE)) return content;
120
+ return `${UTF8_SIGNATURE}${content}`;
328
121
  }
329
- function validateProjectConfig(value) {
330
- const result = clarifyProjectConfigSchema.safeParse(value);
331
- if (!result.success) throw new Error(formatProjectConfigError(result.error));
332
- return result.data;
122
+ function shouldUseUtf8Signature(route) {
123
+ if (route.kind === "mdx") return true;
124
+ return /\.ya?ml$/i.test(route.contentArtifactUrl ?? "");
333
125
  }
334
- function resolveI18nConfig(i18n) {
335
- if (!i18n) return void 0;
336
- const firstLocale = i18n.locales[0]?.code;
337
- const defaultLocale = i18n.defaultLocale ?? firstLocale;
338
- if (!defaultLocale) return void 0;
339
- return {
340
- defaultLocale,
341
- missing: i18n.missing ?? "fallback",
342
- locales: i18n.locales
343
- };
344
- }
345
- function resolveProjectConfig(config = {}) {
346
- return {
347
- title: config.title ?? "Clarify Docs",
348
- description: config.description ?? "",
349
- siteUrl: config.siteUrl,
350
- source: config.source,
351
- logo: config.logo,
352
- favicon: config.favicon,
353
- routePrefix: config.routePrefix ?? "/",
354
- theme: resolveThemeConfig(config.theme),
355
- navbar: config.navbar,
356
- banner: config.banner,
357
- footer: config.footer,
358
- i18n: resolveI18nConfig(config.i18n),
359
- tabs: config.tabs
360
- };
361
- }
362
- //#endregion
363
- //#region source/plugins/content-artifacts/artifacts.ts
364
- var UTF8_SIGNATURE = "";
365
- function normalizeBasePath(basePath) {
366
- if (!basePath || basePath === "/") return "";
367
- return "/" + basePath.replace(/^\/+|\/+$/g, "");
368
- }
369
- function containsNonAscii(content) {
370
- return [...content].some((character) => character.codePointAt(0) > 127);
371
- }
372
- function withUtf8Signature(content) {
373
- if (!containsNonAscii(content) || content.startsWith(UTF8_SIGNATURE)) return content;
374
- return `${UTF8_SIGNATURE}${content}`;
375
- }
376
- function shouldUseUtf8Signature(route) {
377
- if (route.kind === "mdx") return true;
378
- return /\.ya?ml$/i.test(route.contentArtifactUrl ?? "");
379
- }
380
- function routeToMarkdownArtifactUrl(routePath) {
381
- return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.md`;
126
+ function routeToMarkdownArtifactUrl(routePath) {
127
+ return `${routePath === "/" ? "/index" : routePath.replace(/\/$/, "")}.md`;
382
128
  }
383
129
  function routeToOpenAPIArtifactUrl(routePath, filePath) {
384
130
  const extension = extname(filePath).toLowerCase() || ".json";
@@ -610,13 +356,14 @@ function basePathFromRef(ref) {
610
356
  const withoutExtension = ref.replace(/\.mdx?$/, "").replace(/\.openapi\.(json|yaml|yml)$/, "");
611
357
  return normalizePath(withoutExtension === "index" ? "/" : withoutExtension.replace(/\/index$/, ""));
612
358
  }
613
- function openAPIPagePathFromRef(ref, tagFilter) {
359
+ function openAPIPagePathFromRef(ref, tagFilter, explicitPath) {
360
+ if (explicitPath) return normalizePath(explicitPath);
614
361
  const basePath = basePathFromRef(ref);
615
362
  if (!tagFilter?.length) return basePath;
616
363
  return normalizePath(`${basePath}/${openAPITagsPathSegment(tagFilter)}`);
617
364
  }
618
- function routePathFromRef(ref) {
619
- return basePathFromRef(ref);
365
+ function routePathFromRef(ref, explicitPath) {
366
+ return explicitPath ? normalizePath(explicitPath) : basePathFromRef(ref);
620
367
  }
621
368
  function virtualModuleIdFromRef(ref) {
622
369
  return "virtual:clarify-page/" + ref.replace(/\.mdx?$/, "").replace(/\.openapi\.(json|yaml|yml)$/, "").replace(/\/+/g, "/");
@@ -670,6 +417,18 @@ function frontmatterKeywords(frontmatter) {
670
417
  const keywords = Array.isArray(value) ? value.filter((item) => typeof item === "string") : typeof value === "string" ? value.split(",").map((keyword) => keyword.trim()).filter(Boolean) : [];
671
418
  return keywords.length > 0 ? keywords : void 0;
672
419
  }
420
+ function withAlternates$2(route, routes, i18n) {
421
+ const basePath = route.basePath ?? route.path;
422
+ const routeByLocaleAndBase = new Map(routes.map((route) => [`${route.locale ?? ""}:${route.basePath ?? route.path}`, route]));
423
+ const alternates = Object.fromEntries(i18n.locales.flatMap((locale) => {
424
+ const alternate = routeByLocaleAndBase.get(`${locale.code}:${basePath}`);
425
+ return alternate ? [[locale.code, alternate.path]] : [];
426
+ }));
427
+ return {
428
+ ...route,
429
+ alternates
430
+ };
431
+ }
673
432
  function buildNavigation(routes) {
674
433
  const root = [];
675
434
  for (const route of routes) {
@@ -701,11 +460,13 @@ function resolvePageItem(item) {
701
460
  if ("openapi" in item) return {
702
461
  openapiRef: item.openapi,
703
462
  openapiTagFilter: item.filter?.tags,
463
+ path: item.path,
704
464
  title: item.title,
705
465
  icon: item.icon
706
466
  };
707
467
  return {
708
468
  pageRef: item.page,
469
+ path: item.path,
709
470
  redirect: item.redirect,
710
471
  title: item.title,
711
472
  icon: item.icon
@@ -719,6 +480,41 @@ function firstNavigationPath(nodes) {
719
480
  }
720
481
  return "/";
721
482
  }
483
+ function collectExplicitPagePathItems(tabs) {
484
+ const items = [];
485
+ for (const tab of tabs ?? []) {
486
+ if (!tab.pages || tab.pages === "FileTree") continue;
487
+ for (const group of tab.pages) for (const item of group.pages) if (typeof item !== "string" && "page" in item && item.path) items.push({
488
+ pageRef: item.page,
489
+ path: item.path
490
+ });
491
+ }
492
+ return items;
493
+ }
494
+ function applyConfiguredPageRoutePaths(routes, tabs, i18n) {
495
+ const pageItems = collectExplicitPagePathItems(tabs);
496
+ if (!pageItems.length) return routes;
497
+ const additions = [];
498
+ const existingKeys = new Set(routes.map((route) => `${route.locale ?? ""}:${route.basePath ?? route.path}`));
499
+ for (const item of pageItems) {
500
+ const sourceBasePath = routePathFromRef(item.pageRef);
501
+ const targetBasePath = routePathFromRef(item.pageRef, item.path);
502
+ if (sourceBasePath === targetBasePath) continue;
503
+ for (const route of routes) {
504
+ if (route.kind !== "mdx" || (route.basePath ?? route.path) !== sourceBasePath) continue;
505
+ const key = `${route.locale ?? ""}:${targetBasePath}`;
506
+ if (existingKeys.has(key)) continue;
507
+ existingKeys.add(key);
508
+ additions.push({
509
+ ...route,
510
+ path: route.locale && i18n ? localizedRoutePath(targetBasePath, route.locale, i18n) : targetBasePath,
511
+ basePath: targetBasePath
512
+ });
513
+ }
514
+ }
515
+ const nextRoutes = [...routes, ...additions];
516
+ return i18n ? nextRoutes.map((route) => withAlternates$2(route, nextRoutes, i18n)) : nextRoutes;
517
+ }
722
518
  function buildNavigationFromPagesConfig(routes, config) {
723
519
  if (!config || config === "FileTree") return buildNavigation(routes);
724
520
  return buildNavigationFromConfig(routes, config);
@@ -739,9 +535,9 @@ function buildNavigationFromConfig(routes, config) {
739
535
  const routeMap = new Map(routes.map((r) => [r.path, r]));
740
536
  return config.map((group) => {
741
537
  const children = group.pages.map((item) => {
742
- const { pageRef, openapiRef, openapiTagFilter, redirect, title, icon } = resolvePageItem(item);
538
+ const { pageRef, openapiRef, openapiTagFilter, path: explicitPath, redirect, title, icon } = resolvePageItem(item);
743
539
  if (openapiRef) {
744
- const path = openAPIPagePathFromRef(openapiRef, openapiTagFilter);
540
+ const path = openAPIPagePathFromRef(openapiRef, openapiTagFilter, explicitPath);
745
541
  const route = routeMap.get(path);
746
542
  return {
747
543
  path,
@@ -751,10 +547,10 @@ function buildNavigationFromConfig(routes, config) {
751
547
  };
752
548
  }
753
549
  const ref = pageRef ?? "";
754
- const path = ref === "index" ? "/" : "/" + ref;
550
+ const path = routePathFromRef(ref, explicitPath);
755
551
  const route = routeMap.get(path);
756
552
  return {
757
- path: redirect ? redirect : path,
553
+ path: redirect ? normalizePath(redirect) : path,
758
554
  title: resolveLocalizedText(title, "", "") ?? route?.title ?? kebabToTitle$1(path.split("/").pop() ?? ref),
759
555
  icon,
760
556
  sections: route?.sections ? navigationSections(route.sections) : void 0
@@ -783,13 +579,13 @@ function buildLocalizedNavigationForLocale(routes, config, locale, i18n) {
783
579
  const routeMap = new Map(routes.map((route) => [route.basePath ?? route.path, route]));
784
580
  return config.map((group) => {
785
581
  const children = group.pages.map((item) => {
786
- const { pageRef, openapiRef, openapiTagFilter, redirect, title, icon } = resolvePageItem(item);
582
+ const { pageRef, openapiRef, openapiTagFilter, path: explicitPath, redirect, title, icon } = resolvePageItem(item);
787
583
  const ref = openapiRef ?? pageRef ?? "";
788
- const basePath = openapiRef ? openAPIPagePathFromRef(openapiRef, openapiTagFilter) : basePathFromRef(ref);
584
+ const basePath = openapiRef ? openAPIPagePathFromRef(openapiRef, openapiTagFilter, explicitPath) : routePathFromRef(ref, explicitPath);
789
585
  const route = routeMap.get(basePath);
790
586
  const path = route?.path ?? localizedRoutePath(basePath, locale, i18n);
791
587
  return {
792
- path: (redirect ? localizedRoutePath(basePathFromRef(redirect), locale, i18n) : void 0) ?? path,
588
+ path: (redirect ? localizedRoutePath(routePathFromRef(redirect), locale, i18n) : void 0) ?? path,
793
589
  title: resolveLocalizedText(title, locale, i18n.defaultLocale) ?? route?.title ?? kebabToTitle$1(basePath.split("/").pop() ?? ref),
794
590
  icon,
795
591
  sections: route?.sections ? navigationSections(route.sections) : void 0
@@ -1020,7 +816,7 @@ function collectOpenAPIPageItems(config) {
1020
816
  const items = [];
1021
817
  function visitPages(pages) {
1022
818
  if (!pages || pages === "FileTree") return;
1023
- 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);
819
+ 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);
1024
820
  }
1025
821
  for (const tab of config.tabs ?? []) visitPages(tab.pages);
1026
822
  return items;
@@ -1037,28 +833,31 @@ function withAlternates$1(route, routes, i18n) {
1037
833
  alternates
1038
834
  };
1039
835
  }
1040
- function createTaggedOpenAPIRoutes(routes, config) {
836
+ function virtualModuleIdFromPath(path) {
837
+ return `virtual:clarify-page/${path.replace(/^\/+/, "") || "index"}`;
838
+ }
839
+ function createConfiguredOpenAPIRoutes(routes, config) {
1041
840
  const pageItems = collectOpenAPIPageItems(config);
1042
841
  if (!pageItems.length) return routes;
1043
842
  const additions = [];
1044
843
  const existingKeys = new Set(routes.map((route) => `${route.locale ?? ""}:${route.basePath ?? route.path}`));
1045
844
  for (const item of pageItems) {
1046
845
  const tagFilter = item.filter?.tags?.filter(Boolean);
1047
- if (!tagFilter?.length) continue;
1048
846
  const sourceBasePath = openAPIPagePathFromRef(item.openapi);
1049
- const filteredBasePath = openAPIPagePathFromRef(item.openapi, tagFilter);
847
+ const targetBasePath = openAPIPagePathFromRef(item.openapi, tagFilter, item.path);
848
+ if (sourceBasePath === targetBasePath && !tagFilter?.length) continue;
1050
849
  for (const route of routes) {
1051
850
  if (route.kind !== "openapi" || (route.basePath ?? route.path) !== sourceBasePath) continue;
1052
- const key = `${route.locale ?? ""}:${filteredBasePath}`;
851
+ const key = `${route.locale ?? ""}:${targetBasePath}`;
1053
852
  if (existingKeys.has(key)) continue;
1054
853
  existingKeys.add(key);
1055
854
  additions.push({
1056
855
  ...route,
1057
- path: route.locale && config.i18n ? localizedRoutePath(filteredBasePath, route.locale, config.i18n) : filteredBasePath,
1058
- basePath: filteredBasePath,
856
+ path: route.locale && config.i18n ? localizedRoutePath(targetBasePath, route.locale, config.i18n) : targetBasePath,
857
+ basePath: targetBasePath,
1059
858
  title: route.title,
1060
- virtualModuleId: `${route.virtualModuleId}/${openAPITagsPathSegment(tagFilter)}`,
1061
- openapiTagFilter: tagFilter
859
+ virtualModuleId: virtualModuleIdFromPath(targetBasePath),
860
+ openapiTagFilter: tagFilter?.length ? tagFilter : void 0
1062
861
  });
1063
862
  }
1064
863
  }
@@ -1076,7 +875,7 @@ function createOpenAPIPlugin() {
1076
875
  }),
1077
876
  "routes:discovered": async (routes, ctx) => {
1078
877
  for (const key of Object.keys(specs)) delete specs[key];
1079
- const nextRoutes = createTaggedOpenAPIRoutes(routes, ctx.projectConfig);
878
+ const nextRoutes = createConfiguredOpenAPIRoutes(routes, ctx.projectConfig);
1080
879
  const specByFilePath = /* @__PURE__ */ new Map();
1081
880
  for (const route of nextRoutes.filter((route) => route.kind === "openapi")) {
1082
881
  const specFromCache = specByFilePath.get(route.filePath);
@@ -1217,6 +1016,267 @@ function createBuiltinPlugins(options = {}) {
1217
1016
  return plugins;
1218
1017
  }
1219
1018
  //#endregion
1019
+ //#region source/core/config-schema.ts
1020
+ var clarifyLogoConfigSchema = z.union([z.string(), z.object({
1021
+ light: z.string().optional(),
1022
+ dark: z.string().optional()
1023
+ })]);
1024
+ var clarifyFaviconConfigSchema = z.union([z.string(), z.object({
1025
+ light: z.string().optional(),
1026
+ dark: z.string().optional()
1027
+ })]);
1028
+ var clarifyLocalizedTextSchema = z.union([z.string(), z.record(z.string(), z.string())]);
1029
+ var clarifyLocaleConfigSchema = z.object({
1030
+ code: z.string(),
1031
+ label: z.string(),
1032
+ dir: z.union([z.literal("ltr"), z.literal("rtl")]).optional()
1033
+ });
1034
+ var clarifyI18nConfigSchema = z.object({
1035
+ defaultLocale: z.string().optional(),
1036
+ missing: z.union([
1037
+ z.literal("fallback"),
1038
+ z.literal("404"),
1039
+ z.literal("hide")
1040
+ ]).optional(),
1041
+ locales: z.array(clarifyLocaleConfigSchema).min(1)
1042
+ }).superRefine((config, ctx) => {
1043
+ const localeCodes = /* @__PURE__ */ new Set();
1044
+ for (const [index, locale] of config.locales.entries()) {
1045
+ if (localeCodes.has(locale.code)) ctx.addIssue({
1046
+ code: z.ZodIssueCode.custom,
1047
+ message: `Duplicate locale code "${locale.code}"`,
1048
+ path: [
1049
+ "locales",
1050
+ index,
1051
+ "code"
1052
+ ]
1053
+ });
1054
+ localeCodes.add(locale.code);
1055
+ }
1056
+ if (config.defaultLocale && !localeCodes.has(config.defaultLocale)) ctx.addIssue({
1057
+ code: z.ZodIssueCode.custom,
1058
+ message: "defaultLocale must be one of i18n.locales",
1059
+ path: ["defaultLocale"]
1060
+ });
1061
+ });
1062
+ var clarifyNavbarLinkSchema = z.object({
1063
+ label: clarifyLocalizedTextSchema,
1064
+ href: z.string(),
1065
+ external: z.boolean().optional()
1066
+ });
1067
+ var clarifyBannerConfigSchema = z.object({
1068
+ content: clarifyLocalizedTextSchema,
1069
+ dismissible: z.boolean().optional()
1070
+ });
1071
+ var clarifyFooterConfigSchema = z.object({
1072
+ links: z.array(clarifyNavbarLinkSchema).optional(),
1073
+ socials: z.record(z.string(), z.string()).optional(),
1074
+ copyright: clarifyLocalizedTextSchema.optional()
1075
+ });
1076
+ var clarifySourceConfigSchema = z.object({
1077
+ repository: z.string(),
1078
+ branch: z.string().optional(),
1079
+ directory: z.string().optional()
1080
+ });
1081
+ var clarifyPagesItemSchema = z.union([
1082
+ z.string(),
1083
+ z.object({
1084
+ page: z.string(),
1085
+ path: z.string().optional(),
1086
+ title: clarifyLocalizedTextSchema.optional(),
1087
+ icon: z.string().optional(),
1088
+ redirect: z.string().optional()
1089
+ }),
1090
+ z.object({
1091
+ openapi: z.string(),
1092
+ path: z.string().optional(),
1093
+ icon: z.string().optional(),
1094
+ title: clarifyLocalizedTextSchema.optional(),
1095
+ filter: z.object({ tags: z.array(z.string()).optional() }).optional()
1096
+ })
1097
+ ]);
1098
+ var clarifyPagesGroupSchema = z.object({
1099
+ group: clarifyLocalizedTextSchema,
1100
+ icon: z.string().optional(),
1101
+ pages: z.array(clarifyPagesItemSchema)
1102
+ });
1103
+ var clarifyPagesConfigSchema = z.union([z.array(clarifyPagesGroupSchema), z.literal("FileTree")]);
1104
+ var clarifyTabItemSchema = z.object({
1105
+ tab: clarifyLocalizedTextSchema,
1106
+ icon: z.string().optional(),
1107
+ pages: clarifyPagesConfigSchema.optional()
1108
+ });
1109
+ var clarifyTabsConfigSchema = z.array(clarifyTabItemSchema);
1110
+ var clarifyThemePresetSchema = z.enum(["default", "base"]);
1111
+ var clarifyThemeModeColorValueSchema = z.object({
1112
+ light: z.string().optional(),
1113
+ dark: z.string().optional()
1114
+ });
1115
+ var clarifyThemeColorValueSchema = z.union([z.string(), clarifyThemeModeColorValueSchema]);
1116
+ var clarifyThemeColorTokensConfigSchema = z.object({
1117
+ primary: clarifyThemeColorValueSchema.optional(),
1118
+ accent: clarifyThemeColorValueSchema.optional(),
1119
+ background: clarifyThemeColorValueSchema.optional(),
1120
+ foreground: clarifyThemeColorValueSchema.optional(),
1121
+ surface: clarifyThemeColorValueSchema.optional(),
1122
+ muted: clarifyThemeColorValueSchema.optional(),
1123
+ border: clarifyThemeColorValueSchema.optional(),
1124
+ codeBackground: clarifyThemeColorValueSchema.optional()
1125
+ });
1126
+ var clarifyThemeRadiusTokensConfigSchema = z.object({
1127
+ sm: z.string().optional(),
1128
+ md: z.string().optional(),
1129
+ lg: z.string().optional(),
1130
+ xl: z.string().optional()
1131
+ });
1132
+ var clarifyThemeTokensConfigSchema = z.object({
1133
+ colors: clarifyThemeColorTokensConfigSchema.optional(),
1134
+ radius: clarifyThemeRadiusTokensConfigSchema.optional()
1135
+ });
1136
+ var clarifyThemeLayoutConfigSchema = z.object({ maxWidth: z.string().optional() });
1137
+ var clarifyThemeConfigSchema = z.object({
1138
+ preset: clarifyThemePresetSchema.optional(),
1139
+ tokens: clarifyThemeTokensConfigSchema.optional(),
1140
+ layout: clarifyThemeLayoutConfigSchema.optional(),
1141
+ editor: z.boolean().optional()
1142
+ });
1143
+ var clarifyProjectConfigSchema = z.object({
1144
+ title: z.string().optional(),
1145
+ description: z.string().optional(),
1146
+ siteUrl: z.string().optional(),
1147
+ source: clarifySourceConfigSchema.optional(),
1148
+ logo: clarifyLogoConfigSchema.optional(),
1149
+ favicon: clarifyFaviconConfigSchema.optional(),
1150
+ theme: clarifyThemeConfigSchema.optional(),
1151
+ routePrefix: z.string().optional(),
1152
+ navbar: z.object({ links: z.array(clarifyNavbarLinkSchema).optional() }).optional(),
1153
+ banner: clarifyBannerConfigSchema.optional(),
1154
+ footer: clarifyFooterConfigSchema.optional(),
1155
+ i18n: clarifyI18nConfigSchema.optional(),
1156
+ tabs: clarifyTabsConfigSchema.optional()
1157
+ });
1158
+ //#endregion
1159
+ //#region source/core/theme.ts
1160
+ var clarifyThemePresets = {
1161
+ default: {
1162
+ preset: "default",
1163
+ tokens: {
1164
+ colors: {
1165
+ primary: "#047857",
1166
+ accent: "#0D9488",
1167
+ background: "#ffffff",
1168
+ foreground: "#111827",
1169
+ surface: "#ffffff",
1170
+ muted: "#64748b",
1171
+ border: "rgb(15 23 42 / 0.12)",
1172
+ codeBackground: "#f6fbf8"
1173
+ },
1174
+ radius: {
1175
+ sm: "6px",
1176
+ md: "10px",
1177
+ lg: "14px",
1178
+ xl: "18px"
1179
+ }
1180
+ },
1181
+ layout: { maxWidth: "82rem" },
1182
+ editor: false
1183
+ },
1184
+ base: {
1185
+ preset: "base",
1186
+ tokens: {
1187
+ colors: {
1188
+ primary: "#18181b",
1189
+ accent: "#52525b",
1190
+ background: "#ffffff",
1191
+ foreground: "#18181b",
1192
+ surface: "#ffffff",
1193
+ muted: "#71717a",
1194
+ border: "rgb(24 24 27 / 0.12)",
1195
+ codeBackground: "#f4f4f5"
1196
+ },
1197
+ radius: {
1198
+ sm: "6px",
1199
+ md: "8px",
1200
+ lg: "12px",
1201
+ xl: "16px"
1202
+ }
1203
+ },
1204
+ layout: { maxWidth: "80rem" },
1205
+ editor: false
1206
+ }
1207
+ };
1208
+ function resolveThemeConfig(theme = {}) {
1209
+ const preset = theme.preset ?? "default";
1210
+ const presetTheme = clarifyThemePresets[preset];
1211
+ return {
1212
+ preset,
1213
+ tokens: {
1214
+ colors: {
1215
+ ...presetTheme.tokens.colors,
1216
+ ...theme.tokens?.colors
1217
+ },
1218
+ radius: {
1219
+ ...presetTheme.tokens.radius,
1220
+ ...theme.tokens?.radius
1221
+ }
1222
+ },
1223
+ layout: {
1224
+ ...presetTheme.layout,
1225
+ ...theme.layout
1226
+ },
1227
+ editor: theme.editor ?? presetTheme.editor
1228
+ };
1229
+ }
1230
+ //#endregion
1231
+ //#region source/core/config.ts
1232
+ function formatIssuePath(path) {
1233
+ return path.reduce((result, segment) => {
1234
+ if (typeof segment === "number") return `${result}[${segment}]`;
1235
+ const key = String(segment);
1236
+ return result ? `${result}.${key}` : key;
1237
+ }, "");
1238
+ }
1239
+ function formatProjectConfigError(error) {
1240
+ const issue = error.issues[0];
1241
+ if (!issue) return "[clarify] config is invalid";
1242
+ const path = formatIssuePath(issue.path);
1243
+ if (!path) return `[clarify] config must contain an object: ${issue.message}`;
1244
+ return `[clarify] config field "${path}" is invalid: ${issue.message}`;
1245
+ }
1246
+ function validateProjectConfig(value) {
1247
+ const result = clarifyProjectConfigSchema.safeParse(value);
1248
+ if (!result.success) throw new Error(formatProjectConfigError(result.error));
1249
+ return result.data;
1250
+ }
1251
+ function resolveI18nConfig(i18n) {
1252
+ if (!i18n) return void 0;
1253
+ const firstLocale = i18n.locales[0]?.code;
1254
+ const defaultLocale = i18n.defaultLocale ?? firstLocale;
1255
+ if (!defaultLocale) return void 0;
1256
+ return {
1257
+ defaultLocale,
1258
+ missing: i18n.missing ?? "fallback",
1259
+ locales: i18n.locales
1260
+ };
1261
+ }
1262
+ function resolveProjectConfig(config = {}) {
1263
+ return {
1264
+ title: config.title ?? "Clarify Docs",
1265
+ description: config.description ?? "",
1266
+ siteUrl: config.siteUrl,
1267
+ source: config.source,
1268
+ logo: config.logo,
1269
+ favicon: config.favicon,
1270
+ routePrefix: config.routePrefix ?? "/",
1271
+ theme: resolveThemeConfig(config.theme),
1272
+ navbar: config.navbar,
1273
+ banner: config.banner,
1274
+ footer: config.footer,
1275
+ i18n: resolveI18nConfig(config.i18n),
1276
+ tabs: config.tabs
1277
+ };
1278
+ }
1279
+ //#endregion
1220
1280
  //#region source/core/hooks.ts
1221
1281
  async function runHooks(plugins, hookName, input, ctx) {
1222
1282
  let result = input;
@@ -1334,6 +1394,7 @@ async function resolveClarifySite(options = {}, resolveOptions = {}) {
1334
1394
  };
1335
1395
  let routes = await discoverRoutes(root, contentRoot, plugins, ctx);
1336
1396
  routes = await runHooks(plugins, "routes:discovered", routes, ctx);
1397
+ routes = applyConfiguredPageRoutePaths(routes, projectConfig.tabs, projectConfig.i18n);
1337
1398
  const defaultNavigation = projectConfig.tabs ? projectConfig.i18n ? buildLocalizedNavigationFromTabsConfig(routes, projectConfig.tabs, projectConfig.i18n) ?? {} : buildNavigationFromTabsConfig(routes, projectConfig.tabs) : buildNavigation(routes);
1338
1399
  const resolved = await runHooks(plugins, "routes:resolved", {
1339
1400
  routes,
@@ -1758,4 +1819,4 @@ function clarifyPlugin(options = {}) {
1758
1819
  //#endregion
1759
1820
  export { resolveClarifySite as a, createClarifyRuntimeAliases as i, defineConfig as n, loadClarifyConfig as r, clarifyPlugin as t };
1760
1821
 
1761
- //# sourceMappingURL=plugin-BgE7WVoS.js.map
1822
+ //# sourceMappingURL=plugin-Cx9lFenc.js.map