@clarify-labs/cli 0.10.2 → 0.10.3

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.
@@ -53,104 +53,18 @@ export declare const clarifySourceConfigSchema: z.ZodObject<{
53
53
  branch: z.ZodOptional<z.ZodString>;
54
54
  directory: z.ZodOptional<z.ZodString>;
55
55
  }, z.core.$strip>;
56
- export declare const clarifyPagesItemSchema: z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
57
- page: z.ZodString;
58
- path: z.ZodOptional<z.ZodString>;
59
- title: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
60
- icon: z.ZodOptional<z.ZodString>;
61
- redirect: z.ZodOptional<z.ZodString>;
62
- }, z.core.$strip>, z.ZodObject<{
63
- openapi: z.ZodString;
64
- path: z.ZodOptional<z.ZodString>;
65
- icon: z.ZodOptional<z.ZodString>;
66
- title: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
67
- filter: z.ZodOptional<z.ZodObject<{
68
- tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
69
- }, z.core.$strip>>;
70
- }, z.core.$strip>]>;
71
- export declare const clarifyPagesGroupSchema: z.ZodObject<{
72
- group: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
73
- icon: z.ZodOptional<z.ZodString>;
74
- pages: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
75
- page: z.ZodString;
76
- path: z.ZodOptional<z.ZodString>;
77
- title: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
78
- icon: z.ZodOptional<z.ZodString>;
79
- redirect: z.ZodOptional<z.ZodString>;
80
- }, z.core.$strip>, z.ZodObject<{
81
- openapi: z.ZodString;
82
- path: z.ZodOptional<z.ZodString>;
83
- icon: z.ZodOptional<z.ZodString>;
84
- title: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
85
- filter: z.ZodOptional<z.ZodObject<{
86
- tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
87
- }, z.core.$strip>>;
88
- }, z.core.$strip>]>>;
89
- }, z.core.$strip>;
90
- export declare const clarifyPagesConfigSchema: z.ZodUnion<readonly [z.ZodArray<z.ZodObject<{
91
- group: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
92
- icon: z.ZodOptional<z.ZodString>;
93
- pages: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
94
- page: z.ZodString;
95
- path: z.ZodOptional<z.ZodString>;
96
- title: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
97
- icon: z.ZodOptional<z.ZodString>;
98
- redirect: z.ZodOptional<z.ZodString>;
99
- }, z.core.$strip>, z.ZodObject<{
100
- openapi: z.ZodString;
101
- path: z.ZodOptional<z.ZodString>;
102
- icon: z.ZodOptional<z.ZodString>;
103
- title: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
104
- filter: z.ZodOptional<z.ZodObject<{
105
- tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
106
- }, z.core.$strip>>;
107
- }, z.core.$strip>]>>;
108
- }, z.core.$strip>>, z.ZodLiteral<"FileTree">]>;
56
+ export declare const clarifyPagesItemSchema: z.ZodType<import('../../types.js').ClarifyPagesItem, import('../../types.js').ClarifyPagesItem>;
57
+ export declare const clarifyPagesGroupSchema: z.ZodType<import('../../types.js').ClarifyPagesGroup, import('../../types.js').ClarifyPagesGroup>;
58
+ export declare const clarifyPagesConfigSchema: z.ZodUnion<readonly [z.ZodArray<z.ZodType<import('../../types.js').ClarifyPagesGroup, import('../../types.js').ClarifyPagesGroup, z.core.$ZodTypeInternals<import('../../types.js').ClarifyPagesGroup, import('../../types.js').ClarifyPagesGroup>>>, z.ZodLiteral<"FileTree">]>;
109
59
  export declare const clarifyTabItemSchema: z.ZodObject<{
110
60
  tab: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
111
61
  icon: z.ZodOptional<z.ZodString>;
112
- pages: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodObject<{
113
- group: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
114
- icon: z.ZodOptional<z.ZodString>;
115
- pages: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
116
- page: z.ZodString;
117
- path: z.ZodOptional<z.ZodString>;
118
- title: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
119
- icon: z.ZodOptional<z.ZodString>;
120
- redirect: z.ZodOptional<z.ZodString>;
121
- }, z.core.$strip>, z.ZodObject<{
122
- openapi: z.ZodString;
123
- path: z.ZodOptional<z.ZodString>;
124
- icon: z.ZodOptional<z.ZodString>;
125
- title: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
126
- filter: z.ZodOptional<z.ZodObject<{
127
- tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
128
- }, z.core.$strip>>;
129
- }, z.core.$strip>]>>;
130
- }, z.core.$strip>>, z.ZodLiteral<"FileTree">]>>;
62
+ pages: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodType<import('../../types.js').ClarifyPagesGroup, import('../../types.js').ClarifyPagesGroup, z.core.$ZodTypeInternals<import('../../types.js').ClarifyPagesGroup, import('../../types.js').ClarifyPagesGroup>>>, z.ZodLiteral<"FileTree">]>>;
131
63
  }, z.core.$strip>;
132
64
  export declare const clarifyTabsConfigSchema: z.ZodArray<z.ZodObject<{
133
65
  tab: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
134
66
  icon: z.ZodOptional<z.ZodString>;
135
- pages: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodObject<{
136
- group: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
137
- icon: z.ZodOptional<z.ZodString>;
138
- pages: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
139
- page: z.ZodString;
140
- path: z.ZodOptional<z.ZodString>;
141
- title: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
142
- icon: z.ZodOptional<z.ZodString>;
143
- redirect: z.ZodOptional<z.ZodString>;
144
- }, z.core.$strip>, z.ZodObject<{
145
- openapi: z.ZodString;
146
- path: z.ZodOptional<z.ZodString>;
147
- icon: z.ZodOptional<z.ZodString>;
148
- title: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
149
- filter: z.ZodOptional<z.ZodObject<{
150
- tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
151
- }, z.core.$strip>>;
152
- }, z.core.$strip>]>>;
153
- }, z.core.$strip>>, z.ZodLiteral<"FileTree">]>>;
67
+ pages: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodType<import('../../types.js').ClarifyPagesGroup, import('../../types.js').ClarifyPagesGroup, z.core.$ZodTypeInternals<import('../../types.js').ClarifyPagesGroup, import('../../types.js').ClarifyPagesGroup>>>, z.ZodLiteral<"FileTree">]>>;
154
68
  }, z.core.$strip>>;
155
69
  export declare const clarifyThemePresetSchema: z.ZodEnum<{
156
70
  default: "default";
@@ -411,25 +325,7 @@ export declare const clarifyProjectConfigSchema: z.ZodObject<{
411
325
  tabs: z.ZodOptional<z.ZodArray<z.ZodObject<{
412
326
  tab: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
413
327
  icon: z.ZodOptional<z.ZodString>;
414
- pages: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodObject<{
415
- group: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
416
- icon: z.ZodOptional<z.ZodString>;
417
- pages: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
418
- page: z.ZodString;
419
- path: z.ZodOptional<z.ZodString>;
420
- title: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
421
- icon: z.ZodOptional<z.ZodString>;
422
- redirect: z.ZodOptional<z.ZodString>;
423
- }, z.core.$strip>, z.ZodObject<{
424
- openapi: z.ZodString;
425
- path: z.ZodOptional<z.ZodString>;
426
- icon: z.ZodOptional<z.ZodString>;
427
- title: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
428
- filter: z.ZodOptional<z.ZodObject<{
429
- tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
430
- }, z.core.$strip>>;
431
- }, z.core.$strip>]>>;
432
- }, z.core.$strip>>, z.ZodLiteral<"FileTree">]>>;
328
+ pages: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodType<import('../../types.js').ClarifyPagesGroup, import('../../types.js').ClarifyPagesGroup, z.core.$ZodTypeInternals<import('../../types.js').ClarifyPagesGroup, import('../../types.js').ClarifyPagesGroup>>>, z.ZodLiteral<"FileTree">]>>;
433
329
  }, z.core.$strip>>>;
434
330
  }, z.core.$strip>;
435
331
  export type ClarifyProjectConfigInput = z.input<typeof clarifyProjectConfigSchema>;
@@ -6,6 +6,7 @@ import { VirtualModules } from '../runtime/virtual-modules.js';
6
6
  import { ClarifyContext } from './context.js';
7
7
  import { ClarifyEngineRuntime, PrepareOptions } from './types.js';
8
8
  export type { ClarifyEngineMode, ClarifyEngineRuntime, PrepareOptions } from './types.js';
9
+ export declare function assertNoContentDiagnostics(routes: ContentRoute[]): void;
9
10
  export declare class ClarifyEngine {
10
11
  readonly options: ClarifyBuildOptions;
11
12
  readonly root: string;
@@ -47,6 +47,6 @@ export declare function createClientEntryModule(options?: CreateClientEntryModul
47
47
  * type — no runtime validation is needed.
48
48
  */
49
49
  export declare function createRuntimeSlotsModule(plugins?: ClarifyPlugin[], root?: string): string;
50
- export declare function generateMdxErrorModule(diagnostic: ContentDiagnostic): string;
50
+ export declare function generateContentDiagnosticModule(diagnostic: ContentDiagnostic): string;
51
51
  export declare function buildVirtualModules(args: BuildVirtualModulesArgs): VirtualModules;
52
52
  export {};
@@ -53,6 +53,24 @@ let _apidevtools_swagger_parser = require("@apidevtools/swagger-parser");
53
53
  _apidevtools_swagger_parser = __toESM(_apidevtools_swagger_parser, 1);
54
54
  let node_os = require("node:os");
55
55
  let node_module = require("node:module");
56
+ //#region source/parsers/content/diagnostic.ts
57
+ function displayPath(filePath, projectRoot) {
58
+ if (!filePath) return void 0;
59
+ if (!projectRoot) return filePath.replace(/^[A-Za-z]:\//, "").replace(/^\/+/, "");
60
+ const normalized = (0, node_path.relative)(projectRoot, filePath);
61
+ return normalized && !normalized.startsWith("..") && !(0, node_path.isAbsolute)(normalized) ? normalized : filePath.replace(/^[A-Za-z]:\//, "").replace(/^\/+/, "");
62
+ }
63
+ function createContentDiagnostic(options) {
64
+ const { error } = options;
65
+ return {
66
+ kind: options.kind,
67
+ title: options.title,
68
+ message: options.message,
69
+ filePath: displayPath(options.filePath, options.projectRoot),
70
+ details: error instanceof Error ? options.includeStack ? error.stack ?? error.message : error.message : String(error)
71
+ };
72
+ }
73
+ //#endregion
56
74
  //#region source/parsers/markdown/mdx.ts
57
75
  function getLanguage(className) {
58
76
  return (Array.isArray(className) ? className : typeof className === "string" ? className.split(/\s+/) : []).find((item) => typeof item === "string" && item.startsWith("language-"))?.replace(/^language-/, "") || "txt";
@@ -88,9 +106,18 @@ var rehypePlugins = [
88
106
  parseMetaString: _clarify_labs_renderer.parseCodeMeta
89
107
  }]
90
108
  ];
91
- function formatMdxDiagnostic(error) {
92
- if (error instanceof Error) return error.message;
93
- return String(error);
109
+ function createContentCompileDiagnostic(options) {
110
+ const { format, phase, error, filePath, projectRoot } = options;
111
+ const label = format === "mdx" ? "MDX" : "Markdown";
112
+ return createContentDiagnostic({
113
+ kind: format,
114
+ title: `${label} ${phase} error`,
115
+ message: phase === "syntax" ? "This page could not be compiled. Fix the MDX syntax or component usage above, then reload the route." : `This page could not be compiled. Fix the ${label} syntax or plugin error below, then reload the route.`,
116
+ error,
117
+ filePath,
118
+ projectRoot,
119
+ includeStack: phase === "compilation"
120
+ });
94
121
  }
95
122
  /**
96
123
  * Validate MDX content for diagnostics WITHOUT running the build-time rehype
@@ -121,13 +148,13 @@ async function compileMdxContent(content, filePath, projectRoot) {
121
148
  } catch (error) {
122
149
  return {
123
150
  ok: false,
124
- diagnostic: {
125
- kind: "mdx",
126
- title: "MDX syntax error",
127
- message: "This page could not be compiled. Fix the MDX syntax or component usage above, then reload the route.",
128
- filePath: filePath && projectRoot ? filePath.replace(projectRoot + "/", "").replace(/^\/+/, "") : filePath ? filePath.replace(/^[A-Za-z]:\//, "").replace(/^\/+/, "") : void 0,
129
- details: formatMdxDiagnostic(error)
130
- }
151
+ diagnostic: createContentCompileDiagnostic({
152
+ format: "mdx",
153
+ phase: "syntax",
154
+ error,
155
+ filePath,
156
+ projectRoot
157
+ })
131
158
  };
132
159
  }
133
160
  }
@@ -483,6 +510,7 @@ function routeIntentFromPagesItem(item) {
483
510
  kind: "page",
484
511
  ref: item
485
512
  };
513
+ if ("group" in item) throw new Error("Navigation groups do not resolve to route intents");
486
514
  if ("openapi" in item) return {
487
515
  kind: "openapi",
488
516
  ref: item.openapi,
@@ -510,15 +538,19 @@ function firstNavigationPath(nodes) {
510
538
  }
511
539
  function collectExplicitPagePathIntents(tabs) {
512
540
  const intents = [];
513
- for (const tab of tabs ?? []) {
514
- if (!tab.pages || tab.pages === "FileTree") continue;
515
- for (const group of tab.pages) for (const item of group.pages) if (typeof item !== "string" && "page" in item && item.path) {
541
+ const collectItems = (items) => {
542
+ for (const item of items) if (typeof item !== "string" && "group" in item) collectItems(item.pages);
543
+ else if (typeof item !== "string" && "page" in item && item.path) {
516
544
  const intent = routeIntentFromPagesItem(item);
517
545
  if (intent.kind === "page" && intent.path) intents.push({
518
546
  ...intent,
519
547
  path: intent.path
520
548
  });
521
549
  }
550
+ };
551
+ for (const tab of tabs ?? []) {
552
+ if (!tab.pages || tab.pages === "FileTree") continue;
553
+ for (const group of tab.pages) collectItems(group.pages);
522
554
  }
523
555
  return intents;
524
556
  }
@@ -569,28 +601,38 @@ function buildNavigationFromTabsConfig(routes, tabs) {
569
601
  }
570
602
  function buildNavigationFromConfig(routes, config) {
571
603
  const routeMap = new Map(routes.map((r) => [r.path, r]));
572
- return config.map((group) => {
573
- const children = group.pages.map((item) => {
574
- const intent = routeIntentFromPagesItem(item);
575
- if (intent.kind === "openapi") {
576
- const path = openAPIPagePathFromRef(intent.ref, intent.tagFilter, intent.path);
577
- const route = routeMap.get(path);
578
- return {
579
- path,
580
- title: resolveLocalizedText(intent.title, "", "") ?? route?.meta.title ?? kebabToTitle(path.split("/").pop() ?? intent.ref),
581
- icon: intent.icon,
582
- sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
583
- };
584
- }
585
- const path = routePathFromRef(intent.ref, intent.path);
604
+ const buildItem = (item) => {
605
+ if (typeof item !== "string" && "group" in item) {
606
+ const children = item.pages.map(buildItem);
607
+ return {
608
+ path: firstNavigationPath(children),
609
+ title: resolveLocalizedText(item.group, "", "") ?? "",
610
+ icon: item.icon,
611
+ children
612
+ };
613
+ }
614
+ const intent = routeIntentFromPagesItem(item);
615
+ if (intent.kind === "openapi") {
616
+ const path = openAPIPagePathFromRef(intent.ref, intent.tagFilter, intent.path);
586
617
  const route = routeMap.get(path);
587
618
  return {
588
- path: intent.redirect ? normalizePath(intent.redirect) : path,
619
+ path,
589
620
  title: resolveLocalizedText(intent.title, "", "") ?? route?.meta.title ?? kebabToTitle(path.split("/").pop() ?? intent.ref),
590
621
  icon: intent.icon,
591
622
  sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
592
623
  };
593
- });
624
+ }
625
+ const path = routePathFromRef(intent.ref, intent.path);
626
+ const route = routeMap.get(path);
627
+ return {
628
+ path: intent.redirect ? normalizePath(intent.redirect) : path,
629
+ title: resolveLocalizedText(intent.title, "", "") ?? route?.meta.title ?? kebabToTitle(path.split("/").pop() ?? intent.ref),
630
+ icon: intent.icon,
631
+ sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
632
+ };
633
+ };
634
+ return config.map((group) => {
635
+ const children = group.pages.map(buildItem);
594
636
  return {
595
637
  path: children[0]?.path ?? "/",
596
638
  title: resolveLocalizedText(group.group, "", "") ?? "",
@@ -612,19 +654,29 @@ function buildLocalizedNavigationForLocale(routes, config, locale, i18n) {
612
654
  path: route.basePath ?? route.path
613
655
  }))), locale, i18n);
614
656
  const routeMap = new Map(routes.map((route) => [route.basePath ?? route.path, route]));
615
- return config.map((group) => {
616
- const children = group.pages.map((item) => {
617
- const intent = routeIntentFromPagesItem(item);
618
- const basePath = intent.kind === "openapi" ? openAPIPagePathFromRef(intent.ref, intent.tagFilter, intent.path) : routePathFromRef(intent.ref, intent.path);
619
- const route = routeMap.get(basePath);
620
- const path = route?.path ?? localizedRoutePath(basePath, locale, i18n);
657
+ const buildItem = (item) => {
658
+ if (typeof item !== "string" && "group" in item) {
659
+ const children = item.pages.map(buildItem);
621
660
  return {
622
- path: (intent.kind === "page" && intent.redirect ? localizedRoutePath(routePathFromRef(intent.redirect), locale, i18n) : void 0) ?? path,
623
- title: resolveLocalizedText(intent.title, locale, i18n.defaultLocale) ?? route?.meta.title ?? kebabToTitle(basePath.split("/").pop() ?? intent.ref),
624
- icon: intent.icon,
625
- sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
661
+ path: firstNavigationPath(children),
662
+ title: resolveLocalizedText(item.group, locale, i18n.defaultLocale) ?? "",
663
+ icon: item.icon,
664
+ children
626
665
  };
627
- });
666
+ }
667
+ const intent = routeIntentFromPagesItem(item);
668
+ const basePath = intent.kind === "openapi" ? openAPIPagePathFromRef(intent.ref, intent.tagFilter, intent.path) : routePathFromRef(intent.ref, intent.path);
669
+ const route = routeMap.get(basePath);
670
+ const path = route?.path ?? localizedRoutePath(basePath, locale, i18n);
671
+ return {
672
+ path: (intent.kind === "page" && intent.redirect ? localizedRoutePath(routePathFromRef(intent.redirect), locale, i18n) : void 0) ?? path,
673
+ title: resolveLocalizedText(intent.title, locale, i18n.defaultLocale) ?? route?.meta.title ?? kebabToTitle(basePath.split("/").pop() ?? intent.ref),
674
+ icon: intent.icon,
675
+ sections: route?.meta.sections ? navigationSections(route.meta.sections) : void 0
676
+ };
677
+ };
678
+ return config.map((group) => {
679
+ const children = group.pages.map(buildItem);
628
680
  return {
629
681
  path: children[0]?.path ?? localizedRoutePath("/", locale, i18n),
630
682
  title: resolveLocalizedText(group.group, locale, i18n.defaultLocale) ?? "",
@@ -869,8 +921,9 @@ var clarifySourceConfigSchema = zod.z.object({
869
921
  branch: zod.z.string().optional(),
870
922
  directory: zod.z.string().optional()
871
923
  });
872
- var clarifyPagesItemSchema = zod.z.union([
924
+ var clarifyPagesItemSchema = zod.z.lazy(() => zod.z.union([
873
925
  zod.z.string(),
926
+ clarifyPagesGroupSchema,
874
927
  zod.z.object({
875
928
  page: zod.z.string(),
876
929
  path: zod.z.string().optional(),
@@ -885,12 +938,12 @@ var clarifyPagesItemSchema = zod.z.union([
885
938
  title: clarifyLocalizedTextSchema.optional(),
886
939
  filter: zod.z.object({ tags: zod.z.array(zod.z.string()).optional() }).optional()
887
940
  })
888
- ]);
889
- var clarifyPagesGroupSchema = zod.z.object({
941
+ ]));
942
+ var clarifyPagesGroupSchema = zod.z.lazy(() => zod.z.object({
890
943
  group: clarifyLocalizedTextSchema,
891
944
  icon: zod.z.string().optional(),
892
945
  pages: zod.z.array(clarifyPagesItemSchema)
893
- });
946
+ }));
894
947
  var clarifyPagesConfigSchema = zod.z.union([zod.z.array(clarifyPagesGroupSchema), zod.z.literal("FileTree")]);
895
948
  var clarifyTabItemSchema = zod.z.object({
896
949
  tab: clarifyLocalizedTextSchema,
@@ -1238,16 +1291,10 @@ function createRuntimeSlotsModule(plugins = [], root = process.cwd()) {
1238
1291
  }
1239
1292
  return `export const runtimeSlots = {\n${[...grouped.entries()].map(([name, list]) => ` ${JSON.stringify(name)}: [\n ${list.join(",\n ")}\n ]`).join(",\n")}\n};\n`;
1240
1293
  }
1241
- function generateMdxErrorModule(diagnostic) {
1294
+ function generateContentDiagnosticModule(diagnostic) {
1242
1295
  return `import { createContentDiagnosticComponent } from '@clarify-labs/renderer';
1243
1296
 
1244
- export const contentDiagnostic = ${JSON.stringify({
1245
- kind: diagnostic.kind ?? "mdx",
1246
- title: diagnostic.title,
1247
- message: diagnostic.message,
1248
- filePath: diagnostic.filePath,
1249
- details: diagnostic.details
1250
- })};
1297
+ export const contentDiagnostic = ${JSON.stringify(diagnostic)};
1251
1298
 
1252
1299
  export default createContentDiagnosticComponent(contentDiagnostic);
1253
1300
  `;
@@ -1265,7 +1312,7 @@ function buildVirtualModules(args) {
1265
1312
  modules.set(VIRTUAL_CLIENT_ENTRY, clientEntryModule);
1266
1313
  modules.set(RESOLVED_CLIENT_ENTRY, clientEntryModule);
1267
1314
  for (const route of args.routes) {
1268
- const moduleContent = route.diagnostic ? generateMdxErrorModule(route.diagnostic) : `export { default } from ${moduleSpecifier(route.source.filePath)};`;
1315
+ const moduleContent = route.diagnostic ? generateContentDiagnosticModule(route.diagnostic) : `export { default } from ${moduleSpecifier(route.source.filePath)};`;
1269
1316
  modules.set(route.module.virtualModuleId, moduleContent);
1270
1317
  }
1271
1318
  return modules;
@@ -1316,26 +1363,42 @@ function createNormalizedContentPlugin(engine) {
1316
1363
  }
1317
1364
  };
1318
1365
  }
1319
- function createMdxPlugin() {
1320
- return (0, _mdx_js_rollup.default)({
1321
- include: ["**/*.mdx"],
1322
- format: "mdx",
1323
- jsxImportSource: "react",
1324
- providerImportSource: "@clarify-labs/renderer",
1325
- remarkPlugins,
1326
- rehypePlugins
1327
- });
1366
+ function createContentCompileTransform(transform, format, projectRoot, errorPolicy = () => "diagnostic") {
1367
+ return async function contentCompileTransform(code, id) {
1368
+ try {
1369
+ return await transform.call(this, code, id);
1370
+ } catch (error) {
1371
+ if (errorPolicy() === "throw") throw error;
1372
+ return {
1373
+ code: generateContentDiagnosticModule(createContentCompileDiagnostic({
1374
+ format,
1375
+ phase: "compilation",
1376
+ error,
1377
+ filePath: id.replace(/\?.*$/, ""),
1378
+ projectRoot
1379
+ })),
1380
+ map: null
1381
+ };
1382
+ }
1383
+ };
1328
1384
  }
1329
- function createMarkdownPlugin() {
1330
- return (0, _mdx_js_rollup.default)({
1331
- include: ["**/*.md"],
1332
- format: "md",
1385
+ function createContentCompilerPlugin(format, projectRoot, errorPolicy) {
1386
+ const markdown = format === "markdown";
1387
+ const plugin = (0, _mdx_js_rollup.default)({
1388
+ include: [markdown ? "**/*.md" : "**/*.mdx"],
1389
+ format: markdown ? "md" : "mdx",
1333
1390
  jsxImportSource: "react",
1334
1391
  providerImportSource: "@clarify-labs/renderer",
1335
1392
  remarkPlugins,
1336
- remarkRehypeOptions: { allowDangerousHtml: true },
1337
- rehypePlugins: [rehype_raw.default, ...rehypePlugins]
1393
+ ...markdown ? { remarkRehypeOptions: { allowDangerousHtml: true } } : {},
1394
+ rehypePlugins: markdown ? [rehype_raw.default, ...rehypePlugins] : rehypePlugins
1338
1395
  });
1396
+ const transform = typeof plugin.transform === "function" ? plugin.transform : plugin.transform?.handler;
1397
+ if (!transform) return plugin;
1398
+ return {
1399
+ ...plugin,
1400
+ transform: createContentCompileTransform(transform, format, projectRoot, errorPolicy)
1401
+ };
1339
1402
  }
1340
1403
  function createClarifyViteCorePlugin(engine, normalizedContentPlugin, markdown, mdx) {
1341
1404
  let viteConfig;
@@ -1457,13 +1520,22 @@ function createClarifyViteCorePlugin(engine, normalizedContentPlugin, markdown,
1457
1520
  * site itself - it only bridges Vite lifecycle hooks to the engine.
1458
1521
  */
1459
1522
  function createViteAdapter(engine) {
1523
+ let contentCompileErrorPolicy = "diagnostic";
1524
+ const errorPolicy = () => contentCompileErrorPolicy;
1460
1525
  const normalizedContentPlugin = createNormalizedContentPlugin(engine);
1461
- const markdown = createMarkdownPlugin();
1462
- const mdx = createMdxPlugin();
1526
+ const markdown = createContentCompilerPlugin("markdown", engine.root, errorPolicy);
1527
+ const mdx = createContentCompilerPlugin("mdx", engine.root, errorPolicy);
1528
+ const compilePolicyPlugin = {
1529
+ name: "clarify:content-compile-policy",
1530
+ configResolved(config) {
1531
+ contentCompileErrorPolicy = config.command === "build" && engine.generateOptions.ssg.failOnError ? "throw" : "diagnostic";
1532
+ }
1533
+ };
1463
1534
  const tailwind = (0, _tailwindcss_vite.default)();
1464
1535
  return [
1465
1536
  ...(0, _vitejs_plugin_react.default)(),
1466
1537
  ...Array.isArray(tailwind) ? tailwind : [tailwind],
1538
+ compilePolicyPlugin,
1467
1539
  normalizedContentPlugin,
1468
1540
  createClarifyViteCorePlugin(engine, normalizedContentPlugin, markdown, mdx),
1469
1541
  markdown,
@@ -1472,7 +1544,7 @@ function createViteAdapter(engine) {
1472
1544
  }
1473
1545
  var cliPackageVersion = {
1474
1546
  name: "@clarify-labs/cli",
1475
- version: "0.10.2",
1547
+ version: "0.10.3",
1476
1548
  license: "AGPL-3.0-only",
1477
1549
  type: "module",
1478
1550
  repository: {
@@ -1825,9 +1897,6 @@ var OPENAPI_HTTP_METHODS = [
1825
1897
  "trace"
1826
1898
  ];
1827
1899
  var CLARIFY_OPENAPI_SECTION_ID_EXTENSION = "x-clarify-section-id";
1828
- function errorMessage(error) {
1829
- return error instanceof Error ? error.message : String(error);
1830
- }
1831
1900
  function isSameOpenAPIFile(url, filePath) {
1832
1901
  try {
1833
1902
  return (0, node_path.resolve)((0, node_url.fileURLToPath)(url)) === (0, node_path.resolve)(filePath);
@@ -1942,7 +2011,7 @@ function extractOpenAPISections(spec, filterTags) {
1942
2011
  }
1943
2012
  return sections;
1944
2013
  }
1945
- async function readOpenAPISpec(filePath, contentProcessor) {
2014
+ async function readOpenAPISpec(filePath, contentProcessor, projectRoot) {
1946
2015
  try {
1947
2016
  if (!contentProcessor) return {
1948
2017
  ok: true,
@@ -1965,13 +2034,14 @@ async function readOpenAPISpec(filePath, contentProcessor) {
1965
2034
  } catch (error) {
1966
2035
  return {
1967
2036
  ok: false,
1968
- diagnostic: {
2037
+ diagnostic: createContentDiagnostic({
1969
2038
  kind: "openapi",
1970
2039
  title: "OpenAPI spec parse failed",
1971
- message: `Clarify could not parse ${filePath}.`,
2040
+ message: "Clarify could not parse this OpenAPI specification.",
2041
+ error,
1972
2042
  filePath,
1973
- details: errorMessage(error)
1974
- }
2043
+ projectRoot
2044
+ })
1975
2045
  };
1976
2046
  }
1977
2047
  }
@@ -2002,31 +2072,24 @@ export const routeData = {};
2002
2072
  export default createOpenApiRouteComponent({ ...routeData, spec });
2003
2073
  `;
2004
2074
  }
2005
- function generateOpenAPIErrorModule(diagnostic) {
2006
- return `import { createContentDiagnosticComponent } from '@clarify-labs/renderer';
2007
-
2008
- export const contentDiagnostic = ${JSON.stringify({
2009
- kind: diagnostic.kind ?? "openapi",
2010
- title: diagnostic.title,
2011
- message: diagnostic.message,
2012
- filePath: diagnostic.filePath,
2013
- details: diagnostic.details
2014
- })};
2015
-
2016
- export default createContentDiagnosticComponent(contentDiagnostic);
2017
- `;
2018
- }
2019
2075
  //#endregion
2020
2076
  //#region source/plugins/openapi/index.ts
2021
2077
  function collectOpenAPIPageIntents(config) {
2022
2078
  const intents = [];
2023
- function visitPages(pages) {
2024
- if (!pages || pages === "FileTree") return;
2025
- for (const group of pages) for (const item of group.pages) {
2079
+ function visitItems(items) {
2080
+ for (const item of items) {
2081
+ if (typeof item !== "string" && "group" in item) {
2082
+ visitItems(item.pages);
2083
+ continue;
2084
+ }
2026
2085
  const intent = routeIntentFromPagesItem(item);
2027
2086
  if (intent.kind === "openapi" && (intent.path || intent.tagFilter?.length)) intents.push(intent);
2028
2087
  }
2029
2088
  }
2089
+ function visitPages(pages) {
2090
+ if (!pages || pages === "FileTree") return;
2091
+ for (const group of pages) visitItems(group.pages);
2092
+ }
2030
2093
  for (const tab of config.tabs ?? []) visitPages(tab.pages);
2031
2094
  return intents;
2032
2095
  }
@@ -2172,7 +2235,7 @@ function createOpenAPIPlugin() {
2172
2235
  const result = specFromCache ? {
2173
2236
  ok: true,
2174
2237
  spec: specFromCache
2175
- } : await readOpenAPISpec(route.source.filePath, processor);
2238
+ } : await readOpenAPISpec(route.source.filePath, processor, ctx.generateOptions.projectRoot);
2176
2239
  if (!result.ok) {
2177
2240
  route.diagnostic = result.diagnostic;
2178
2241
  route.meta.title = route.meta.title || "OpenAPI parse error";
@@ -2219,7 +2282,7 @@ function createOpenAPIPlugin() {
2219
2282
  for (const [routeSpecModuleId, spec] of specModules) modules.set(specVirtualModuleId(routeSpecModuleId), generateOpenAPISpecModule(spec));
2220
2283
  for (const route of ctx.routes) {
2221
2284
  if (route.kind !== "openapi") continue;
2222
- if (route.diagnostic) modules.set(route.module.virtualModuleId, generateOpenAPIErrorModule(route.diagnostic));
2285
+ if (route.diagnostic) modules.set(route.module.virtualModuleId, generateContentDiagnosticModule(route.diagnostic));
2223
2286
  else if (route.openapi?.sourceSpecId) {
2224
2287
  const routeSpecModuleId = route.openapi.routeSpecModuleId ?? route.openapi.sourceSpecId;
2225
2288
  if (!specModules.has(routeSpecModuleId)) continue;
@@ -3051,6 +3114,12 @@ function loadVirtualModule(id, modules) {
3051
3114
  const bareId = stripVirtualPrefix(id);
3052
3115
  return modules.get(bareId) ?? modules.get(id) ?? null;
3053
3116
  }
3117
+ function assertNoContentDiagnostics(routes) {
3118
+ const failures = routes.filter((route) => route.diagnostic);
3119
+ if (failures.length === 0) return;
3120
+ const summary = failures.map((route) => `- ${route.path}: ${route.diagnostic?.title ?? "Content error"}${route.diagnostic?.filePath ? ` (${route.diagnostic.filePath})` : ""}`).join("\n");
3121
+ throw new Error(`[clarify] Content diagnostics prevented the build:\n${summary}`);
3122
+ }
3054
3123
  var ClarifyEngine = class {
3055
3124
  options;
3056
3125
  root;
@@ -3275,6 +3344,7 @@ var ClarifyEngine = class {
3275
3344
  await runPhase(this.plugins, "ssg", this.ctx, async () => {
3276
3345
  if (process.env.SKIP_CLARIFY_SSG) return;
3277
3346
  if (!await runInterceptHooks(this.plugins, "ssg:shouldRun", this.ctx)) return;
3347
+ if (this.generateOptions.ssg.failOnError) assertNoContentDiagnostics(this.routes);
3278
3348
  const outputDir = this.runtime.outputDirectory ?? this.generateOptions.outputDirectory;
3279
3349
  if (!outputDir) throw new Error("[clarify] outputDirectory is required before SSG runs");
3280
3350
  const ssrOutputDir = (0, node_path.join)(outputDir, ".ssr");
@@ -3382,4 +3452,4 @@ Object.defineProperty(exports, "defineConfig", {
3382
3452
  }
3383
3453
  });
3384
3454
 
3385
- //# sourceMappingURL=engine-qafLfnXQ.cjs.map
3455
+ //# sourceMappingURL=engine-BDdPszs2.cjs.map