Files
angular/packages/compiler-cli/ngcc/src/analysis/migration_host.ts
JoostK 7659f2e24b fix(ngcc): do not attempt compilation when analysis fails (#34889)
In #34288, ngtsc was refactored to separate the result of the analysis
and resolve phase for more granular incremental rebuilds. In this model,
any errors in one phase transition the trait into an error state, which
prevents it from being ran through subsequent phases. The ngcc compiler
on the other hand did not adopt this strict error model, which would
cause incomplete metadata—due to errors in earlier phases—to be offered
for compilation that could result in a hard crash.

This commit updates ngcc to take advantage of ngtsc's `TraitCompiler`,
that internally manages all Ivy classes that are part of the
compilation. This effectively replaces ngcc's own `AnalyzedFile` and
`AnalyzedClass` types, together with all of the logic to drive the
`DecoratorHandler`s. All of this is now handled in the `TraitCompiler`,
benefiting from its explicit state transitions of `Trait`s so that the
ngcc crash is a thing of the past.

Fixes #34500
Resolves FW-1788

PR Close #34889
2020-01-23 14:47:03 -08:00

90 lines
2.9 KiB
TypeScript

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import {AbsoluteFsPath} from '../../../src/ngtsc/file_system';
import {MetadataReader} from '../../../src/ngtsc/metadata';
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
import {ClassDeclaration, Decorator} from '../../../src/ngtsc/reflection';
import {HandlerFlags, TraitState} from '../../../src/ngtsc/transform';
import {NgccReflectionHost} from '../host/ngcc_host';
import {MigrationHost} from '../migrations/migration';
import {NgccTraitCompiler} from './ngcc_trait_compiler';
import {isWithinPackage} from './util';
/**
* The standard implementation of `MigrationHost`, which is created by the `DecorationAnalyzer`.
*/
export class DefaultMigrationHost implements MigrationHost {
constructor(
readonly reflectionHost: NgccReflectionHost, readonly metadata: MetadataReader,
readonly evaluator: PartialEvaluator, private compiler: NgccTraitCompiler,
private entryPointPath: AbsoluteFsPath) {}
injectSyntheticDecorator(clazz: ClassDeclaration, decorator: Decorator, flags?: HandlerFlags):
void {
const migratedTraits = this.compiler.injectSyntheticDecorator(clazz, decorator, flags);
for (const trait of migratedTraits) {
if (trait.state === TraitState.ERRORED) {
trait.diagnostics =
trait.diagnostics.map(diag => createMigrationDiagnostic(diag, clazz, decorator));
}
}
}
getAllDecorators(clazz: ClassDeclaration): Decorator[]|null {
return this.compiler.getAllDecorators(clazz);
}
isInScope(clazz: ClassDeclaration): boolean {
return isWithinPackage(this.entryPointPath, clazz.getSourceFile());
}
}
/**
* Creates a diagnostic from another one, containing additional information about the synthetic
* decorator.
*/
function createMigrationDiagnostic(
diagnostic: ts.Diagnostic, source: ts.Node, decorator: Decorator): ts.Diagnostic {
const clone = {...diagnostic};
const chain: ts.DiagnosticMessageChain[] = [{
messageText: `Occurs for @${decorator.name} decorator inserted by an automatic migration`,
category: ts.DiagnosticCategory.Message,
code: 0,
}];
if (decorator.args !== null) {
const args = decorator.args.map(arg => arg.getText()).join(', ');
chain.push({
messageText: `@${decorator.name}(${args})`,
category: ts.DiagnosticCategory.Message,
code: 0,
});
}
if (typeof clone.messageText === 'string') {
clone.messageText = {
messageText: clone.messageText,
category: diagnostic.category,
code: diagnostic.code,
next: chain,
};
} else {
if (clone.messageText.next === undefined) {
clone.messageText.next = chain;
} else {
clone.messageText.next.push(...chain);
}
}
return clone;
}