@valbuild/cli 0.91.0 → 0.91.2

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.
@@ -126,6 +126,8 @@ const textEncoder = new TextEncoder();
126
126
 
127
127
  // Types for handler system
128
128
 
129
+ // Cache types for avoiding redundant service.get() calls
130
+
129
131
  // Handler functions
130
132
  async function handleFileMetadata(ctx) {
131
133
  const [, modulePath] = core.Internal.splitModuleFilePathAndModulePath(ctx.sourcePath);
@@ -183,7 +185,7 @@ async function handleKeyOfCheck(ctx) {
183
185
  errorMessage: `Unexpected error in ${sourcePath}: ${ctx.validationError.message} (Expected value property 'sourcePath' to be a string - this is likely a bug in Val)`
184
186
  };
185
187
  }
186
- const res = await checkKeyIsValid(key, sourcePath, ctx.service);
188
+ const res = await checkKeyIsValid(key, sourcePath, ctx.service, ctx.keyOfCache);
187
189
  if (res.error) {
188
190
  return {
189
191
  success: false,
@@ -397,15 +399,28 @@ async function handleRemoteFileCheck() {
397
399
  }
398
400
 
399
401
  // Helper function
400
- async function checkKeyIsValid(key, sourcePath, service) {
402
+ async function checkKeyIsValid(key, sourcePath, service, cache) {
401
403
  const [moduleFilePath, modulePath] = core.Internal.splitModuleFilePathAndModulePath(sourcePath);
402
- const keyOfModule = await service.get(moduleFilePath, modulePath, {
403
- source: true,
404
- schema: false,
405
- validate: false
406
- });
407
- const keyOfModuleSource = keyOfModule.source;
408
- const keyOfModuleSchema = keyOfModule.schema;
404
+ const cacheKey = `${moduleFilePath}::${modulePath}`;
405
+ let keyOfModuleSource;
406
+ let keyOfModuleSchema;
407
+ const cached = cache.get(cacheKey);
408
+ if (cached) {
409
+ keyOfModuleSource = cached.source;
410
+ keyOfModuleSchema = cached.schema;
411
+ } else {
412
+ const keyOfModule = await service.get(moduleFilePath, modulePath, {
413
+ source: true,
414
+ schema: true,
415
+ validate: false
416
+ });
417
+ keyOfModuleSource = keyOfModule.source;
418
+ keyOfModuleSchema = keyOfModule.schema;
419
+ cache.set(cacheKey, {
420
+ source: keyOfModuleSource,
421
+ schema: keyOfModuleSchema
422
+ });
423
+ }
409
424
  if (keyOfModuleSchema && keyOfModuleSchema.type !== "record") {
410
425
  return {
411
426
  error: true,
@@ -434,25 +449,28 @@ async function checkKeyIsValid(key, sourcePath, service) {
434
449
  * Check if a route is valid by scanning all router modules
435
450
  * and validating against include/exclude patterns
436
451
  */
437
- async function checkRouteIsValid(route, include, exclude, service, valFiles) {
438
- // 1. Scan all val files to find modules with routers
439
- const routerModules = {};
440
- for (const file of valFiles) {
441
- var _valModule$schema;
442
- const moduleFilePath = `/${file}`;
443
- const valModule = await service.get(moduleFilePath, "", {
444
- source: true,
445
- schema: true,
446
- validate: false
447
- });
452
+ async function checkRouteIsValid(route, include, exclude, service, valFiles, cache) {
453
+ // 1. Scan all val files to find modules with routers (use cache if available)
454
+ if (!cache.loaded) {
455
+ for (const file of valFiles) {
456
+ var _valModule$schema;
457
+ const moduleFilePath = `/${file}`;
458
+ const valModule = await service.get(moduleFilePath, "", {
459
+ source: true,
460
+ schema: true,
461
+ validate: false
462
+ });
448
463
 
449
- // Check if this module has a router defined
450
- if (((_valModule$schema = valModule.schema) === null || _valModule$schema === void 0 ? void 0 : _valModule$schema.type) === "record" && valModule.schema.router) {
451
- if (valModule.source && typeof valModule.source === "object") {
452
- routerModules[moduleFilePath] = valModule.source;
464
+ // Check if this module has a router defined
465
+ if (((_valModule$schema = valModule.schema) === null || _valModule$schema === void 0 ? void 0 : _valModule$schema.type) === "record" && valModule.schema.router) {
466
+ if (valModule.source && typeof valModule.source === "object") {
467
+ cache.modules[moduleFilePath] = valModule.source;
468
+ }
453
469
  }
454
470
  }
471
+ cache.loaded = true;
455
472
  }
473
+ const routerModules = cache.modules;
456
474
 
457
475
  // 2. Check if route exists in any router module
458
476
  let foundInModule = null;
@@ -502,7 +520,8 @@ async function handleRouteCheck(ctx) {
502
520
  sourcePath,
503
521
  validationError,
504
522
  service,
505
- valFiles
523
+ valFiles,
524
+ routerModulesCache
506
525
  } = ctx;
507
526
 
508
527
  // Extract route and patterns from validation error value
@@ -516,16 +535,13 @@ async function handleRouteCheck(ctx) {
516
535
  const route = value.route;
517
536
 
518
537
  // Check if the route is valid
519
- const result = await checkRouteIsValid(route, value.include, value.exclude, service, valFiles);
538
+ const result = await checkRouteIsValid(route, value.include, value.exclude, service, valFiles, routerModulesCache);
520
539
  if (result.error) {
521
540
  return {
522
541
  success: false,
523
542
  errorMessage: `${sourcePath}: ${result.message}`
524
543
  };
525
544
  }
526
-
527
- // Route is valid - no fix needed
528
- console.log(picocolors__default["default"].green("✓"), `Route '${route}' is valid in`, sourcePath);
529
545
  return {
530
546
  success: true
531
547
  };
@@ -577,6 +593,13 @@ async function validate({
577
593
  console.log(picocolors__default["default"].greenBright(`Found ${valFiles.length} files...`));
578
594
  let publicProjectId;
579
595
  let didFix = false;
596
+
597
+ // Create caches that persist across all file validations
598
+ const keyOfCache = new Map();
599
+ const routerModulesCache = {
600
+ loaded: false,
601
+ modules: {}
602
+ };
580
603
  async function validateFile(file) {
581
604
  const moduleFilePath = `/${file}`; // TODO: check if this always works? (Windows?)
582
605
  const start = Date.now();
@@ -631,7 +654,9 @@ async function validate({
631
654
  remoteFilesCounter,
632
655
  valRemoteHost,
633
656
  contentHostUrl,
634
- valConfigFile: valConfigFile ?? undefined
657
+ valConfigFile: valConfigFile ?? undefined,
658
+ keyOfCache,
659
+ routerModulesCache
635
660
  });
636
661
 
637
662
  // Update shared state from handler result
@@ -126,6 +126,8 @@ const textEncoder = new TextEncoder();
126
126
 
127
127
  // Types for handler system
128
128
 
129
+ // Cache types for avoiding redundant service.get() calls
130
+
129
131
  // Handler functions
130
132
  async function handleFileMetadata(ctx) {
131
133
  const [, modulePath] = core.Internal.splitModuleFilePathAndModulePath(ctx.sourcePath);
@@ -183,7 +185,7 @@ async function handleKeyOfCheck(ctx) {
183
185
  errorMessage: `Unexpected error in ${sourcePath}: ${ctx.validationError.message} (Expected value property 'sourcePath' to be a string - this is likely a bug in Val)`
184
186
  };
185
187
  }
186
- const res = await checkKeyIsValid(key, sourcePath, ctx.service);
188
+ const res = await checkKeyIsValid(key, sourcePath, ctx.service, ctx.keyOfCache);
187
189
  if (res.error) {
188
190
  return {
189
191
  success: false,
@@ -397,15 +399,28 @@ async function handleRemoteFileCheck() {
397
399
  }
398
400
 
399
401
  // Helper function
400
- async function checkKeyIsValid(key, sourcePath, service) {
402
+ async function checkKeyIsValid(key, sourcePath, service, cache) {
401
403
  const [moduleFilePath, modulePath] = core.Internal.splitModuleFilePathAndModulePath(sourcePath);
402
- const keyOfModule = await service.get(moduleFilePath, modulePath, {
403
- source: true,
404
- schema: false,
405
- validate: false
406
- });
407
- const keyOfModuleSource = keyOfModule.source;
408
- const keyOfModuleSchema = keyOfModule.schema;
404
+ const cacheKey = `${moduleFilePath}::${modulePath}`;
405
+ let keyOfModuleSource;
406
+ let keyOfModuleSchema;
407
+ const cached = cache.get(cacheKey);
408
+ if (cached) {
409
+ keyOfModuleSource = cached.source;
410
+ keyOfModuleSchema = cached.schema;
411
+ } else {
412
+ const keyOfModule = await service.get(moduleFilePath, modulePath, {
413
+ source: true,
414
+ schema: true,
415
+ validate: false
416
+ });
417
+ keyOfModuleSource = keyOfModule.source;
418
+ keyOfModuleSchema = keyOfModule.schema;
419
+ cache.set(cacheKey, {
420
+ source: keyOfModuleSource,
421
+ schema: keyOfModuleSchema
422
+ });
423
+ }
409
424
  if (keyOfModuleSchema && keyOfModuleSchema.type !== "record") {
410
425
  return {
411
426
  error: true,
@@ -434,25 +449,28 @@ async function checkKeyIsValid(key, sourcePath, service) {
434
449
  * Check if a route is valid by scanning all router modules
435
450
  * and validating against include/exclude patterns
436
451
  */
437
- async function checkRouteIsValid(route, include, exclude, service, valFiles) {
438
- // 1. Scan all val files to find modules with routers
439
- const routerModules = {};
440
- for (const file of valFiles) {
441
- var _valModule$schema;
442
- const moduleFilePath = `/${file}`;
443
- const valModule = await service.get(moduleFilePath, "", {
444
- source: true,
445
- schema: true,
446
- validate: false
447
- });
452
+ async function checkRouteIsValid(route, include, exclude, service, valFiles, cache) {
453
+ // 1. Scan all val files to find modules with routers (use cache if available)
454
+ if (!cache.loaded) {
455
+ for (const file of valFiles) {
456
+ var _valModule$schema;
457
+ const moduleFilePath = `/${file}`;
458
+ const valModule = await service.get(moduleFilePath, "", {
459
+ source: true,
460
+ schema: true,
461
+ validate: false
462
+ });
448
463
 
449
- // Check if this module has a router defined
450
- if (((_valModule$schema = valModule.schema) === null || _valModule$schema === void 0 ? void 0 : _valModule$schema.type) === "record" && valModule.schema.router) {
451
- if (valModule.source && typeof valModule.source === "object") {
452
- routerModules[moduleFilePath] = valModule.source;
464
+ // Check if this module has a router defined
465
+ if (((_valModule$schema = valModule.schema) === null || _valModule$schema === void 0 ? void 0 : _valModule$schema.type) === "record" && valModule.schema.router) {
466
+ if (valModule.source && typeof valModule.source === "object") {
467
+ cache.modules[moduleFilePath] = valModule.source;
468
+ }
453
469
  }
454
470
  }
471
+ cache.loaded = true;
455
472
  }
473
+ const routerModules = cache.modules;
456
474
 
457
475
  // 2. Check if route exists in any router module
458
476
  let foundInModule = null;
@@ -502,7 +520,8 @@ async function handleRouteCheck(ctx) {
502
520
  sourcePath,
503
521
  validationError,
504
522
  service,
505
- valFiles
523
+ valFiles,
524
+ routerModulesCache
506
525
  } = ctx;
507
526
 
508
527
  // Extract route and patterns from validation error value
@@ -516,16 +535,13 @@ async function handleRouteCheck(ctx) {
516
535
  const route = value.route;
517
536
 
518
537
  // Check if the route is valid
519
- const result = await checkRouteIsValid(route, value.include, value.exclude, service, valFiles);
538
+ const result = await checkRouteIsValid(route, value.include, value.exclude, service, valFiles, routerModulesCache);
520
539
  if (result.error) {
521
540
  return {
522
541
  success: false,
523
542
  errorMessage: `${sourcePath}: ${result.message}`
524
543
  };
525
544
  }
526
-
527
- // Route is valid - no fix needed
528
- console.log(picocolors__default["default"].green("✓"), `Route '${route}' is valid in`, sourcePath);
529
545
  return {
530
546
  success: true
531
547
  };
@@ -577,6 +593,13 @@ async function validate({
577
593
  console.log(picocolors__default["default"].greenBright(`Found ${valFiles.length} files...`));
578
594
  let publicProjectId;
579
595
  let didFix = false;
596
+
597
+ // Create caches that persist across all file validations
598
+ const keyOfCache = new Map();
599
+ const routerModulesCache = {
600
+ loaded: false,
601
+ modules: {}
602
+ };
580
603
  async function validateFile(file) {
581
604
  const moduleFilePath = `/${file}`; // TODO: check if this always works? (Windows?)
582
605
  const start = Date.now();
@@ -631,7 +654,9 @@ async function validate({
631
654
  remoteFilesCounter,
632
655
  valRemoteHost,
633
656
  contentHostUrl,
634
- valConfigFile: valConfigFile ?? undefined
657
+ valConfigFile: valConfigFile ?? undefined,
658
+ keyOfCache,
659
+ routerModulesCache
635
660
  });
636
661
 
637
662
  // Update shared state from handler result
@@ -94,6 +94,8 @@ const textEncoder = new TextEncoder();
94
94
 
95
95
  // Types for handler system
96
96
 
97
+ // Cache types for avoiding redundant service.get() calls
98
+
97
99
  // Handler functions
98
100
  async function handleFileMetadata(ctx) {
99
101
  const [, modulePath] = Internal.splitModuleFilePathAndModulePath(ctx.sourcePath);
@@ -151,7 +153,7 @@ async function handleKeyOfCheck(ctx) {
151
153
  errorMessage: `Unexpected error in ${sourcePath}: ${ctx.validationError.message} (Expected value property 'sourcePath' to be a string - this is likely a bug in Val)`
152
154
  };
153
155
  }
154
- const res = await checkKeyIsValid(key, sourcePath, ctx.service);
156
+ const res = await checkKeyIsValid(key, sourcePath, ctx.service, ctx.keyOfCache);
155
157
  if (res.error) {
156
158
  return {
157
159
  success: false,
@@ -365,15 +367,28 @@ async function handleRemoteFileCheck() {
365
367
  }
366
368
 
367
369
  // Helper function
368
- async function checkKeyIsValid(key, sourcePath, service) {
370
+ async function checkKeyIsValid(key, sourcePath, service, cache) {
369
371
  const [moduleFilePath, modulePath] = Internal.splitModuleFilePathAndModulePath(sourcePath);
370
- const keyOfModule = await service.get(moduleFilePath, modulePath, {
371
- source: true,
372
- schema: false,
373
- validate: false
374
- });
375
- const keyOfModuleSource = keyOfModule.source;
376
- const keyOfModuleSchema = keyOfModule.schema;
372
+ const cacheKey = `${moduleFilePath}::${modulePath}`;
373
+ let keyOfModuleSource;
374
+ let keyOfModuleSchema;
375
+ const cached = cache.get(cacheKey);
376
+ if (cached) {
377
+ keyOfModuleSource = cached.source;
378
+ keyOfModuleSchema = cached.schema;
379
+ } else {
380
+ const keyOfModule = await service.get(moduleFilePath, modulePath, {
381
+ source: true,
382
+ schema: true,
383
+ validate: false
384
+ });
385
+ keyOfModuleSource = keyOfModule.source;
386
+ keyOfModuleSchema = keyOfModule.schema;
387
+ cache.set(cacheKey, {
388
+ source: keyOfModuleSource,
389
+ schema: keyOfModuleSchema
390
+ });
391
+ }
377
392
  if (keyOfModuleSchema && keyOfModuleSchema.type !== "record") {
378
393
  return {
379
394
  error: true,
@@ -402,25 +417,28 @@ async function checkKeyIsValid(key, sourcePath, service) {
402
417
  * Check if a route is valid by scanning all router modules
403
418
  * and validating against include/exclude patterns
404
419
  */
405
- async function checkRouteIsValid(route, include, exclude, service, valFiles) {
406
- // 1. Scan all val files to find modules with routers
407
- const routerModules = {};
408
- for (const file of valFiles) {
409
- var _valModule$schema;
410
- const moduleFilePath = `/${file}`;
411
- const valModule = await service.get(moduleFilePath, "", {
412
- source: true,
413
- schema: true,
414
- validate: false
415
- });
420
+ async function checkRouteIsValid(route, include, exclude, service, valFiles, cache) {
421
+ // 1. Scan all val files to find modules with routers (use cache if available)
422
+ if (!cache.loaded) {
423
+ for (const file of valFiles) {
424
+ var _valModule$schema;
425
+ const moduleFilePath = `/${file}`;
426
+ const valModule = await service.get(moduleFilePath, "", {
427
+ source: true,
428
+ schema: true,
429
+ validate: false
430
+ });
416
431
 
417
- // Check if this module has a router defined
418
- if (((_valModule$schema = valModule.schema) === null || _valModule$schema === void 0 ? void 0 : _valModule$schema.type) === "record" && valModule.schema.router) {
419
- if (valModule.source && typeof valModule.source === "object") {
420
- routerModules[moduleFilePath] = valModule.source;
432
+ // Check if this module has a router defined
433
+ if (((_valModule$schema = valModule.schema) === null || _valModule$schema === void 0 ? void 0 : _valModule$schema.type) === "record" && valModule.schema.router) {
434
+ if (valModule.source && typeof valModule.source === "object") {
435
+ cache.modules[moduleFilePath] = valModule.source;
436
+ }
421
437
  }
422
438
  }
439
+ cache.loaded = true;
423
440
  }
441
+ const routerModules = cache.modules;
424
442
 
425
443
  // 2. Check if route exists in any router module
426
444
  let foundInModule = null;
@@ -470,7 +488,8 @@ async function handleRouteCheck(ctx) {
470
488
  sourcePath,
471
489
  validationError,
472
490
  service,
473
- valFiles
491
+ valFiles,
492
+ routerModulesCache
474
493
  } = ctx;
475
494
 
476
495
  // Extract route and patterns from validation error value
@@ -484,16 +503,13 @@ async function handleRouteCheck(ctx) {
484
503
  const route = value.route;
485
504
 
486
505
  // Check if the route is valid
487
- const result = await checkRouteIsValid(route, value.include, value.exclude, service, valFiles);
506
+ const result = await checkRouteIsValid(route, value.include, value.exclude, service, valFiles, routerModulesCache);
488
507
  if (result.error) {
489
508
  return {
490
509
  success: false,
491
510
  errorMessage: `${sourcePath}: ${result.message}`
492
511
  };
493
512
  }
494
-
495
- // Route is valid - no fix needed
496
- console.log(picocolors.green("✓"), `Route '${route}' is valid in`, sourcePath);
497
513
  return {
498
514
  success: true
499
515
  };
@@ -545,6 +561,13 @@ async function validate({
545
561
  console.log(picocolors.greenBright(`Found ${valFiles.length} files...`));
546
562
  let publicProjectId;
547
563
  let didFix = false;
564
+
565
+ // Create caches that persist across all file validations
566
+ const keyOfCache = new Map();
567
+ const routerModulesCache = {
568
+ loaded: false,
569
+ modules: {}
570
+ };
548
571
  async function validateFile(file) {
549
572
  const moduleFilePath = `/${file}`; // TODO: check if this always works? (Windows?)
550
573
  const start = Date.now();
@@ -599,7 +622,9 @@ async function validate({
599
622
  remoteFilesCounter,
600
623
  valRemoteHost,
601
624
  contentHostUrl,
602
- valConfigFile: valConfigFile ?? undefined
625
+ valConfigFile: valConfigFile ?? undefined,
626
+ keyOfCache,
627
+ routerModulesCache
603
628
  });
604
629
 
605
630
  // Update shared state from handler result
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@valbuild/cli",
3
3
  "private": false,
4
- "version": "0.91.0",
4
+ "version": "0.91.2",
5
5
  "description": "Val CLI tools",
6
6
  "repository": {
7
7
  "type": "git",
@@ -22,10 +22,10 @@
22
22
  "typecheck": "tsc --noEmit"
23
23
  },
24
24
  "dependencies": {
25
- "@valbuild/core": "~0.91.0",
26
- "@valbuild/eslint-plugin": "~0.91.0",
27
- "@valbuild/server": "~0.91.0",
28
- "@valbuild/shared": "~0.91.0",
25
+ "@valbuild/core": "~0.91.2",
26
+ "@valbuild/eslint-plugin": "~0.91.2",
27
+ "@valbuild/server": "~0.91.2",
28
+ "@valbuild/shared": "~0.91.2",
29
29
  "chalk": "^4.1.2",
30
30
  "cors": "^2.8.5",
31
31
  "fast-glob": "^3.3.1",
package/src/validate.ts CHANGED
@@ -42,6 +42,16 @@ type ValidationError = {
42
42
  fixes?: ValidationFix[];
43
43
  };
44
44
 
45
+ // Cache types for avoiding redundant service.get() calls
46
+ type KeyOfCache = Map<
47
+ string, // moduleFilePath + modulePath key
48
+ { source: unknown; schema: { type: string } | undefined }
49
+ >;
50
+ type RouterModulesCache = {
51
+ loaded: boolean;
52
+ modules: Record<string, Record<string, unknown>>;
53
+ };
54
+
45
55
  type FixHandlerContext = {
46
56
  sourcePath: SourcePath;
47
57
  validationError: ValidationError;
@@ -63,6 +73,9 @@ type FixHandlerContext = {
63
73
  valRemoteHost: string;
64
74
  contentHostUrl: string;
65
75
  valConfigFile?: { project?: string };
76
+ // Caches for validation
77
+ keyOfCache: KeyOfCache;
78
+ routerModulesCache: RouterModulesCache;
66
79
  };
67
80
 
68
81
  type FixHandlerResult = {
@@ -157,7 +170,12 @@ async function handleKeyOfCheck(
157
170
  };
158
171
  }
159
172
 
160
- const res = await checkKeyIsValid(key, sourcePath, ctx.service);
173
+ const res = await checkKeyIsValid(
174
+ key,
175
+ sourcePath,
176
+ ctx.service,
177
+ ctx.keyOfCache,
178
+ );
161
179
  if (res.error) {
162
180
  return {
163
181
  success: false,
@@ -438,17 +456,33 @@ async function checkKeyIsValid(
438
456
  key: string,
439
457
  sourcePath: string,
440
458
  service: Service,
459
+ cache: KeyOfCache,
441
460
  ): Promise<{ error: false } | { error: true; message: string }> {
442
461
  const [moduleFilePath, modulePath] =
443
462
  Internal.splitModuleFilePathAndModulePath(sourcePath as SourcePath);
444
- const keyOfModule = await service.get(moduleFilePath, modulePath, {
445
- source: true,
446
- schema: false,
447
- validate: false,
448
- });
449
463
 
450
- const keyOfModuleSource = keyOfModule.source;
451
- const keyOfModuleSchema = keyOfModule.schema;
464
+ const cacheKey = `${moduleFilePath}::${modulePath}`;
465
+ let keyOfModuleSource: unknown;
466
+ let keyOfModuleSchema: { type: string } | undefined;
467
+
468
+ const cached = cache.get(cacheKey);
469
+ if (cached) {
470
+ keyOfModuleSource = cached.source;
471
+ keyOfModuleSchema = cached.schema;
472
+ } else {
473
+ const keyOfModule = await service.get(moduleFilePath, modulePath, {
474
+ source: true,
475
+ schema: true,
476
+ validate: false,
477
+ });
478
+ keyOfModuleSource = keyOfModule.source;
479
+ keyOfModuleSchema = keyOfModule.schema as { type: string } | undefined;
480
+ cache.set(cacheKey, {
481
+ source: keyOfModuleSource,
482
+ schema: keyOfModuleSchema,
483
+ });
484
+ }
485
+
452
486
  if (keyOfModuleSchema && keyOfModuleSchema.type !== "record") {
453
487
  return {
454
488
  error: true,
@@ -488,29 +522,33 @@ async function checkRouteIsValid(
488
522
  exclude: SerializedRegExpPattern | undefined,
489
523
  service: Service,
490
524
  valFiles: string[],
525
+ cache: RouterModulesCache,
491
526
  ): Promise<{ error: false } | { error: true; message: string }> {
492
- // 1. Scan all val files to find modules with routers
493
- const routerModules: Record<string, Record<string, unknown>> = {};
494
-
495
- for (const file of valFiles) {
496
- const moduleFilePath = `/${file}` as ModuleFilePath;
497
- const valModule = await service.get(moduleFilePath, "" as ModulePath, {
498
- source: true,
499
- schema: true,
500
- validate: false,
501
- });
527
+ // 1. Scan all val files to find modules with routers (use cache if available)
528
+ if (!cache.loaded) {
529
+ for (const file of valFiles) {
530
+ const moduleFilePath = `/${file}` as ModuleFilePath;
531
+ const valModule = await service.get(moduleFilePath, "" as ModulePath, {
532
+ source: true,
533
+ schema: true,
534
+ validate: false,
535
+ });
502
536
 
503
- // Check if this module has a router defined
504
- if (valModule.schema?.type === "record" && valModule.schema.router) {
505
- if (valModule.source && typeof valModule.source === "object") {
506
- routerModules[moduleFilePath] = valModule.source as Record<
507
- string,
508
- unknown
509
- >;
537
+ // Check if this module has a router defined
538
+ if (valModule.schema?.type === "record" && valModule.schema.router) {
539
+ if (valModule.source && typeof valModule.source === "object") {
540
+ cache.modules[moduleFilePath] = valModule.source as Record<
541
+ string,
542
+ unknown
543
+ >;
544
+ }
510
545
  }
511
546
  }
547
+ cache.loaded = true;
512
548
  }
513
549
 
550
+ const routerModules = cache.modules;
551
+
514
552
  // 2. Check if route exists in any router module
515
553
  let foundInModule: string | null = null;
516
554
  for (const [moduleFilePath, source] of Object.entries(routerModules)) {
@@ -569,7 +607,8 @@ async function checkRouteIsValid(
569
607
  async function handleRouteCheck(
570
608
  ctx: FixHandlerContext,
571
609
  ): Promise<FixHandlerResult> {
572
- const { sourcePath, validationError, service, valFiles } = ctx;
610
+ const { sourcePath, validationError, service, valFiles, routerModulesCache } =
611
+ ctx;
573
612
 
574
613
  // Extract route and patterns from validation error value
575
614
  const value = validationError.value as
@@ -596,6 +635,7 @@ async function handleRouteCheck(
596
635
  value.exclude,
597
636
  service,
598
637
  valFiles,
638
+ routerModulesCache,
599
639
  );
600
640
 
601
641
  if (result.error) {
@@ -605,12 +645,6 @@ async function handleRouteCheck(
605
645
  };
606
646
  }
607
647
 
608
- // Route is valid - no fix needed
609
- console.log(
610
- picocolors.green("✓"),
611
- `Route '${route}' is valid in`,
612
- sourcePath,
613
- );
614
648
  return { success: true };
615
649
  }
616
650
 
@@ -672,6 +706,11 @@ export async function validate({
672
706
  console.log(picocolors.greenBright(`Found ${valFiles.length} files...`));
673
707
  let publicProjectId: string | undefined;
674
708
  let didFix = false;
709
+
710
+ // Create caches that persist across all file validations
711
+ const keyOfCache: KeyOfCache = new Map();
712
+ const routerModulesCache: RouterModulesCache = { loaded: false, modules: {} };
713
+
675
714
  async function validateFile(file: string): Promise<number> {
676
715
  const moduleFilePath = `/${file}` as ModuleFilePath; // TODO: check if this always works? (Windows?)
677
716
  const start = Date.now();
@@ -748,6 +787,8 @@ export async function validate({
748
787
  valRemoteHost,
749
788
  contentHostUrl,
750
789
  valConfigFile: valConfigFile ?? undefined,
790
+ keyOfCache,
791
+ routerModulesCache,
751
792
  });
752
793
 
753
794
  // Update shared state from handler result