3535 * @see https://github.com/fabien0102/ts-to-zod
3636 * @see https://github.com/dsherret/ts-morph
3737 */
38- import { existsSync , mkdirSync , readFileSync , writeFileSync } from 'node:fs' ;
38+ import { execSync } from 'node:child_process' ;
39+ import { existsSync , mkdirSync , readFileSync , writeFileSync , statSync } from 'node:fs' ;
3940import { dirname , join } from 'node:path' ;
4041import { fileURLToPath } from 'node:url' ;
4142import { generate } from 'ts-to-zod' ;
@@ -59,6 +60,42 @@ const SDK_TYPES_FILE = join(PROJECT_ROOT, 'src', 'generated', 'sdk.types.ts');
5960const GENERATED_DIR = join ( PROJECT_ROOT , 'src' , 'generated' ) ;
6061const SCHEMA_OUTPUT_FILE = join ( GENERATED_DIR , 'sdk.schemas.ts' ) ;
6162const SCHEMA_TEST_OUTPUT_FILE = join ( GENERATED_DIR , 'sdk.schemas.zod.test.ts' ) ;
63+ const GENERATE_SCRIPT_FILE = join ( PROJECT_ROOT , 'scripts' , 'generate-schemas.ts' ) ;
64+
65+ // Input files that trigger regeneration
66+ const INPUT_FILES = [ SPEC_TYPES_FILE , GENERATE_SCRIPT_FILE ] ;
67+ // Output files that are generated
68+ const OUTPUT_FILES = [ SDK_TYPES_FILE , SCHEMA_OUTPUT_FILE , SCHEMA_TEST_OUTPUT_FILE ] ;
69+
70+ /**
71+ * Check if any input file is newer than any output file.
72+ * Returns true if regeneration is needed.
73+ */
74+ function needsRegeneration ( ) : boolean {
75+ // Get the newest input mtime
76+ let newestInput = 0 ;
77+ for ( const file of INPUT_FILES ) {
78+ if ( ! existsSync ( file ) ) {
79+ console . log ( ` Input file missing: ${ file } ` ) ;
80+ return true ;
81+ }
82+ const mtime = statSync ( file ) . mtimeMs ;
83+ if ( mtime > newestInput ) newestInput = mtime ;
84+ }
85+
86+ // Get the oldest output mtime
87+ let oldestOutput = Infinity ;
88+ for ( const file of OUTPUT_FILES ) {
89+ if ( ! existsSync ( file ) ) {
90+ console . log ( ` Output file missing: ${ file } ` ) ;
91+ return true ;
92+ }
93+ const mtime = statSync ( file ) . mtimeMs ;
94+ if ( mtime < oldestOutput ) oldestOutput = mtime ;
95+ }
96+
97+ return newestInput > oldestOutput ;
98+ }
6299
63100// =============================================================================
64101// Configuration: Field-level validation overrides
@@ -1344,7 +1381,17 @@ function addElicitationPreprocess(sourceFile: SourceFile): void {
13441381// =============================================================================
13451382
13461383async function main ( ) {
1347- console . log ( '🔧 Generating Zod schemas from spec.types.ts...\n' ) ;
1384+ const ifChanged = process . argv . includes ( '--if-changed' ) ;
1385+
1386+ if ( ifChanged ) {
1387+ if ( ! needsRegeneration ( ) ) {
1388+ console . log ( '✅ Schemas are up to date, skipping generation.' ) ;
1389+ return ;
1390+ }
1391+ console . log ( '🔄 Input files changed, regenerating schemas...\n' ) ;
1392+ } else {
1393+ console . log ( '🔧 Generating Zod schemas from spec.types.ts...\n' ) ;
1394+ }
13481395
13491396 // Ensure generated directory exists
13501397 if ( ! existsSync ( GENERATED_DIR ) ) {
@@ -1418,6 +1465,13 @@ ${cleanedTypesContent.replace(/^\/\*\*[\s\S]*?\*\/\n/, '')}`;
14181465 console . log ( `✅ Written: ${ SCHEMA_TEST_OUTPUT_FILE } ` ) ;
14191466 }
14201467
1468+ // Format generated files with prettier
1469+ console . log ( '\n📝 Formatting generated files...' ) ;
1470+ execSync ( 'npx prettier --write "src/generated/**/*"' , {
1471+ cwd : PROJECT_ROOT ,
1472+ stdio : 'inherit'
1473+ } ) ;
1474+
14211475 console . log ( '\n🎉 Schema generation complete!' ) ;
14221476}
14231477
0 commit comments