88
99const child_process = require ( 'child_process' ) ;
1010const fs = require ( 'fs' ) ;
11+ const path = require ( 'path' ) ;
1112
1213const exec = function ( cmd ) {
1314 return new Promise ( function ( resolve , reject ) {
@@ -33,7 +34,16 @@ const blocked_statements = [
3334 'from \\\'rxjs/Rx\\\'' ,
3435] ;
3536
37+ // Retrieves all scope packages dynamically from the source.
38+ // Developers shouldn't be able to import from other scope packages by using relative paths.
39+ const componentFolders = fs
40+ . readdirSync ( `${ __dirname } /../../src/components` )
41+ . map ( componentName => `src/components/${ componentName } ` ) ;
42+
43+ const scopePackages = [ 'src/core' ] . concat ( componentFolders ) ;
44+
3645const blockedRegex = new RegExp ( blocked_statements . join ( '|' ) , 'g' ) ;
46+ const importRegex = / f r o m \s + ' ( .* ) ' ; / g;
3747
3848/**
3949 * Find the fork point between HEAD of the current branch, and master.
@@ -85,6 +95,63 @@ function findChangedFiles() {
8595 } ) ;
8696}
8797
98+ /**
99+ * Checks the line for any relative imports of a scope package, which should be imported by using
100+ * the scope package name instead of the relative path.
101+ * @param fileName Filename to validate the path
102+ * @param line Line to be checked.
103+ */
104+ function isRelativeScopeImport ( fileName , line ) {
105+ let importMatch = importRegex . exec ( line ) ;
106+
107+ // We have to reset the last index of the import regex, otherwise we
108+ // would have incorrect matches in the next execution.
109+ importRegex . lastIndex = 0 ;
110+
111+ // Skip the check if the current line doesn't match any imports.
112+ if ( ! importMatch ) {
113+ return false ;
114+ }
115+
116+ let importPath = importMatch [ 1 ] ;
117+
118+ // Skip the check when the import doesn't start with a dot, because the line
119+ // isn't importing any file relatively. Also applies to scope packages starting
120+ // with `@`.
121+ if ( ! importPath . startsWith ( '.' ) ) {
122+ return false ;
123+ }
124+
125+ // Transform the relative import path into an absolute path.
126+ importPath = path . resolve ( path . dirname ( fileName ) , importPath ) ;
127+
128+ let currentScope = findScope ( fileName ) ;
129+ let importScope = findScope ( importPath ) ;
130+
131+ if ( currentScope !== importScope ) {
132+ // Transforms the invalid import statement into a correct import statement, which uses the scope package.
133+ let scopeImport = `@angular2-material/${ path . basename ( importScope ) } /${ path . relative ( importScope , importPath ) } ` ;
134+
135+ return {
136+ currentScope : currentScope ,
137+ importScope : importScope ,
138+ invalidStatement : importMatch [ 0 ] ,
139+ scopeImportPath : scopeImport
140+ }
141+ }
142+
143+ return false ;
144+
145+ function findScope ( filePath ) {
146+ // Normalize the filePath as well, to avoid issues with the different environments path delimiter.
147+ filePath = path . normalize ( filePath ) ;
148+
149+ // Iterate through all scopes and try to find them inside of the file path.
150+ return scopePackages . filter ( scope => filePath . indexOf ( path . normalize ( scope ) ) !== - 1 ) . pop ( ) ;
151+ }
152+
153+ }
154+
88155
89156// Find all files, check for errors, and output every errors found.
90157findChangedFiles ( )
@@ -116,23 +183,35 @@ findChangedFiles()
116183 content . forEach ( function ( line ) {
117184 ln ++ ;
118185
119- let m = line . match ( blockedRegex ) ;
120- if ( m ) {
186+ let matches = line . match ( blockedRegex ) ;
187+ let isScopeImport = isRelativeScopeImport ( fileName , line ) ;
188+
189+ if ( matches || isScopeImport ) {
121190 // Accumulate all errors at once.
122- errors . push ( {
191+ let error = {
123192 fileName : fileName ,
124193 lineNumber : ln ,
125- statement : m [ 0 ]
126- } ) ;
194+ statement : matches && matches [ 0 ] || isScopeImport . invalidStatement
195+ } ;
196+
197+ if ( isScopeImport ) {
198+ error . message =
199+ ' You are using an import statement, which imports a file from another scope package.\n' +
200+ ` Please import the file by using the following path: ${ isScopeImport . scopeImportPath } ` ;
201+ }
202+
203+ errors . push ( error ) ;
127204 }
128205 } ) ;
129206 return errors ;
130207 } , [ ] ) ;
131208 } )
132209 . then ( errors => {
133210 if ( errors . length > 0 ) {
134- console . error ( 'Error: You are using a statement in your commit, which is not allowed.' ) ;
211+ console . error ( 'Error: You are using a statement in your commit, which is not allowed.\n' ) ;
212+
135213 errors . forEach ( entry => {
214+ if ( entry . message ) console . error ( entry . message ) ;
136215 console . error ( ` ${ entry . fileName } @${ entry . lineNumber } , Statement: ${ entry . statement } .\n` ) ;
137216 } ) ;
138217
0 commit comments