@likec4/config 1.44.0 → 1.46.0

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.
package/dist/index.d.mts CHANGED
@@ -7,13 +7,18 @@ import * as z from 'zod';
7
7
  import { ElementColorValues, RelationshipColorValues, ThemeColorValues, ColorLiteral } from '@likec4/core/styles';
8
8
 
9
9
  declare const ManualLayoutsConfigSchema: z.ZodObject<{
10
- outDir: z.ZodString;
10
+ outDir: z.ZodDefault<z.ZodString>;
11
11
  }, z.core.$strip>;
12
12
  declare const LikeC4ProjectJsonConfigSchema: z.ZodObject<{
13
13
  name: z.ZodString;
14
14
  title: z.ZodOptional<z.ZodString>;
15
15
  contactPerson: z.ZodOptional<z.ZodString>;
16
16
  imageAliases: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
17
+ include: z.ZodOptional<z.ZodObject<{
18
+ paths: z.ZodArray<z.ZodString>;
19
+ maxDepth: z.ZodDefault<z.ZodNumber>;
20
+ fileThreshold: z.ZodDefault<z.ZodNumber>;
21
+ }, z.core.$strip>>;
17
22
  styles: z.ZodOptional<z.ZodPipe<z.ZodObject<{
18
23
  theme: z.ZodOptional<z.ZodPipe<z.ZodObject<{
19
24
  colors: z.ZodOptional<z.ZodUnion<readonly [z.ZodRecord<z.ZodPipe<z.ZodUnion<readonly [z.ZodLiteral<"amber" | "blue" | "gray" | "slate" | "green" | "indigo" | "muted" | "primary" | "red" | "secondary" | "sky">, z.ZodString]>, z.ZodTransform<"amber" | "blue" | "gray" | "slate" | "green" | "indigo" | "muted" | "primary" | "red" | "secondary" | "sky", string>>, z.ZodPipe<z.ZodUnion<readonly [z.ZodPipe<z.ZodString, z.ZodTransform<`#${string}` | `rgb(${number},${number},${number})` | `rgba(${number},${number},${number},${number})`, string>>, z.ZodObject<{
@@ -779,7 +784,7 @@ declare const LikeC4ProjectJsonConfigSchema: z.ZodObject<{
779
784
  }>>>;
780
785
  exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
781
786
  manualLayouts: z.ZodOptional<z.ZodObject<{
782
- outDir: z.ZodString;
787
+ outDir: z.ZodDefault<z.ZodString>;
783
788
  }, z.core.$strip>>;
784
789
  }, z.core.$strip>;
785
790
  type LikeC4ProjectJsonConfig = SimplifyDeep<z.input<typeof LikeC4ProjectJsonConfigSchema>>;
@@ -912,12 +917,21 @@ type LikeC4ProjectConfigInput = SimplifyDeep<z.input<typeof LikeC4ProjectJsonCon
912
917
  * Validates JSON string or JSON object into a LikeC4ProjectConfig object.
913
918
  */
914
919
  declare function validateProjectConfig<C extends string | Record<string, unknown>>(config: C): LikeC4ProjectConfig;
915
- /**
916
- * Converts a LikeC4ProjectConfig object into a LikeC4ProjectJsonConfig object.
917
- * Omit generators property (as it is not serializable)
918
- */
919
- declare function serializableLikeC4ProjectConfig({ generators: _, // omit
920
- ...config }: LikeC4ProjectConfig): LikeC4ProjectJsonConfig;
920
+
921
+ declare const IncludeConfigSchema: z.ZodObject<{
922
+ paths: z.ZodArray<z.ZodString>;
923
+ maxDepth: z.ZodDefault<z.ZodNumber>;
924
+ fileThreshold: z.ZodDefault<z.ZodNumber>;
925
+ }, z.core.$strip>;
926
+ type IncludeConfig = z.infer<typeof IncludeConfigSchema>;
927
+ declare const IncludeSchema: z.ZodOptional<z.ZodObject<{
928
+ paths: z.ZodArray<z.ZodString>;
929
+ maxDepth: z.ZodDefault<z.ZodNumber>;
930
+ fileThreshold: z.ZodDefault<z.ZodNumber>;
931
+ }, z.core.$strip>>;
932
+ type LikeC4IncludeConfig = z.infer<typeof IncludeSchema>;
933
+ declare function normalizeIncludeConfig(include?: LikeC4IncludeConfig): IncludeConfig;
934
+ declare function validateIncludePaths(include?: LikeC4IncludeConfig): void;
921
935
 
922
936
  declare const ThemeColorValuesSchema: z.ZodPipe<z.ZodUnion<readonly [z.ZodPipe<z.ZodString, z.ZodTransform<`#${string}` | `rgb(${number},${number},${number})` | `rgba(${number},${number},${number},${number})`, string>>, z.ZodObject<{
923
937
  elements: z.ZodPipe<z.ZodUnion<readonly [z.ZodPipe<z.ZodString, z.ZodTransform<`#${string}` | `rgb(${number},${number},${number})` | `rgba(${number},${number},${number},${number})`, string>>, z.ZodPipe<z.ZodObject<{
@@ -2378,4 +2392,4 @@ declare function defineTheme<const S extends LikeC4ConfigThemeInput>(theme: S):
2378
2392
  */
2379
2393
  declare function defineStyle<const S extends LikeC4StylesConfigInput>(styles: S): LikeC4ProjectStylesConfig;
2380
2394
 
2381
- export { ConfigFilenames, type GeneratorFn, type GeneratorFnContext, type GeneratorFnParams, type LikeC4ProjectConfig, type LikeC4ProjectConfigInput, type LikeC4ProjectJsonConfig, type LikeC4StylesConfig, type LikeC4StylesConfigInput, type LocateResult, ManualLayoutsConfigSchema, defineConfig, defineGenerators, defineStyle, defineTheme, defineThemeColor, isLikeC4Config, isLikeC4JsonConfig, isLikeC4NonJsonConfig, serializableLikeC4ProjectConfig, validateProjectConfig };
2395
+ export { ConfigFilenames, type GeneratorFn, type GeneratorFnContext, type GeneratorFnParams, type IncludeConfig, type LikeC4ProjectConfig, type LikeC4ProjectConfigInput, type LikeC4ProjectJsonConfig, type LikeC4StylesConfig, type LikeC4StylesConfigInput, type LocateResult, ManualLayoutsConfigSchema, defineConfig, defineGenerators, defineStyle, defineTheme, defineThemeColor, isLikeC4Config, isLikeC4JsonConfig, isLikeC4NonJsonConfig, normalizeIncludeConfig, validateIncludePaths, validateProjectConfig };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- export { C as ConfigFilenames, d as defineConfig, c as defineGenerators, e as defineStyle, f as defineTheme, g as defineThemeColor, i as isLikeC4Config, a as isLikeC4JsonConfig, b as isLikeC4NonJsonConfig, s as serializableLikeC4ProjectConfig, v as validateProjectConfig } from './shared/config.FegrYs1S.mjs';
1
+ export { C as ConfigFilenames, d as defineConfig, e as defineGenerators, f as defineStyle, g as defineTheme, h as defineThemeColor, i as isLikeC4Config, b as isLikeC4JsonConfig, c as isLikeC4NonJsonConfig, n as normalizeIncludeConfig, a as validateIncludePaths, v as validateProjectConfig } from './shared/config.CUC_rqhf.mjs';
@@ -1,6 +1,6 @@
1
1
  import { URI } from 'vscode-uri';
2
2
  import { LikeC4ProjectConfig } from '../index.mjs';
3
- export { ConfigFilenames, GeneratorFn, GeneratorFnContext, GeneratorFnParams, LikeC4ProjectConfigInput, LikeC4ProjectJsonConfig, LikeC4StylesConfig, LikeC4StylesConfigInput, LocateResult, ManualLayoutsConfigSchema, defineConfig, defineGenerators, defineStyle, defineTheme, defineThemeColor, isLikeC4Config, isLikeC4JsonConfig, isLikeC4NonJsonConfig, serializableLikeC4ProjectConfig, validateProjectConfig } from '../index.mjs';
3
+ export { ConfigFilenames, GeneratorFn, GeneratorFnContext, GeneratorFnParams, IncludeConfig, LikeC4ProjectConfigInput, LikeC4ProjectJsonConfig, LikeC4StylesConfig, LikeC4StylesConfigInput, LocateResult, ManualLayoutsConfigSchema, defineConfig, defineGenerators, defineStyle, defineTheme, defineThemeColor, isLikeC4Config, isLikeC4JsonConfig, isLikeC4NonJsonConfig, normalizeIncludeConfig, validateIncludePaths, validateProjectConfig } from '../index.mjs';
4
4
  import '@likec4/core';
5
5
  import '@likec4/core/model';
6
6
  import '@likec4/core/types';
@@ -2,8 +2,8 @@ import { invariant } from '@likec4/core';
2
2
  import { bundleRequire } from 'bundle-require';
3
3
  import * as fs from 'node:fs/promises';
4
4
  import { dirname } from 'node:path';
5
- import { a as isLikeC4JsonConfig, v as validateProjectConfig, b as isLikeC4NonJsonConfig, d as defineConfig } from '../shared/config.FegrYs1S.mjs';
6
- export { C as ConfigFilenames, c as defineGenerators, e as defineStyle, f as defineTheme, g as defineThemeColor, i as isLikeC4Config, s as serializableLikeC4ProjectConfig } from '../shared/config.FegrYs1S.mjs';
5
+ import { b as isLikeC4JsonConfig, v as validateProjectConfig, c as isLikeC4NonJsonConfig, d as defineConfig } from '../shared/config.CUC_rqhf.mjs';
6
+ export { C as ConfigFilenames, e as defineGenerators, f as defineStyle, g as defineTheme, h as defineThemeColor, i as isLikeC4Config, n as normalizeIncludeConfig, a as validateIncludePaths } from '../shared/config.CUC_rqhf.mjs';
7
7
  import { rootLogger } from '@likec4/log';
8
8
 
9
9
  const logger = rootLogger.getChild("config");
@@ -5,13 +5,13 @@ import { exact } from '@likec4/core/types';
5
5
 
6
6
  const IMAGE_ALIAS_KEY_REGEX = /^@[A-Za-z0-9_-]*$/;
7
7
  const IMAGE_ALIAS_VALUE_REGEX = /^(?!\/|[A-Za-z]:[\\\/])(?!.*:\/\/).*$/;
8
- const ImageAliasValue = z.string().nonempty("Image alias value cannot be empty").regex(
8
+ const ImageAliasValue = z.string().min(1, "Image alias value cannot be empty").regex(
9
9
  IMAGE_ALIAS_VALUE_REGEX,
10
10
  "Image alias value must be a relative path (no leading slash or protocol)"
11
11
  );
12
12
  const ImageAliasesSchema = z.record(
13
13
  z.string(),
14
- // PLAIN key schema - valibot JSON schema export-safe.
14
+ // PLAIN key schema - zod JSON schema export-safe.
15
15
  ImageAliasValue
16
16
  ).meta({
17
17
  description: "Map of image alias prefixes to relative paths (keys must match /^@\\w+$/; values must be relative paths without protocol or leading slash)."
@@ -45,6 +45,66 @@ function validateImageAliases(imageAliases) {
45
45
  }
46
46
  }
47
47
 
48
+ const RELATIVE_PATH_REGEX = /^(?!\/|[A-Za-z]:[\\\/])(?!.*:\/\/).*$/;
49
+ const IncludePathValue = z.string().min(1, "Include path cannot be empty").regex(
50
+ RELATIVE_PATH_REGEX,
51
+ "Include path must be a relative path (no leading slash, drive letter, or protocol)"
52
+ );
53
+ const IncludeConfigSchema = z.object({
54
+ paths: z.array(IncludePathValue).min(1, "Include paths cannot be empty").meta({
55
+ description: [
56
+ "Additional relative directory paths to include LikeC4 source files from, searched recursively.",
57
+ "Paths are relative to the project folder (the folder containing this config file).",
58
+ 'Example: ["../shared", "../common/specs"]'
59
+ ].join("\n")
60
+ }),
61
+ maxDepth: z.number().int().min(1).max(20).default(3).meta({
62
+ description: [
63
+ "Maximum directory depth to scan when searching for .c4 files in include paths.",
64
+ "Prevents excessive scanning of deeply nested directories.",
65
+ "Default: 3"
66
+ ].join("\n")
67
+ }),
68
+ fileThreshold: z.number().int().min(1).default(30).meta({
69
+ description: [
70
+ "Maximum number of files to load from include paths before warning.",
71
+ "Helps identify performance issues from accidentally including large directories.",
72
+ "Default: 30"
73
+ ].join("\n")
74
+ })
75
+ }).meta({
76
+ id: "include-config",
77
+ description: "Configuration for including additional LikeC4 source files"
78
+ });
79
+ const IncludeSchema = IncludeConfigSchema.optional().meta({
80
+ description: [
81
+ "Configuration for including additional LikeC4 source files from other directories.",
82
+ 'Example: { "paths": ["../shared", "../common/specs"], "maxDepth": 5, "fileThreshold": 50 }'
83
+ ].join("\n")
84
+ });
85
+ function normalizeIncludeConfig(include) {
86
+ if (!include) {
87
+ return { paths: [], maxDepth: 3, fileThreshold: 30 };
88
+ }
89
+ return include;
90
+ }
91
+ function validateIncludePaths(include) {
92
+ if (!include?.paths) {
93
+ return;
94
+ }
95
+ const invalidPaths = [];
96
+ for (const path of include.paths) {
97
+ if (!RELATIVE_PATH_REGEX.test(path)) {
98
+ invalidPaths.push(path);
99
+ }
100
+ }
101
+ if (invalidPaths.length) {
102
+ throw new Error(
103
+ `Invalid include path(s): ${invalidPaths.map((p) => JSON.stringify(p)).join(", ")} (must be relative paths without leading slash, drive letter, or protocol)`
104
+ );
105
+ }
106
+ }
107
+
48
108
  function e(e,t,n){let r=n=>e(n,...t);return n===void 0?r:Object.assign(r,{lazy:n,lazyArgs:t})}
49
109
 
50
110
  function t$1(t,n,r){let i=t.length-n.length;if(i===0)return t(...n);if(i===1)return e(t,n,r);throw Error(`Wrong number of arguments`)}
@@ -63,9 +123,9 @@ const line = z.literal(["dashed", "solid", "dotted"]).meta({ id: "LineType" });
63
123
  const themeColor = z.literal(ThemeColors).meta({ id: "ThemeColor" });
64
124
  const color = z.union([
65
125
  themeColor,
66
- z.string().nonempty("Color name cannot be empty")
126
+ z.string().min(1, "Color name cannot be empty")
67
127
  ]).transform((value) => value).meta({ id: "ColorName" });
68
- const colorValue = z.string().nonempty("Color value cannot be empty").transform((value) => value).meta({
128
+ const colorValue = z.string().min(1, "Color value cannot be empty").transform((value) => value).meta({
69
129
  id: "ColorLiteral",
70
130
  description: "Color value in any valid CSS format: hex, rgb, rgba, hsl, hsla ..."
71
131
  });
@@ -211,8 +271,13 @@ function normalizeDefaults(defaults) {
211
271
  }
212
272
 
213
273
  const ManualLayoutsConfigSchema = z.object({
214
- outDir: z.string().meta({
215
- description: "Path to the directory where manual layouts will be stored, relative to the folder containing the project config"
274
+ outDir: z.string().default(".likec4").meta({
275
+ description: [
276
+ "Path to the directory where manual layouts will be stored,",
277
+ "relative to the folder containing the project config. ",
278
+ "",
279
+ "Defaults to '.likec4'."
280
+ ].join("\n")
216
281
  })
217
282
  }).meta({
218
283
  id: "manual-layouts-config",
@@ -229,6 +294,7 @@ const LikeC4ProjectJsonConfigSchema = z.object({
229
294
  title: z.string().nonempty("Project title cannot be empty if specified").optional().meta({ description: "A human readable title for the project" }),
230
295
  contactPerson: z.string().nonempty("Contact person cannot be empty if specified").optional().meta({ description: "A person who has been involved in creating or maintaining this project" }),
231
296
  imageAliases: ImageAliasesSchema.optional(),
297
+ include: IncludeSchema,
232
298
  styles: LikeC4StylesConfigSchema.optional(),
233
299
  exclude: z.array(z.string()).optional().meta({ description: 'List of file patterns to exclude from the project, default is ["**/node_modules/**"]' }),
234
300
  manualLayouts: ManualLayoutsConfigSchema.optional()
@@ -250,15 +316,11 @@ function validateProjectConfig(config) {
250
316
  if (parsed.data.imageAliases) {
251
317
  validateImageAliases(parsed.data.imageAliases);
252
318
  }
319
+ if (parsed.data.include) {
320
+ validateIncludePaths(parsed.data.include);
321
+ }
253
322
  return parsed.data;
254
323
  }
255
- function serializableLikeC4ProjectConfig({
256
- generators: _,
257
- // omit
258
- ...config
259
- }) {
260
- return LikeC4ProjectJsonConfigSchema.parse(config);
261
- }
262
324
 
263
325
  const configJsonFilenames = [
264
326
  ".likec4rc",
@@ -311,4 +373,4 @@ function defineStyle(styles) {
311
373
  return LikeC4StylesConfigSchema.parse(styles);
312
374
  }
313
375
 
314
- export { ConfigFilenames as C, isLikeC4JsonConfig as a, isLikeC4NonJsonConfig as b, defineGenerators as c, defineConfig as d, defineStyle as e, defineTheme as f, defineThemeColor as g, isLikeC4Config as i, serializableLikeC4ProjectConfig as s, validateProjectConfig as v };
376
+ export { ConfigFilenames as C, validateIncludePaths as a, isLikeC4JsonConfig as b, isLikeC4NonJsonConfig as c, defineConfig as d, defineGenerators as e, defineStyle as f, defineTheme as g, defineThemeColor as h, isLikeC4Config as i, normalizeIncludeConfig as n, validateProjectConfig as v };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@likec4/config",
3
- "version": "1.44.0",
3
+ "version": "1.46.0",
4
4
  "license": "MIT",
5
5
  "homepage": "https://likec4.dev",
6
6
  "author": "Denis Davydkov <denis@davydkov.com>",
@@ -58,14 +58,14 @@
58
58
  "bundle-require": "^5.1.0",
59
59
  "defu": "^6.1.4",
60
60
  "json5": "^2.2.3",
61
- "zod": "^4.1.12",
61
+ "zod": "^4.1.13",
62
62
  "type-fest": "^4.41.0"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "esbuild": "^0.25.11",
66
66
  "vscode-uri": "3.1.0",
67
- "@likec4/core": "1.44.0",
68
- "@likec4/log": "1.44.0"
67
+ "@likec4/core": "1.46.0",
68
+ "@likec4/log": "1.46.0"
69
69
  },
70
70
  "peerDependenciesMeta": {
71
71
  "vscode-uri": {
@@ -73,18 +73,18 @@
73
73
  }
74
74
  },
75
75
  "devDependencies": {
76
- "@types/node": "~20.19.22",
76
+ "@types/node": "~22.19.1",
77
77
  "remeda": "^2.32.0",
78
- "tsx": "4.20.6",
79
- "turbo": "2.6.0",
78
+ "tsx": "4.21.0",
79
+ "turbo": "2.6.3",
80
80
  "typescript": "5.9.3",
81
81
  "unbuild": "3.5.0",
82
82
  "nano-spawn": "^1.0.3",
83
- "vitest": "4.0.8",
84
- "@likec4/core": "1.44.0",
85
- "@likec4/devops": "1.42.0",
86
- "@likec4/log": "1.44.0",
87
- "@likec4/tsconfig": "1.44.0"
83
+ "vitest": "4.0.15",
84
+ "@likec4/core": "1.46.0",
85
+ "@likec4/log": "1.46.0",
86
+ "@likec4/tsconfig": "1.46.0",
87
+ "@likec4/devops": "1.42.0"
88
88
  },
89
89
  "scripts": {
90
90
  "typecheck": "tsc -b --verbose",
package/schema.json CHANGED
@@ -30,6 +30,10 @@
30
30
  "pattern": "^(?!\\/|[A-Za-z]:[\\\\\\/])(?!.*:\\/\\/).*$"
31
31
  }
32
32
  },
33
+ "include": {
34
+ "description": "Configuration for including additional LikeC4 source files from other directories.\nExample: { \"paths\": [\"../shared\", \"../common/specs\"], \"maxDepth\": 5, \"fileThreshold\": 50 }",
35
+ "$ref": "#/$defs/include-config"
36
+ },
33
37
  "styles": {
34
38
  "$ref": "#/$defs/StylesConfiguration"
35
39
  },
@@ -48,6 +52,40 @@
48
52
  "name"
49
53
  ],
50
54
  "$defs": {
55
+ "include-config": {
56
+ "id": "include-config",
57
+ "description": "Configuration for including additional LikeC4 source files",
58
+ "type": "object",
59
+ "properties": {
60
+ "paths": {
61
+ "description": "Additional relative directory paths to include LikeC4 source files from, searched recursively.\nPaths are relative to the project folder (the folder containing this config file).\nExample: [\"../shared\", \"../common/specs\"]",
62
+ "minItems": 1,
63
+ "type": "array",
64
+ "items": {
65
+ "type": "string",
66
+ "minLength": 1,
67
+ "pattern": "^(?!\\/|[A-Za-z]:[\\\\\\/])(?!.*:\\/\\/).*$"
68
+ }
69
+ },
70
+ "maxDepth": {
71
+ "description": "Maximum directory depth to scan when searching for .c4 files in include paths.\nPrevents excessive scanning of deeply nested directories.\nDefault: 3",
72
+ "default": 3,
73
+ "type": "integer",
74
+ "minimum": 1,
75
+ "maximum": 20
76
+ },
77
+ "fileThreshold": {
78
+ "description": "Maximum number of files to load from include paths before warning.\nHelps identify performance issues from accidentally including large directories.\nDefault: 30",
79
+ "default": 30,
80
+ "type": "integer",
81
+ "minimum": 1,
82
+ "maximum": 9007199254740991
83
+ }
84
+ },
85
+ "required": [
86
+ "paths"
87
+ ]
88
+ },
51
89
  "StylesConfiguration": {
52
90
  "id": "StylesConfiguration",
53
91
  "description": "Project styles customization",
@@ -448,13 +486,11 @@
448
486
  "type": "object",
449
487
  "properties": {
450
488
  "outDir": {
451
- "description": "Path to the directory where manual layouts will be stored, relative to the folder containing the project config",
489
+ "description": "Path to the directory where manual layouts will be stored,\nrelative to the folder containing the project config. \n\nDefaults to '.likec4'.",
490
+ "default": ".likec4",
452
491
  "type": "string"
453
492
  }
454
- },
455
- "required": [
456
- "outDir"
457
- ]
493
+ }
458
494
  }
459
495
  }
460
496
  }
package/src/index.ts CHANGED
@@ -9,12 +9,18 @@ export type {
9
9
  ManualLayoutsConfigSchema,
10
10
  } from './schema'
11
11
 
12
+ export type {
13
+ IncludeConfig,
14
+ } from './schema.include'
15
+
12
16
  export type {
13
17
  LikeC4StylesConfig,
14
18
  LikeC4StylesConfigInput,
15
19
  } from './schema.theme'
16
20
 
17
- export { serializableLikeC4ProjectConfig, validateProjectConfig } from './schema'
21
+ export { validateProjectConfig } from './schema'
22
+
23
+ export { normalizeIncludeConfig, validateIncludePaths } from './schema.include'
18
24
 
19
25
  export {
20
26
  ConfigFilenames,
@@ -8,14 +8,14 @@ const IMAGE_ALIAS_VALUE_REGEX = /^(?!\/|[A-Za-z]:[\\\/])(?!.*:\/\/).*$/
8
8
  // Schema for an image alias value: must be a non-empty string representing a relative path (no leading slash, drive letter, or protocol).
9
9
  const ImageAliasValue = z
10
10
  .string()
11
- .nonempty('Image alias value cannot be empty')
11
+ .min(1, 'Image alias value cannot be empty')
12
12
  .regex(
13
13
  IMAGE_ALIAS_VALUE_REGEX,
14
14
  'Image alias value must be a relative path (no leading slash or protocol)',
15
15
  )
16
16
 
17
17
  export const ImageAliasesSchema = z.record(
18
- z.string(), // PLAIN key schema - valibot JSON schema export-safe.
18
+ z.string(), // PLAIN key schema - zod JSON schema export-safe.
19
19
  ImageAliasValue,
20
20
  ).meta({
21
21
  description:
@@ -34,7 +34,7 @@ export function validateImageAliases(imageAliases?: LikeC4ImageAliasConfig) {
34
34
  if (!IMAGE_ALIAS_KEY_REGEX.test(key)) {
35
35
  invalidKeys.push(key)
36
36
  }
37
- // Value regex is technically already enforced by Valibot,
37
+ // Value regex is technically already enforced by zod,
38
38
  // so this check is purely defensive.
39
39
  if (!IMAGE_ALIAS_VALUE_REGEX.test(value)) {
40
40
  invalidValues.push(`${key} -> ${value}`)
@@ -0,0 +1,97 @@
1
+ import * as z from 'zod'
2
+
3
+ // Relative path (no leading slash, drive letter, or protocol)
4
+ // eslint-disable-next-line no-useless-escape
5
+ const RELATIVE_PATH_REGEX = /^(?!\/|[A-Za-z]:[\\\/])(?!.*:\/\/).*$/
6
+
7
+ // Schema for an include path value: must be a non-empty string representing a relative path
8
+ const IncludePathValue = z
9
+ .string()
10
+ .min(1, 'Include path cannot be empty')
11
+ .regex(
12
+ RELATIVE_PATH_REGEX,
13
+ 'Include path must be a relative path (no leading slash, drive letter, or protocol)',
14
+ )
15
+
16
+ export const IncludeConfigSchema = z.object({
17
+ paths: z.array(IncludePathValue)
18
+ .min(1, 'Include paths cannot be empty')
19
+ .meta({
20
+ description: [
21
+ 'Additional relative directory paths to include LikeC4 source files from, searched recursively.',
22
+ 'Paths are relative to the project folder (the folder containing this config file).',
23
+ 'Example: ["../shared", "../common/specs"]',
24
+ ].join('\n'),
25
+ }),
26
+ maxDepth: z.number()
27
+ .int()
28
+ .min(1)
29
+ .max(20)
30
+ .default(3)
31
+ .meta({
32
+ description: [
33
+ 'Maximum directory depth to scan when searching for .c4 files in include paths.',
34
+ 'Prevents excessive scanning of deeply nested directories.',
35
+ 'Default: 3',
36
+ ].join('\n'),
37
+ }),
38
+ fileThreshold: z.number()
39
+ .int()
40
+ .min(1)
41
+ .default(30)
42
+ .meta({
43
+ description: [
44
+ 'Maximum number of files to load from include paths before warning.',
45
+ 'Helps identify performance issues from accidentally including large directories.',
46
+ 'Default: 30',
47
+ ].join('\n'),
48
+ }),
49
+ })
50
+ .meta({
51
+ id: 'include-config',
52
+ description: 'Configuration for including additional LikeC4 source files',
53
+ })
54
+
55
+ export type IncludeConfig = z.infer<typeof IncludeConfigSchema>
56
+
57
+ export const IncludeSchema = IncludeConfigSchema
58
+ .optional()
59
+ .meta({
60
+ description: [
61
+ 'Configuration for including additional LikeC4 source files from other directories.',
62
+ 'Example: { "paths": ["../shared", "../common/specs"], "maxDepth": 5, "fileThreshold": 50 }',
63
+ ].join('\n'),
64
+ })
65
+
66
+ type LikeC4IncludeConfig = z.infer<typeof IncludeSchema>
67
+
68
+ export function normalizeIncludeConfig(include?: LikeC4IncludeConfig): IncludeConfig {
69
+ if (!include) {
70
+ return { paths: [], maxDepth: 3, fileThreshold: 30 }
71
+ }
72
+ return include
73
+ }
74
+
75
+ export function validateIncludePaths(include?: LikeC4IncludeConfig) {
76
+ if (!include?.paths) {
77
+ return
78
+ }
79
+
80
+ const invalidPaths: string[] = []
81
+
82
+ for (const path of include.paths) {
83
+ if (!RELATIVE_PATH_REGEX.test(path)) {
84
+ invalidPaths.push(path)
85
+ }
86
+ }
87
+
88
+ if (invalidPaths.length) {
89
+ throw new Error(
90
+ `Invalid include path(s): ${
91
+ invalidPaths
92
+ .map((p) => JSON.stringify(p))
93
+ .join(', ')
94
+ } (must be relative paths without leading slash, drive letter, or protocol)`,
95
+ )
96
+ }
97
+ }
@@ -56,14 +56,14 @@ const themeColor = z
56
56
  const color = z
57
57
  .union([
58
58
  themeColor,
59
- z.string().nonempty('Color name cannot be empty'),
59
+ z.string().min(1, 'Color name cannot be empty'),
60
60
  ])
61
61
  .transform(value => value as ThemeColor)
62
62
  .meta({ id: 'ColorName' })
63
63
 
64
64
  const colorValue = z
65
65
  .string()
66
- .nonempty('Color value cannot be empty')
66
+ .min(1, 'Color value cannot be empty')
67
67
  .transform(value => value as ColorLiteral)
68
68
  .meta({
69
69
  id: 'ColorLiteral',
package/src/schema.ts CHANGED
@@ -15,14 +15,20 @@ import type { SimplifyDeep } from 'type-fest'
15
15
  import type { URI } from 'vscode-uri'
16
16
  import * as z from 'zod'
17
17
  import { ImageAliasesSchema, validateImageAliases } from './schema.image-alias'
18
+ import { IncludeSchema, validateIncludePaths } from './schema.include'
18
19
  import { LikeC4StylesConfigSchema } from './schema.theme'
19
20
 
20
21
  export const ManualLayoutsConfigSchema = z
21
22
  .object({
22
23
  outDir: z.string()
24
+ .default('.likec4')
23
25
  .meta({
24
- description:
25
- 'Path to the directory where manual layouts will be stored, relative to the folder containing the project config',
26
+ description: [
27
+ 'Path to the directory where manual layouts will be stored,',
28
+ 'relative to the folder containing the project config. ',
29
+ '',
30
+ 'Defaults to \'.likec4\'.',
31
+ ].join('\n'),
26
32
  }),
27
33
  })
28
34
  .meta({
@@ -54,6 +60,7 @@ export const LikeC4ProjectJsonConfigSchema = z.object({
54
60
  .meta({ description: 'A person who has been involved in creating or maintaining this project' }),
55
61
  imageAliases: ImageAliasesSchema
56
62
  .optional(),
63
+ include: IncludeSchema,
57
64
  styles: LikeC4StylesConfigSchema
58
65
  .optional(),
59
66
  exclude: z.array(z.string())
@@ -242,16 +249,8 @@ export function validateProjectConfig<C extends string | Record<string, unknown>
242
249
  if (parsed.data.imageAliases) {
243
250
  validateImageAliases(parsed.data.imageAliases)
244
251
  }
252
+ if (parsed.data.include) {
253
+ validateIncludePaths(parsed.data.include)
254
+ }
245
255
  return parsed.data as unknown as LikeC4ProjectConfig
246
256
  }
247
-
248
- /**
249
- * Converts a LikeC4ProjectConfig object into a LikeC4ProjectJsonConfig object.
250
- * Omit generators property (as it is not serializable)
251
- */
252
- export function serializableLikeC4ProjectConfig({
253
- generators: _, // omit
254
- ...config
255
- }: LikeC4ProjectConfig): LikeC4ProjectJsonConfig {
256
- return LikeC4ProjectJsonConfigSchema.parse(config) as unknown as LikeC4ProjectJsonConfig
257
- }