mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-22 15:02:11 +08:00
fix(@ngtools/webpack): show a compilation error on invalid TypeScript version
A TypeScript version mismatch with the Angular compiler will no longer cause an exception to propagate up through the Webpack system. In Node.js v14, this resulted in an unhandled promise rejection warning and the build command never completing. This can also be reproduced in newer versions of Node.js by using the Node.js option `--unhandled-rejections=warn`. To correct this issue, the version mismatch is now treated as a compilation error and added to the list of errors that are displayed at the end of the build. This also has the benefit of avoiding the stack trace of the exception from being shown which previously drew attention away from the actual error message.
This commit is contained in:
parent
a69000407c
commit
34ecf669dd
@ -53,6 +53,16 @@ export interface AngularWebpackPluginOptions {
|
||||
inlineStyleFileExtension?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Angular compilation state that is maintained across each Webpack compilation.
|
||||
*/
|
||||
interface AngularCompilationState {
|
||||
ngccProcessor?: NgccProcessor;
|
||||
resourceLoader?: WebpackResourceLoader;
|
||||
previousUnused?: Set<string>;
|
||||
pathsPlugin: TypeScriptPathsPlugin;
|
||||
}
|
||||
|
||||
function initializeNgccProcessor(
|
||||
compiler: Compiler,
|
||||
tsconfig: string,
|
||||
@ -138,9 +148,8 @@ export class AngularWebpackPlugin {
|
||||
return this.pluginOptions;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
apply(compiler: Compiler): void {
|
||||
const { NormalModuleReplacementPlugin, util } = compiler.webpack;
|
||||
const { NormalModuleReplacementPlugin, WebpackError, util } = compiler.webpack;
|
||||
this.webpackCreateHash = util.createHash;
|
||||
|
||||
// Setup file replacements with webpack
|
||||
@ -175,10 +184,25 @@ export class AngularWebpackPlugin {
|
||||
// Load the compiler-cli if not already available
|
||||
compiler.hooks.beforeCompile.tapPromise(PLUGIN_NAME, () => this.initializeCompilerCli());
|
||||
|
||||
let ngccProcessor: NgccProcessor | undefined;
|
||||
let resourceLoader: WebpackResourceLoader | undefined;
|
||||
let previousUnused: Set<string> | undefined;
|
||||
const compilationState: AngularCompilationState = { pathsPlugin };
|
||||
compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
|
||||
try {
|
||||
this.setupCompilation(compilation, compilationState);
|
||||
} catch (error) {
|
||||
compilation.errors.push(
|
||||
new WebpackError(
|
||||
`Failed to initialize Angular compilation - ${
|
||||
error instanceof Error ? error.message : error
|
||||
}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private setupCompilation(compilation: Compilation, state: AngularCompilationState): void {
|
||||
const compiler = compilation.compiler;
|
||||
|
||||
// Register plugin to ensure deterministic emit order in multi-plugin usage
|
||||
const emitRegistration = this.registerWithCompilation(compilation);
|
||||
this.watchMode = compiler.watchMode;
|
||||
@ -189,12 +213,12 @@ export class AngularWebpackPlugin {
|
||||
}
|
||||
|
||||
// Initialize the resource loader if not already setup
|
||||
if (!resourceLoader) {
|
||||
resourceLoader = new WebpackResourceLoader(this.watchMode);
|
||||
if (!state.resourceLoader) {
|
||||
state.resourceLoader = new WebpackResourceLoader(this.watchMode);
|
||||
}
|
||||
|
||||
// Initialize and process eager ngcc if not already setup
|
||||
if (!ngccProcessor) {
|
||||
if (!state.ngccProcessor) {
|
||||
const { processor, errors, warnings } = initializeNgccProcessor(
|
||||
compiler,
|
||||
this.pluginOptions.tsconfig,
|
||||
@ -205,7 +229,7 @@ export class AngularWebpackPlugin {
|
||||
warnings.forEach((warning) => addWarning(compilation, warning));
|
||||
errors.forEach((error) => addError(compilation, error));
|
||||
|
||||
ngccProcessor = processor;
|
||||
state.ngccProcessor = processor;
|
||||
}
|
||||
|
||||
// Setup and read TypeScript and Angular compiler configuration
|
||||
@ -218,7 +242,7 @@ export class AngularWebpackPlugin {
|
||||
diagnosticsReporter(errors);
|
||||
|
||||
// Update TypeScript path mapping plugin with new configuration
|
||||
pathsPlugin.update(compilerOptions);
|
||||
state.pathsPlugin.update(compilerOptions);
|
||||
|
||||
// Create a Webpack-based TypeScript compiler host
|
||||
const system = createWebpackSystem(
|
||||
@ -262,11 +286,11 @@ export class AngularWebpackPlugin {
|
||||
augmentHostWithDependencyCollection(host, this.fileDependencies, moduleResolutionCache);
|
||||
|
||||
// Setup on demand ngcc
|
||||
augmentHostWithNgcc(host, ngccProcessor, moduleResolutionCache);
|
||||
augmentHostWithNgcc(host, state.ngccProcessor, moduleResolutionCache);
|
||||
|
||||
// Setup resource loading
|
||||
resourceLoader.update(compilation, changedFiles);
|
||||
augmentHostWithResources(host, resourceLoader, {
|
||||
state.resourceLoader.update(compilation, changedFiles);
|
||||
augmentHostWithResources(host, state.resourceLoader, {
|
||||
directTemplateLoading: this.pluginOptions.directTemplateLoading,
|
||||
inlineStyleFileExtension: this.pluginOptions.inlineStyleFileExtension,
|
||||
});
|
||||
@ -283,7 +307,7 @@ export class AngularWebpackPlugin {
|
||||
rootNames,
|
||||
host,
|
||||
diagnosticsReporter,
|
||||
resourceLoader,
|
||||
state.resourceLoader,
|
||||
);
|
||||
|
||||
// Set of files used during the unused TypeScript file analysis
|
||||
@ -310,7 +334,7 @@ export class AngularWebpackPlugin {
|
||||
await this.rebuildRequiredFiles(modules, compilation, fileEmitter);
|
||||
|
||||
// Clear out the Webpack compilation to avoid an extra retaining reference
|
||||
resourceLoader?.clearParentCompilation();
|
||||
state.resourceLoader?.clearParentCompilation();
|
||||
|
||||
// Analyze program for unused files
|
||||
if (compilation.errors.length > 0) {
|
||||
@ -325,7 +349,7 @@ export class AngularWebpackPlugin {
|
||||
}
|
||||
|
||||
for (const unused of currentUnused) {
|
||||
if (previousUnused && previousUnused.has(unused)) {
|
||||
if (state.previousUnused?.has(unused)) {
|
||||
continue;
|
||||
}
|
||||
addWarning(
|
||||
@ -334,12 +358,11 @@ export class AngularWebpackPlugin {
|
||||
`Add only entry points to the 'files' or 'include' properties in your tsconfig.`,
|
||||
);
|
||||
}
|
||||
previousUnused = currentUnused;
|
||||
state.previousUnused = currentUnused;
|
||||
});
|
||||
|
||||
// Store file emitter for loader usage
|
||||
emitRegistration.update(fileEmitter);
|
||||
});
|
||||
}
|
||||
|
||||
private registerWithCompilation(compilation: Compilation) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user