@@ -20,7 +20,8 @@ export interface CompilerErrorInterface {
2020export interface SyntaxErrorInterface {
2121 message : string ;
2222 plainMessage ?: string
23- loc : SourceLocation | null | undefined ,
23+ loc ?: SourceLocation | null | undefined ,
24+ fileName ?: string ;
2425}
2526
2627interface File {
@@ -32,6 +33,8 @@ export interface ErrorReporterOptions {
3233 logger : ( msg : string ) => void
3334}
3435
36+ const NO_FILE_SPECIFIED = '_No-file-specified' ;
37+
3538export class ErrorReporter {
3639 protected warnings : SyntaxErrorInterface [ ] = [ ] ;
3740
@@ -56,13 +59,15 @@ export class ErrorReporter {
5659 this . file = file ;
5760 }
5861
59- public warning ( e : SyntaxErrorInterface ) {
62+ public warning ( e : SyntaxErrorInterface , fileName ?: string ) {
63+ const targetFileName = fileName || e . fileName || this . file ?. fileName ;
64+
6065 if ( this . file && e . loc ) {
6166 const codeFrame = codeFrameColumns ( this . file . content , e . loc , {
6267 message : e . message ,
6368 highlightCode : true ,
6469 } ) ;
65- const plainMessage = `Warning: ${ e . message } in ${ this . file . fileName } ` ;
70+ const plainMessage = `Warning: ${ e . message } ` ;
6671 const message = `${ plainMessage } \n${ codeFrame } ` ;
6772
6873 if ( this . rootReporter ( ) . warnings . find ( m => ( m . message || m ) === message ) ) {
@@ -73,27 +78,41 @@ export class ErrorReporter {
7378 message,
7479 plainMessage,
7580 loc : e . loc ,
81+ fileName : targetFileName ,
7682 } ) ;
7783
78- this . options . logger ( message ) ;
84+ if ( targetFileName ) {
85+ this . options . logger ( `${ targetFileName } :\n${ message } ` ) ;
86+ } else {
87+ this . options . logger ( message ) ;
88+ }
7989 } else {
8090 if ( this . rootReporter ( ) . warnings . find ( m => ( m . message || m ) === e . message ) ) {
8191 return ;
8292 }
8393
84- this . rootReporter ( ) . warnings . push ( e ) ;
94+ this . rootReporter ( ) . warnings . push ( {
95+ ...e ,
96+ fileName : targetFileName ,
97+ } ) ;
8598
86- this . options . logger ( e . message ) ;
99+ if ( targetFileName ) {
100+ this . options . logger ( `${ targetFileName } :\n${ e . message } ` ) ;
101+ } else {
102+ this . options . logger ( e . message ) ;
103+ }
87104 }
88105 }
89106
90- public syntaxError ( e : SyntaxErrorInterface ) {
107+ public syntaxError ( e : SyntaxErrorInterface , fileName ?: string ) {
108+ const targetFileName = fileName || e . fileName || this . file ?. fileName ;
109+
91110 if ( this . file && e . loc ) {
92111 const codeFrame = codeFrameColumns ( this . file . content , e . loc , {
93112 message : e . message ,
94113 highlightCode : true ,
95114 } ) ;
96- const plainMessage = `Syntax Error: ${ e . message } in ${ this . file . fileName } ` ;
115+ const plainMessage = `Syntax Error: ${ e . message } ` ;
97116 const message = `${ plainMessage } \n${ codeFrame } ` ;
98117
99118 if ( this . rootReporter ( ) . errors . find ( m => ( m . message || m ) === message ) ) {
@@ -103,44 +122,98 @@ export class ErrorReporter {
103122 this . rootReporter ( ) . errors . push ( {
104123 message,
105124 plainMessage,
125+ fileName : targetFileName ,
106126 } ) ;
107127 } else {
108128 if ( this . rootReporter ( ) . errors . find ( m => ( m . message || m ) === e . message ) ) {
109129 return ;
110130 }
111131
112- this . rootReporter ( ) . errors . push ( e ) ;
132+ this . rootReporter ( ) . errors . push ( {
133+ ...e ,
134+ fileName : targetFileName ,
135+ } ) ;
113136 }
114137 }
115138
116139 public error ( e : any , fileName ?: any , lineNumber ?: any , position ?: any ) {
140+ const targetFileName = fileName || this . file ?. fileName ;
117141 const message = `${ this . context . length ? `${ this . context . join ( ' -> ' ) } : ` : '' } ${ e . message ? e . message : ( e . stack || e ) } ` ;
118142 if ( this . rootReporter ( ) . errors . find ( m => ( m . message || m ) === message ) ) {
119143 return ;
120144 }
121145
122- if ( fileName ) {
123- this . rootReporter ( ) . errors . push ( {
124- message, fileName, lineNumber, position
125- } ) ;
126- } else {
127- this . rootReporter ( ) . errors . push ( {
128- message,
129- } ) ;
130- }
146+ this . rootReporter ( ) . errors . push ( {
147+ message,
148+ fileName : targetFileName ,
149+ lineNumber,
150+ position
151+ } ) ;
131152 }
132153
133154 public inContext ( context : string ) {
134155 return new ErrorReporter ( this , this . context . concat ( context ) ) ;
135156 }
136157
158+ private groupErrors ( ) : Map < string , CompilerErrorInterface [ ] > {
159+ const { errors } = this . rootReporter ( ) ;
160+
161+ const errorsByFile = new Map < string , CompilerErrorInterface [ ] > ( ) ;
162+
163+ for ( const error of errors ) {
164+ const key = error . fileName || NO_FILE_SPECIFIED ;
165+ if ( ! errorsByFile . has ( key ) ) {
166+ errorsByFile . set ( key , [ ] ) ;
167+ }
168+ errorsByFile . get ( key ) ! . push ( error ) ;
169+ }
170+
171+ return errorsByFile ;
172+ }
173+
137174 public throwIfAny ( ) {
138- if ( this . rootReporter ( ) . errors . length ) {
139- throw new CompileError (
140- this . rootReporter ( ) . errors . map ( ( e ) => e . message ) . join ( '\n' ) ,
141- this . rootReporter ( ) . errors . map ( ( e ) => e . plainMessage ) . join ( '\n' )
142- ) ;
175+ const { errors } = this . rootReporter ( ) ;
176+
177+ if ( errors . length === 0 ) {
178+ return ;
143179 }
180+
181+ const errorsByFile = this . groupErrors ( ) ;
182+
183+ // Build formatted report
184+ const messageParts : string [ ] = [ ] ;
185+ const plainMessageParts : string [ ] = [ ] ;
186+
187+ const sortedFiles = Array . from ( errorsByFile . keys ( ) ) . sort ( ) ;
188+
189+ for ( const fileName of sortedFiles ) {
190+ const fileErrors = errorsByFile . get ( fileName ) ! ;
191+ const reportFileName = fileName === NO_FILE_SPECIFIED ? '' : `${ fileName } ` ;
192+
193+ messageParts . push ( `${ reportFileName } Errors:` ) ;
194+
195+ const plainMessagesForFile : string [ ] = [ ] ;
196+ for ( const error of fileErrors ) {
197+ messageParts . push ( error . message ) ;
198+ if ( error . plainMessage ) {
199+ plainMessagesForFile . push ( error . plainMessage ) ;
200+ }
201+ }
202+
203+ if ( plainMessagesForFile . length > 0 ) {
204+ plainMessageParts . push ( `${ reportFileName } Errors:` ) ;
205+ plainMessageParts . push ( ...plainMessagesForFile ) ;
206+ plainMessageParts . push ( '' ) ;
207+ }
208+
209+ // Add blank line between file groups
210+ messageParts . push ( '' ) ;
211+ }
212+
213+ throw new CompileError (
214+ messageParts . join ( '\n' ) ,
215+ plainMessageParts . join ( '\n' )
216+ ) ;
144217 }
145218
146219 public getErrors ( ) {
0 commit comments