angular/packages/compiler-cli/ngcc/src/analysis/ngcc_trait_compiler.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

86 lines
3.1 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 {IncrementalBuild} from '../../../src/ngtsc/incremental/api';
import {NOOP_PERF_RECORDER} from '../../../src/ngtsc/perf';
import {ClassDeclaration, Decorator} from '../../../src/ngtsc/reflection';
import {DecoratorHandler, DtsTransformRegistry, HandlerFlags, Trait, TraitCompiler} from '../../../src/ngtsc/transform';
import {NgccReflectionHost} from '../host/ngcc_host';
import {isDefined} from '../utils';
/**
* Specializes the `TraitCompiler` for ngcc purposes. Mainly, this includes an alternative way of
* scanning for classes to compile using the reflection host's `findClassSymbols`, together with
* support to inject synthetic decorators into the compilation for ad-hoc migrations that ngcc
* performs.
*/
export class NgccTraitCompiler extends TraitCompiler {
constructor(
handlers: DecoratorHandler<unknown, unknown, unknown>[],
private ngccReflector: NgccReflectionHost) {
super(
handlers, ngccReflector, NOOP_PERF_RECORDER, new NoIncrementalBuild(),
/* compileNonExportedClasses */ true, new DtsTransformRegistry());
}
get analyzedFiles(): ts.SourceFile[] { return Array.from(this.fileToClasses.keys()); }
/**
* Analyzes the source file in search for classes to process. For any class that is found in the
* file, a `ClassRecord` is created and the source file is included in the `analyzedFiles` array.
*/
analyzeFile(sf: ts.SourceFile): void {
const ngccClassSymbols = this.ngccReflector.findClassSymbols(sf);
for (const classSymbol of ngccClassSymbols) {
this.analyzeClass(classSymbol.declaration.valueDeclaration, null);
}
return undefined;
}
/**
* Associate a new synthesized decorator, which did not appear in the original source, with a
* given class.
* @param clazz the class to receive the new decorator.
* @param decorator the decorator to inject.
* @param flags optional bitwise flag to influence the compilation of the decorator.
*/
injectSyntheticDecorator(clazz: ClassDeclaration, decorator: Decorator, flags?: HandlerFlags):
Trait<unknown, unknown, unknown>[] {
const migratedTraits = this.detectTraits(clazz, [decorator]);
if (migratedTraits === null) {
return [];
}
for (const trait of migratedTraits) {
this.analyzeTrait(clazz, trait, flags);
}
return migratedTraits;
}
/**
* Returns all decorators that have been recognized for the provided class, including any
* synthetically injected decorators.
* @param clazz the declaration for which the decorators are returned.
*/
getAllDecorators(clazz: ClassDeclaration): Decorator[]|null {
const record = this.recordFor(clazz);
if (record === null) {
return null;
}
return record.traits.map(trait => trait.detected.decorator).filter(isDefined);
}
}
class NoIncrementalBuild implements IncrementalBuild<any> {
priorWorkFor(sf: ts.SourceFile): any[]|null { return null; }
}