@@ -2,8 +2,12 @@ import * as ts from 'typescript';
22import * as chalk from 'chalk' ;
33import * as tslint from 'tslint' ;
44import * as path from 'path' ;
5- import { IInternalTypeCheckerOptions , END_LINE } from './interfaces' ;
5+ import { IInternalTypeCheckerOptions , END_LINE , ITSLintError , ITSError } from './interfaces' ;
66
7+ type TypeCheckError = ITSLintError | ITSError ;
8+ function isTSError ( error : TypeCheckError ) {
9+ return ( < ITSError > error ) . code !== undefined ;
10+ }
711
812export class Checker {
913
@@ -146,22 +150,52 @@ export class Checker {
146150 ) ;
147151
148152 // get the lint errors messages
149- let lintErrorMessages = this . processLintFiles ( ) ;
153+ let lintErrorMessages : TypeCheckError [ ] = this . processLintFiles ( ) ;
150154
151155 // loop diagnostics and get the errors messages
152- let tsErrorMessages = this . processTsDiagnostics ( ) ;
156+ let tsErrorMessages : TypeCheckError [ ] = this . processTsDiagnostics ( ) ;
153157
154158 // combine errors and print if any
155- let allErrors = tsErrorMessages . concat ( lintErrorMessages ) ;
159+ let combinedErrors : TypeCheckError [ ] = tsErrorMessages . concat ( lintErrorMessages ) ;
160+
161+ // group by filename
162+ let groupedErrors : { [ k : string ] : TypeCheckError [ ] } = { } ;
163+ combinedErrors . forEach ( ( error : TypeCheckError ) => {
164+ if ( ! groupedErrors [ error . fileName ] ) {
165+ groupedErrors [ error . fileName ] = [ ] as TypeCheckError [ ] ;
166+ }
167+
168+ groupedErrors [ error . fileName ] . push ( error ) ;
169+ } ) ;
170+
171+ let allErrors = Object . entries ( groupedErrors )
172+ . map ( ( [ fileName , errors ] ) => {
173+ const short = this . options . shortenFilenames ;
174+ const fullFileName = path . resolve ( fileName ) ;
175+ const shortFileName = fullFileName . split ( options . basePath ) . join ( '.' ) ;
176+ return chalk . white ( `└── ${ shortFileName } ` ) + END_LINE + errors . map ( ( err : TypeCheckError ) => {
177+ let text = chalk . red ( ' |' ) ;
178+ text += chalk [ err . color ] ( ` ${ short ? shortFileName : fullFileName } (${ err . line + 1 } ,${ err . char + 1 } ) ` ) ;
179+ if ( isTSError ( err ) ) {
180+ text += chalk . white ( `(${ ( < ITSError > err ) . category } :` ) ;
181+ text += chalk . white ( `${ ( < ITSError > err ) . code } )` ) ;
182+ text += ' ' + ( < ITSError > err ) . message ;
183+ } else {
184+ text += chalk . white ( `(${ ( < ITSLintError > err ) . ruleSeverity } :` ) ;
185+ text += chalk . white ( `${ ( < ITSLintError > err ) . ruleName } )` ) ;
186+ text += ' ' + ( < ITSLintError > err ) . failure ;
187+ }
188+
189+ return text ;
190+ } ) . join ( END_LINE ) ;
191+ } ) ;
156192
157193 // print if any
158194 if ( allErrors . length > 0 ) {
159-
160195 // insert header
161196 allErrors . unshift (
162197 chalk . underline ( `${ END_LINE } File errors` ) + chalk . white ( ':' ) // fix windows
163198 ) ;
164-
165199 print ( allErrors . join ( END_LINE ) ) ;
166200 }
167201
@@ -280,118 +314,65 @@ export class Checker {
280314 * loops lint failures and return pretty failure string ready to be printed
281315 *
282316 */
283- private processLintFiles ( ) : string [ ] {
284-
285- const options = this . options ;
286-
287- // loop lint results
288- let lintResultsFilesMessages =
289- this . lintFileResult . map ( ( fileResult : tslint . LintResult ) => {
290- let messages : string [ ] = [ ] ;
291- if ( fileResult . failures ) {
292- // we have a failure, lets check its failures
293- messages = fileResult . failures . map ( ( failure : any ) => {
294-
295- // simplify it so its more readable later
296- let r = {
297- fileName : failure . fileName ,
298- line : failure . startPosition . lineAndCharacter . line ,
299- char : failure . startPosition . lineAndCharacter . character ,
300- ruleSeverity : failure . ruleSeverity . charAt ( 0 ) . toUpperCase ( ) + failure . ruleSeverity . slice ( 1 ) ,
301- ruleName : failure . ruleName ,
302- failure : failure . failure
303- } ;
304-
305- // make error pretty and return it
306- let message = chalk . red ( '└── ' ) ;
307- message += chalk [ options . yellowOnLint ? 'yellow' : 'red' ] ( `${ r . fileName } (${ r . line + 1 } ,${ r . char + 1 } ) ` ) ;
308- message += chalk . white ( `(${ r . ruleSeverity } :` ) ;
309- message += chalk . white ( `${ r . ruleName } )` ) ;
310- message += ' ' + r . failure ;
311- return message ;
312- } ) ;
313- }
314- // return messages
315- return messages ;
316-
317- } ) . filter ( ( res : string [ ] ) => {
318- // filter our only messages with content
319- return res . length === 0 ? false : true ;
320- } ) ;
321-
322- // flatten/concatenate lint files - > failures
323- let lintErrorMessages : string [ ] = [ ] ;
324- try {
325- if ( lintResultsFilesMessages . length ) {
326- lintErrorMessages = lintResultsFilesMessages . reduce ( ( a : string [ ] , b : string [ ] ) => {
327- return a . concat ( b ) ;
328- } ) ;
329- }
330- } catch ( err ) {
331- console . log ( err ) ;
332- }
333-
334- return lintErrorMessages ;
317+ private processLintFiles ( ) : ITSLintError [ ] {
318+ const options = this . options ;
319+ const erroredLintFiles = this . lintFileResult
320+ . filter ( ( fileResult : tslint . LintResult ) => fileResult . failures ) ;
321+ const errors = erroredLintFiles
322+ . map (
323+ ( fileResult : tslint . LintResult ) =>
324+ fileResult . failures . map ( ( failure : any ) => ( {
325+ fileName : failure . fileName ,
326+ line : failure . startPosition . lineAndCharacter . line ,
327+ char : failure . startPosition . lineAndCharacter . character ,
328+ ruleSeverity : failure . ruleSeverity . charAt ( 0 ) . toUpperCase ( ) + failure . ruleSeverity . slice ( 1 ) ,
329+ ruleName : failure . ruleName ,
330+ color : options . yellowOnLint ? 'yellow' : 'red' ,
331+ failure : failure . failure
332+ } ) ) ) . reduce ( ( acc , curr ) => acc . concat ( curr ) , [ ] ) ;
333+ return errors ;
335334 }
336335
337-
338-
339336 /**
340337 * loops ts failures and return pretty failure string ready to be printed
341338 *
342339 */
343- private processTsDiagnostics ( ) : string [ ] {
344-
345- const options = this . options ;
346- let tsErrorMessages : string [ ] = [ ] ;
347-
348- if ( this . tsDiagnostics . length > 0 ) {
349- tsErrorMessages = this . tsDiagnostics . map ( ( diag : any ) => {
350-
351- // get message type error, warn, info
352- let message = chalk . red ( '└── ' ) ;
353-
354- // set color from options
355- let color : string ;
356- switch ( diag . _type ) {
357- case 'options' :
358- color = options . yellowOnOptions ? 'yellow' : 'red' ;
359- break ;
360- case 'global' :
361- color = options . yellowOnGlobal ? 'yellow' : 'red' ;
362- break ;
363- case 'syntactic' :
364- color = options . yellowOnSyntactic ? 'yellow' : 'red' ;
365- break ;
366- case 'semantic' :
367- color = options . yellowOnSemantic ? 'yellow' : 'red' ;
368- break ;
369- default :
370- color = 'red' ;
371- }
372-
373- // if file
374- if ( diag . file ) {
375- const {
376- line,
377- character
378- } = diag . file . getLineAndCharacterOfPosition ( diag . start ) ;
379-
380- message += chalk [ color ] ( `${ diag . file . fileName } (${ line + 1 } ,${ character + 1 } ) ` ) ;
381- message += chalk . white ( `(${ ts . DiagnosticCategory [ diag . category ] } :` ) ;
382- message += chalk . white ( `TS${ diag . code } )` ) ;
383- }
384-
385- // flatten error message
386- message += ' ' + ts . flattenDiagnosticMessageText ( diag . messageText , END_LINE ) ;
387-
388- // return message
389- return message ;
390- } ) ;
340+ private processTsDiagnostics ( ) : ITSError [ ] {
341+ const options = this . options ;
342+ return this . tsDiagnostics
343+ . filter ( ( diag : any ) => diag . file )
344+ . map ( ( diag : any ) => {
345+ // set color from options
346+ let color : string ;
347+ switch ( diag . _type ) {
348+ case 'options' :
349+ color = options . yellowOnOptions ? 'yellow' : 'red' ;
350+ break ;
351+ case 'global' :
352+ color = options . yellowOnGlobal ? 'yellow' : 'red' ;
353+ break ;
354+ case 'syntactic' :
355+ color = options . yellowOnSyntactic ? 'yellow' : 'red' ;
356+ break ;
357+ case 'semantic' :
358+ color = options . yellowOnSemantic ? 'yellow' : 'red' ;
359+ break ;
360+ default :
361+ color = 'red' ;
391362 }
392-
393- return tsErrorMessages ;
363+ const {
364+ line,
365+ character
366+ } = diag . file . getLineAndCharacterOfPosition ( diag . start ) ;
367+ return {
368+ fileName : diag . file . fileName ,
369+ line : line + 1 , // `(${line + 1},${character + 1})`,
370+ message : ts . flattenDiagnosticMessageText ( diag . messageText , END_LINE ) ,
371+ char : character + 1 ,
372+ color : color ,
373+ category : `${ ts . DiagnosticCategory [ diag . category ] } :` ,
374+ code : `TS${ diag . code } `
375+ } ;
376+ } ) ;
394377 }
395-
396-
397378}
0 commit comments