mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
build: ensure that there are no invalid dynamic imports (#14619)
**Note**: In a follow-up I want to wire up the release output validations as part of a CircleCI job. It just makes sense to keep all of these release output validations in the same place. References https://github.com/angular/material2/issues/12877
This commit is contained in:
committed by
mmalerba
parent
4acff50ee7
commit
452c6d391a
@@ -2,11 +2,18 @@ import {bold, red, yellow} from 'chalk';
|
||||
import {existsSync} from 'fs';
|
||||
import {sync as glob} from 'glob';
|
||||
import {join} from 'path';
|
||||
import {checkMaterialPackage, checkReleaseBundle} from './output-validations';
|
||||
import {
|
||||
checkMaterialPackage,
|
||||
checkReleaseBundle,
|
||||
checkTypeDefinitionFile
|
||||
} from './output-validations';
|
||||
|
||||
/** Glob that matches all JavaScript bundle files within a release package. */
|
||||
const releaseBundlesGlob = '+(esm5|esm2015|bundles)/*.js';
|
||||
|
||||
/** Glob that matches all TypeScript definition files within a release package. */
|
||||
const releaseTypeDefinitionsGlob = '**/*.d.ts';
|
||||
|
||||
/**
|
||||
* Type that describes a map of package failures. The keys are failure messages and
|
||||
* their value is an array of specifically affected files.
|
||||
@@ -21,12 +28,14 @@ type PackageFailures = Map<string, string[]>;
|
||||
*/
|
||||
export function checkReleasePackage(releasesPath: string, packageName: string): boolean {
|
||||
const packagePath = join(releasesPath, packageName);
|
||||
const bundlePaths = glob(releaseBundlesGlob, {cwd: packagePath, absolute: true});
|
||||
const failures = new Map() as PackageFailures;
|
||||
const addFailure = (message, filePath?) => {
|
||||
failures.set(message, (failures.get(message) || []).concat(filePath));
|
||||
};
|
||||
|
||||
const bundlePaths = glob(releaseBundlesGlob, {cwd: packagePath, absolute: true});
|
||||
const typeDefinitions = glob(releaseTypeDefinitionsGlob, {cwd: packagePath, absolute: true});
|
||||
|
||||
// We want to walk through each bundle within the current package and run
|
||||
// release validations that ensure that the bundles are not invalid.
|
||||
bundlePaths.forEach(bundlePath => {
|
||||
@@ -34,6 +43,12 @@ export function checkReleasePackage(releasesPath: string, packageName: string):
|
||||
.forEach(message => addFailure(message, bundlePath));
|
||||
});
|
||||
|
||||
// Run output validations for all TypeScript definition files within the release output.
|
||||
typeDefinitions.forEach(filePath => {
|
||||
checkTypeDefinitionFile(filePath)
|
||||
.forEach(message => addFailure(message, filePath));
|
||||
});
|
||||
|
||||
// Special release validation checks for the "material" release package.
|
||||
if (packageName === 'material') {
|
||||
checkMaterialPackage(join(releasesPath, packageName))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {existsSync, readFileSync} from 'fs';
|
||||
import {sync as glob} from 'glob';
|
||||
import {join} from 'path';
|
||||
import {dirname, isAbsolute, join} from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
/** RegExp that matches Angular component inline styles that contain a sourcemap reference. */
|
||||
const inlineStylesSourcemapRegex = /styles: ?\[["'].*sourceMappingURL=.*["']/;
|
||||
@@ -14,7 +15,7 @@ const externalReferencesRegex = /(templateUrl|styleUrls): *["'[]/;
|
||||
*/
|
||||
export function checkReleaseBundle(bundlePath: string): string[] {
|
||||
const bundleContent = readFileSync(bundlePath, 'utf8');
|
||||
let failures: string[] = [];
|
||||
const failures: string[] = [];
|
||||
|
||||
if (inlineStylesSourcemapRegex.exec(bundleContent) !== null) {
|
||||
failures.push('Found sourcemap references in component styles.');
|
||||
@@ -27,6 +28,46 @@ export function checkReleaseBundle(bundlePath: string): string[] {
|
||||
return failures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the specified TypeScript definition file by ensuring it does not contain invalid
|
||||
* dynamic import statements. There can be invalid type imports paths because we compose the
|
||||
* release package by moving things in a desired output structure. See Angular package format
|
||||
* specification and https://github.com/angular/material2/pull/12876
|
||||
*/
|
||||
export function checkTypeDefinitionFile(filePath: string): string[] {
|
||||
const baseDir = dirname(filePath);
|
||||
const fileContent = readFileSync(filePath, 'utf8');
|
||||
const failures = [];
|
||||
|
||||
const sourceFile = ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.Latest, true);
|
||||
const nodeQueue = [...sourceFile.getChildren()];
|
||||
|
||||
while (nodeQueue.length) {
|
||||
const node = nodeQueue.shift()!;
|
||||
|
||||
// Check all dynamic type imports and ensure that the import path is valid within the release
|
||||
// output. Note that we don't want to enforce that there are no dynamic type imports because
|
||||
// type inference is heavily used within the schematics and is useful in some situations.
|
||||
if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) &&
|
||||
ts.isStringLiteral(node.argument.literal)) {
|
||||
const importPath = node.argument.literal.text;
|
||||
|
||||
// In case the type import path starts with a dot, we know that this is a relative path
|
||||
// and can ensure that the target path exists. Note that we cannot completely rely on
|
||||
// "isAbsolute" because dynamic imports can also import from modules (e.g. "my-npm-module")
|
||||
if (importPath.startsWith('.') && !existsSync(join(baseDir, `${importPath}.d.ts`))) {
|
||||
failures.push('Found relative type imports which do not exist.');
|
||||
} else if (isAbsolute(importPath)) {
|
||||
failures.push('Found absolute type imports in definition file.');
|
||||
}
|
||||
}
|
||||
|
||||
nodeQueue.push(...node.getChildren());
|
||||
}
|
||||
|
||||
return failures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the Angular Material release package and ensures that prebuilt themes
|
||||
* and the theming bundle are built properly.
|
||||
|
||||
Reference in New Issue
Block a user