11import { existsSync , readFileSync } from 'fs' ;
22import { sync as glob } from 'glob' ;
3- import { join } from 'path' ;
3+ import { dirname , isAbsolute , join } from 'path' ;
4+ import * as ts from 'typescript' ;
45
56/** RegExp that matches Angular component inline styles that contain a sourcemap reference. */
67const 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): *["'[]/;
1415 */
1516export function checkReleaseBundle ( bundlePath : string ) : string [ ] {
1617 const bundleContent = readFileSync ( bundlePath , 'utf8' ) ;
17- let failures : string [ ] = [ ] ;
18+ const failures : string [ ] = [ ] ;
1819
1920 if ( inlineStylesSourcemapRegex . exec ( bundleContent ) !== null ) {
2021 failures . push ( 'Found sourcemap references in component styles.' ) ;
@@ -27,6 +28,46 @@ export function checkReleaseBundle(bundlePath: string): string[] {
2728 return failures ;
2829}
2930
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+
3071/**
3172 * Checks the Angular Material release package and ensures that prebuilt themes
3273 * and the theming bundle are built properly.
0 commit comments