@clarify-labs/cli 0.11.9 → 0.11.10

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.
@@ -14,6 +14,7 @@ import GithubSlugger, { slug } from "github-slugger";
14
14
  import { toString } from "mdast-util-to-string";
15
15
  import { remark } from "remark";
16
16
  import matter from "gray-matter";
17
+ import { spawnSync } from "node:child_process";
17
18
  import { createReadStream, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
18
19
  import { build, loadConfigFromFile } from "vite";
19
20
  import { z } from "zod";
@@ -320,6 +321,42 @@ async function compileMarkdownContent(content, options = {}) {
320
321
  }
321
322
  }
322
323
  //#endregion
324
+ //#region source/parsers/routes/git-metadata.ts
325
+ function normalizeDate(value) {
326
+ if (!value) return void 0;
327
+ const date = new Date(value);
328
+ return Number.isNaN(date.getTime()) ? void 0 : date.toISOString();
329
+ }
330
+ async function getFileUpdatedAt(filePath, contentRoot) {
331
+ try {
332
+ const result = spawnSync("git", [
333
+ "log",
334
+ "-1",
335
+ "--format=%cI",
336
+ "--",
337
+ relative(contentRoot, filePath)
338
+ ], {
339
+ cwd: contentRoot,
340
+ encoding: "utf8",
341
+ stdio: [
342
+ "ignore",
343
+ "pipe",
344
+ "ignore"
345
+ ]
346
+ });
347
+ const committedAt = normalizeDate(result.status === 0 ? result.stdout.trim() : void 0);
348
+ if (committedAt) return committedAt;
349
+ } catch {}
350
+ try {
351
+ return (await stat(filePath)).mtime.toISOString();
352
+ } catch {
353
+ return;
354
+ }
355
+ }
356
+ function resolveUpdatedAt(frontmatter, detectedAt) {
357
+ return normalizeDate(typeof frontmatter.updatedAt === "string" ? frontmatter.updatedAt : void 0) ?? detectedAt;
358
+ }
359
+ //#endregion
323
360
  //#region source/parsers/routes/routes.ts
324
361
  function kebabToTitle(str) {
325
362
  return str.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
@@ -419,6 +456,7 @@ async function findContentRoutes(dir, base = dir, options = {}) {
419
456
  const cleanPath = ("/" + pathParts.map((p) => p === "index" ? "" : p).filter(Boolean).join("/")).replace(/\/+/g, "/").replace(/\/$/, "") || "/";
420
457
  const source = await readFile(fullPath, "utf-8");
421
458
  const { frontmatter, content } = await (options.contentProcessor ?? createContentProcessor()).processMdx(source, fullPath);
459
+ const updatedAt = resolveUpdatedAt(frontmatter, await getFileUpdatedAt(fullPath, base));
422
460
  const page = {
423
461
  path: cleanPath,
424
462
  filePath: fullPath,
@@ -439,6 +477,9 @@ async function findContentRoutes(dir, base = dir, options = {}) {
439
477
  title,
440
478
  description: typeof page.frontmatter.description === "string" ? page.frontmatter.description : void 0,
441
479
  keywords: frontmatterKeywords(page.frontmatter),
480
+ group: typeof page.frontmatter.group === "string" ? page.frontmatter.group : void 0,
481
+ layout: page.frontmatter.layout === "documentation" || page.frontmatter.layout === "blog" ? page.frontmatter.layout : void 0,
482
+ updatedAt,
442
483
  sections: extractMdxSections(page.content)
443
484
  },
444
485
  module: {
@@ -548,8 +589,15 @@ async function findLocalizedContentRoutes(contentRoot, locales, options = {}) {
548
589
  }
549
590
  function buildNavigation(routes) {
550
591
  const root = [];
592
+ const groupedRoutes = /* @__PURE__ */ new Map();
551
593
  for (const route of routes) {
552
594
  if (route.path === "/" || (route.basePath ?? route.path) === "/404") continue;
595
+ if (route.meta.group) {
596
+ const grouped = groupedRoutes.get(route.meta.group) ?? [];
597
+ grouped.push(route);
598
+ groupedRoutes.set(route.meta.group, grouped);
599
+ continue;
600
+ }
553
601
  const parts = route.path.replace(/^\//, "").split("/");
554
602
  let current = root;
555
603
  for (let i = 0; i < parts.length; i++) {
@@ -561,6 +609,7 @@ function buildNavigation(routes) {
561
609
  title: i === parts.length - 1 ? route.meta.title : kebabToTitle(parts[i]),
562
610
  children: []
563
611
  };
612
+ if (i === parts.length - 1 && route.meta.layout) node.layout = route.meta.layout;
564
613
  current.push(node);
565
614
  }
566
615
  if (i === parts.length - 1 && route.meta.sections) node.sections = navigationSections(route.meta.sections);
@@ -570,6 +619,22 @@ function buildNavigation(routes) {
570
619
  }
571
620
  }
572
621
  }
622
+ for (const [title, grouped] of groupedRoutes) {
623
+ const children = buildNavigation(grouped.map((route) => ({
624
+ ...route,
625
+ meta: {
626
+ ...route.meta,
627
+ group: void 0
628
+ }
629
+ })));
630
+ const node = {
631
+ path: children[0]?.path ?? "/",
632
+ title,
633
+ children
634
+ };
635
+ if (grouped[0]?.meta.layout) node.layout = grouped[0].meta.layout;
636
+ root.push(node);
637
+ }
573
638
  return root;
574
639
  }
575
640
  function routeIntentFromPagesItem(item) {
@@ -676,6 +741,7 @@ function buildNavigationFromConfig(routes, config) {
676
741
  path: firstNavigationPath(children),
677
742
  title: resolveLocalizedText(item.group, "", "") ?? "",
678
743
  icon: item.icon,
744
+ layout: item.layout,
679
745
  children
680
746
  };
681
747
  }
@@ -729,6 +795,7 @@ function buildLocalizedNavigationForLocale(routes, config, locale, locales) {
729
795
  path: firstNavigationPath(children),
730
796
  title: resolveLocalizedText(item.group, locale, locales.default) ?? "",
731
797
  icon: item.icon,
798
+ layout: item.layout,
732
799
  children
733
800
  };
734
801
  }
@@ -940,10 +1007,10 @@ var clarifyLocalesConfigSchema = z.object({
940
1007
  z.literal("404"),
941
1008
  z.literal("hide")
942
1009
  ]).optional(),
943
- locales: z.array(clarifyLocaleConfigSchema).min(1)
1010
+ locales: z.array(clarifyLocaleConfigSchema).min(1).optional()
944
1011
  }).strict().superRefine((config, ctx) => {
945
1012
  const localeCodes = /* @__PURE__ */ new Set();
946
- for (const [index, locale] of config.locales.entries()) {
1013
+ for (const [index, locale] of (config.locales ?? []).entries()) {
947
1014
  if (localeCodes.has(locale.code)) ctx.addIssue({
948
1015
  code: z.ZodIssueCode.custom,
949
1016
  message: `Duplicate locale code "${locale.code}"`,
@@ -955,24 +1022,45 @@ var clarifyLocalesConfigSchema = z.object({
955
1022
  });
956
1023
  localeCodes.add(locale.code);
957
1024
  }
958
- if (config.default && !localeCodes.has(config.default)) ctx.addIssue({
1025
+ if (config.default && config.locales && !localeCodes.has(config.default)) ctx.addIssue({
959
1026
  code: z.ZodIssueCode.custom,
960
1027
  message: "default must be one of locales.locales",
961
1028
  path: ["default"]
962
1029
  });
963
1030
  });
964
- var clarifyNavbarLinkSchema = z.object({
1031
+ var clarifyLinkConfigSchema = z.object({
965
1032
  label: clarifyLocalizedTextSchema,
966
- href: z.string(),
967
- external: z.boolean().optional()
1033
+ href: z.string()
968
1034
  }).strict();
1035
+ var clarifyNavbarMenuItemBaseSchema = {
1036
+ label: clarifyLocalizedTextSchema,
1037
+ description: clarifyLocalizedTextSchema.optional(),
1038
+ icon: z.string().optional()
1039
+ };
1040
+ var clarifyNavbarMenuHrefSchema = z.object({
1041
+ ...clarifyNavbarMenuItemBaseSchema,
1042
+ href: z.string()
1043
+ }).strict();
1044
+ var clarifyNavbarMenuPageSchema = z.object({
1045
+ ...clarifyNavbarMenuItemBaseSchema,
1046
+ page: z.string()
1047
+ }).strict();
1048
+ var clarifyNavbarMenuItemsSchema = z.lazy(() => z.object({
1049
+ ...clarifyNavbarMenuItemBaseSchema,
1050
+ items: z.array(clarifyNavbarMenuItemSchema)
1051
+ }).strict());
1052
+ var clarifyNavbarMenuItemSchema = z.lazy(() => z.union([
1053
+ clarifyNavbarMenuHrefSchema,
1054
+ clarifyNavbarMenuPageSchema,
1055
+ clarifyNavbarMenuItemsSchema
1056
+ ]));
969
1057
  var clarifyBannerConfigOptionsSchema = z.object({
970
1058
  content: clarifyLocalizedTextSchema,
971
1059
  dismissible: z.boolean().optional(),
972
- link: clarifyNavbarLinkSchema.optional()
1060
+ link: clarifyLinkConfigSchema.optional()
973
1061
  }).strict();
974
1062
  var clarifyFooterConfigSchema = z.object({
975
- links: z.array(clarifyNavbarLinkSchema).optional(),
1063
+ links: z.array(clarifyLinkConfigSchema).optional(),
976
1064
  socials: z.record(z.string(), z.string()).optional(),
977
1065
  copyright: clarifyLocalizedTextSchema.optional()
978
1066
  }).strict();
@@ -1025,6 +1113,7 @@ var clarifyPagesItemSchema = z.lazy(() => z.union([
1025
1113
  var clarifyPagesGroupSchema = z.lazy(() => z.object({
1026
1114
  group: clarifyLocalizedTextSchema,
1027
1115
  icon: z.string().optional(),
1116
+ layout: z.enum(["documentation", "blog"]).optional(),
1028
1117
  pages: z.array(clarifyPagesItemSchema)
1029
1118
  }).strict());
1030
1119
  var clarifyPagesConfigSchema = z.union([z.array(clarifyPagesGroupSchema), z.literal("FileTree")]);
@@ -1099,7 +1188,7 @@ var clarifyProjectConfigSchema = z.object({
1099
1188
  theme: clarifyThemeConfigSchema.optional(),
1100
1189
  layout: clarifyLayoutConfigSchema.optional(),
1101
1190
  navigation: z.object({
1102
- links: z.array(clarifyNavbarLinkSchema).optional(),
1191
+ menus: z.array(clarifyNavbarMenuItemSchema).optional(),
1103
1192
  tabs: clarifyTabsConfigSchema.optional()
1104
1193
  }).strict().optional(),
1105
1194
  banner: clarifyBannerConfigOptionsSchema.optional(),
@@ -1130,7 +1219,7 @@ function validateProjectConfig(value) {
1130
1219
  return result.data;
1131
1220
  }
1132
1221
  function resolveLocalesConfig(locales) {
1133
- if (!locales) return void 0;
1222
+ if (!locales?.locales) return void 0;
1134
1223
  const firstLocale = locales.locales[0]?.code;
1135
1224
  const defaultLocale = locales.default ?? firstLocale;
1136
1225
  if (!defaultLocale) return void 0;
@@ -1174,6 +1263,7 @@ function resolveProjectConfig(config = {}) {
1174
1263
  banner: config.banner,
1175
1264
  footer: config.footer,
1176
1265
  locales: resolveLocalesConfig(config.locales),
1266
+ defaultLocale: config.locales?.default,
1177
1267
  variables: config.variables ?? {},
1178
1268
  features: resolveFeaturesConfig(config.features)
1179
1269
  };
@@ -1330,6 +1420,7 @@ function generateConfigModule(projectConfig, version, themeEditor = false) {
1330
1420
  banner: projectConfig.banner,
1331
1421
  footer: projectConfig.footer,
1332
1422
  locales: projectConfig.locales,
1423
+ defaultLocale: projectConfig.defaultLocale,
1333
1424
  variables: projectConfig.variables,
1334
1425
  features: projectConfig.features
1335
1426
  };
@@ -1354,6 +1445,8 @@ function routeToRuntimeManifestEntry(route, component, mode) {
1354
1445
  if (route.alternates) entry.alternates = route.alternates;
1355
1446
  if (route.meta.description) entry.description = route.meta.description;
1356
1447
  if (route.meta.keywords?.length) entry.keywords = route.meta.keywords;
1448
+ if (route.meta.layout) entry.layout = route.meta.layout;
1449
+ if (route.meta.updatedAt) entry.updatedAt = route.meta.updatedAt;
1357
1450
  if (route.artifacts?.contentArtifactUrl) entry.contentArtifactUrl = route.artifacts.contentArtifactUrl;
1358
1451
  if (route.source?.sourceEditUrl) entry.sourceEditUrl = route.source.sourceEditUrl;
1359
1452
  if (route.meta.sections?.length) entry.sections = route.meta.sections.map((section) => ({
@@ -1680,7 +1773,7 @@ function createViteAdapter(engine) {
1680
1773
  }
1681
1774
  var cliPackageVersion = {
1682
1775
  name: "@clarify-labs/cli",
1683
- version: "0.11.9",
1776
+ version: "0.11.10",
1684
1777
  license: "AGPL-3.0-only",
1685
1778
  type: "module",
1686
1779
  repository: {
@@ -2722,6 +2815,7 @@ var OpenAPIPluginState = class {
2722
2815
  const specByFilePath = /* @__PURE__ */ new Map();
2723
2816
  for (const route of routes) {
2724
2817
  if (route.kind !== "openapi") continue;
2818
+ const updatedAt = await getFileUpdatedAt(route.source.filePath, ctx.contentRoot);
2725
2819
  const specFromCache = specByFilePath.get(route.source.filePath);
2726
2820
  const processor = createProjectContentProcessor(ctx.plugins, ctx);
2727
2821
  const result = specFromCache ? {
@@ -2735,6 +2829,13 @@ var OpenAPIPluginState = class {
2735
2829
  continue;
2736
2830
  }
2737
2831
  const spec = normalizeOpenAPISpecSectionIds(result.spec);
2832
+ const specWithMetadata = updatedAt && spec.info ? {
2833
+ ...spec,
2834
+ info: {
2835
+ ...spec.info,
2836
+ "x-clarify-updated-at": updatedAt
2837
+ }
2838
+ } : spec;
2738
2839
  specByFilePath.set(route.source.filePath, spec);
2739
2840
  const sourceSpecId = sourceSpecIdFromPath(route.source.filePath, ctx.generateOptions.projectRoot);
2740
2841
  route.openapi = {
@@ -2743,8 +2844,9 @@ var OpenAPIPluginState = class {
2743
2844
  };
2744
2845
  route.meta.title = spec.info?.title ?? route.meta.title;
2745
2846
  route.meta.description = spec.info?.description ?? route.meta.description;
2847
+ route.meta.updatedAt = updatedAt;
2746
2848
  route.meta.sections = extractOpenAPISections(spec, route.openapi.tagFilter);
2747
- const pageSpec = route.openapi.tagFilter?.length ? filterSpecByTags(spec, route.openapi.tagFilter) : spec;
2849
+ const pageSpec = route.openapi.tagFilter?.length ? filterSpecByTags(specWithMetadata, route.openapi.tagFilter) : specWithMetadata;
2748
2850
  const routeSpecModuleId = routeSpecModuleIdFromSourceSpecId(sourceSpecId, route.openapi.tagFilter);
2749
2851
  route.openapi.routeSpecModuleId = routeSpecModuleId;
2750
2852
  this.specModules.set(routeSpecModuleId, pageSpec);
@@ -4021,4 +4123,4 @@ function createClarifyEngine(options = {}) {
4021
4123
  //#endregion
4022
4124
  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 };
4023
4125
 
4024
- //# sourceMappingURL=engine-CdH3JL_y.js.map
4126
+ //# sourceMappingURL=engine-DisWfZp-.js.map