refactor(compiler-cli): track a dependency on a default import on `WrappedNodeExpr` (#41557)

Previously, the `DefaultImportRecorder` interface was used as follows:

1. During the analysis phase, the default import declaration of an
   identifier was recorded.

2. During the emit phase each emitted identifier would be recorded.
   The information from step 1 would then be used to determine the
   default import declaration of the identifier which would be
   registered as used.

3. A TypeScript transform would taint all default imports that were
   registered as used in step 2 such that the imports are not elided
   by TypeScript.

In incremental compilations, a file may have to be emitted even if its
analysis data has been reused from the prior compilation. This would
mean that step 1 is not executed, resulting in a mismatch in step 2 and
ultimately in incorrectly eliding the default. This was mitigated by
storing the mapping from identifier to import declaration on the
`ts.SourceFile` instead of a member of `DefaultImportTracker` such that
it would also be visible to the `DefaultImportRecorder` of subsequent
compiles even if step 1 had not been executed.

Ultimately however, the information that is being recorded into the
`DefaultImportRecorder` has a longer lifetime than a single
`DefaultImportRecorder` instance, as that is only valid during a single
compilation whereas the identifier to import declaration mapping
outlives a single compilation. This commit replaces the registration of
this mapping by attaching the default import declaration on the output
AST node that captures the identifier. This enables the removal of
all of the `DefaultImportRecorder` usages throughout the analysis phase
together with the `DefaultImportRecorder` interface itself.

PR Close #41557
This commit is contained in:
JoostK 2021-04-10 22:55:22 +02:00 committed by Zach Arend
parent 7f1651574e
commit 1381301afe
21 changed files with 126 additions and 199 deletions

View File

@ -14,7 +14,7 @@ import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecorato
import {CycleAnalyzer, CycleHandlingStrategy, ImportGraph} from '../../../src/ngtsc/cycles'; import {CycleAnalyzer, CycleHandlingStrategy, ImportGraph} from '../../../src/ngtsc/cycles';
import {isFatalDiagnosticError} from '../../../src/ngtsc/diagnostics'; import {isFatalDiagnosticError} from '../../../src/ngtsc/diagnostics';
import {absoluteFromSourceFile, LogicalFileSystem, ReadonlyFileSystem} from '../../../src/ngtsc/file_system'; import {absoluteFromSourceFile, LogicalFileSystem, ReadonlyFileSystem} from '../../../src/ngtsc/file_system';
import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, PrivateExportAliasingHost, Reexport, ReferenceEmitter} from '../../../src/ngtsc/imports'; import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, PrivateExportAliasingHost, Reexport, ReferenceEmitter} from '../../../src/ngtsc/imports';
import {SemanticSymbol} from '../../../src/ngtsc/incremental/semantic_graph'; import {SemanticSymbol} from '../../../src/ngtsc/incremental/semantic_graph';
import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, ResourceRegistry} from '../../../src/ngtsc/metadata'; import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, ResourceRegistry} from '../../../src/ngtsc/metadata';
import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator'; import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator';
@ -107,8 +107,8 @@ export class DecorationAnalyzer {
/* i18nUseExternalIds */ true, this.bundle.enableI18nLegacyMessageIdFormat, /* i18nUseExternalIds */ true, this.bundle.enableI18nLegacyMessageIdFormat,
/* usePoisonedData */ false, /* usePoisonedData */ false,
/* i18nNormalizeLineEndingsInICUs */ false, this.moduleResolver, this.cycleAnalyzer, /* i18nNormalizeLineEndingsInICUs */ false, this.moduleResolver, this.cycleAnalyzer,
CycleHandlingStrategy.UseRemoteScoping, this.refEmitter, NOOP_DEFAULT_IMPORT_RECORDER, CycleHandlingStrategy.UseRemoteScoping, this.refEmitter, NOOP_DEPENDENCY_TRACKER,
NOOP_DEPENDENCY_TRACKER, this.injectableRegistry, this.injectableRegistry,
/* semanticDepGraphUpdater */ null, !!this.compilerOptions.annotateForClosureCompiler, /* semanticDepGraphUpdater */ null, !!this.compilerOptions.annotateForClosureCompiler,
NOOP_PERF_RECORDER), NOOP_PERF_RECORDER),
@ -116,7 +116,7 @@ export class DecorationAnalyzer {
// clang-format off // clang-format off
new DirectiveDecoratorHandler( new DirectiveDecoratorHandler(
this.reflectionHost, this.evaluator, this.fullRegistry, this.scopeRegistry, this.reflectionHost, this.evaluator, this.fullRegistry, this.scopeRegistry,
this.fullMetaReader, NOOP_DEFAULT_IMPORT_RECORDER, this.injectableRegistry, this.isCore, this.fullMetaReader, this.injectableRegistry, this.isCore,
/* semanticDepGraphUpdater */ null, /* semanticDepGraphUpdater */ null,
!!this.compilerOptions.annotateForClosureCompiler, !!this.compilerOptions.annotateForClosureCompiler,
// In ngcc we want to compile undecorated classes with Angular features. As of // In ngcc we want to compile undecorated classes with Angular features. As of
@ -131,18 +131,17 @@ export class DecorationAnalyzer {
// before injectable factories (so injectable factories can delegate to them) // before injectable factories (so injectable factories can delegate to them)
new PipeDecoratorHandler( new PipeDecoratorHandler(
this.reflectionHost, this.evaluator, this.metaRegistry, this.scopeRegistry, this.reflectionHost, this.evaluator, this.metaRegistry, this.scopeRegistry,
NOOP_DEFAULT_IMPORT_RECORDER, this.injectableRegistry, this.isCore, NOOP_PERF_RECORDER), this.injectableRegistry, this.isCore, NOOP_PERF_RECORDER),
new InjectableDecoratorHandler( new InjectableDecoratorHandler(
this.reflectionHost, NOOP_DEFAULT_IMPORT_RECORDER, this.isCore, this.reflectionHost, this.isCore,
/* strictCtorDeps */ false, this.injectableRegistry, NOOP_PERF_RECORDER, /* strictCtorDeps */ false, this.injectableRegistry, NOOP_PERF_RECORDER,
/* errorOnDuplicateProv */ false), /* errorOnDuplicateProv */ false),
new NgModuleDecoratorHandler( new NgModuleDecoratorHandler(
this.reflectionHost, this.evaluator, this.fullMetaReader, this.fullRegistry, this.reflectionHost, this.evaluator, this.fullMetaReader, this.fullRegistry,
this.scopeRegistry, this.referencesRegistry, this.isCore, /* routeAnalyzer */ null, this.scopeRegistry, this.referencesRegistry, this.isCore, /* routeAnalyzer */ null,
this.refEmitter, this.refEmitter,
/* factoryTracker */ null, NOOP_DEFAULT_IMPORT_RECORDER, /* factoryTracker */ null, !!this.compilerOptions.annotateForClosureCompiler,
!!this.compilerOptions.annotateForClosureCompiler, this.injectableRegistry, this.injectableRegistry, NOOP_PERF_RECORDER),
NOOP_PERF_RECORDER),
]; ];
compiler = new NgccTraitCompiler(this.handlers, this.reflectionHost); compiler = new NgccTraitCompiler(this.handlers, this.reflectionHost);
migrations: Migration[] = [ migrations: Migration[] = [

View File

@ -12,7 +12,7 @@ import * as ts from 'typescript';
import {Cycle, CycleAnalyzer, CycleHandlingStrategy} from '../../cycles'; import {Cycle, CycleAnalyzer, CycleHandlingStrategy} from '../../cycles';
import {ErrorCode, FatalDiagnosticError, makeDiagnostic, makeRelatedInformation} from '../../diagnostics'; import {ErrorCode, FatalDiagnosticError, makeDiagnostic, makeRelatedInformation} from '../../diagnostics';
import {absoluteFrom, relative} from '../../file_system'; import {absoluteFrom, relative} from '../../file_system';
import {DefaultImportRecorder, ImportedFile, ModuleResolver, Reference, ReferenceEmitter} from '../../imports'; import {ImportedFile, ModuleResolver, Reference, ReferenceEmitter} from '../../imports';
import {DependencyTracker} from '../../incremental/api'; import {DependencyTracker} from '../../incremental/api';
import {extractSemanticTypeParameters, isArrayEqual, isReferenceEqual, SemanticDepGraphUpdater, SemanticReference, SemanticSymbol} from '../../incremental/semantic_graph'; import {extractSemanticTypeParameters, isArrayEqual, isReferenceEqual, SemanticDepGraphUpdater, SemanticReference, SemanticSymbol} from '../../incremental/semantic_graph';
import {IndexingContext} from '../../indexer'; import {IndexingContext} from '../../indexer';
@ -205,7 +205,6 @@ export class ComponentDecoratorHandler implements
private i18nNormalizeLineEndingsInICUs: boolean|undefined, private i18nNormalizeLineEndingsInICUs: boolean|undefined,
private moduleResolver: ModuleResolver, private cycleAnalyzer: CycleAnalyzer, private moduleResolver: ModuleResolver, private cycleAnalyzer: CycleAnalyzer,
private cycleHandlingStrategy: CycleHandlingStrategy, private refEmitter: ReferenceEmitter, private cycleHandlingStrategy: CycleHandlingStrategy, private refEmitter: ReferenceEmitter,
private defaultImportRecorder: DefaultImportRecorder,
private depTracker: DependencyTracker|null, private depTracker: DependencyTracker|null,
private injectableRegistry: InjectableClassRegistry, private injectableRegistry: InjectableClassRegistry,
private semanticDepGraphUpdater: SemanticDepGraphUpdater|null, private semanticDepGraphUpdater: SemanticDepGraphUpdater|null,
@ -326,8 +325,8 @@ export class ComponentDecoratorHandler implements
// @Component inherits @Directive, so begin by extracting the @Directive metadata and building // @Component inherits @Directive, so begin by extracting the @Directive metadata and building
// on it. // on it.
const directiveResult = extractDirectiveMetadata( const directiveResult = extractDirectiveMetadata(
node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore, node, decorator, this.reflector, this.evaluator, this.isCore, flags,
flags, this.annotateForClosureCompiler, this.annotateForClosureCompiler,
this.elementSchemaRegistry.getDefaultComponentElementName()); this.elementSchemaRegistry.getDefaultComponentElementName());
if (directiveResult === undefined) { if (directiveResult === undefined) {
// `extractDirectiveMetadata` returns undefined when the @Directive has `jit: true`. In this // `extractDirectiveMetadata` returns undefined when the @Directive has `jit: true`. In this
@ -490,8 +489,7 @@ export class ComponentDecoratorHandler implements
}, },
typeCheckMeta: extractDirectiveTypeCheckMeta(node, inputs, this.reflector), typeCheckMeta: extractDirectiveTypeCheckMeta(node, inputs, this.reflector),
classMetadata: extractClassMetadata( classMetadata: extractClassMetadata(
node, this.reflector, this.defaultImportRecorder, this.isCore, node, this.reflector, this.isCore, this.annotateForClosureCompiler),
this.annotateForClosureCompiler),
template, template,
providersRequiringFactory, providersRequiringFactory,
viewProvidersRequiringFactory, viewProvidersRequiringFactory,

View File

@ -11,7 +11,7 @@ import {emitDistinctChangesOnlyDefaultValue} from '@angular/compiler/src/core';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {DefaultImportRecorder, Reference} from '../../imports'; import {Reference} from '../../imports';
import {areTypeParametersEqual, extractSemanticTypeParameters, isArrayEqual, isSetEqual, isSymbolEqual, SemanticDepGraphUpdater, SemanticSymbol, SemanticTypeParameter} from '../../incremental/semantic_graph'; import {areTypeParametersEqual, extractSemanticTypeParameters, isArrayEqual, isSetEqual, isSymbolEqual, SemanticDepGraphUpdater, SemanticSymbol, SemanticTypeParameter} from '../../incremental/semantic_graph';
import {BindingPropertyName, ClassPropertyMapping, ClassPropertyName, DirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, TemplateGuardMeta} from '../../metadata'; import {BindingPropertyName, ClassPropertyMapping, ClassPropertyName, DirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, TemplateGuardMeta} from '../../metadata';
import {extractDirectiveTypeCheckMeta} from '../../metadata/src/util'; import {extractDirectiveTypeCheckMeta} from '../../metadata/src/util';
@ -177,9 +177,8 @@ export class DirectiveDecoratorHandler implements
constructor( constructor(
private reflector: ReflectionHost, private evaluator: PartialEvaluator, private reflector: ReflectionHost, private evaluator: PartialEvaluator,
private metaRegistry: MetadataRegistry, private scopeRegistry: LocalModuleScopeRegistry, private metaRegistry: MetadataRegistry, private scopeRegistry: LocalModuleScopeRegistry,
private metaReader: MetadataReader, private defaultImportRecorder: DefaultImportRecorder, private metaReader: MetadataReader, private injectableRegistry: InjectableClassRegistry,
private injectableRegistry: InjectableClassRegistry, private isCore: boolean, private isCore: boolean, private semanticDepGraphUpdater: SemanticDepGraphUpdater|null,
private semanticDepGraphUpdater: SemanticDepGraphUpdater|null,
private annotateForClosureCompiler: boolean, private annotateForClosureCompiler: boolean,
private compileUndecoratedClassesWithAngularFeatures: boolean, private perf: PerfRecorder) {} private compileUndecoratedClassesWithAngularFeatures: boolean, private perf: PerfRecorder) {}
@ -215,8 +214,8 @@ export class DirectiveDecoratorHandler implements
this.perf.eventCount(PerfEvent.AnalyzeDirective); this.perf.eventCount(PerfEvent.AnalyzeDirective);
const directiveResult = extractDirectiveMetadata( const directiveResult = extractDirectiveMetadata(
node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore, node, decorator, this.reflector, this.evaluator, this.isCore, flags,
flags, this.annotateForClosureCompiler); this.annotateForClosureCompiler);
if (directiveResult === undefined) { if (directiveResult === undefined) {
return {}; return {};
} }
@ -234,8 +233,7 @@ export class DirectiveDecoratorHandler implements
outputs: directiveResult.outputs, outputs: directiveResult.outputs,
meta: analysis, meta: analysis,
classMetadata: extractClassMetadata( classMetadata: extractClassMetadata(
node, this.reflector, this.defaultImportRecorder, this.isCore, node, this.reflector, this.isCore, this.annotateForClosureCompiler),
this.annotateForClosureCompiler),
baseClass: readBaseClass(node, this.reflector, this.evaluator), baseClass: readBaseClass(node, this.reflector, this.evaluator),
typeCheckMeta: extractDirectiveTypeCheckMeta(node, directiveResult.inputs, this.reflector), typeCheckMeta: extractDirectiveTypeCheckMeta(node, directiveResult.inputs, this.reflector),
providersRequiringFactory, providersRequiringFactory,
@ -351,9 +349,8 @@ export class DirectiveDecoratorHandler implements
*/ */
export function extractDirectiveMetadata( export function extractDirectiveMetadata(
clazz: ClassDeclaration, decorator: Readonly<Decorator|null>, reflector: ReflectionHost, clazz: ClassDeclaration, decorator: Readonly<Decorator|null>, reflector: ReflectionHost,
evaluator: PartialEvaluator, defaultImportRecorder: DefaultImportRecorder, isCore: boolean, evaluator: PartialEvaluator, isCore: boolean, flags: HandlerFlags,
flags: HandlerFlags, annotateForClosureCompiler: boolean, annotateForClosureCompiler: boolean, defaultSelector: string|null = null): {
defaultSelector: string|null = null): {
decorator: Map<string, ts.Expression>, decorator: Map<string, ts.Expression>,
metadata: R3DirectiveMetadata, metadata: R3DirectiveMetadata,
inputs: ClassPropertyMapping, inputs: ClassPropertyMapping,
@ -473,7 +470,7 @@ export function extractDirectiveMetadata(
exportAs = resolved.split(',').map(part => part.trim()); exportAs = resolved.split(',').map(part => part.trim());
} }
const rawCtorDeps = getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore); const rawCtorDeps = getConstructorDependencies(clazz, reflector, isCore);
// Non-abstract directives (those with a selector) require valid constructor dependencies, whereas // Non-abstract directives (those with a selector) require valid constructor dependencies, whereas
// abstract directives are allowed to have invalid dependencies, given that a subclass may call // abstract directives are allowed to have invalid dependencies, given that a subclass may call

View File

@ -10,7 +10,6 @@ import {compileClassMetadata, CompileClassMetadataFn, compileDeclareClassMetadat
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {DefaultImportRecorder} from '../../imports';
import {InjectableClassRegistry} from '../../metadata'; import {InjectableClassRegistry} from '../../metadata';
import {PerfEvent, PerfRecorder} from '../../perf'; import {PerfEvent, PerfRecorder} from '../../perf';
import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
@ -33,8 +32,7 @@ export interface InjectableHandlerData {
export class InjectableDecoratorHandler implements export class InjectableDecoratorHandler implements
DecoratorHandler<Decorator, InjectableHandlerData, null, unknown> { DecoratorHandler<Decorator, InjectableHandlerData, null, unknown> {
constructor( constructor(
private reflector: ReflectionHost, private defaultImportRecorder: DefaultImportRecorder, private reflector: ReflectionHost, private isCore: boolean, private strictCtorDeps: boolean,
private isCore: boolean, private strictCtorDeps: boolean,
private injectableRegistry: InjectableClassRegistry, private perf: PerfRecorder, private injectableRegistry: InjectableClassRegistry, private perf: PerfRecorder,
/** /**
* What to do if the injectable already contains a ɵprov property. * What to do if the injectable already contains a ɵprov property.
@ -74,10 +72,8 @@ export class InjectableDecoratorHandler implements
analysis: { analysis: {
meta, meta,
ctorDeps: extractInjectableCtorDeps( ctorDeps: extractInjectableCtorDeps(
node, meta, decorator, this.reflector, this.defaultImportRecorder, this.isCore, node, meta, decorator, this.reflector, this.isCore, this.strictCtorDeps),
this.strictCtorDeps), classMetadata: extractClassMetadata(node, this.reflector, this.isCore),
classMetadata:
extractClassMetadata(node, this.reflector, this.defaultImportRecorder, this.isCore),
// Avoid generating multiple factories if a class has // Avoid generating multiple factories if a class has
// more Angular decorators, apart from Injectable. // more Angular decorators, apart from Injectable.
needsFactory: !decorators || needsFactory: !decorators ||
@ -232,8 +228,7 @@ function getProviderExpression(
function extractInjectableCtorDeps( function extractInjectableCtorDeps(
clazz: ClassDeclaration, meta: R3InjectableMetadata, decorator: Decorator, clazz: ClassDeclaration, meta: R3InjectableMetadata, decorator: Decorator,
reflector: ReflectionHost, defaultImportRecorder: DefaultImportRecorder, isCore: boolean, reflector: ReflectionHost, isCore: boolean, strictCtorDeps: boolean) {
strictCtorDeps: boolean) {
if (decorator.args === null) { if (decorator.args === null) {
throw new FatalDiagnosticError( throw new FatalDiagnosticError(
ErrorCode.DECORATOR_NOT_CALLED, Decorator.nodeForError(decorator), ErrorCode.DECORATOR_NOT_CALLED, Decorator.nodeForError(decorator),
@ -252,15 +247,15 @@ function extractInjectableCtorDeps(
// constructor signature does not work for DI then a factory definition (ɵfac) that throws is // constructor signature does not work for DI then a factory definition (ɵfac) that throws is
// generated. // generated.
if (strictCtorDeps) { if (strictCtorDeps) {
ctorDeps = getValidConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore); ctorDeps = getValidConstructorDependencies(clazz, reflector, isCore);
} else { } else {
ctorDeps = unwrapConstructorDependencies( ctorDeps =
getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore)); unwrapConstructorDependencies(getConstructorDependencies(clazz, reflector, isCore));
} }
return ctorDeps; return ctorDeps;
} else if (decorator.args.length === 1) { } else if (decorator.args.length === 1) {
const rawCtorDeps = getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore); const rawCtorDeps = getConstructorDependencies(clazz, reflector, isCore);
if (strictCtorDeps && meta.useValue === undefined && meta.useExisting === undefined && if (strictCtorDeps && meta.useValue === undefined && meta.useExisting === undefined &&
meta.useClass === undefined && meta.useFactory === undefined) { meta.useClass === undefined && meta.useFactory === undefined) {

View File

@ -9,7 +9,6 @@
import {Expression, FunctionExpr, LiteralArrayExpr, LiteralExpr, literalMap, R3ClassMetadata, ReturnStatement, WrappedNodeExpr} from '@angular/compiler'; import {Expression, FunctionExpr, LiteralArrayExpr, LiteralExpr, literalMap, R3ClassMetadata, ReturnStatement, WrappedNodeExpr} from '@angular/compiler';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {DefaultImportRecorder} from '../../imports';
import {CtorParameter, DeclarationNode, Decorator, ReflectionHost, TypeValueReferenceKind} from '../../reflection'; import {CtorParameter, DeclarationNode, Decorator, ReflectionHost, TypeValueReferenceKind} from '../../reflection';
import {valueReferenceToExpression, wrapFunctionExpressionsInParens} from './util'; import {valueReferenceToExpression, wrapFunctionExpressionsInParens} from './util';
@ -23,8 +22,7 @@ import {valueReferenceToExpression, wrapFunctionExpressionsInParens} from './uti
* as a `Statement` for inclusion along with the class. * as a `Statement` for inclusion along with the class.
*/ */
export function extractClassMetadata( export function extractClassMetadata(
clazz: DeclarationNode, reflection: ReflectionHost, clazz: DeclarationNode, reflection: ReflectionHost, isCore: boolean,
defaultImportRecorder: DefaultImportRecorder, isCore: boolean,
annotateForClosureCompiler?: boolean): R3ClassMetadata|null { annotateForClosureCompiler?: boolean): R3ClassMetadata|null {
if (!reflection.isClass(clazz)) { if (!reflection.isClass(clazz)) {
return null; return null;
@ -55,8 +53,7 @@ export function extractClassMetadata(
let metaCtorParameters: Expression|null = null; let metaCtorParameters: Expression|null = null;
const classCtorParameters = reflection.getConstructorParameters(clazz); const classCtorParameters = reflection.getConstructorParameters(clazz);
if (classCtorParameters !== null) { if (classCtorParameters !== null) {
const ctorParameters = classCtorParameters.map( const ctorParameters = classCtorParameters.map(param => ctorParameterToMetadata(param, isCore));
param => ctorParameterToMetadata(param, defaultImportRecorder, isCore));
metaCtorParameters = new FunctionExpr([], [ metaCtorParameters = new FunctionExpr([], [
new ReturnStatement(new LiteralArrayExpr(ctorParameters)), new ReturnStatement(new LiteralArrayExpr(ctorParameters)),
]); ]);
@ -93,13 +90,11 @@ export function extractClassMetadata(
/** /**
* Convert a reflected constructor parameter to metadata. * Convert a reflected constructor parameter to metadata.
*/ */
function ctorParameterToMetadata( function ctorParameterToMetadata(param: CtorParameter, isCore: boolean): Expression {
param: CtorParameter, defaultImportRecorder: DefaultImportRecorder,
isCore: boolean): Expression {
// Parameters sometimes have a type that can be referenced. If so, then use it, otherwise // Parameters sometimes have a type that can be referenced. If so, then use it, otherwise
// its type is undefined. // its type is undefined.
const type = param.typeValueReference.kind !== TypeValueReferenceKind.UNAVAILABLE ? const type = param.typeValueReference.kind !== TypeValueReferenceKind.UNAVAILABLE ?
valueReferenceToExpression(param.typeValueReference, defaultImportRecorder) : valueReferenceToExpression(param.typeValueReference) :
new LiteralExpr(undefined); new LiteralExpr(undefined);
const mapEntries: {key: string, value: Expression, quoted: false}[] = [ const mapEntries: {key: string, value: Expression, quoted: false}[] = [

View File

@ -10,7 +10,7 @@ import {compileClassMetadata, compileDeclareClassMetadata, compileDeclareInjecto
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError, makeDiagnostic, makeRelatedInformation} from '../../diagnostics'; import {ErrorCode, FatalDiagnosticError, makeDiagnostic, makeRelatedInformation} from '../../diagnostics';
import {DefaultImportRecorder, Reference, ReferenceEmitter} from '../../imports'; import {Reference, ReferenceEmitter} from '../../imports';
import {isArrayEqual, isReferenceEqual, isSymbolEqual, SemanticReference, SemanticSymbol} from '../../incremental/semantic_graph'; import {isArrayEqual, isReferenceEqual, isSymbolEqual, SemanticReference, SemanticSymbol} from '../../incremental/semantic_graph';
import {InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata'; import {InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata';
import {PartialEvaluator, ResolvedValue} from '../../partial_evaluator'; import {PartialEvaluator, ResolvedValue} from '../../partial_evaluator';
@ -130,9 +130,7 @@ export class NgModuleDecoratorHandler implements
private scopeRegistry: LocalModuleScopeRegistry, private scopeRegistry: LocalModuleScopeRegistry,
private referencesRegistry: ReferencesRegistry, private isCore: boolean, private referencesRegistry: ReferencesRegistry, private isCore: boolean,
private routeAnalyzer: NgModuleRouteAnalyzer|null, private refEmitter: ReferenceEmitter, private routeAnalyzer: NgModuleRouteAnalyzer|null, private refEmitter: ReferenceEmitter,
private factoryTracker: FactoryTracker|null, private factoryTracker: FactoryTracker|null, private annotateForClosureCompiler: boolean,
private defaultImportRecorder: DefaultImportRecorder,
private annotateForClosureCompiler: boolean,
private injectableRegistry: InjectableClassRegistry, private perf: PerfRecorder, private injectableRegistry: InjectableClassRegistry, private perf: PerfRecorder,
private localeId?: string) {} private localeId?: string) {}
@ -350,8 +348,7 @@ export class NgModuleDecoratorHandler implements
type, type,
internalType, internalType,
typeArgumentCount: 0, typeArgumentCount: 0,
deps: getValidConstructorDependencies( deps: getValidConstructorDependencies(node, this.reflector, this.isCore),
node, this.reflector, this.defaultImportRecorder, this.isCore),
target: FactoryTarget.NgModule, target: FactoryTarget.NgModule,
}; };
@ -371,8 +368,7 @@ export class NgModuleDecoratorHandler implements
resolveProvidersRequiringFactory(rawProviders, this.reflector, this.evaluator) : resolveProvidersRequiringFactory(rawProviders, this.reflector, this.evaluator) :
null, null,
classMetadata: extractClassMetadata( classMetadata: extractClassMetadata(
node, this.reflector, this.defaultImportRecorder, this.isCore, node, this.reflector, this.isCore, this.annotateForClosureCompiler),
this.annotateForClosureCompiler),
factorySymbolName: node.name.text, factorySymbolName: node.name.text,
}, },
}; };

View File

@ -10,7 +10,7 @@ import {compileClassMetadata, compileDeclareClassMetadata, compileDeclarePipeFro
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {DefaultImportRecorder, Reference} from '../../imports'; import {Reference} from '../../imports';
import {SemanticSymbol} from '../../incremental/semantic_graph'; import {SemanticSymbol} from '../../incremental/semantic_graph';
import {InjectableClassRegistry, MetadataRegistry} from '../../metadata'; import {InjectableClassRegistry, MetadataRegistry} from '../../metadata';
import {PartialEvaluator} from '../../partial_evaluator'; import {PartialEvaluator} from '../../partial_evaluator';
@ -55,7 +55,6 @@ export class PipeDecoratorHandler implements
constructor( constructor(
private reflector: ReflectionHost, private evaluator: PartialEvaluator, private reflector: ReflectionHost, private evaluator: PartialEvaluator,
private metaRegistry: MetadataRegistry, private scopeRegistry: LocalModuleScopeRegistry, private metaRegistry: MetadataRegistry, private scopeRegistry: LocalModuleScopeRegistry,
private defaultImportRecorder: DefaultImportRecorder,
private injectableRegistry: InjectableClassRegistry, private isCore: boolean, private injectableRegistry: InjectableClassRegistry, private isCore: boolean,
private perf: PerfRecorder) {} private perf: PerfRecorder) {}
@ -131,12 +130,10 @@ export class PipeDecoratorHandler implements
internalType, internalType,
typeArgumentCount: this.reflector.getGenericArityOfClass(clazz) || 0, typeArgumentCount: this.reflector.getGenericArityOfClass(clazz) || 0,
pipeName, pipeName,
deps: getValidConstructorDependencies( deps: getValidConstructorDependencies(clazz, this.reflector, this.isCore),
clazz, this.reflector, this.defaultImportRecorder, this.isCore),
pure, pure,
}, },
classMetadata: classMetadata: extractClassMetadata(clazz, this.reflector, this.isCore),
extractClassMetadata(clazz, this.reflector, this.defaultImportRecorder, this.isCore),
}, },
}; };
} }

View File

@ -12,7 +12,8 @@ import {FactoryTarget} from '@angular/compiler/src/render3/partial/api';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ErrorCode, FatalDiagnosticError, makeDiagnostic, makeRelatedInformation} from '../../diagnostics'; import {ErrorCode, FatalDiagnosticError, makeDiagnostic, makeRelatedInformation} from '../../diagnostics';
import {DefaultImportRecorder, ImportFlags, Reference, ReferenceEmitter} from '../../imports'; import {ImportFlags, Reference, ReferenceEmitter} from '../../imports';
import {attachDefaultImportDeclaration} from '../../imports/src/default';
import {ForeignFunctionResolver, PartialEvaluator} from '../../partial_evaluator'; import {ForeignFunctionResolver, PartialEvaluator} from '../../partial_evaluator';
import {ClassDeclaration, CtorParameter, Decorator, Import, ImportedTypeValueReference, isNamedClassDeclaration, LocalTypeValueReference, ReflectionHost, TypeValueReference, TypeValueReferenceKind, UnavailableValue, ValueUnavailableKind} from '../../reflection'; import {ClassDeclaration, CtorParameter, Decorator, Import, ImportedTypeValueReference, isNamedClassDeclaration, LocalTypeValueReference, ReflectionHost, TypeValueReference, TypeValueReferenceKind, UnavailableValue, ValueUnavailableKind} from '../../reflection';
import {DeclarationData} from '../../scope'; import {DeclarationData} from '../../scope';
@ -32,8 +33,7 @@ export interface ConstructorDepError {
} }
export function getConstructorDependencies( export function getConstructorDependencies(
clazz: ClassDeclaration, reflector: ReflectionHost, clazz: ClassDeclaration, reflector: ReflectionHost, isCore: boolean): ConstructorDeps|null {
defaultImportRecorder: DefaultImportRecorder, isCore: boolean): ConstructorDeps|null {
const deps: R3DependencyMetadata[] = []; const deps: R3DependencyMetadata[] = [];
const errors: ConstructorDepError[] = []; const errors: ConstructorDepError[] = [];
let ctorParams = reflector.getConstructorParameters(clazz); let ctorParams = reflector.getConstructorParameters(clazz);
@ -45,7 +45,7 @@ export function getConstructorDependencies(
} }
} }
ctorParams.forEach((param, idx) => { ctorParams.forEach((param, idx) => {
let token = valueReferenceToExpression(param.typeValueReference, defaultImportRecorder); let token = valueReferenceToExpression(param.typeValueReference);
let attributeNameType: Expression|null = null; let attributeNameType: Expression|null = null;
let optional = false, self = false, skipSelf = false, host = false; let optional = false, self = false, skipSelf = false, host = false;
@ -115,22 +115,18 @@ export function getConstructorDependencies(
* references are converted to an `ExternalExpr`. Note that this is only valid in the context of the * references are converted to an `ExternalExpr`. Note that this is only valid in the context of the
* file in which the `TypeValueReference` originated. * file in which the `TypeValueReference` originated.
*/ */
export function valueReferenceToExpression( export function valueReferenceToExpression(valueRef: LocalTypeValueReference|
valueRef: LocalTypeValueReference|ImportedTypeValueReference, ImportedTypeValueReference): Expression;
defaultImportRecorder: DefaultImportRecorder): Expression; export function valueReferenceToExpression(valueRef: TypeValueReference): Expression|null;
export function valueReferenceToExpression( export function valueReferenceToExpression(valueRef: TypeValueReference): Expression|null {
valueRef: TypeValueReference, defaultImportRecorder: DefaultImportRecorder): Expression|null;
export function valueReferenceToExpression(
valueRef: TypeValueReference, defaultImportRecorder: DefaultImportRecorder): Expression|null {
if (valueRef.kind === TypeValueReferenceKind.UNAVAILABLE) { if (valueRef.kind === TypeValueReferenceKind.UNAVAILABLE) {
return null; return null;
} else if (valueRef.kind === TypeValueReferenceKind.LOCAL) { } else if (valueRef.kind === TypeValueReferenceKind.LOCAL) {
if (defaultImportRecorder !== null && valueRef.defaultImportStatement !== null && const expr = new WrappedNodeExpr(valueRef.expression);
ts.isIdentifier(valueRef.expression)) { if (valueRef.defaultImportStatement !== null) {
defaultImportRecorder.recordImportedIdentifier( attachDefaultImportDeclaration(expr, valueRef.defaultImportStatement);
valueRef.expression, valueRef.defaultImportStatement);
} }
return new WrappedNodeExpr(valueRef.expression); return expr;
} else { } else {
let importExpr: Expression = let importExpr: Expression =
new ExternalExpr({moduleName: valueRef.moduleName, name: valueRef.importedName}); new ExternalExpr({moduleName: valueRef.moduleName, name: valueRef.importedName});
@ -163,10 +159,10 @@ export function unwrapConstructorDependencies(deps: ConstructorDeps|null): R3Dep
} }
export function getValidConstructorDependencies( export function getValidConstructorDependencies(
clazz: ClassDeclaration, reflector: ReflectionHost, clazz: ClassDeclaration, reflector: ReflectionHost, isCore: boolean): R3DependencyMetadata[]|
defaultImportRecorder: DefaultImportRecorder, isCore: boolean): R3DependencyMetadata[]|null { null {
return validateConstructorDependencies( return validateConstructorDependencies(
clazz, getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore)); clazz, getConstructorDependencies(clazz, reflector, isCore));
} }
/** /**

View File

@ -13,7 +13,7 @@ import {CycleAnalyzer, CycleHandlingStrategy, ImportGraph} from '../../cycles';
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
import {absoluteFrom} from '../../file_system'; import {absoluteFrom} from '../../file_system';
import {runInEachFileSystem} from '../../file_system/testing'; import {runInEachFileSystem} from '../../file_system/testing';
import {ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports'; import {ModuleResolver, ReferenceEmitter} from '../../imports';
import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, ResourceRegistry} from '../../metadata'; import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, ResourceRegistry} from '../../metadata';
import {PartialEvaluator} from '../../partial_evaluator'; import {PartialEvaluator} from '../../partial_evaluator';
import {NOOP_PERF_RECORDER} from '../../perf'; import {NOOP_PERF_RECORDER} from '../../perf';
@ -81,7 +81,6 @@ function setup(program: ts.Program, options: ts.CompilerOptions, host: ts.Compil
cycleAnalyzer, cycleAnalyzer,
CycleHandlingStrategy.UseRemoteScoping, CycleHandlingStrategy.UseRemoteScoping,
refEmitter, refEmitter,
NOOP_DEFAULT_IMPORT_RECORDER,
/* depTracker */ null, /* depTracker */ null,
injectableRegistry, injectableRegistry,
/* semanticDepGraphUpdater */ null, /* semanticDepGraphUpdater */ null,

View File

@ -10,7 +10,7 @@ import * as ts from 'typescript';
import {absoluteFrom} from '../../file_system'; import {absoluteFrom} from '../../file_system';
import {runInEachFileSystem} from '../../file_system/testing'; import {runInEachFileSystem} from '../../file_system/testing';
import {NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports'; import {ReferenceEmitter} from '../../imports';
import {DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata'; import {DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata';
import {PartialEvaluator} from '../../partial_evaluator'; import {PartialEvaluator} from '../../partial_evaluator';
import {NOOP_PERF_RECORDER} from '../../perf'; import {NOOP_PERF_RECORDER} from '../../perf';
@ -168,8 +168,8 @@ runInEachFileSystem(() => {
null); null);
const injectableRegistry = new InjectableClassRegistry(reflectionHost); const injectableRegistry = new InjectableClassRegistry(reflectionHost);
const handler = new DirectiveDecoratorHandler( const handler = new DirectiveDecoratorHandler(
reflectionHost, evaluator, scopeRegistry, scopeRegistry, metaReader, reflectionHost, evaluator, scopeRegistry, scopeRegistry, metaReader, injectableRegistry,
NOOP_DEFAULT_IMPORT_RECORDER, injectableRegistry, /*isCore*/ false, /*isCore*/ false,
/*semanticDepGraphUpdater*/ null, /*semanticDepGraphUpdater*/ null,
/*annotateForClosureCompiler*/ false, /*annotateForClosureCompiler*/ false,
/*detectUndecoratedClassesWithAngularFeatures*/ false, NOOP_PERF_RECORDER); /*detectUndecoratedClassesWithAngularFeatures*/ false, NOOP_PERF_RECORDER);

View File

@ -8,7 +8,6 @@
import {ErrorCode, FatalDiagnosticError, ngErrorCode} from '../../diagnostics'; import {ErrorCode, FatalDiagnosticError, ngErrorCode} from '../../diagnostics';
import {absoluteFrom} from '../../file_system'; import {absoluteFrom} from '../../file_system';
import {runInEachFileSystem} from '../../file_system/testing'; import {runInEachFileSystem} from '../../file_system/testing';
import {NOOP_DEFAULT_IMPORT_RECORDER} from '../../imports';
import {InjectableClassRegistry} from '../../metadata'; import {InjectableClassRegistry} from '../../metadata';
import {NOOP_PERF_RECORDER} from '../../perf'; import {NOOP_PERF_RECORDER} from '../../perf';
import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection';
@ -70,7 +69,7 @@ function setupHandler(errorOnDuplicateProv: boolean) {
const reflectionHost = new TypeScriptReflectionHost(checker); const reflectionHost = new TypeScriptReflectionHost(checker);
const injectableRegistry = new InjectableClassRegistry(reflectionHost); const injectableRegistry = new InjectableClassRegistry(reflectionHost);
const handler = new InjectableDecoratorHandler( const handler = new InjectableDecoratorHandler(
reflectionHost, NOOP_DEFAULT_IMPORT_RECORDER, /* isCore */ false, reflectionHost, /* isCore */ false,
/* strictCtorDeps */ false, injectableRegistry, NOOP_PERF_RECORDER, errorOnDuplicateProv); /* strictCtorDeps */ false, injectableRegistry, NOOP_PERF_RECORDER, errorOnDuplicateProv);
const TestClass = getDeclaration(program, ENTRY_FILE, 'TestClass', isNamedClassDeclaration); const TestClass = getDeclaration(program, ENTRY_FILE, 'TestClass', isNamedClassDeclaration);
const ɵprov = reflectionHost.getMembersOfClass(TestClass).find(member => member.name === 'ɵprov'); const ɵprov = reflectionHost.getMembersOfClass(TestClass).find(member => member.name === 'ɵprov');

View File

@ -10,7 +10,7 @@ import * as ts from 'typescript';
import {absoluteFrom, getSourceFileOrError} from '../../file_system'; import {absoluteFrom, getSourceFileOrError} from '../../file_system';
import {runInEachFileSystem, TestFile} from '../../file_system/testing'; import {runInEachFileSystem, TestFile} from '../../file_system/testing';
import {NOOP_DEFAULT_IMPORT_RECORDER, NoopImportRewriter} from '../../imports'; import {NoopImportRewriter} from '../../imports';
import {TypeScriptReflectionHost} from '../../reflection'; import {TypeScriptReflectionHost} from '../../reflection';
import {getDeclaration, makeProgram} from '../../testing'; import {getDeclaration, makeProgram} from '../../testing';
import {ImportManager, translateStatement} from '../../translator'; import {ImportManager, translateStatement} from '../../translator';
@ -128,7 +128,7 @@ runInEachFileSystem(() => {
{target: ts.ScriptTarget.ES2015}); {target: ts.ScriptTarget.ES2015});
const host = new TypeScriptReflectionHost(program.getTypeChecker()); const host = new TypeScriptReflectionHost(program.getTypeChecker());
const target = getDeclaration(program, _('/index.ts'), 'Target', ts.isClassDeclaration); const target = getDeclaration(program, _('/index.ts'), 'Target', ts.isClassDeclaration);
const call = extractClassMetadata(target, host, NOOP_DEFAULT_IMPORT_RECORDER, false); const call = extractClassMetadata(target, host, false);
if (call === null) { if (call === null) {
return ''; return '';
} }

View File

@ -11,7 +11,7 @@ import * as ts from 'typescript';
import {absoluteFrom} from '../../file_system'; import {absoluteFrom} from '../../file_system';
import {runInEachFileSystem} from '../../file_system/testing'; import {runInEachFileSystem} from '../../file_system/testing';
import {LocalIdentifierStrategy, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports'; import {LocalIdentifierStrategy, ReferenceEmitter} from '../../imports';
import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata'; import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata';
import {PartialEvaluator} from '../../partial_evaluator'; import {PartialEvaluator} from '../../partial_evaluator';
import {NOOP_PERF_RECORDER} from '../../perf'; import {NOOP_PERF_RECORDER} from '../../perf';
@ -72,8 +72,7 @@ runInEachFileSystem(() => {
const handler = new NgModuleDecoratorHandler( const handler = new NgModuleDecoratorHandler(
reflectionHost, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, reflectionHost, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry,
/* isCore */ false, /* routeAnalyzer */ null, refEmitter, /* factoryTracker */ null, /* isCore */ false, /* routeAnalyzer */ null, refEmitter, /* factoryTracker */ null,
NOOP_DEFAULT_IMPORT_RECORDER, /* annotateForClosureCompiler */ false, injectableRegistry, /* annotateForClosureCompiler */ false, injectableRegistry, NOOP_PERF_RECORDER);
NOOP_PERF_RECORDER);
const TestModule = const TestModule =
getDeclaration(program, _('/entry.ts'), 'TestModule', isNamedClassDeclaration); getDeclaration(program, _('/entry.ts'), 'TestModule', isNamedClassDeclaration);
const detected = const detected =

View File

@ -52,7 +52,6 @@ interface LazyCompilationState {
routeAnalyzer: NgModuleRouteAnalyzer; routeAnalyzer: NgModuleRouteAnalyzer;
dtsTransforms: DtsTransformRegistry; dtsTransforms: DtsTransformRegistry;
mwpScanner: ModuleWithProvidersScanner; mwpScanner: ModuleWithProvidersScanner;
defaultImportTracker: DefaultImportTracker;
aliasingHost: AliasingHost|null; aliasingHost: AliasingHost|null;
refEmitter: ReferenceEmitter; refEmitter: ReferenceEmitter;
templateTypeChecker: TemplateTypeChecker; templateTypeChecker: TemplateTypeChecker;
@ -613,13 +612,14 @@ export class NgCompiler {
importRewriter = new NoopImportRewriter(); importRewriter = new NoopImportRewriter();
} }
const defaultImportTracker = new DefaultImportTracker();
const before = [ const before = [
ivyTransformFactory( ivyTransformFactory(
compilation.traitCompiler, compilation.reflector, importRewriter, compilation.traitCompiler, compilation.reflector, importRewriter, defaultImportTracker,
compilation.defaultImportTracker, this.delegatingPerfRecorder, compilation.isCore, this.delegatingPerfRecorder, compilation.isCore, this.closureCompilerEnabled),
this.closureCompilerEnabled),
aliasTransformFactory(compilation.traitCompiler.exportStatements), aliasTransformFactory(compilation.traitCompiler.exportStatements),
compilation.defaultImportTracker.importPreservingTransformer(), defaultImportTracker.importPreservingTransformer(),
]; ];
const afterDeclarations: ts.TransformerFactory<ts.SourceFile>[] = []; const afterDeclarations: ts.TransformerFactory<ts.SourceFile>[] = [];
@ -971,7 +971,6 @@ export class NgCompiler {
const isCore = isAngularCorePackage(this.inputProgram); const isCore = isAngularCorePackage(this.inputProgram);
const defaultImportTracker = new DefaultImportTracker();
const resourceRegistry = new ResourceRegistry(); const resourceRegistry = new ResourceRegistry();
const compilationMode = const compilationMode =
@ -993,16 +992,15 @@ export class NgCompiler {
this.options.i18nUseExternalIds !== false, this.options.i18nUseExternalIds !== false,
this.options.enableI18nLegacyMessageIdFormat !== false, this.usePoisonedData, this.options.enableI18nLegacyMessageIdFormat !== false, this.usePoisonedData,
this.options.i18nNormalizeLineEndingsInICUs, this.moduleResolver, this.cycleAnalyzer, this.options.i18nNormalizeLineEndingsInICUs, this.moduleResolver, this.cycleAnalyzer,
cycleHandlingStrategy, refEmitter, defaultImportTracker, this.incrementalDriver.depGraph, cycleHandlingStrategy, refEmitter, this.incrementalDriver.depGraph, injectableRegistry,
injectableRegistry, semanticDepGraphUpdater, this.closureCompilerEnabled, semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder),
this.delegatingPerfRecorder),
// TODO(alxhub): understand why the cast here is necessary (something to do with `null` // TODO(alxhub): understand why the cast here is necessary (something to do with `null`
// not being assignable to `unknown` when wrapped in `Readonly`). // not being assignable to `unknown` when wrapped in `Readonly`).
// clang-format off // clang-format off
new DirectiveDecoratorHandler( new DirectiveDecoratorHandler(
reflector, evaluator, metaRegistry, scopeRegistry, metaReader, reflector, evaluator, metaRegistry, scopeRegistry, metaReader,
defaultImportTracker, injectableRegistry, isCore, semanticDepGraphUpdater, injectableRegistry, isCore, semanticDepGraphUpdater,
this.closureCompilerEnabled, compileUndecoratedClassesWithAngularFeatures, this.closureCompilerEnabled, compileUndecoratedClassesWithAngularFeatures,
this.delegatingPerfRecorder, this.delegatingPerfRecorder,
) as Readonly<DecoratorHandler<unknown, unknown, SemanticSymbol | null,unknown>>, ) as Readonly<DecoratorHandler<unknown, unknown, SemanticSymbol | null,unknown>>,
@ -1010,16 +1008,15 @@ export class NgCompiler {
// Pipe handler must be before injectable handler in list so pipe factories are printed // Pipe handler must be before injectable handler in list so pipe factories are printed
// before injectable factories (so injectable factories can delegate to them) // before injectable factories (so injectable factories can delegate to them)
new PipeDecoratorHandler( new PipeDecoratorHandler(
reflector, evaluator, metaRegistry, scopeRegistry, defaultImportTracker, reflector, evaluator, metaRegistry, scopeRegistry, injectableRegistry, isCore,
injectableRegistry, isCore, this.delegatingPerfRecorder), this.delegatingPerfRecorder),
new InjectableDecoratorHandler( new InjectableDecoratorHandler(
reflector, defaultImportTracker, isCore, this.options.strictInjectionParameters || false, reflector, isCore, this.options.strictInjectionParameters || false, injectableRegistry,
injectableRegistry, this.delegatingPerfRecorder), this.delegatingPerfRecorder),
new NgModuleDecoratorHandler( new NgModuleDecoratorHandler(
reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore,
routeAnalyzer, refEmitter, this.adapter.factoryTracker, defaultImportTracker, routeAnalyzer, refEmitter, this.adapter.factoryTracker, this.closureCompilerEnabled,
this.closureCompilerEnabled, injectableRegistry, this.delegatingPerfRecorder, injectableRegistry, this.delegatingPerfRecorder, this.options.i18nInLocale),
this.options.i18nInLocale),
]; ];
const traitCompiler = new TraitCompiler( const traitCompiler = new TraitCompiler(
@ -1051,7 +1048,6 @@ export class NgCompiler {
mwpScanner, mwpScanner,
metaReader, metaReader,
typeCheckScopeRegistry, typeCheckScopeRegistry,
defaultImportTracker,
aliasingHost, aliasingHost,
refEmitter, refEmitter,
templateTypeChecker, templateTypeChecker,

View File

@ -167,8 +167,6 @@ It consists of two mechanisms:
1. A `DefaultImportTracker`, which records information about both default imports encountered in the program as well as usages of those imports added during compilation. 1. A `DefaultImportTracker`, which records information about both default imports encountered in the program as well as usages of those imports added during compilation.
A `DefaultImportRecorder` interface is used to allow for a noop implementation in cases (like ngcc) where this tracking isn't necessary.
2. A TypeScript transformer which processes default import statements and can preserve those which are actually used. 2. A TypeScript transformer which processes default import statements and can preserve those which are actually used.
This is accessed via `DefaultImportTracker.importPreservingTransformer`. This is accessed via `DefaultImportTracker.importPreservingTransformer`.

View File

@ -8,7 +8,7 @@
export {AliasingHost, AliasStrategy, PrivateExportAliasingHost, UnifiedModulesAliasingHost} from './src/alias'; export {AliasingHost, AliasStrategy, PrivateExportAliasingHost, UnifiedModulesAliasingHost} from './src/alias';
export {ImportRewriter, NoopImportRewriter, R3SymbolsImportRewriter, validateAndRewriteCoreSymbol} from './src/core'; export {ImportRewriter, NoopImportRewriter, R3SymbolsImportRewriter, validateAndRewriteCoreSymbol} from './src/core';
export {DefaultImportRecorder, DefaultImportTracker, NOOP_DEFAULT_IMPORT_RECORDER} from './src/default'; export {DefaultImportTracker} from './src/default';
export {AbsoluteModuleStrategy, EmittedReference, ImportedFile, ImportFlags, LocalIdentifierStrategy, LogicalProjectStrategy, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesStrategy} from './src/emitter'; export {AbsoluteModuleStrategy, EmittedReference, ImportedFile, ImportFlags, LocalIdentifierStrategy, LogicalProjectStrategy, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesStrategy} from './src/emitter';
export {Reexport} from './src/reexport'; export {Reexport} from './src/reexport';
export {OwningModule, Reference} from './src/references'; export {OwningModule, Reference} from './src/references';

View File

@ -6,52 +6,33 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {WrappedNodeExpr} from '@angular/compiler';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {getSourceFile} from '../../util/src/typescript'; import {getSourceFile} from '../../util/src/typescript';
/** const DefaultImportDeclaration = Symbol('DefaultImportDeclaration');
* Registers and records usages of `ts.Identifer`s that came from default import statements.
*
* See `DefaultImportTracker` for details.
*/
export interface DefaultImportRecorder {
/**
* Record an association between a `ts.Identifier` which might be emitted and the
* `ts.ImportDeclaration` from which it came.
*
* Alone, this method has no effect as the `ts.Identifier` might not be used in the output.
* The identifier must later be marked as used with `recordUsedIdentifier` in order for its
* import to be preserved.
*/
recordImportedIdentifier(id: ts.Identifier, decl: ts.ImportDeclaration): void;
/** interface WithDefaultImportDeclaration {
* Record the fact that the given `ts.Identifer` will be emitted, and thus its [DefaultImportDeclaration]?: ts.ImportDeclaration;
* `ts.ImportDeclaration`, if it was a previously registered default import, must be preserved.
*
* This method can be called safely for any `ts.Identifer`, regardless of its origin. It will only
* have an effect if the identifier came from a `ts.ImportDeclaration` default import which was
* previously registered with `recordImportedIdentifier`.
*/
recordUsedIdentifier(id: ts.Identifier): void;
} }
/** /**
* An implementation of `DefaultImportRecorder` which does nothing. * Attaches a default import declaration to `expr` to indicate the dependency of `expr` on the
* * default import.
* This is useful when default import tracking isn't required, such as when emitting .d.ts code
* or for ngcc.
*/ */
export const NOOP_DEFAULT_IMPORT_RECORDER: DefaultImportRecorder = { export function attachDefaultImportDeclaration(
recordImportedIdentifier: (id: ts.Identifier) => void{}, expr: WrappedNodeExpr<unknown>, importDecl: ts.ImportDeclaration): void {
recordUsedIdentifier: (id: ts.Identifier) => void{}, (expr as WithDefaultImportDeclaration)[DefaultImportDeclaration] = importDecl;
}; }
const ImportDeclarationMapping = Symbol('ImportDeclarationMapping'); /**
* Obtains the default import declaration that `expr` depends on, or `null` if there is no such
interface SourceFileWithImportDeclarationMapping extends ts.SourceFile { * dependency.
[ImportDeclarationMapping]?: Map<ts.Identifier, ts.ImportDeclaration>; */
export function getDefaultImportDeclaration(expr: WrappedNodeExpr<unknown>): ts.ImportDeclaration|
null {
return (expr as WithDefaultImportDeclaration)[DefaultImportDeclaration] ?? null;
} }
/** /**
@ -84,45 +65,28 @@ interface SourceFileWithImportDeclarationMapping extends ts.SourceFile {
* This problem does not exist for non-default imports as the compiler can easily insert * This problem does not exist for non-default imports as the compiler can easily insert
* "import * as X" style imports for those, and the "X" identifier survives transformation. * "import * as X" style imports for those, and the "X" identifier survives transformation.
*/ */
export class DefaultImportTracker implements DefaultImportRecorder { export class DefaultImportTracker {
/** /**
* A `Map` which tracks the `Set` of `ts.ImportDeclaration`s for default imports that were used in * A `Map` which tracks the `Set` of `ts.ImportDeclaration`s for default imports that were used in
* a given `ts.SourceFile` and need to be preserved. * a given `ts.SourceFile` and need to be preserved.
*/ */
private sourceFileToUsedImports = new Map<ts.SourceFile, Set<ts.ImportDeclaration>>(); private sourceFileToUsedImports = new Map<ts.SourceFile, Set<ts.ImportDeclaration>>();
recordImportedIdentifier(id: ts.Identifier, decl: ts.ImportDeclaration): void {
const sf = getSourceFile(id) as SourceFileWithImportDeclarationMapping;
if (sf[ImportDeclarationMapping] === undefined) {
sf[ImportDeclarationMapping] = new Map<ts.Identifier, ts.ImportDeclaration>();
}
sf[ImportDeclarationMapping]!.set(id, decl);
}
recordUsedIdentifier(id: ts.Identifier): void { recordUsedImport(importDecl: ts.ImportDeclaration): void {
const sf = getSourceFile(id) as SourceFileWithImportDeclarationMapping; const sf = getSourceFile(importDecl);
const identifierToDeclaration = sf[ImportDeclarationMapping];
if (identifierToDeclaration === undefined) {
// The identifier's source file has no registered default imports at all.
return;
}
if (!identifierToDeclaration.has(id)) {
// The identifier isn't from a registered default import.
return;
}
const decl = identifierToDeclaration.get(id)!;
// Add the default import declaration to the set of used import declarations for the file. // Add the default import declaration to the set of used import declarations for the file.
if (!this.sourceFileToUsedImports.has(sf)) { if (!this.sourceFileToUsedImports.has(sf)) {
this.sourceFileToUsedImports.set(sf, new Set<ts.ImportDeclaration>()); this.sourceFileToUsedImports.set(sf, new Set<ts.ImportDeclaration>());
} }
this.sourceFileToUsedImports.get(sf)!.add(decl); this.sourceFileToUsedImports.get(sf)!.add(importDecl);
} }
/** /**
* Get a `ts.TransformerFactory` which will preserve default imports that were previously marked * Get a `ts.TransformerFactory` which will preserve default imports that were previously marked
* as used. * as used.
* *
* This transformer must run after any other transformers which call `recordUsedIdentifier`. * This transformer must run after any other transformers which call `recordUsedImport`.
*/ */
importPreservingTransformer(): ts.TransformerFactory<ts.SourceFile> { importPreservingTransformer(): ts.TransformerFactory<ts.SourceFile> {
return (context: ts.TransformationContext) => { return (context: ts.TransformationContext) => {

View File

@ -39,12 +39,10 @@ runInEachFileSystem(() => {
module: ts.ModuleKind.ES2015, module: ts.ModuleKind.ES2015,
}); });
const fooClause = getDeclaration(program, _('/test.ts'), 'Foo', ts.isImportClause); const fooClause = getDeclaration(program, _('/test.ts'), 'Foo', ts.isImportClause);
const fooId = fooClause.name!;
const fooDecl = fooClause.parent; const fooDecl = fooClause.parent;
const tracker = new DefaultImportTracker(); const tracker = new DefaultImportTracker();
tracker.recordImportedIdentifier(fooId, fooDecl); tracker.recordUsedImport(fooDecl);
tracker.recordUsedIdentifier(fooId);
program.emit(undefined, undefined, undefined, undefined, { program.emit(undefined, undefined, undefined, undefined, {
before: [tracker.importPreservingTransformer()], before: [tracker.importPreservingTransformer()],
}); });
@ -73,8 +71,7 @@ runInEachFileSystem(() => {
const fooDecl = fooClause.parent; const fooDecl = fooClause.parent;
const tracker = new DefaultImportTracker(); const tracker = new DefaultImportTracker();
tracker.recordImportedIdentifier(fooId, fooDecl); tracker.recordUsedImport(fooDecl);
tracker.recordUsedIdentifier(fooId);
program.emit(undefined, undefined, undefined, undefined, { program.emit(undefined, undefined, undefined, undefined, {
before: [ before: [
addReferenceTransformer(fooId), addReferenceTransformer(fooId),

View File

@ -9,10 +9,11 @@
import {ConstantPool} from '@angular/compiler'; import {ConstantPool} from '@angular/compiler';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {DefaultImportRecorder, ImportRewriter} from '../../imports'; import {DefaultImportTracker, ImportRewriter} from '../../imports';
import {getDefaultImportDeclaration} from '../../imports/src/default';
import {PerfPhase, PerfRecorder} from '../../perf'; import {PerfPhase, PerfRecorder} from '../../perf';
import {Decorator, ReflectionHost} from '../../reflection'; import {Decorator, ReflectionHost} from '../../reflection';
import {ImportManager, RecordWrappedNodeExprFn, translateExpression, translateStatement, TranslatorOptions} from '../../translator'; import {ImportManager, RecordWrappedNodeFn, translateExpression, translateStatement, TranslatorOptions} from '../../translator';
import {visit, VisitListEntryResult, Visitor} from '../../util/src/visitor'; import {visit, VisitListEntryResult, Visitor} from '../../util/src/visitor';
import {CompileResult} from './api'; import {CompileResult} from './api';
@ -34,16 +35,16 @@ interface FileOverviewMeta {
export function ivyTransformFactory( export function ivyTransformFactory(
compilation: TraitCompiler, reflector: ReflectionHost, importRewriter: ImportRewriter, compilation: TraitCompiler, reflector: ReflectionHost, importRewriter: ImportRewriter,
defaultImportRecorder: DefaultImportRecorder, perf: PerfRecorder, isCore: boolean, defaultImportTracker: DefaultImportTracker, perf: PerfRecorder, isCore: boolean,
isClosureCompilerEnabled: boolean): ts.TransformerFactory<ts.SourceFile> { isClosureCompilerEnabled: boolean): ts.TransformerFactory<ts.SourceFile> {
const recordWrappedNodeExpr = createRecorderFn(defaultImportRecorder); const recordWrappedNode = createRecorderFn(defaultImportTracker);
return (context: ts.TransformationContext): ts.Transformer<ts.SourceFile> => { return (context: ts.TransformationContext): ts.Transformer<ts.SourceFile> => {
return (file: ts.SourceFile): ts.SourceFile => { return (file: ts.SourceFile): ts.SourceFile => {
return perf.inPhase( return perf.inPhase(
PerfPhase.Compile, PerfPhase.Compile,
() => transformIvySourceFile( () => transformIvySourceFile(
compilation, context, reflector, importRewriter, file, isCore, compilation, context, reflector, importRewriter, file, isCore,
isClosureCompilerEnabled, recordWrappedNodeExpr)); isClosureCompilerEnabled, recordWrappedNode));
}; };
}; };
} }
@ -81,7 +82,7 @@ class IvyTransformationVisitor extends Visitor {
private compilation: TraitCompiler, private compilation: TraitCompiler,
private classCompilationMap: Map<ts.ClassDeclaration, CompileResult[]>, private classCompilationMap: Map<ts.ClassDeclaration, CompileResult[]>,
private reflector: ReflectionHost, private importManager: ImportManager, private reflector: ReflectionHost, private importManager: ImportManager,
private recordWrappedNodeExpr: RecordWrappedNodeExprFn<ts.Expression>, private recordWrappedNodeExpr: RecordWrappedNodeFn<ts.Expression>,
private isClosureCompilerEnabled: boolean, private isCore: boolean) { private isClosureCompilerEnabled: boolean, private isCore: boolean) {
super(); super();
} }
@ -95,7 +96,7 @@ class IvyTransformationVisitor extends Visitor {
} }
const translateOptions: TranslatorOptions<ts.Expression> = { const translateOptions: TranslatorOptions<ts.Expression> = {
recordWrappedNodeExpr: this.recordWrappedNodeExpr, recordWrappedNode: this.recordWrappedNodeExpr,
annotateForClosureCompiler: this.isClosureCompilerEnabled, annotateForClosureCompiler: this.isClosureCompilerEnabled,
}; };
@ -252,7 +253,7 @@ function transformIvySourceFile(
compilation: TraitCompiler, context: ts.TransformationContext, reflector: ReflectionHost, compilation: TraitCompiler, context: ts.TransformationContext, reflector: ReflectionHost,
importRewriter: ImportRewriter, file: ts.SourceFile, isCore: boolean, importRewriter: ImportRewriter, file: ts.SourceFile, isCore: boolean,
isClosureCompilerEnabled: boolean, isClosureCompilerEnabled: boolean,
recordWrappedNodeExpr: RecordWrappedNodeExprFn<ts.Expression>): ts.SourceFile { recordWrappedNode: RecordWrappedNodeFn<ts.Expression>): ts.SourceFile {
const constantPool = new ConstantPool(isClosureCompilerEnabled); const constantPool = new ConstantPool(isClosureCompilerEnabled);
const importManager = new ImportManager(importRewriter); const importManager = new ImportManager(importRewriter);
@ -274,7 +275,7 @@ function transformIvySourceFile(
// results obtained at Step 1. // results obtained at Step 1.
const transformationVisitor = new IvyTransformationVisitor( const transformationVisitor = new IvyTransformationVisitor(
compilation, compilationVisitor.classCompilationMap, reflector, importManager, compilation, compilationVisitor.classCompilationMap, reflector, importManager,
recordWrappedNodeExpr, isClosureCompilerEnabled, isCore); recordWrappedNode, isClosureCompilerEnabled, isCore);
let sf = visit(file, transformationVisitor, context); let sf = visit(file, transformationVisitor, context);
// Generate the constant statements first, as they may involve adding additional imports // Generate the constant statements first, as they may involve adding additional imports
@ -282,7 +283,7 @@ function transformIvySourceFile(
const downlevelTranslatedCode = getLocalizeCompileTarget(context) < ts.ScriptTarget.ES2015; const downlevelTranslatedCode = getLocalizeCompileTarget(context) < ts.ScriptTarget.ES2015;
const constants = const constants =
constantPool.statements.map(stmt => translateStatement(stmt, importManager, { constantPool.statements.map(stmt => translateStatement(stmt, importManager, {
recordWrappedNodeExpr, recordWrappedNode,
downlevelTaggedTemplates: downlevelTranslatedCode, downlevelTaggedTemplates: downlevelTranslatedCode,
downlevelVariableDeclarations: downlevelTranslatedCode, downlevelVariableDeclarations: downlevelTranslatedCode,
annotateForClosureCompiler: isClosureCompilerEnabled, annotateForClosureCompiler: isClosureCompilerEnabled,
@ -370,11 +371,12 @@ function isFromAngularCore(decorator: Decorator): boolean {
return decorator.import !== null && decorator.import.from === '@angular/core'; return decorator.import !== null && decorator.import.from === '@angular/core';
} }
function createRecorderFn(defaultImportRecorder: DefaultImportRecorder): function createRecorderFn(defaultImportTracker: DefaultImportTracker):
RecordWrappedNodeExprFn<ts.Expression> { RecordWrappedNodeFn<ts.Expression> {
return expr => { return node => {
if (ts.isIdentifier(expr)) { const importDecl = getDefaultImportDeclaration(node);
defaultImportRecorder.recordUsedIdentifier(expr); if (importDecl !== null) {
defaultImportTracker.recordUsedImport(importDecl);
} }
}; };
} }

View File

@ -10,7 +10,7 @@ export {AstFactory, BinaryOperator, LeadingComment, ObjectLiteralProperty, Sourc
export {ImportGenerator, NamedImport} from './src/api/import_generator'; export {ImportGenerator, NamedImport} from './src/api/import_generator';
export {Context} from './src/context'; export {Context} from './src/context';
export {Import, ImportManager} from './src/import_manager'; export {Import, ImportManager} from './src/import_manager';
export {ExpressionTranslatorVisitor, RecordWrappedNodeExprFn, TranslatorOptions} from './src/translator'; export {ExpressionTranslatorVisitor, RecordWrappedNodeFn, TranslatorOptions} from './src/translator';
export {translateType} from './src/type_translator'; export {translateType} from './src/type_translator';
export {attachComments, createTemplateMiddle, createTemplateTail, TypeScriptAstFactory} from './src/typescript_ast_factory'; export {attachComments, createTemplateMiddle, createTemplateTail, TypeScriptAstFactory} from './src/typescript_ast_factory';
export {translateExpression, translateStatement} from './src/typescript_translator'; export {translateExpression, translateStatement} from './src/typescript_translator';

View File

@ -37,12 +37,12 @@ const BINARY_OPERATORS = new Map<o.BinaryOperator, BinaryOperator>([
[o.BinaryOperator.NullishCoalesce, '??'], [o.BinaryOperator.NullishCoalesce, '??'],
]); ]);
export type RecordWrappedNodeExprFn<TExpression> = (expr: TExpression) => void; export type RecordWrappedNodeFn<TExpression> = (node: o.WrappedNodeExpr<TExpression>) => void;
export interface TranslatorOptions<TExpression> { export interface TranslatorOptions<TExpression> {
downlevelTaggedTemplates?: boolean; downlevelTaggedTemplates?: boolean;
downlevelVariableDeclarations?: boolean; downlevelVariableDeclarations?: boolean;
recordWrappedNodeExpr?: RecordWrappedNodeExprFn<TExpression>; recordWrappedNode?: RecordWrappedNodeFn<TExpression>;
annotateForClosureCompiler?: boolean; annotateForClosureCompiler?: boolean;
} }
@ -50,14 +50,14 @@ export class ExpressionTranslatorVisitor<TStatement, TExpression> implements o.E
o.StatementVisitor { o.StatementVisitor {
private downlevelTaggedTemplates: boolean; private downlevelTaggedTemplates: boolean;
private downlevelVariableDeclarations: boolean; private downlevelVariableDeclarations: boolean;
private recordWrappedNodeExpr: RecordWrappedNodeExprFn<TExpression>; private recordWrappedNode: RecordWrappedNodeFn<TExpression>;
constructor( constructor(
private factory: AstFactory<TStatement, TExpression>, private factory: AstFactory<TStatement, TExpression>,
private imports: ImportGenerator<TExpression>, options: TranslatorOptions<TExpression>) { private imports: ImportGenerator<TExpression>, options: TranslatorOptions<TExpression>) {
this.downlevelTaggedTemplates = options.downlevelTaggedTemplates === true; this.downlevelTaggedTemplates = options.downlevelTaggedTemplates === true;
this.downlevelVariableDeclarations = options.downlevelVariableDeclarations === true; this.downlevelVariableDeclarations = options.downlevelVariableDeclarations === true;
this.recordWrappedNodeExpr = options.recordWrappedNodeExpr || (() => {}); this.recordWrappedNode = options.recordWrappedNode || (() => {});
} }
visitDeclareVarStmt(stmt: o.DeclareVarStmt, context: Context): TStatement { visitDeclareVarStmt(stmt: o.DeclareVarStmt, context: Context): TStatement {
@ -382,7 +382,7 @@ export class ExpressionTranslatorVisitor<TStatement, TExpression> implements o.E
} }
visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>, _context: Context): any { visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>, _context: Context): any {
this.recordWrappedNodeExpr(ast.node); this.recordWrappedNode(ast);
return ast.node; return ast.node;
} }