openuispec 0.1.28 → 0.1.29

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.
@@ -111,6 +111,16 @@
111
111
  "type": "string"
112
112
  }
113
113
  },
114
+ "code_roots": {
115
+ "type": "object",
116
+ "description": "Additional project code roots used as generation context.",
117
+ "properties": {
118
+ "backend": {
119
+ "type": "string"
120
+ }
121
+ },
122
+ "additionalProperties": false
123
+ },
114
124
  "output_format": {
115
125
  "type": "object",
116
126
  "description": "Per-target generation config",
@@ -1,6 +1,7 @@
1
- import { existsSync, readFileSync, readdirSync } from "node:fs";
1
+ import { existsSync, readFileSync } from "node:fs";
2
2
  import { basename, join, resolve } from "node:path";
3
3
  import YAML from "yaml";
4
+ import { listFiles } from "../drift/index.js";
4
5
 
5
6
  type UnknownRecord = Record<string, unknown>;
6
7
 
@@ -19,6 +20,7 @@ export interface UsageLint {
19
20
  }
20
21
 
21
22
  interface SemanticContext {
23
+ manifest: unknown;
22
24
  localeFiles: Map<string, Set<string>>;
23
25
  formatterNames: Set<string>;
24
26
  mapperNames: Set<string>;
@@ -66,17 +68,6 @@ function loadData(filePath: string): unknown {
66
68
  return filePath.endsWith(".json") ? loadJson(filePath) : loadYaml(filePath);
67
69
  }
68
70
 
69
- function listFiles(dir: string, ext: string): string[] {
70
- try {
71
- return readdirSync(dir)
72
- .filter((file) => file.endsWith(ext))
73
- .sort()
74
- .map((file) => join(dir, file));
75
- } catch {
76
- return [];
77
- }
78
- }
79
-
80
71
  function isRecord(value: unknown): value is UnknownRecord {
81
72
  return typeof value === "object" && value !== null && !Array.isArray(value);
82
73
  }
@@ -281,6 +272,7 @@ function buildContext(projectDir: string, includes: Includes): SemanticContext {
281
272
  : localeFiles.keys().next().value ?? null;
282
273
 
283
274
  return {
275
+ manifest,
284
276
  localeFiles,
285
277
  formatterNames,
286
278
  mapperNames,
@@ -552,6 +544,37 @@ function lintLocaleCoverage(context: SemanticContext): UsageLint[] {
552
544
  return errors;
553
545
  }
554
546
 
547
+ function lintManifestGenerationContext(projectDir: string, manifest: unknown): UsageLint[] {
548
+ if (!isRecord(manifest)) return [];
549
+
550
+ const hasApiEndpoints =
551
+ isRecord(manifest.api) &&
552
+ isRecord(manifest.api.endpoints) &&
553
+ Object.keys(manifest.api.endpoints).length > 0;
554
+ if (!hasApiEndpoints) return [];
555
+
556
+ const generation = isRecord(manifest.generation) ? manifest.generation : {};
557
+ const codeRoots = isRecord(generation.code_roots) ? generation.code_roots : null;
558
+ const backendRoot = codeRoots && typeof codeRoots.backend === "string" ? codeRoots.backend.trim() : "";
559
+
560
+ if (!backendRoot) {
561
+ return [{
562
+ path: "openuispec.yaml",
563
+ message: 'api endpoints require generation.code_roots.backend to point at the backend folder',
564
+ }];
565
+ }
566
+
567
+ const resolvedBackendRoot = resolve(projectDir, backendRoot);
568
+ if (!existsSync(resolvedBackendRoot)) {
569
+ return [{
570
+ path: "openuispec.yaml",
571
+ message: `generation.code_roots.backend points to a missing folder: ${backendRoot}`,
572
+ }];
573
+ }
574
+
575
+ return [];
576
+ }
577
+
555
578
  function printSemanticErrors(label: string, errors: UsageLint[]): number {
556
579
  if (errors.length === 0) return 0;
557
580
  const previewLimit = 10;
@@ -572,6 +595,7 @@ export function runSemanticLint(projectDir: string, includes: Includes): number
572
595
  const contractsDir = resolve(projectDir, includes.contracts);
573
596
 
574
597
  total += printSemanticErrors("locales", lintLocaleCoverage(context));
598
+ total += printSemanticErrors("openuispec.yaml", lintManifestGenerationContext(projectDir, context.manifest));
575
599
 
576
600
  const files = [
577
601
  join(projectDir, "openuispec.yaml"),