@@ -26,12 +26,11 @@ const blocked_statements = [
2626 '\\bxit\\(' ,
2727 '\\bdebugger;' ,
2828 'from \\\'rxjs/Rx\\\'' ,
29+ '\\r'
2930] ;
3031
3132const sourceFolders = [ './src' , './e2e' ] ;
32- const libRoot = 'src/lib' ;
3333const blockedRegex = new RegExp ( blocked_statements . join ( '|' ) , 'g' ) ;
34- const importRegex = / f r o m \s + ' ( .* ) ' ; / g;
3534
3635/*
3736 * Verify that the current PR is not adding any forbidden identifiers.
@@ -47,69 +46,105 @@ findTestFiles()
4746 . then ( files => files . map ( name => ( { name, content : fs . readFileSync ( name , 'utf-8' ) } ) ) )
4847
4948 /* Run checks against content of the filtered files. */
50- . then ( diffList => {
49+ . then ( diffList => findErrors ( diffList ) )
5150
52- return diffList . reduce ( ( errors , diffFile ) => {
53- let fileName = diffFile . name ;
54- let content = diffFile . content . split ( '\n' ) ;
55- let lineCount = 0 ;
51+ /* Groups similar errors to simplify console output */
52+ . then ( errors => groupErrors ( errors ) )
5653
57- content . forEach ( line => {
58- lineCount ++ ;
54+ /* Print the resolved errors to the console */
55+ . then ( errors => printErrors ( errors ) )
56+
57+ . catch ( err => {
58+ // An error occurred in this script. Output the error and the stack.
59+ console . error ( err . stack || err ) ;
60+ process . exit ( 2 ) ;
61+ } ) ;
62+
63+ /**
64+ * Finds errors inside of changed files by running them against the blocked statements.
65+ * @param {{name: string, content: string}[] } diffList
66+ */
67+ function findErrors ( diffList ) {
68+ return diffList . reduce ( ( errors , diffFile ) => {
69+ let fileName = diffFile . name ;
70+ let content = diffFile . content . split ( '\n' ) ;
71+ let lineNumber = 0 ;
5972
60- let matches = line . match ( blockedRegex ) ;
61- let scopeImport = path . relative ( libRoot , fileName ) . startsWith ( '..' )
62- ? isRelativeScopeImport ( fileName , line )
63- : false ;
73+ content . forEach ( line => {
74+ lineNumber ++ ;
6475
65- if ( matches || scopeImport ) {
76+ let matches = line . match ( blockedRegex ) ;
6677
67- let error = {
68- fileName : fileName ,
69- lineNumber : lineCount ,
70- statement : matches && matches [ 0 ] || scopeImport . invalidStatement
71- } ;
78+ if ( matches ) {
7279
73- if ( scopeImport ) {
74- error . messages = [
75- 'You are using an import statement, which imports a file from another scope package.' ,
76- `Please import the file by using the following path: ${ scopeImport . scopeImportPath } `
77- ] ;
78- }
80+ errors . push ( {
81+ fileName,
82+ lineNumber,
83+ statement : matches [ 0 ]
84+ } ) ;
7985
80- errors . push ( error ) ;
81- }
82- } ) ;
86+ }
87+ } ) ;
8388
84- return errors ;
89+ return errors ;
8590
86- } , [ ] ) ;
87- } )
91+ } , [ ] ) ;
92+ }
8893
89- /* Print the resolved errors to the console */
90- . then ( errors => {
91- if ( errors . length > 0 ) {
92- console . error ( 'Error: You are using one or more blocked statements:\n' ) ;
94+ /**
95+ * Groups similar errors in the same file which are present over a range of lines.
96+ * @param {{fileName: string, lineNumber: number, statement: string}[] } errors
97+ */
98+ function groupErrors ( errors ) {
9399
94- errors . forEach ( entry => {
95- if ( entry . messages ) {
96- entry . messages . forEach ( message => console . error ( ` ${ message } ` ) )
97- }
100+ let initialError , initialLine , previousLine ;
98101
99- console . error ( ` ${ entry . fileName } @${ entry . lineNumber } , Statement: ${ entry . statement } .\n` ) ;
100- } ) ;
102+ return errors . filter ( error => {
101103
102- process . exit ( 1 ) ;
104+ if ( initialError && isSimilarError ( error ) ) {
105+ previousLine = error . lineNumber ;
106+
107+ // Overwrite the lineNumber with a string, which indicates the range of lines.
108+ initialError . lineNumber = `${ initialLine } -${ previousLine } ` ;
109+
110+ return false ;
103111 }
104- } )
105112
106- . catch ( err => {
107- // An error occurred in this script. Output the error and the stack.
108- console . error ( 'An error occurred during execution:' ) ;
109- console . error ( err . stack || err ) ;
110- process . exit ( 2 ) ;
113+ initialLine = previousLine = error . lineNumber ;
114+ initialError = error ;
115+
116+ return true ;
111117 } ) ;
112118
119+ /** Checks whether the given error is similar to the previous one. */
120+ function isSimilarError ( error ) {
121+ return initialError . fileName === error . fileName &&
122+ initialError . statement === error . statement &&
123+ previousLine === ( error . lineNumber - 1 ) ;
124+ }
125+ }
126+
127+ /**
128+ * Prints all errors to the console and terminates the process if errors were present.
129+ * @param {{fileName: string, lineNumber: number, statement: string}[] } errors
130+ */
131+ function printErrors ( errors ) {
132+ if ( errors . length > 0 ) {
133+
134+ console . error ( 'Error: You are using one or more blocked statements:\n' ) ;
135+
136+ errors . forEach ( entry => {
137+
138+ // Stringify the statement to represent line-endings or other unescaped characters.
139+ let statement = JSON . stringify ( entry . statement ) ;
140+
141+ console . error ( ` ${ entry . fileName } @${ entry . lineNumber } , Statement: ${ statement } .\n` ) ;
142+ } ) ;
143+
144+ // Exit the process with an error exit code to notify the CI.
145+ process . exit ( 1 ) ;
146+ }
147+ }
113148
114149/**
115150 * Resolves all files, which should run against the forbidden identifiers check.
@@ -120,8 +155,8 @@ function findTestFiles() {
120155 return findChangedFiles ( ) ;
121156 }
122157
123- var files = sourceFolders . map ( folder => {
124- return glob ( `${ folder } /**/*.ts ` ) ;
158+ let files = sourceFolders . map ( folder => {
159+ return glob ( `${ folder } /**/*` ) ;
125160 } ) . reduce ( ( files , fileSet ) => files . concat ( fileSet ) , [ ] ) ;
126161
127162 return Promise . resolve ( files ) ;
@@ -150,72 +185,6 @@ function findChangedFiles() {
150185 } ) ;
151186}
152187
153- /**
154- * Checks the line for any relative imports of a scope package, which should be imported by using
155- * the scope package name instead of the relative path.
156- * @param fileName Filename to validate the path
157- * @param line Line to be checked.
158- */
159- function isRelativeScopeImport ( fileName , line ) {
160- if ( fileName . startsWith ( libRoot ) ) {
161- return false ;
162- }
163-
164- let importMatch = importRegex . exec ( line ) ;
165-
166- // We have to reset the last index of the import regex, otherwise we
167- // would have incorrect matches in the next execution.
168- importRegex . lastIndex = 0 ;
169-
170- // Skip the check if the current line doesn't match any imports.
171- if ( ! importMatch ) {
172- return false ;
173- }
174-
175- let importPath = importMatch [ 1 ] ;
176-
177- // Skip the check when the import doesn't start with a dot, because the line
178- // isn't importing any file relatively. Also applies to scope packages starting
179- // with `@`.
180- if ( ! importPath . startsWith ( '.' ) ) {
181- return false ;
182- }
183-
184- // Transform the relative import path into an absolute path.
185- importPath = path . resolve ( path . dirname ( fileName ) , importPath ) ;
186-
187- let fileScope = findScope ( fileName ) ;
188- let importScope = findScope ( importPath ) ;
189-
190- if ( fileScope . path !== importScope . path ) {
191-
192- // Creates a valid import statement, which uses the correct scope package.
193- let validImportPath = `@angular/material` ;
194-
195- return {
196- fileScope : fileScope . name ,
197- importScope : importScope . name ,
198- invalidStatement : importMatch [ 0 ] ,
199- scopeImportPath : validImportPath
200- }
201- }
202-
203- function findScope ( filePath ) {
204- // Normalize the filePath, to avoid issues with the different environments path delimiter.
205- filePath = path . normalize ( filePath ) ;
206-
207- // Iterate through all scope paths and try to find them inside of the file path.
208- var scopePath = filePath . indexOf ( path . normalize ( libRoot ) ) == - 1 ? libRoot : filePath ;
209-
210- // Return an object containing the name of the scope and the associated path.
211- return {
212- name : path . basename ( scopePath ) ,
213- path : scopePath
214- }
215- }
216-
217- }
218-
219188/**
220189 * Executes a process command and wraps it inside of a promise.
221190 * @returns {Promise.<String> }
0 commit comments