1- import { existsSync } from 'fs' ;
1+ import { existsSync , writeFileSync } from 'fs' ;
2+ import { tmpdir } from 'os' ;
3+ import { join } from 'path' ;
24import { Command , Option } from '@commander-js/extra-typings' ;
3- import {
4- ProtoMessage ,
5- ProtoEnum ,
6- BackwardCompatibilityError
7- } from './types' ;
5+ import { ProtoMessage , ProtoEnum } from './types' ;
86import { parseProtoFile } from './parser' ;
97import { mergeMessage , mergeEnum } from './CompatibilityMerger' ;
108import { writeProtoFile , CUSTOM_MESSAGE_NAMES , CUSTOM_ENUM_NAMES } from './writer' ;
9+ import { CompatibilityReporter } from './CompatibilityReporter' ;
1110import logger from '../utils/logger' ;
1211
1312export class BackwardCompatibleWriter {
1413 private existingMessages : ProtoMessage [ ] ;
1514 private existingEnums : ProtoEnum [ ] ;
1615 private incomingMessageMap : Map < string , ProtoMessage > = new Map ( ) ;
1716 private incomingEnumMap : Map < string , ProtoEnum > = new Map ( ) ;
18- private errors : string [ ] = [ ] ;
1917 private outputPath : string ;
18+ private reporter : CompatibilityReporter = new CompatibilityReporter ( ) ;
2019
2120 constructor ( existingPath : string , incomingPaths : string [ ] , outputPath : string ) {
2221 this . outputPath = outputPath ;
@@ -46,7 +45,7 @@ export class BackwardCompatibleWriter {
4645 }
4746 }
4847
49- process ( ) : void {
48+ process ( dryRun : boolean = false ) : void {
5049 const finalMessages : ProtoMessage [ ] = [ ] ;
5150 const finalEnums : ProtoEnum [ ] = [ ] ;
5251
@@ -59,7 +58,7 @@ export class BackwardCompatibleWriter {
5958
6059 const incomingMsg = this . incomingMessageMap . get ( existingMsg . name ) ;
6160 if ( incomingMsg ) {
62- finalMessages . push ( mergeMessage ( existingMsg , incomingMsg , this . errors ) ) ;
61+ finalMessages . push ( mergeMessage ( existingMsg , incomingMsg , this . reporter ) ) ;
6362 this . incomingMessageMap . delete ( existingMsg . name ) ;
6463 } else {
6564 finalMessages . push ( existingMsg ) ;
@@ -75,7 +74,7 @@ export class BackwardCompatibleWriter {
7574
7675 const incomingEnum = this . incomingEnumMap . get ( existingEnum . name ) ;
7776 if ( incomingEnum ) {
78- finalEnums . push ( mergeEnum ( existingEnum , incomingEnum ) ) ;
77+ finalEnums . push ( mergeEnum ( existingEnum , incomingEnum , this . reporter ) ) ;
7978 this . incomingEnumMap . delete ( existingEnum . name ) ;
8079 } else {
8180 finalEnums . push ( existingEnum ) ;
@@ -96,20 +95,20 @@ export class BackwardCompatibleWriter {
9695 }
9796 }
9897
99- // Check for errors before writing
100- if ( this . errors . length > 0 ) {
101- logger . error ( 'Backward compatibility errors:' ) ;
102- for ( const error of this . errors ) {
103- logger . error ( ` ${ error } ` ) ;
104- }
105- throw new BackwardCompatibilityError (
106- `Found ${ this . errors . length } backward compatibility violation(s).`
107- ) ;
98+ // Write output using shared function (skip if dry-run)
99+ if ( dryRun ) {
100+ logger . info ( `Dry run: would update ${ this . outputPath } ` ) ;
101+ } else {
102+ writeProtoFile ( finalMessages , finalEnums , this . outputPath ) ;
103+ logger . info ( `Updated: ${ this . outputPath } ` ) ;
108104 }
105+ }
109106
110- // Write output using shared function
111- writeProtoFile ( finalMessages , finalEnums , this . outputPath ) ;
112- logger . info ( `Updated: ${ this . outputPath } ` ) ;
107+ /**
108+ * Get the merge reporter for accessing change reports.
109+ */
110+ getReporter ( ) : CompatibilityReporter {
111+ return this . reporter ;
113112 }
114113}
115114
@@ -124,13 +123,15 @@ if (require.main === module) {
124123 . argParser ( ( val : string ) => val . split ( ',' ) . map ( s => s . trim ( ) ) )
125124 . default ( [ 'protos/generated/models/aggregated_models.proto' , 'protos/generated/services/default_service.proto' ] ) )
126125 . addOption ( new Option ( '-o, --output <path>' , 'output proto file' ) . default ( 'protos/schemas/common.proto' ) )
126+ . addOption ( new Option ( '-d, --dry-run' , 'preview changes without writing output file' ) . default ( false ) )
127127 . allowExcessArguments ( false )
128128 . parse ( ) ;
129129
130130 type BackwardCompatOpts = {
131131 existing : string ;
132132 incoming : string [ ] ;
133133 output : string ;
134+ dryRun : boolean ;
134135 } ;
135136
136137 const opts = command . opts ( ) as BackwardCompatOpts ;
@@ -146,17 +147,17 @@ if (require.main === module) {
146147 process . exit ( 1 ) ;
147148 }
148149
149- try {
150- const writer = new BackwardCompatibleWriter (
151- opts . existing ,
152- opts . incoming ,
153- opts . output
154- ) ;
155- writer . process ( ) ;
156- } catch ( error ) {
157- if ( error instanceof BackwardCompatibilityError ) {
158- process . exit ( 1 ) ;
159- }
160- throw error ;
161- }
150+ const writer = new BackwardCompatibleWriter (
151+ opts . existing ,
152+ opts . incoming ,
153+ opts . output
154+ ) ;
155+
156+ // Process and merge
157+ writer . process ( opts . dryRun ) ;
158+
159+ // write report to temp directory
160+ const reportPath = join ( tmpdir ( ) , 'merge-report.md' ) ;
161+ writeFileSync ( reportPath , writer . getReporter ( ) . toMarkdown ( ) ) ;
162+ logger . info ( `Report written: ${ reportPath } ` ) ;
162163}
0 commit comments