Skip to content

Commit

Permalink
fix(@ngtools/webpack): handle promise rejection during Angular progra…
Browse files Browse the repository at this point in the history
…m analyzes

Currently, when `analyzeAsync` promise is rejected, the build fails with an `UnhandledPromiseRejectionWarning`.

Example:
```
(node:69086) UnhandledPromiseRejectionWarning: Error: Cannot use a JavaScript or TypeScript file (/Users/error-testing/src/app/app.component.ts) in a component's styleUrls or templateUrl.
    at WebpackResourceLoader._compile (/Users/error-testing/node_modules/@ngtools/webpack/src/resource_loader.js:107:19)
    at WebpackResourceLoader.get (/Users/error-testing/node_modules/@ngtools/webpack/src/resource_loader.js:266:44)
    at Object.resourceHost.readResource (/Users/error-testing/node_modules/@ngtools/webpack/src/ivy/host.js:48:35)
    at AdapterResourceLoader.preload (file:///Users/error-testing/node_modules/@angular/compiler-cli/bundles/index.js:12177:31)
    at resolveStyleUrl (file:///Users/error-testing/node_modules/@angular/compiler-cli/bundles/index.js:9593:36)
    at file:///Users/error-testing/node_modules/@angular/compiler-cli/bundles/index.js:9621:47
    at Array.map (<anonymous>)
    at ComponentDecoratorHandler.preanalyze (file:///Users/error-testing/node_modules/@angular/compiler-cli/bundles/index.js:9621:29)
    at TraitCompiler.analyzeClass (file:///Users/error-testing/node_modules/@angular/compiler-cli/bundles/index.js:6647:39)
    at visit2 (file:///Users/error-testing/node_modules/@angular/compiler-cli/bundles/index.js:6494:14)
```

With this change we handle such error and also hide stacktraces
```
./src/app/app.module.ts - Error: Module build failed (from ./node_modules/@ngtools/webpack/src/ivy/index.js):
Error: Cannot use a JavaScript or TypeScript file (/Users/error-testing/src/app/app.component.ts) in a component's styleUrls or templateUrl.
    at /Users/error-testing/node_modules/@ngtools/webpack/src/ivy/loader.js:75:34
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
```
  • Loading branch information
alan-agius4 authored and filipesilva committed Dec 2, 2021
1 parent 0e9ef95 commit 10d4ede
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 60 deletions.
4 changes: 3 additions & 1 deletion packages/ngtools/webpack/src/ivy/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ export function angularWebpackLoader(this: LoaderContext<unknown>, content: stri
callback(undefined, resultContent, resultMap);
})
.catch((err) => {
callback(err);
// The below is needed to hide stacktraces from users.
const message = err instanceof Error ? err.message : err;
callback(new Error(message));
});
}

Expand Down
121 changes: 62 additions & 59 deletions packages/ngtools/webpack/src/ivy/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -553,74 +553,77 @@ export class AngularWebpackPlugin {

// Required to support asynchronous resource loading
// Must be done before creating transformers or getting template diagnostics
const pendingAnalysis = angularCompiler.analyzeAsync().then(() => {
this.requiredFilesToEmit.clear();
const pendingAnalysis = angularCompiler
.analyzeAsync()
.then(() => {
this.requiredFilesToEmit.clear();

for (const sourceFile of builder.getSourceFiles()) {
if (sourceFile.isDeclarationFile) {
continue;
}
for (const sourceFile of builder.getSourceFiles()) {
if (sourceFile.isDeclarationFile) {
continue;
}

// Collect sources that are required to be emitted
if (
!ignoreForEmit.has(sourceFile) &&
!angularCompiler.incrementalDriver.safeToSkipEmit(sourceFile)
) {
this.requiredFilesToEmit.add(normalizePath(sourceFile.fileName));
// Collect sources that are required to be emitted
if (
!ignoreForEmit.has(sourceFile) &&
!angularCompiler.incrementalDriver.safeToSkipEmit(sourceFile)
) {
this.requiredFilesToEmit.add(normalizePath(sourceFile.fileName));

// If required to emit, diagnostics may have also changed
if (!ignoreForDiagnostics.has(sourceFile)) {
affectedFiles.add(sourceFile);
}
} else if (
this.sourceFileCache &&
!affectedFiles.has(sourceFile) &&
!ignoreForDiagnostics.has(sourceFile)
) {
// Use cached Angular diagnostics for unchanged and unaffected files
const angularDiagnostics = this.sourceFileCache.getAngularDiagnostics(sourceFile);
if (angularDiagnostics) {
diagnosticsReporter(angularDiagnostics);
// If required to emit, diagnostics may have also changed
if (!ignoreForDiagnostics.has(sourceFile)) {
affectedFiles.add(sourceFile);
}
} else if (
this.sourceFileCache &&
!affectedFiles.has(sourceFile) &&
!ignoreForDiagnostics.has(sourceFile)
) {
// Use cached Angular diagnostics for unchanged and unaffected files
const angularDiagnostics = this.sourceFileCache.getAngularDiagnostics(sourceFile);
if (angularDiagnostics) {
diagnosticsReporter(angularDiagnostics);
}
}
}
}

// Temporary workaround during transition to ESM-only @angular/compiler-cli
// TODO_ESM: This workaround should be removed prior to the final release of v13
// and replaced with only `this.compilerCli.OptimizeFor`.
const OptimizeFor =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.compilerCli as any).OptimizeFor ??
require('@angular/compiler-cli/src/ngtsc/typecheck/api').OptimizeFor;

// Collect new Angular diagnostics for files affected by changes
const optimizeDiagnosticsFor =
affectedFiles.size <= DIAGNOSTICS_AFFECTED_THRESHOLD
? OptimizeFor.SingleFile
: OptimizeFor.WholeProgram;
for (const affectedFile of affectedFiles) {
const angularDiagnostics = angularCompiler.getDiagnosticsForFile(
affectedFile,
optimizeDiagnosticsFor,
);
diagnosticsReporter(angularDiagnostics);
this.sourceFileCache?.updateAngularDiagnostics(affectedFile, angularDiagnostics);
}
// Collect new Angular diagnostics for files affected by changes
const OptimizeFor = this.compilerCli.OptimizeFor;
const optimizeDiagnosticsFor =
affectedFiles.size <= DIAGNOSTICS_AFFECTED_THRESHOLD
? OptimizeFor.SingleFile
: OptimizeFor.WholeProgram;
for (const affectedFile of affectedFiles) {
const angularDiagnostics = angularCompiler.getDiagnosticsForFile(
affectedFile,
optimizeDiagnosticsFor,
);
diagnosticsReporter(angularDiagnostics);
this.sourceFileCache?.updateAngularDiagnostics(affectedFile, angularDiagnostics);
}

return {
emitter: this.createFileEmitter(
builder,
mergeTransformers(angularCompiler.prepareEmit().transformers, transformers),
getDependencies,
(sourceFile) => {
this.requiredFilesToEmit.delete(normalizePath(sourceFile.fileName));
angularCompiler.incrementalDriver.recordSuccessfulEmit(sourceFile);
},
),
};
})
.catch((err) => ({ errorMessage: err instanceof Error ? err.message : `${err}` }));

return this.createFileEmitter(
builder,
mergeTransformers(angularCompiler.prepareEmit().transformers, transformers),
getDependencies,
(sourceFile) => {
this.requiredFilesToEmit.delete(normalizePath(sourceFile.fileName));
angularCompiler.incrementalDriver.recordSuccessfulEmit(sourceFile);
},
);
});
const analyzingFileEmitter: FileEmitter = async (file) => {
const innerFileEmitter = await pendingAnalysis;
const analysis = await pendingAnalysis;

if ('errorMessage' in analysis) {
throw new Error(analysis.errorMessage);
}

return innerFileEmitter(file);
return analysis.emitter(file);
};

return {
Expand Down

0 comments on commit 10d4ede

Please sign in to comment.