From 1381301afe861fdff0e69e595a68b6b50009678f Mon Sep 17 00:00:00 2001 From: JoostK Date: Sat, 10 Apr 2021 22:55:22 +0200 Subject: [PATCH] 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 --- .../ngcc/src/analysis/decoration_analyzer.ts | 17 ++-- .../src/ngtsc/annotations/src/component.ts | 10 +-- .../src/ngtsc/annotations/src/directive.ts | 21 +++-- .../src/ngtsc/annotations/src/injectable.ts | 21 ++--- .../src/ngtsc/annotations/src/metadata.ts | 13 +-- .../src/ngtsc/annotations/src/ng_module.ts | 12 +-- .../src/ngtsc/annotations/src/pipe.ts | 9 +-- .../src/ngtsc/annotations/src/util.ts | 34 ++++---- .../ngtsc/annotations/test/component_spec.ts | 3 +- .../ngtsc/annotations/test/directive_spec.ts | 6 +- .../ngtsc/annotations/test/injectable_spec.ts | 3 +- .../ngtsc/annotations/test/metadata_spec.ts | 4 +- .../ngtsc/annotations/test/ng_module_spec.ts | 5 +- .../src/ngtsc/core/src/compiler.ts | 32 ++++---- .../compiler-cli/src/ngtsc/imports/README.md | 2 - .../compiler-cli/src/ngtsc/imports/index.ts | 2 +- .../src/ngtsc/imports/src/default.ts | 80 +++++-------------- .../src/ngtsc/imports/test/default_spec.ts | 7 +- .../src/ngtsc/transform/src/transform.ts | 32 ++++---- .../src/ngtsc/translator/index.ts | 2 +- .../src/ngtsc/translator/src/translator.ts | 10 +-- 21 files changed, 126 insertions(+), 199 deletions(-) diff --git a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts index ce1a5a154b..60ab38d4c8 100644 --- a/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts +++ b/packages/compiler-cli/ngcc/src/analysis/decoration_analyzer.ts @@ -14,7 +14,7 @@ import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecorato import {CycleAnalyzer, CycleHandlingStrategy, ImportGraph} from '../../../src/ngtsc/cycles'; import {isFatalDiagnosticError} from '../../../src/ngtsc/diagnostics'; 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 {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, ResourceRegistry} from '../../../src/ngtsc/metadata'; import {PartialEvaluator} from '../../../src/ngtsc/partial_evaluator'; @@ -107,8 +107,8 @@ export class DecorationAnalyzer { /* i18nUseExternalIds */ true, this.bundle.enableI18nLegacyMessageIdFormat, /* usePoisonedData */ false, /* i18nNormalizeLineEndingsInICUs */ false, this.moduleResolver, this.cycleAnalyzer, - CycleHandlingStrategy.UseRemoteScoping, this.refEmitter, NOOP_DEFAULT_IMPORT_RECORDER, - NOOP_DEPENDENCY_TRACKER, this.injectableRegistry, + CycleHandlingStrategy.UseRemoteScoping, this.refEmitter, NOOP_DEPENDENCY_TRACKER, + this.injectableRegistry, /* semanticDepGraphUpdater */ null, !!this.compilerOptions.annotateForClosureCompiler, NOOP_PERF_RECORDER), @@ -116,7 +116,7 @@ export class DecorationAnalyzer { // clang-format off new DirectiveDecoratorHandler( 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, !!this.compilerOptions.annotateForClosureCompiler, // 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) new PipeDecoratorHandler( 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( - this.reflectionHost, NOOP_DEFAULT_IMPORT_RECORDER, this.isCore, + this.reflectionHost, this.isCore, /* strictCtorDeps */ false, this.injectableRegistry, NOOP_PERF_RECORDER, /* errorOnDuplicateProv */ false), new NgModuleDecoratorHandler( this.reflectionHost, this.evaluator, this.fullMetaReader, this.fullRegistry, this.scopeRegistry, this.referencesRegistry, this.isCore, /* routeAnalyzer */ null, this.refEmitter, - /* factoryTracker */ null, NOOP_DEFAULT_IMPORT_RECORDER, - !!this.compilerOptions.annotateForClosureCompiler, this.injectableRegistry, - NOOP_PERF_RECORDER), + /* factoryTracker */ null, !!this.compilerOptions.annotateForClosureCompiler, + this.injectableRegistry, NOOP_PERF_RECORDER), ]; compiler = new NgccTraitCompiler(this.handlers, this.reflectionHost); migrations: Migration[] = [ diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index 1f970328fd..ba723a1cd5 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -12,7 +12,7 @@ import * as ts from 'typescript'; import {Cycle, CycleAnalyzer, CycleHandlingStrategy} from '../../cycles'; import {ErrorCode, FatalDiagnosticError, makeDiagnostic, makeRelatedInformation} from '../../diagnostics'; 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 {extractSemanticTypeParameters, isArrayEqual, isReferenceEqual, SemanticDepGraphUpdater, SemanticReference, SemanticSymbol} from '../../incremental/semantic_graph'; import {IndexingContext} from '../../indexer'; @@ -205,7 +205,6 @@ export class ComponentDecoratorHandler implements private i18nNormalizeLineEndingsInICUs: boolean|undefined, private moduleResolver: ModuleResolver, private cycleAnalyzer: CycleAnalyzer, private cycleHandlingStrategy: CycleHandlingStrategy, private refEmitter: ReferenceEmitter, - private defaultImportRecorder: DefaultImportRecorder, private depTracker: DependencyTracker|null, private injectableRegistry: InjectableClassRegistry, private semanticDepGraphUpdater: SemanticDepGraphUpdater|null, @@ -326,8 +325,8 @@ export class ComponentDecoratorHandler implements // @Component inherits @Directive, so begin by extracting the @Directive metadata and building // on it. const directiveResult = extractDirectiveMetadata( - node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore, - flags, this.annotateForClosureCompiler, + node, decorator, this.reflector, this.evaluator, this.isCore, flags, + this.annotateForClosureCompiler, this.elementSchemaRegistry.getDefaultComponentElementName()); if (directiveResult === undefined) { // `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), classMetadata: extractClassMetadata( - node, this.reflector, this.defaultImportRecorder, this.isCore, - this.annotateForClosureCompiler), + node, this.reflector, this.isCore, this.annotateForClosureCompiler), template, providersRequiringFactory, viewProvidersRequiringFactory, diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts index 4c3e2369ac..3893ad0725 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts @@ -11,7 +11,7 @@ import {emitDistinctChangesOnlyDefaultValue} from '@angular/compiler/src/core'; import * as ts from 'typescript'; 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 {BindingPropertyName, ClassPropertyMapping, ClassPropertyName, DirectiveTypeCheckMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, TemplateGuardMeta} from '../../metadata'; import {extractDirectiveTypeCheckMeta} from '../../metadata/src/util'; @@ -177,9 +177,8 @@ export class DirectiveDecoratorHandler implements constructor( private reflector: ReflectionHost, private evaluator: PartialEvaluator, private metaRegistry: MetadataRegistry, private scopeRegistry: LocalModuleScopeRegistry, - private metaReader: MetadataReader, private defaultImportRecorder: DefaultImportRecorder, - private injectableRegistry: InjectableClassRegistry, private isCore: boolean, - private semanticDepGraphUpdater: SemanticDepGraphUpdater|null, + private metaReader: MetadataReader, private injectableRegistry: InjectableClassRegistry, + private isCore: boolean, private semanticDepGraphUpdater: SemanticDepGraphUpdater|null, private annotateForClosureCompiler: boolean, private compileUndecoratedClassesWithAngularFeatures: boolean, private perf: PerfRecorder) {} @@ -215,8 +214,8 @@ export class DirectiveDecoratorHandler implements this.perf.eventCount(PerfEvent.AnalyzeDirective); const directiveResult = extractDirectiveMetadata( - node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore, - flags, this.annotateForClosureCompiler); + node, decorator, this.reflector, this.evaluator, this.isCore, flags, + this.annotateForClosureCompiler); if (directiveResult === undefined) { return {}; } @@ -234,8 +233,7 @@ export class DirectiveDecoratorHandler implements outputs: directiveResult.outputs, meta: analysis, classMetadata: extractClassMetadata( - node, this.reflector, this.defaultImportRecorder, this.isCore, - this.annotateForClosureCompiler), + node, this.reflector, this.isCore, this.annotateForClosureCompiler), baseClass: readBaseClass(node, this.reflector, this.evaluator), typeCheckMeta: extractDirectiveTypeCheckMeta(node, directiveResult.inputs, this.reflector), providersRequiringFactory, @@ -351,9 +349,8 @@ export class DirectiveDecoratorHandler implements */ export function extractDirectiveMetadata( clazz: ClassDeclaration, decorator: Readonly, reflector: ReflectionHost, - evaluator: PartialEvaluator, defaultImportRecorder: DefaultImportRecorder, isCore: boolean, - flags: HandlerFlags, annotateForClosureCompiler: boolean, - defaultSelector: string|null = null): { + evaluator: PartialEvaluator, isCore: boolean, flags: HandlerFlags, + annotateForClosureCompiler: boolean, defaultSelector: string|null = null): { decorator: Map, metadata: R3DirectiveMetadata, inputs: ClassPropertyMapping, @@ -473,7 +470,7 @@ export function extractDirectiveMetadata( 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 // abstract directives are allowed to have invalid dependencies, given that a subclass may call diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts b/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts index 734e2748ed..ee0ad85820 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts @@ -10,7 +10,6 @@ import {compileClassMetadata, CompileClassMetadataFn, compileDeclareClassMetadat import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; -import {DefaultImportRecorder} from '../../imports'; import {InjectableClassRegistry} from '../../metadata'; import {PerfEvent, PerfRecorder} from '../../perf'; import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; @@ -33,8 +32,7 @@ export interface InjectableHandlerData { export class InjectableDecoratorHandler implements DecoratorHandler { constructor( - private reflector: ReflectionHost, private defaultImportRecorder: DefaultImportRecorder, - private isCore: boolean, private strictCtorDeps: boolean, + private reflector: ReflectionHost, private isCore: boolean, private strictCtorDeps: boolean, private injectableRegistry: InjectableClassRegistry, private perf: PerfRecorder, /** * What to do if the injectable already contains a ɵprov property. @@ -74,10 +72,8 @@ export class InjectableDecoratorHandler implements analysis: { meta, ctorDeps: extractInjectableCtorDeps( - node, meta, decorator, this.reflector, this.defaultImportRecorder, this.isCore, - this.strictCtorDeps), - classMetadata: - extractClassMetadata(node, this.reflector, this.defaultImportRecorder, this.isCore), + node, meta, decorator, this.reflector, this.isCore, this.strictCtorDeps), + classMetadata: extractClassMetadata(node, this.reflector, this.isCore), // Avoid generating multiple factories if a class has // more Angular decorators, apart from Injectable. needsFactory: !decorators || @@ -232,8 +228,7 @@ function getProviderExpression( function extractInjectableCtorDeps( clazz: ClassDeclaration, meta: R3InjectableMetadata, decorator: Decorator, - reflector: ReflectionHost, defaultImportRecorder: DefaultImportRecorder, isCore: boolean, - strictCtorDeps: boolean) { + reflector: ReflectionHost, isCore: boolean, strictCtorDeps: boolean) { if (decorator.args === null) { throw new FatalDiagnosticError( 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 // generated. if (strictCtorDeps) { - ctorDeps = getValidConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore); + ctorDeps = getValidConstructorDependencies(clazz, reflector, isCore); } else { - ctorDeps = unwrapConstructorDependencies( - getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore)); + ctorDeps = + unwrapConstructorDependencies(getConstructorDependencies(clazz, reflector, isCore)); } return ctorDeps; } 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 && meta.useClass === undefined && meta.useFactory === undefined) { diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts b/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts index aac4bbe4f0..223a0ed16f 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/metadata.ts @@ -9,7 +9,6 @@ import {Expression, FunctionExpr, LiteralArrayExpr, LiteralExpr, literalMap, R3ClassMetadata, ReturnStatement, WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; -import {DefaultImportRecorder} from '../../imports'; import {CtorParameter, DeclarationNode, Decorator, ReflectionHost, TypeValueReferenceKind} from '../../reflection'; import {valueReferenceToExpression, wrapFunctionExpressionsInParens} from './util'; @@ -23,8 +22,7 @@ import {valueReferenceToExpression, wrapFunctionExpressionsInParens} from './uti * as a `Statement` for inclusion along with the class. */ export function extractClassMetadata( - clazz: DeclarationNode, reflection: ReflectionHost, - defaultImportRecorder: DefaultImportRecorder, isCore: boolean, + clazz: DeclarationNode, reflection: ReflectionHost, isCore: boolean, annotateForClosureCompiler?: boolean): R3ClassMetadata|null { if (!reflection.isClass(clazz)) { return null; @@ -55,8 +53,7 @@ export function extractClassMetadata( let metaCtorParameters: Expression|null = null; const classCtorParameters = reflection.getConstructorParameters(clazz); if (classCtorParameters !== null) { - const ctorParameters = classCtorParameters.map( - param => ctorParameterToMetadata(param, defaultImportRecorder, isCore)); + const ctorParameters = classCtorParameters.map(param => ctorParameterToMetadata(param, isCore)); metaCtorParameters = new FunctionExpr([], [ new ReturnStatement(new LiteralArrayExpr(ctorParameters)), ]); @@ -93,13 +90,11 @@ export function extractClassMetadata( /** * Convert a reflected constructor parameter to metadata. */ -function ctorParameterToMetadata( - param: CtorParameter, defaultImportRecorder: DefaultImportRecorder, - isCore: boolean): Expression { +function ctorParameterToMetadata(param: CtorParameter, isCore: boolean): Expression { // Parameters sometimes have a type that can be referenced. If so, then use it, otherwise // its type is undefined. const type = param.typeValueReference.kind !== TypeValueReferenceKind.UNAVAILABLE ? - valueReferenceToExpression(param.typeValueReference, defaultImportRecorder) : + valueReferenceToExpression(param.typeValueReference) : new LiteralExpr(undefined); const mapEntries: {key: string, value: Expression, quoted: false}[] = [ diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts index 51e3c4ea7e..cb98a2fa78 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts @@ -10,7 +10,7 @@ import {compileClassMetadata, compileDeclareClassMetadata, compileDeclareInjecto import * as ts from 'typescript'; 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 {InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata'; import {PartialEvaluator, ResolvedValue} from '../../partial_evaluator'; @@ -130,9 +130,7 @@ export class NgModuleDecoratorHandler implements private scopeRegistry: LocalModuleScopeRegistry, private referencesRegistry: ReferencesRegistry, private isCore: boolean, private routeAnalyzer: NgModuleRouteAnalyzer|null, private refEmitter: ReferenceEmitter, - private factoryTracker: FactoryTracker|null, - private defaultImportRecorder: DefaultImportRecorder, - private annotateForClosureCompiler: boolean, + private factoryTracker: FactoryTracker|null, private annotateForClosureCompiler: boolean, private injectableRegistry: InjectableClassRegistry, private perf: PerfRecorder, private localeId?: string) {} @@ -350,8 +348,7 @@ export class NgModuleDecoratorHandler implements type, internalType, typeArgumentCount: 0, - deps: getValidConstructorDependencies( - node, this.reflector, this.defaultImportRecorder, this.isCore), + deps: getValidConstructorDependencies(node, this.reflector, this.isCore), target: FactoryTarget.NgModule, }; @@ -371,8 +368,7 @@ export class NgModuleDecoratorHandler implements resolveProvidersRequiringFactory(rawProviders, this.reflector, this.evaluator) : null, classMetadata: extractClassMetadata( - node, this.reflector, this.defaultImportRecorder, this.isCore, - this.annotateForClosureCompiler), + node, this.reflector, this.isCore, this.annotateForClosureCompiler), factorySymbolName: node.name.text, }, }; diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts b/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts index 691d563eb0..1dd0d4b961 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts @@ -10,7 +10,7 @@ import {compileClassMetadata, compileDeclareClassMetadata, compileDeclarePipeFro import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; -import {DefaultImportRecorder, Reference} from '../../imports'; +import {Reference} from '../../imports'; import {SemanticSymbol} from '../../incremental/semantic_graph'; import {InjectableClassRegistry, MetadataRegistry} from '../../metadata'; import {PartialEvaluator} from '../../partial_evaluator'; @@ -55,7 +55,6 @@ export class PipeDecoratorHandler implements constructor( private reflector: ReflectionHost, private evaluator: PartialEvaluator, private metaRegistry: MetadataRegistry, private scopeRegistry: LocalModuleScopeRegistry, - private defaultImportRecorder: DefaultImportRecorder, private injectableRegistry: InjectableClassRegistry, private isCore: boolean, private perf: PerfRecorder) {} @@ -131,12 +130,10 @@ export class PipeDecoratorHandler implements internalType, typeArgumentCount: this.reflector.getGenericArityOfClass(clazz) || 0, pipeName, - deps: getValidConstructorDependencies( - clazz, this.reflector, this.defaultImportRecorder, this.isCore), + deps: getValidConstructorDependencies(clazz, this.reflector, this.isCore), pure, }, - classMetadata: - extractClassMetadata(clazz, this.reflector, this.defaultImportRecorder, this.isCore), + classMetadata: extractClassMetadata(clazz, this.reflector, this.isCore), }, }; } diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/util.ts b/packages/compiler-cli/src/ngtsc/annotations/src/util.ts index 098b18391d..69d29dbd01 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/util.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/util.ts @@ -12,7 +12,8 @@ import {FactoryTarget} from '@angular/compiler/src/render3/partial/api'; import * as ts from 'typescript'; 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 {ClassDeclaration, CtorParameter, Decorator, Import, ImportedTypeValueReference, isNamedClassDeclaration, LocalTypeValueReference, ReflectionHost, TypeValueReference, TypeValueReferenceKind, UnavailableValue, ValueUnavailableKind} from '../../reflection'; import {DeclarationData} from '../../scope'; @@ -32,8 +33,7 @@ export interface ConstructorDepError { } export function getConstructorDependencies( - clazz: ClassDeclaration, reflector: ReflectionHost, - defaultImportRecorder: DefaultImportRecorder, isCore: boolean): ConstructorDeps|null { + clazz: ClassDeclaration, reflector: ReflectionHost, isCore: boolean): ConstructorDeps|null { const deps: R3DependencyMetadata[] = []; const errors: ConstructorDepError[] = []; let ctorParams = reflector.getConstructorParameters(clazz); @@ -45,7 +45,7 @@ export function getConstructorDependencies( } } ctorParams.forEach((param, idx) => { - let token = valueReferenceToExpression(param.typeValueReference, defaultImportRecorder); + let token = valueReferenceToExpression(param.typeValueReference); let attributeNameType: Expression|null = null; 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 * file in which the `TypeValueReference` originated. */ -export function valueReferenceToExpression( - valueRef: LocalTypeValueReference|ImportedTypeValueReference, - defaultImportRecorder: DefaultImportRecorder): Expression; -export function valueReferenceToExpression( - valueRef: TypeValueReference, defaultImportRecorder: DefaultImportRecorder): Expression|null; -export function valueReferenceToExpression( - valueRef: TypeValueReference, defaultImportRecorder: DefaultImportRecorder): Expression|null { +export function valueReferenceToExpression(valueRef: LocalTypeValueReference| + ImportedTypeValueReference): Expression; +export function valueReferenceToExpression(valueRef: TypeValueReference): Expression|null; +export function valueReferenceToExpression(valueRef: TypeValueReference): Expression|null { if (valueRef.kind === TypeValueReferenceKind.UNAVAILABLE) { return null; } else if (valueRef.kind === TypeValueReferenceKind.LOCAL) { - if (defaultImportRecorder !== null && valueRef.defaultImportStatement !== null && - ts.isIdentifier(valueRef.expression)) { - defaultImportRecorder.recordImportedIdentifier( - valueRef.expression, valueRef.defaultImportStatement); + const expr = new WrappedNodeExpr(valueRef.expression); + if (valueRef.defaultImportStatement !== null) { + attachDefaultImportDeclaration(expr, valueRef.defaultImportStatement); } - return new WrappedNodeExpr(valueRef.expression); + return expr; } else { let importExpr: Expression = new ExternalExpr({moduleName: valueRef.moduleName, name: valueRef.importedName}); @@ -163,10 +159,10 @@ export function unwrapConstructorDependencies(deps: ConstructorDeps|null): R3Dep } export function getValidConstructorDependencies( - clazz: ClassDeclaration, reflector: ReflectionHost, - defaultImportRecorder: DefaultImportRecorder, isCore: boolean): R3DependencyMetadata[]|null { + clazz: ClassDeclaration, reflector: ReflectionHost, isCore: boolean): R3DependencyMetadata[]| + null { return validateConstructorDependencies( - clazz, getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore)); + clazz, getConstructorDependencies(clazz, reflector, isCore)); } /** diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts index 63d33bd2c3..8fcfca7eb6 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts @@ -13,7 +13,7 @@ import {CycleAnalyzer, CycleHandlingStrategy, ImportGraph} from '../../cycles'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {absoluteFrom} from '../../file_system'; 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 {PartialEvaluator} from '../../partial_evaluator'; import {NOOP_PERF_RECORDER} from '../../perf'; @@ -81,7 +81,6 @@ function setup(program: ts.Program, options: ts.CompilerOptions, host: ts.Compil cycleAnalyzer, CycleHandlingStrategy.UseRemoteScoping, refEmitter, - NOOP_DEFAULT_IMPORT_RECORDER, /* depTracker */ null, injectableRegistry, /* semanticDepGraphUpdater */ null, diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/directive_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/directive_spec.ts index 898a0a28eb..bfef378682 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/directive_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/directive_spec.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {absoluteFrom} from '../../file_system'; 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 {PartialEvaluator} from '../../partial_evaluator'; import {NOOP_PERF_RECORDER} from '../../perf'; @@ -168,8 +168,8 @@ runInEachFileSystem(() => { null); const injectableRegistry = new InjectableClassRegistry(reflectionHost); const handler = new DirectiveDecoratorHandler( - reflectionHost, evaluator, scopeRegistry, scopeRegistry, metaReader, - NOOP_DEFAULT_IMPORT_RECORDER, injectableRegistry, /*isCore*/ false, + reflectionHost, evaluator, scopeRegistry, scopeRegistry, metaReader, injectableRegistry, + /*isCore*/ false, /*semanticDepGraphUpdater*/ null, /*annotateForClosureCompiler*/ false, /*detectUndecoratedClassesWithAngularFeatures*/ false, NOOP_PERF_RECORDER); diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts index 293fd25533..901463b7e4 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts @@ -8,7 +8,6 @@ import {ErrorCode, FatalDiagnosticError, ngErrorCode} from '../../diagnostics'; import {absoluteFrom} from '../../file_system'; import {runInEachFileSystem} from '../../file_system/testing'; -import {NOOP_DEFAULT_IMPORT_RECORDER} from '../../imports'; import {InjectableClassRegistry} from '../../metadata'; import {NOOP_PERF_RECORDER} from '../../perf'; import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; @@ -70,7 +69,7 @@ function setupHandler(errorOnDuplicateProv: boolean) { const reflectionHost = new TypeScriptReflectionHost(checker); const injectableRegistry = new InjectableClassRegistry(reflectionHost); const handler = new InjectableDecoratorHandler( - reflectionHost, NOOP_DEFAULT_IMPORT_RECORDER, /* isCore */ false, + reflectionHost, /* isCore */ false, /* strictCtorDeps */ false, injectableRegistry, NOOP_PERF_RECORDER, errorOnDuplicateProv); const TestClass = getDeclaration(program, ENTRY_FILE, 'TestClass', isNamedClassDeclaration); const ɵprov = reflectionHost.getMembersOfClass(TestClass).find(member => member.name === 'ɵprov'); diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts index 569e81e731..180ccab12d 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/metadata_spec.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {absoluteFrom, getSourceFileOrError} from '../../file_system'; import {runInEachFileSystem, TestFile} from '../../file_system/testing'; -import {NOOP_DEFAULT_IMPORT_RECORDER, NoopImportRewriter} from '../../imports'; +import {NoopImportRewriter} from '../../imports'; import {TypeScriptReflectionHost} from '../../reflection'; import {getDeclaration, makeProgram} from '../../testing'; import {ImportManager, translateStatement} from '../../translator'; @@ -128,7 +128,7 @@ runInEachFileSystem(() => { {target: ts.ScriptTarget.ES2015}); const host = new TypeScriptReflectionHost(program.getTypeChecker()); 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) { return ''; } diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/ng_module_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/ng_module_spec.ts index 5e0387b1db..bd16ad693e 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/ng_module_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/ng_module_spec.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {absoluteFrom} from '../../file_system'; 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 {PartialEvaluator} from '../../partial_evaluator'; import {NOOP_PERF_RECORDER} from '../../perf'; @@ -72,8 +72,7 @@ runInEachFileSystem(() => { const handler = new NgModuleDecoratorHandler( reflectionHost, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, /* isCore */ false, /* routeAnalyzer */ null, refEmitter, /* factoryTracker */ null, - NOOP_DEFAULT_IMPORT_RECORDER, /* annotateForClosureCompiler */ false, injectableRegistry, - NOOP_PERF_RECORDER); + /* annotateForClosureCompiler */ false, injectableRegistry, NOOP_PERF_RECORDER); const TestModule = getDeclaration(program, _('/entry.ts'), 'TestModule', isNamedClassDeclaration); const detected = diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index 8a0ae5b4b3..6a0ebb4a13 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -52,7 +52,6 @@ interface LazyCompilationState { routeAnalyzer: NgModuleRouteAnalyzer; dtsTransforms: DtsTransformRegistry; mwpScanner: ModuleWithProvidersScanner; - defaultImportTracker: DefaultImportTracker; aliasingHost: AliasingHost|null; refEmitter: ReferenceEmitter; templateTypeChecker: TemplateTypeChecker; @@ -613,13 +612,14 @@ export class NgCompiler { importRewriter = new NoopImportRewriter(); } + const defaultImportTracker = new DefaultImportTracker(); + const before = [ ivyTransformFactory( - compilation.traitCompiler, compilation.reflector, importRewriter, - compilation.defaultImportTracker, this.delegatingPerfRecorder, compilation.isCore, - this.closureCompilerEnabled), + compilation.traitCompiler, compilation.reflector, importRewriter, defaultImportTracker, + this.delegatingPerfRecorder, compilation.isCore, this.closureCompilerEnabled), aliasTransformFactory(compilation.traitCompiler.exportStatements), - compilation.defaultImportTracker.importPreservingTransformer(), + defaultImportTracker.importPreservingTransformer(), ]; const afterDeclarations: ts.TransformerFactory[] = []; @@ -971,7 +971,6 @@ export class NgCompiler { const isCore = isAngularCorePackage(this.inputProgram); - const defaultImportTracker = new DefaultImportTracker(); const resourceRegistry = new ResourceRegistry(); const compilationMode = @@ -993,16 +992,15 @@ export class NgCompiler { this.options.i18nUseExternalIds !== false, this.options.enableI18nLegacyMessageIdFormat !== false, this.usePoisonedData, this.options.i18nNormalizeLineEndingsInICUs, this.moduleResolver, this.cycleAnalyzer, - cycleHandlingStrategy, refEmitter, defaultImportTracker, this.incrementalDriver.depGraph, - injectableRegistry, semanticDepGraphUpdater, this.closureCompilerEnabled, - this.delegatingPerfRecorder), + cycleHandlingStrategy, refEmitter, this.incrementalDriver.depGraph, injectableRegistry, + semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder), // TODO(alxhub): understand why the cast here is necessary (something to do with `null` // not being assignable to `unknown` when wrapped in `Readonly`). // clang-format off new DirectiveDecoratorHandler( reflector, evaluator, metaRegistry, scopeRegistry, metaReader, - defaultImportTracker, injectableRegistry, isCore, semanticDepGraphUpdater, + injectableRegistry, isCore, semanticDepGraphUpdater, this.closureCompilerEnabled, compileUndecoratedClassesWithAngularFeatures, this.delegatingPerfRecorder, ) as Readonly>, @@ -1010,16 +1008,15 @@ export class NgCompiler { // Pipe handler must be before injectable handler in list so pipe factories are printed // before injectable factories (so injectable factories can delegate to them) new PipeDecoratorHandler( - reflector, evaluator, metaRegistry, scopeRegistry, defaultImportTracker, - injectableRegistry, isCore, this.delegatingPerfRecorder), + reflector, evaluator, metaRegistry, scopeRegistry, injectableRegistry, isCore, + this.delegatingPerfRecorder), new InjectableDecoratorHandler( - reflector, defaultImportTracker, isCore, this.options.strictInjectionParameters || false, - injectableRegistry, this.delegatingPerfRecorder), + reflector, isCore, this.options.strictInjectionParameters || false, injectableRegistry, + this.delegatingPerfRecorder), new NgModuleDecoratorHandler( reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, - routeAnalyzer, refEmitter, this.adapter.factoryTracker, defaultImportTracker, - this.closureCompilerEnabled, injectableRegistry, this.delegatingPerfRecorder, - this.options.i18nInLocale), + routeAnalyzer, refEmitter, this.adapter.factoryTracker, this.closureCompilerEnabled, + injectableRegistry, this.delegatingPerfRecorder, this.options.i18nInLocale), ]; const traitCompiler = new TraitCompiler( @@ -1051,7 +1048,6 @@ export class NgCompiler { mwpScanner, metaReader, typeCheckScopeRegistry, - defaultImportTracker, aliasingHost, refEmitter, templateTypeChecker, diff --git a/packages/compiler-cli/src/ngtsc/imports/README.md b/packages/compiler-cli/src/ngtsc/imports/README.md index 67b6c87e83..3112283847 100644 --- a/packages/compiler-cli/src/ngtsc/imports/README.md +++ b/packages/compiler-cli/src/ngtsc/imports/README.md @@ -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. -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. This is accessed via `DefaultImportTracker.importPreservingTransformer`. diff --git a/packages/compiler-cli/src/ngtsc/imports/index.ts b/packages/compiler-cli/src/ngtsc/imports/index.ts index 8843ad1824..523bdffe6b 100644 --- a/packages/compiler-cli/src/ngtsc/imports/index.ts +++ b/packages/compiler-cli/src/ngtsc/imports/index.ts @@ -8,7 +8,7 @@ export {AliasingHost, AliasStrategy, PrivateExportAliasingHost, UnifiedModulesAliasingHost} from './src/alias'; 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 {Reexport} from './src/reexport'; export {OwningModule, Reference} from './src/references'; diff --git a/packages/compiler-cli/src/ngtsc/imports/src/default.ts b/packages/compiler-cli/src/ngtsc/imports/src/default.ts index 23ff5be771..05bf46627e 100644 --- a/packages/compiler-cli/src/ngtsc/imports/src/default.ts +++ b/packages/compiler-cli/src/ngtsc/imports/src/default.ts @@ -6,52 +6,33 @@ * found in the LICENSE file at https://angular.io/license */ +import {WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; import {getSourceFile} from '../../util/src/typescript'; -/** - * 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; +const DefaultImportDeclaration = Symbol('DefaultImportDeclaration'); - /** - * Record the fact that the given `ts.Identifer` will be emitted, and thus its - * `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; +interface WithDefaultImportDeclaration { + [DefaultImportDeclaration]?: ts.ImportDeclaration; } /** - * An implementation of `DefaultImportRecorder` which does nothing. - * - * This is useful when default import tracking isn't required, such as when emitting .d.ts code - * or for ngcc. + * Attaches a default import declaration to `expr` to indicate the dependency of `expr` on the + * default import. */ -export const NOOP_DEFAULT_IMPORT_RECORDER: DefaultImportRecorder = { - recordImportedIdentifier: (id: ts.Identifier) => void{}, - recordUsedIdentifier: (id: ts.Identifier) => void{}, -}; +export function attachDefaultImportDeclaration( + expr: WrappedNodeExpr, importDecl: ts.ImportDeclaration): void { + (expr as WithDefaultImportDeclaration)[DefaultImportDeclaration] = importDecl; +} -const ImportDeclarationMapping = Symbol('ImportDeclarationMapping'); - -interface SourceFileWithImportDeclarationMapping extends ts.SourceFile { - [ImportDeclarationMapping]?: Map; +/** + * Obtains the default import declaration that `expr` depends on, or `null` if there is no such + * dependency. + */ +export function getDefaultImportDeclaration(expr: WrappedNodeExpr): 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 * "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 given `ts.SourceFile` and need to be preserved. */ private sourceFileToUsedImports = new Map>(); - recordImportedIdentifier(id: ts.Identifier, decl: ts.ImportDeclaration): void { - const sf = getSourceFile(id) as SourceFileWithImportDeclarationMapping; - if (sf[ImportDeclarationMapping] === undefined) { - sf[ImportDeclarationMapping] = new Map(); - } - sf[ImportDeclarationMapping]!.set(id, decl); - } - recordUsedIdentifier(id: ts.Identifier): void { - const sf = getSourceFile(id) as SourceFileWithImportDeclarationMapping; - 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)!; + recordUsedImport(importDecl: ts.ImportDeclaration): void { + const sf = getSourceFile(importDecl); // Add the default import declaration to the set of used import declarations for the file. if (!this.sourceFileToUsedImports.has(sf)) { this.sourceFileToUsedImports.set(sf, new Set()); } - 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 * 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 { return (context: ts.TransformationContext) => { diff --git a/packages/compiler-cli/src/ngtsc/imports/test/default_spec.ts b/packages/compiler-cli/src/ngtsc/imports/test/default_spec.ts index f07ea36462..307ecceda1 100644 --- a/packages/compiler-cli/src/ngtsc/imports/test/default_spec.ts +++ b/packages/compiler-cli/src/ngtsc/imports/test/default_spec.ts @@ -39,12 +39,10 @@ runInEachFileSystem(() => { module: ts.ModuleKind.ES2015, }); const fooClause = getDeclaration(program, _('/test.ts'), 'Foo', ts.isImportClause); - const fooId = fooClause.name!; const fooDecl = fooClause.parent; const tracker = new DefaultImportTracker(); - tracker.recordImportedIdentifier(fooId, fooDecl); - tracker.recordUsedIdentifier(fooId); + tracker.recordUsedImport(fooDecl); program.emit(undefined, undefined, undefined, undefined, { before: [tracker.importPreservingTransformer()], }); @@ -73,8 +71,7 @@ runInEachFileSystem(() => { const fooDecl = fooClause.parent; const tracker = new DefaultImportTracker(); - tracker.recordImportedIdentifier(fooId, fooDecl); - tracker.recordUsedIdentifier(fooId); + tracker.recordUsedImport(fooDecl); program.emit(undefined, undefined, undefined, undefined, { before: [ addReferenceTransformer(fooId), diff --git a/packages/compiler-cli/src/ngtsc/transform/src/transform.ts b/packages/compiler-cli/src/ngtsc/transform/src/transform.ts index c9a0d7bd10..7d29340e8f 100644 --- a/packages/compiler-cli/src/ngtsc/transform/src/transform.ts +++ b/packages/compiler-cli/src/ngtsc/transform/src/transform.ts @@ -9,10 +9,11 @@ import {ConstantPool} from '@angular/compiler'; 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 {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 {CompileResult} from './api'; @@ -34,16 +35,16 @@ interface FileOverviewMeta { export function ivyTransformFactory( compilation: TraitCompiler, reflector: ReflectionHost, importRewriter: ImportRewriter, - defaultImportRecorder: DefaultImportRecorder, perf: PerfRecorder, isCore: boolean, + defaultImportTracker: DefaultImportTracker, perf: PerfRecorder, isCore: boolean, isClosureCompilerEnabled: boolean): ts.TransformerFactory { - const recordWrappedNodeExpr = createRecorderFn(defaultImportRecorder); + const recordWrappedNode = createRecorderFn(defaultImportTracker); return (context: ts.TransformationContext): ts.Transformer => { return (file: ts.SourceFile): ts.SourceFile => { return perf.inPhase( PerfPhase.Compile, () => transformIvySourceFile( compilation, context, reflector, importRewriter, file, isCore, - isClosureCompilerEnabled, recordWrappedNodeExpr)); + isClosureCompilerEnabled, recordWrappedNode)); }; }; } @@ -81,7 +82,7 @@ class IvyTransformationVisitor extends Visitor { private compilation: TraitCompiler, private classCompilationMap: Map, private reflector: ReflectionHost, private importManager: ImportManager, - private recordWrappedNodeExpr: RecordWrappedNodeExprFn, + private recordWrappedNodeExpr: RecordWrappedNodeFn, private isClosureCompilerEnabled: boolean, private isCore: boolean) { super(); } @@ -95,7 +96,7 @@ class IvyTransformationVisitor extends Visitor { } const translateOptions: TranslatorOptions = { - recordWrappedNodeExpr: this.recordWrappedNodeExpr, + recordWrappedNode: this.recordWrappedNodeExpr, annotateForClosureCompiler: this.isClosureCompilerEnabled, }; @@ -252,7 +253,7 @@ function transformIvySourceFile( compilation: TraitCompiler, context: ts.TransformationContext, reflector: ReflectionHost, importRewriter: ImportRewriter, file: ts.SourceFile, isCore: boolean, isClosureCompilerEnabled: boolean, - recordWrappedNodeExpr: RecordWrappedNodeExprFn): ts.SourceFile { + recordWrappedNode: RecordWrappedNodeFn): ts.SourceFile { const constantPool = new ConstantPool(isClosureCompilerEnabled); const importManager = new ImportManager(importRewriter); @@ -274,7 +275,7 @@ function transformIvySourceFile( // results obtained at Step 1. const transformationVisitor = new IvyTransformationVisitor( compilation, compilationVisitor.classCompilationMap, reflector, importManager, - recordWrappedNodeExpr, isClosureCompilerEnabled, isCore); + recordWrappedNode, isClosureCompilerEnabled, isCore); let sf = visit(file, transformationVisitor, context); // 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 constants = constantPool.statements.map(stmt => translateStatement(stmt, importManager, { - recordWrappedNodeExpr, + recordWrappedNode, downlevelTaggedTemplates: downlevelTranslatedCode, downlevelVariableDeclarations: downlevelTranslatedCode, annotateForClosureCompiler: isClosureCompilerEnabled, @@ -370,11 +371,12 @@ function isFromAngularCore(decorator: Decorator): boolean { return decorator.import !== null && decorator.import.from === '@angular/core'; } -function createRecorderFn(defaultImportRecorder: DefaultImportRecorder): - RecordWrappedNodeExprFn { - return expr => { - if (ts.isIdentifier(expr)) { - defaultImportRecorder.recordUsedIdentifier(expr); +function createRecorderFn(defaultImportTracker: DefaultImportTracker): + RecordWrappedNodeFn { + return node => { + const importDecl = getDefaultImportDeclaration(node); + if (importDecl !== null) { + defaultImportTracker.recordUsedImport(importDecl); } }; } diff --git a/packages/compiler-cli/src/ngtsc/translator/index.ts b/packages/compiler-cli/src/ngtsc/translator/index.ts index b6300ba02a..7fa09856f8 100644 --- a/packages/compiler-cli/src/ngtsc/translator/index.ts +++ b/packages/compiler-cli/src/ngtsc/translator/index.ts @@ -10,7 +10,7 @@ export {AstFactory, BinaryOperator, LeadingComment, ObjectLiteralProperty, Sourc export {ImportGenerator, NamedImport} from './src/api/import_generator'; export {Context} from './src/context'; 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 {attachComments, createTemplateMiddle, createTemplateTail, TypeScriptAstFactory} from './src/typescript_ast_factory'; export {translateExpression, translateStatement} from './src/typescript_translator'; diff --git a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts index 1aea892ea3..81a6377abf 100644 --- a/packages/compiler-cli/src/ngtsc/translator/src/translator.ts +++ b/packages/compiler-cli/src/ngtsc/translator/src/translator.ts @@ -37,12 +37,12 @@ const BINARY_OPERATORS = new Map([ [o.BinaryOperator.NullishCoalesce, '??'], ]); -export type RecordWrappedNodeExprFn = (expr: TExpression) => void; +export type RecordWrappedNodeFn = (node: o.WrappedNodeExpr) => void; export interface TranslatorOptions { downlevelTaggedTemplates?: boolean; downlevelVariableDeclarations?: boolean; - recordWrappedNodeExpr?: RecordWrappedNodeExprFn; + recordWrappedNode?: RecordWrappedNodeFn; annotateForClosureCompiler?: boolean; } @@ -50,14 +50,14 @@ export class ExpressionTranslatorVisitor implements o.E o.StatementVisitor { private downlevelTaggedTemplates: boolean; private downlevelVariableDeclarations: boolean; - private recordWrappedNodeExpr: RecordWrappedNodeExprFn; + private recordWrappedNode: RecordWrappedNodeFn; constructor( private factory: AstFactory, private imports: ImportGenerator, options: TranslatorOptions) { this.downlevelTaggedTemplates = options.downlevelTaggedTemplates === true; this.downlevelVariableDeclarations = options.downlevelVariableDeclarations === true; - this.recordWrappedNodeExpr = options.recordWrappedNodeExpr || (() => {}); + this.recordWrappedNode = options.recordWrappedNode || (() => {}); } visitDeclareVarStmt(stmt: o.DeclareVarStmt, context: Context): TStatement { @@ -382,7 +382,7 @@ export class ExpressionTranslatorVisitor implements o.E } visitWrappedNodeExpr(ast: o.WrappedNodeExpr, _context: Context): any { - this.recordWrappedNodeExpr(ast.node); + this.recordWrappedNode(ast); return ast.node; }