1
1
import { existsSync , readFileSync } from 'fs' ;
2
2
import { sync as glob } from 'glob' ;
3
- import { join } from 'path' ;
3
+ import { dirname , isAbsolute , join } from 'path' ;
4
+ import * as ts from 'typescript' ;
4
5
5
6
/** RegExp that matches Angular component inline styles that contain a sourcemap reference. */
6
7
const inlineStylesSourcemapRegex = / s t y l e s : ? \[ [ " ' ] .* s o u r c e M a p p i n g U R L = .* [ " ' ] / ;
@@ -14,7 +15,7 @@ const externalReferencesRegex = /(templateUrl|styleUrls): *["'[]/;
14
15
*/
15
16
export function checkReleaseBundle ( bundlePath : string ) : string [ ] {
16
17
const bundleContent = readFileSync ( bundlePath , 'utf8' ) ;
17
- let failures : string [ ] = [ ] ;
18
+ const failures : string [ ] = [ ] ;
18
19
19
20
if ( inlineStylesSourcemapRegex . exec ( bundleContent ) !== null ) {
20
21
failures . push ( 'Found sourcemap references in component styles.' ) ;
@@ -27,6 +28,46 @@ export function checkReleaseBundle(bundlePath: string): string[] {
27
28
return failures ;
28
29
}
29
30
31
+ /**
32
+ * Checks the specified TypeScript definition file by ensuring it does not contain invalid
33
+ * dynamic import statements. There can be invalid type imports paths because we compose the
34
+ * release package by moving things in a desired output structure. See Angular package format
35
+ * specification and https://github.com/angular/material2/pull/12876
36
+ */
37
+ export function checkTypeDefinitionFile ( filePath : string ) : string [ ] {
38
+ const baseDir = dirname ( filePath ) ;
39
+ const fileContent = readFileSync ( filePath , 'utf8' ) ;
40
+ const failures = [ ] ;
41
+
42
+ const sourceFile = ts . createSourceFile ( filePath , fileContent , ts . ScriptTarget . Latest , true ) ;
43
+ const nodeQueue = [ ...sourceFile . getChildren ( ) ] ;
44
+
45
+ while ( nodeQueue . length ) {
46
+ const node = nodeQueue . shift ( ) ! ;
47
+
48
+ // Check all dynamic type imports and ensure that the import path is valid within the release
49
+ // output. Note that we don't want to enforce that there are no dynamic type imports because
50
+ // type inference is heavily used within the schematics and is useful in some situations.
51
+ if ( ts . isImportTypeNode ( node ) && ts . isLiteralTypeNode ( node . argument ) &&
52
+ ts . isStringLiteral ( node . argument . literal ) ) {
53
+ const importPath = node . argument . literal . text ;
54
+
55
+ // In case the type import path starts with a dot, we know that this is a relative path
56
+ // and can ensure that the target path exists. Note that we cannot completely rely on
57
+ // "isAbsolute" because dynamic imports can also import from modules (e.g. "my-npm-module")
58
+ if ( importPath . startsWith ( '.' ) && ! existsSync ( join ( baseDir , `${ importPath } .d.ts` ) ) ) {
59
+ failures . push ( 'Found relative type imports which do not exist.' ) ;
60
+ } else if ( isAbsolute ( importPath ) ) {
61
+ failures . push ( 'Found absolute type imports in definition file.' ) ;
62
+ }
63
+ }
64
+
65
+ nodeQueue . push ( ...node . getChildren ( ) ) ;
66
+ }
67
+
68
+ return failures ;
69
+ }
70
+
30
71
/**
31
72
* Checks the Angular Material release package and ensures that prebuilt themes
32
73
* and the theming bundle are built properly.
0 commit comments