diff --git a/packages/compiler-cli/src/ngtsc/annotations/common/src/di.ts b/packages/compiler-cli/src/ngtsc/annotations/common/src/di.ts index bb34a2108c62b..ebf54c171e5a1 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/common/src/di.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/common/src/di.ts @@ -6,11 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ -import {Expression, LiteralExpr, R3DependencyMetadata, WrappedNodeExpr} from '@angular/compiler'; +import {Expression, LiteralExpr, R3DependencyMetadata, ReadPropExpr, WrappedNodeExpr} from '@angular/compiler'; import ts from 'typescript'; import {ErrorCode, FatalDiagnosticError, makeRelatedInformation} from '../../../diagnostics'; import {ClassDeclaration, CtorParameter, ReflectionHost, TypeValueReferenceKind, UnavailableValue, ValueUnavailableKind,} from '../../../reflection'; +import {CompilationMode} from '../../../transform'; import {isAngularCore, valueReferenceToExpression} from './util'; @@ -28,7 +29,8 @@ export interface ConstructorDepError { } export function getConstructorDependencies( - clazz: ClassDeclaration, reflector: ReflectionHost, isCore: boolean): ConstructorDeps|null { + clazz: ClassDeclaration, reflector: ReflectionHost, isCore: boolean, + compilationMode: CompilationMode): ConstructorDeps|null { const deps: R3DependencyMetadata[] = []; const errors: ConstructorDepError[] = []; let ctorParams = reflector.getConstructorParameters(clazz); @@ -40,7 +42,37 @@ export function getConstructorDependencies( } } ctorParams.forEach((param, idx) => { - let token = valueReferenceToExpression(param.typeValueReference); + let token: Expression|null = null; + + if (compilationMode === CompilationMode.LOCAL && + param.typeValueReference.kind === TypeValueReferenceKind.UNAVAILABLE && + param.typeValueReference.reason.kind !== ValueUnavailableKind.MISSING_TYPE) { + // The case of local compilation where injection token cannot be resolved because it is + // "probably" imported from another file + + const typeNode = param.typeValueReference.reason.typeNode; + + if (ts.isTypeReferenceNode(typeNode)) { + if (ts.isIdentifier(typeNode.typeName)) { + token = new WrappedNodeExpr(typeNode.typeName); + } else if (ts.isQualifiedName(typeNode.typeName)) { + const receiver = new WrappedNodeExpr(typeNode.typeName.left); + + // Here we manually create the token out of the typeName without caring about its + // references for better TS tracking. This is because in this code path the typeNode is + // imported from another file and since we are in local compilation mode (=single file + // mode) the reference of this node (or its typeName node) cannot be resolved. So all we + // can do is just to create a new expression. + token = new ReadPropExpr(receiver, typeNode.typeName.right.getText()); + } else { + throw new Error('Impossible state!'); + } + } + } else { + // In all other cases resolve the injection token + token = valueReferenceToExpression(param.typeValueReference); + } + let attributeNameType: Expression|null = null; let optional = false, self = false, skipSelf = false, host = false; @@ -123,10 +155,10 @@ export function unwrapConstructorDependencies(deps: ConstructorDeps|null): R3Dep } export function getValidConstructorDependencies( - clazz: ClassDeclaration, reflector: ReflectionHost, isCore: boolean): R3DependencyMetadata[]| - null { + clazz: ClassDeclaration, reflector: ReflectionHost, isCore: boolean, + compilationMode: CompilationMode): R3DependencyMetadata[]|null { return validateConstructorDependencies( - clazz, getConstructorDependencies(clazz, reflector, isCore)); + clazz, getConstructorDependencies(clazz, reflector, isCore, compilationMode)); } /** diff --git a/packages/compiler-cli/src/ngtsc/annotations/common/src/injectable_registry.ts b/packages/compiler-cli/src/ngtsc/annotations/common/src/injectable_registry.ts index 0d1a94ab2aabb..70151715f248a 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/common/src/injectable_registry.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/common/src/injectable_registry.ts @@ -9,6 +9,7 @@ import {R3DependencyMetadata} from '@angular/compiler'; import {hasInjectableFields} from '../../../metadata'; import {ClassDeclaration, ReflectionHost} from '../../../reflection'; +import {CompilationMode} from '../../../transform'; import {getConstructorDependencies, unwrapConstructorDependencies} from './di'; @@ -42,7 +43,8 @@ export class InjectableClassRegistry { return null; } - const ctorDeps = getConstructorDependencies(declaration, this.host, this.isCore); + const ctorDeps = + getConstructorDependencies(declaration, this.host, this.isCore, CompilationMode.FULL); const meta: InjectableMeta = { ctorDeps: unwrapConstructorDependencies(ctorDeps), }; diff --git a/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts b/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts index 8708457a0dd25..8227bef37377b 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts @@ -220,7 +220,7 @@ export class ComponentDecoratorHandler implements // on it. const directiveResult = extractDirectiveMetadata( node, decorator, this.reflector, this.evaluator, this.refEmitter, this.referencesRegistry, - this.isCore, this.annotateForClosureCompiler, + this.isCore, this.annotateForClosureCompiler, this.compilationMode, this.elementSchemaRegistry.getDefaultComponentElementName()); if (directiveResult === undefined) { // `extractDirectiveMetadata` returns undefined when the @Directive has `jit: true`. In this diff --git a/packages/compiler-cli/src/ngtsc/annotations/directive/src/handler.ts b/packages/compiler-cli/src/ngtsc/annotations/directive/src/handler.ts index 901518cd61942..0c7310711fbd3 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/directive/src/handler.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/directive/src/handler.ts @@ -16,7 +16,7 @@ import {PartialEvaluator} from '../../../partial_evaluator'; import {PerfEvent, PerfRecorder} from '../../../perf'; import {ClassDeclaration, ClassMember, ClassMemberKind, Decorator, ReflectionHost} from '../../../reflection'; import {LocalModuleScopeRegistry} from '../../../scope'; -import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence, ResolveResult} from '../../../transform'; +import {AnalysisOutput, CompilationMode, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence, ResolveResult} from '../../../transform'; import {compileDeclareFactory, compileInputTransformFields, compileNgFactoryDefField, compileResults, extractClassMetadata, findAngularDecorator, getDirectiveDiagnostics, getProviderDiagnostics, getUndecoratedClassWithAngularFeaturesDiagnostic, InjectableClassRegistry, isAngularDecorator, readBaseClass, ReferencesRegistry, resolveProvidersRequiringFactory, toFactoryMetadata, validateHostDirectives} from '../../common'; import {extractDirectiveMetadata} from './shared'; @@ -49,14 +49,22 @@ export interface DirectiveHandlerData { export class DirectiveDecoratorHandler implements DecoratorHandler { constructor( - private reflector: ReflectionHost, private evaluator: PartialEvaluator, - private metaRegistry: MetadataRegistry, private scopeRegistry: LocalModuleScopeRegistry, - private metaReader: MetadataReader, private injectableRegistry: InjectableClassRegistry, - private refEmitter: ReferenceEmitter, private referencesRegistry: ReferencesRegistry, - private isCore: boolean, private strictCtorDeps: boolean, + private reflector: ReflectionHost, + private evaluator: PartialEvaluator, + private metaRegistry: MetadataRegistry, + private scopeRegistry: LocalModuleScopeRegistry, + private metaReader: MetadataReader, + private injectableRegistry: InjectableClassRegistry, + private refEmitter: ReferenceEmitter, + private referencesRegistry: ReferencesRegistry, + private isCore: boolean, + private strictCtorDeps: boolean, private semanticDepGraphUpdater: SemanticDepGraphUpdater|null, - private annotateForClosureCompiler: boolean, private perf: PerfRecorder, - private includeClassMetadata: boolean) {} + private annotateForClosureCompiler: boolean, + private perf: PerfRecorder, + private includeClassMetadata: boolean, + private readonly compilationMode: CompilationMode, + ) {} readonly precedence = HandlerPrecedence.PRIMARY; readonly name = 'DirectiveDecoratorHandler'; @@ -95,7 +103,7 @@ export class DirectiveDecoratorHandler implements const directiveResult = extractDirectiveMetadata( node, decorator, this.reflector, this.evaluator, this.refEmitter, this.referencesRegistry, - this.isCore, this.annotateForClosureCompiler); + this.isCore, this.annotateForClosureCompiler, this.compilationMode); if (directiveResult === undefined) { return {}; } diff --git a/packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts b/packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts index 7749916536297..000234aaf966a 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts @@ -14,6 +14,7 @@ import {assertSuccessfulReferenceEmit, ImportFlags, Reference, ReferenceEmitter} import {ClassPropertyMapping, HostDirectiveMeta, InputMapping, InputTransform} from '../../../metadata'; import {DynamicValue, EnumValue, PartialEvaluator, ResolvedValue} from '../../../partial_evaluator'; import {ClassDeclaration, ClassMember, ClassMemberKind, Decorator, filterToMembersWithDecorator, isNamedClassDeclaration, ReflectionHost, reflectObjectLiteral} from '../../../reflection'; +import {CompilationMode} from '../../../transform'; import {createSourceSpan, createValueHasWrongTypeError, forwardRefResolver, getConstructorDependencies, ReferencesRegistry, toR3Reference, tryUnwrapForwardRef, unwrapConstructorDependencies, unwrapExpression, validateConstructorDependencies, wrapFunctionExpressionsInParens, wrapTypeReference,} from '../../common'; const EMPTY_OBJECT: {[key: string]: string} = {}; @@ -34,7 +35,7 @@ export function extractDirectiveMetadata( clazz: ClassDeclaration, decorator: Readonly, reflector: ReflectionHost, evaluator: PartialEvaluator, refEmitter: ReferenceEmitter, referencesRegistry: ReferencesRegistry, isCore: boolean, annotateForClosureCompiler: boolean, - defaultSelector: string|null = null): { + compilationMode: CompilationMode, defaultSelector: string|null = null): { decorator: Map, metadata: R3DirectiveMetadata, inputs: ClassPropertyMapping, @@ -155,7 +156,7 @@ export function extractDirectiveMetadata( exportAs = resolved.split(',').map(part => part.trim()); } - const rawCtorDeps = getConstructorDependencies(clazz, reflector, isCore); + const rawCtorDeps = getConstructorDependencies(clazz, reflector, isCore, compilationMode); // 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/directive/test/BUILD.bazel b/packages/compiler-cli/src/ngtsc/annotations/directive/test/BUILD.bazel index 35926ab6dd477..e93cee8d8b619 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/directive/test/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/annotations/directive/test/BUILD.bazel @@ -22,6 +22,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/scope", "//packages/compiler-cli/src/ngtsc/testing", + "//packages/compiler-cli/src/ngtsc/transform", "@npm//typescript", ], ) diff --git a/packages/compiler-cli/src/ngtsc/annotations/directive/test/directive_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/directive/test/directive_spec.ts index df16eb9cf9708..6b2f2308fdd0d 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/directive/test/directive_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/directive/test/directive_spec.ts @@ -17,6 +17,7 @@ import {NOOP_PERF_RECORDER} from '../../../perf'; import {ClassDeclaration, isNamedClassDeclaration, TypeScriptReflectionHost} from '../../../reflection'; import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../../scope'; import {getDeclaration, makeProgram} from '../../../testing'; +import {CompilationMode} from '../../../transform'; import {InjectableClassRegistry, NoopReferencesRegistry} from '../../common'; import {DirectiveDecoratorHandler} from '../index'; @@ -177,7 +178,8 @@ runInEachFileSystem(() => { /*isCore*/ false, /*strictCtorDeps*/ false, /*semanticDepGraphUpdater*/ null, - /*annotateForClosureCompiler*/ false, NOOP_PERF_RECORDER, /*includeClassMetadata*/ true); + /*annotateForClosureCompiler*/ false, NOOP_PERF_RECORDER, /*includeClassMetadata*/ true, + /*compilationMode */ CompilationMode.FULL); const DirNode = getDeclaration(program, _('/entry.ts'), dirName, isNamedClassDeclaration); diff --git a/packages/compiler-cli/src/ngtsc/annotations/ng_module/src/handler.ts b/packages/compiler-cli/src/ngtsc/annotations/ng_module/src/handler.ts index 694a92501b687..b3a5313f61cf7 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/ng_module/src/handler.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/ng_module/src/handler.ts @@ -458,7 +458,8 @@ export class NgModuleDecoratorHandler implements name, type, typeArgumentCount: 0, - deps: getValidConstructorDependencies(node, this.reflector, this.isCore), + deps: + getValidConstructorDependencies(node, this.reflector, this.isCore, this.compilationMode), target: FactoryTarget.NgModule, }; diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts b/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts index 2c9e35b3eb72a..3c8db758f687b 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts @@ -14,7 +14,7 @@ import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; import {PartialEvaluator} from '../../partial_evaluator'; import {PerfEvent, PerfRecorder} from '../../perf'; import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; -import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence, ResolveResult,} from '../../transform'; +import {AnalysisOutput, CompilationMode, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence, ResolveResult,} from '../../transform'; import {checkInheritanceOfInjectable, compileDeclareFactory, CompileFactoryFn, compileNgFactoryDefField, extractClassMetadata, findAngularDecorator, getConstructorDependencies, getValidConstructorDependencies, isAngularCore, toFactoryMetadata, tryUnwrapForwardRef, unwrapConstructorDependencies, validateConstructorDependencies, wrapTypeReference,} from '../common'; export interface InjectableHandlerData { @@ -33,7 +33,7 @@ export class InjectableDecoratorHandler implements private reflector: ReflectionHost, private evaluator: PartialEvaluator, private isCore: boolean, private strictCtorDeps: boolean, private injectableRegistry: InjectableClassRegistry, private perf: PerfRecorder, - private includeClassMetadata: boolean, + private includeClassMetadata: boolean, private readonly compilationMode: CompilationMode, /** * What to do if the injectable already contains a ɵprov property. * @@ -72,7 +72,8 @@ export class InjectableDecoratorHandler implements analysis: { meta, ctorDeps: extractInjectableCtorDeps( - node, meta, decorator, this.reflector, this.isCore, this.strictCtorDeps), + node, meta, decorator, this.reflector, this.isCore, this.strictCtorDeps, + this.compilationMode), classMetadata: this.includeClassMetadata ? extractClassMetadata(node, this.reflector, this.isCore) : null, @@ -257,7 +258,8 @@ function getProviderExpression( function extractInjectableCtorDeps( clazz: ClassDeclaration, meta: R3InjectableMetadata, decorator: Decorator, - reflector: ReflectionHost, isCore: boolean, strictCtorDeps: boolean) { + reflector: ReflectionHost, isCore: boolean, strictCtorDeps: boolean, + compilationMode: CompilationMode) { if (decorator.args === null) { throw new FatalDiagnosticError( ErrorCode.DECORATOR_NOT_CALLED, decorator.node, '@Injectable must be called'); @@ -275,15 +277,15 @@ function extractInjectableCtorDeps( // constructor signature does not work for DI then a factory definition (ɵfac) that throws is // generated. if (strictCtorDeps && !isAbstractClassDeclaration(clazz)) { - ctorDeps = getValidConstructorDependencies(clazz, reflector, isCore); + ctorDeps = getValidConstructorDependencies(clazz, reflector, isCore, compilationMode); } else { - ctorDeps = - unwrapConstructorDependencies(getConstructorDependencies(clazz, reflector, isCore)); + ctorDeps = unwrapConstructorDependencies( + getConstructorDependencies(clazz, reflector, isCore, compilationMode)); } return ctorDeps; } else if (decorator.args.length === 1) { - const rawCtorDeps = getConstructorDependencies(clazz, reflector, isCore); + const rawCtorDeps = getConstructorDependencies(clazz, reflector, isCore, compilationMode); if (strictCtorDeps && !isAbstractClassDeclaration(clazz) && requiresValidCtor(meta)) { // Since use* was not provided for a concrete class, validate the deps according to diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts b/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts index 1bd4c4a868ef5..7da00374061a1 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts @@ -17,7 +17,7 @@ import {PartialEvaluator} from '../../partial_evaluator'; import {PerfEvent, PerfRecorder} from '../../perf'; import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection'; import {LocalModuleScopeRegistry} from '../../scope'; -import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence, ResolveResult,} from '../../transform'; +import {AnalysisOutput, CompilationMode, CompileResult, DecoratorHandler, DetectResult, HandlerPrecedence, ResolveResult,} from '../../transform'; import {compileDeclareFactory, compileNgFactoryDefField, compileResults, createValueHasWrongTypeError, extractClassMetadata, findAngularDecorator, getValidConstructorDependencies, InjectableClassRegistry, makeDuplicateDeclarationError, toFactoryMetadata, unwrapExpression, wrapTypeReference,} from '../common'; export interface PipeHandlerData { @@ -54,7 +54,8 @@ export class PipeDecoratorHandler implements private reflector: ReflectionHost, private evaluator: PartialEvaluator, private metaRegistry: MetadataRegistry, private scopeRegistry: LocalModuleScopeRegistry, private injectableRegistry: InjectableClassRegistry, private isCore: boolean, - private perf: PerfRecorder, private includeClassMetadata: boolean) {} + private perf: PerfRecorder, private includeClassMetadata: boolean, + private readonly compilationMode: CompilationMode) {} readonly precedence = HandlerPrecedence.PRIMARY; readonly name = 'PipeDecoratorHandler'; @@ -134,7 +135,8 @@ export class PipeDecoratorHandler implements type, typeArgumentCount: this.reflector.getGenericArityOfClass(clazz) || 0, pipeName, - deps: getValidConstructorDependencies(clazz, this.reflector, this.isCore), + deps: getValidConstructorDependencies( + clazz, this.reflector, this.isCore, this.compilationMode), pure, isStandalone, }, diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/BUILD.bazel b/packages/compiler-cli/src/ngtsc/annotations/test/BUILD.bazel index 5711721ace3e7..eccec0c51b59b 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/BUILD.bazel +++ b/packages/compiler-cli/src/ngtsc/annotations/test/BUILD.bazel @@ -25,6 +25,7 @@ ts_library( "//packages/compiler-cli/src/ngtsc/reflection", "//packages/compiler-cli/src/ngtsc/scope", "//packages/compiler-cli/src/ngtsc/testing", + "//packages/compiler-cli/src/ngtsc/transform", "//packages/compiler-cli/src/ngtsc/translator", "@npm//typescript", ], 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 e762d2cfa7199..276bdd3745dd2 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/injectable_spec.ts @@ -13,6 +13,7 @@ import {PartialEvaluator} from '../../partial_evaluator'; import {NOOP_PERF_RECORDER} from '../../perf'; import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; import {getDeclaration, makeProgram} from '../../testing'; +import {CompilationMode} from '../../transform'; import {InjectableDecoratorHandler} from '../src/injectable'; runInEachFileSystem(() => { @@ -73,7 +74,7 @@ function setupHandler(errorOnDuplicateProv: boolean) { const handler = new InjectableDecoratorHandler( reflectionHost, evaluator, /* isCore */ false, /* strictCtorDeps */ false, injectableRegistry, NOOP_PERF_RECORDER, true, - errorOnDuplicateProv); + /*compilationMode */ CompilationMode.FULL, errorOnDuplicateProv); const TestClass = getDeclaration(program, ENTRY_FILE, 'TestClass', isNamedClassDeclaration); const ɵprov = reflectionHost.getMembersOfClass(TestClass).find(member => member.name === 'ɵprov'); if (ɵprov === undefined) { diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index 8f5a03561ec09..836a9e7160893 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -1082,17 +1082,17 @@ export class NgCompiler { injectableRegistry, refEmitter, referencesRegistry, isCore, strictCtorDeps, semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder, - supportTestBed, + supportTestBed, compilationMode, ) as Readonly>, // clang-format on // 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, ngModuleScopeRegistry, injectableRegistry, isCore, - this.delegatingPerfRecorder, supportTestBed), + this.delegatingPerfRecorder, supportTestBed, compilationMode), new InjectableDecoratorHandler( reflector, evaluator, isCore, strictCtorDeps, injectableRegistry, - this.delegatingPerfRecorder, supportTestBed), + this.delegatingPerfRecorder, supportTestBed, compilationMode), new NgModuleDecoratorHandler( reflector, evaluator, metaReader, metaRegistry, ngModuleScopeRegistry, referencesRegistry, exportedProviderStatusResolver, semanticDepGraphUpdater, isCore, refEmitter, diff --git a/packages/compiler-cli/test/ngtsc/local_compilation_spec.ts b/packages/compiler-cli/test/ngtsc/local_compilation_spec.ts index 22d748c02ac81..81beb0a00c6b8 100644 --- a/packages/compiler-cli/test/ngtsc/local_compilation_spec.ts +++ b/packages/compiler-cli/test/ngtsc/local_compilation_spec.ts @@ -233,5 +233,273 @@ runInEachFileSystem(() => { expect(jsContents).not.toContain('i0.ɵɵgetComponentDepsFactory'); }); }); + + describe('constructor injection', () => { + it('should include injector types with all possible import/injection styles into component factory', + () => { + env.write('test.ts', ` + import {Component, NgModule, Attribute, Inject} from '@angular/core'; + import {SomeClass} from './some-where' + import {SomeService1} from './some-where1' + import SomeService2 from './some-where2' + import * as SomeWhere3 from './some-where3' + + @Component({ + selector: 'test-main', + template: 'Hello world', + }) + export class MainComponent { + constructor( + private someService1: SomeService1, + private someService2: SomeService2, + private someService3: SomeWhere3.SomeService3, + @Attribute('title') title: string, + @Inject(MESSAGE_TOKEN) tokenMessage: SomeClass, + ) {} + } + + @NgModule({ + declarations: [MainComponent], + }) + export class MainModule { + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + + expect(jsContents) + .toContain( + `MainComponent.ɵfac = function MainComponent_Factory(t) { return new (t || MainComponent)(i0.ɵɵdirectiveInject(SomeService1), i0.ɵɵdirectiveInject(SomeService2), i0.ɵɵdirectiveInject(SomeWhere3.SomeService3), i0.ɵɵinjectAttribute('title'), i0.ɵɵdirectiveInject(MESSAGE_TOKEN)); };`); + }); + + it('should include injector types with all possible import/injection styles into standalone component factory', + () => { + env.write('test.ts', ` + import {Component, NgModule, Attribute, Inject} from '@angular/core'; + import {SomeClass} from './some-where' + import {SomeService1} from './some-where1' + import SomeService2 from './some-where2' + import * as SomeWhere3 from './some-where3' + + @Component({ + standalone: true, + selector: 'test-main', + template: 'Hello world', + }) + export class MainComponent { + constructor( + private someService1: SomeService1, + private someService2: SomeService2, + private someService3: SomeWhere3.SomeService3, + @Attribute('title') title: string, + @Inject(MESSAGE_TOKEN) tokenMessage: SomeClass, + ) {} + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + + expect(jsContents) + .toContain( + `MainComponent.ɵfac = function MainComponent_Factory(t) { return new (t || MainComponent)(i0.ɵɵdirectiveInject(SomeService1), i0.ɵɵdirectiveInject(SomeService2), i0.ɵɵdirectiveInject(SomeWhere3.SomeService3), i0.ɵɵinjectAttribute('title'), i0.ɵɵdirectiveInject(MESSAGE_TOKEN)); };`); + }); + + it('should include injector types with all possible import/injection styles into directive factory', + () => { + env.write('test.ts', ` + import {Directive, NgModule, Attribute, Inject} from '@angular/core'; + import {SomeClass} from './some-where' + import {SomeService1} from './some-where1' + import SomeService2 from './some-where2' + import * as SomeWhere3 from './some-where3' + + @Directive({ + }) + export class MainDirective { + constructor( + private someService1: SomeService1, + private someService2: SomeService2, + private someService3: SomeWhere3.SomeService3, + @Attribute('title') title: string, + @Inject(MESSAGE_TOKEN) tokenMessage: SomeClass, + ) {} + } + + @NgModule({ + declarations: [MainDirective], + }) + export class MainModule { + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + + expect(jsContents) + .toContain( + `MainDirective.ɵfac = function MainDirective_Factory(t) { return new (t || MainDirective)(i0.ɵɵdirectiveInject(SomeService1), i0.ɵɵdirectiveInject(SomeService2), i0.ɵɵdirectiveInject(SomeWhere3.SomeService3), i0.ɵɵinjectAttribute('title'), i0.ɵɵdirectiveInject(MESSAGE_TOKEN)); };`); + }); + + it('should include injector types with all possible import/injection styles into standalone directive factory', + () => { + env.write('test.ts', ` + import {Directive, Attribute, Inject} from '@angular/core'; + import {SomeClass} from './some-where' + import {SomeService1} from './some-where1' + import SomeService2 from './some-where2' + import * as SomeWhere3 from './some-where3' + + @Directive({ + standalone: true, + }) + export class MainDirective { + constructor( + private someService1: SomeService1, + private someService2: SomeService2, + private someService3: SomeWhere3.SomeService3, + @Attribute('title') title: string, + @Inject(MESSAGE_TOKEN) tokenMessage: SomeClass, + ) {} + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + + expect(jsContents) + .toContain( + `MainDirective.ɵfac = function MainDirective_Factory(t) { return new (t || MainDirective)(i0.ɵɵdirectiveInject(SomeService1), i0.ɵɵdirectiveInject(SomeService2), i0.ɵɵdirectiveInject(SomeWhere3.SomeService3), i0.ɵɵinjectAttribute('title'), i0.ɵɵdirectiveInject(MESSAGE_TOKEN)); };`); + }); + + it('should include injector types with all possible import/injection styles into pipe factory', + () => { + env.write('test.ts', ` + import {Pipe, NgModule, Attribute, Inject} from '@angular/core'; + import {SomeClass} from './some-where' + import {SomeService1} from './some-where1' + import SomeService2 from './some-where2' + import * as SomeWhere3 from './some-where3' + + @Pipe({name: 'pipe'}) + export class MainPipe { + constructor( + private someService1: SomeService1, + private someService2: SomeService2, + private someService3: SomeWhere3.SomeService3, + @Attribute('title') title: string, + @Inject(MESSAGE_TOKEN) tokenMessage: SomeClass, + ) {} + } + + @NgModule({ + declarations: [MainPipe], + }) + export class MainModule { + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + + expect(jsContents) + .toContain( + `MainPipe.ɵfac = function MainPipe_Factory(t) { return new (t || MainPipe)(i0.ɵɵdirectiveInject(SomeService1, 16), i0.ɵɵdirectiveInject(SomeService2, 16), i0.ɵɵdirectiveInject(SomeWhere3.SomeService3, 16), i0.ɵɵinjectAttribute('title'), i0.ɵɵdirectiveInject(MESSAGE_TOKEN, 16)); };`); + }); + + it('should include injector types with all possible import/injection styles into standalone pipe factory', + () => { + env.write('test.ts', ` + import {Pipe, Attribute, Inject} from '@angular/core'; + import {SomeClass} from './some-where' + import {SomeService1} from './some-where1' + import SomeService2 from './some-where2' + import * as SomeWhere3 from './some-where3' + + @Pipe({ + name: 'pipe', + standalone: true, + }) + export class MainPipe { + constructor( + private someService1: SomeService1, + private someService2: SomeService2, + private someService3: SomeWhere3.SomeService3, + @Attribute('title') title: string, + @Inject(MESSAGE_TOKEN) tokenMessage: SomeClass, + ) {} + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + + expect(jsContents) + .toContain( + `MainPipe.ɵfac = function MainPipe_Factory(t) { return new (t || MainPipe)(i0.ɵɵdirectiveInject(SomeService1, 16), i0.ɵɵdirectiveInject(SomeService2, 16), i0.ɵɵdirectiveInject(SomeWhere3.SomeService3, 16), i0.ɵɵinjectAttribute('title'), i0.ɵɵdirectiveInject(MESSAGE_TOKEN, 16)); };`); + }); + + it('should include injector types with all possible import/injection styles into injectable factory', + () => { + env.write('test.ts', ` + import {Injectable, Attribute, Inject} from '@angular/core'; + import {SomeClass} from './some-where' + import {SomeService1} from './some-where1' + import SomeService2 from './some-where2' + import * as SomeWhere3 from './some-where3' + + @Injectable({ + providedIn: 'root', + }) + export class MainService { + constructor( + private someService1: SomeService1, + private someService2: SomeService2, + private someService3: SomeWhere3.SomeService3, + @Attribute('title') title: string, + @Inject(MESSAGE_TOKEN) tokenMessage: SomeClass, + ) {} + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + + expect(jsContents) + .toContain( + `MainService.ɵfac = function MainService_Factory(t) { return new (t || MainService)(i0.ɵɵinject(SomeService1), i0.ɵɵinject(SomeService2), i0.ɵɵinject(SomeWhere3.SomeService3), i0.ɵɵinjectAttribute('title'), i0.ɵɵinject(MESSAGE_TOKEN)); };`); + }); + + it('should include injector types with all possible import/injection styles into ng module factory', + () => { + env.write('test.ts', ` + import {Component, NgModule, Attribute, Inject} from '@angular/core'; + import {SomeClass} from './some-where' + import {SomeService1} from './some-where1' + import SomeService2 from './some-where2' + import * as SomeWhere3 from './some-where3' + + @NgModule({ + }) + export class MainModule { + constructor( + private someService1: SomeService1, + private someService2: SomeService2, + private someService3: SomeWhere3.SomeService3, + @Attribute('title') title: string, + @Inject(MESSAGE_TOKEN) tokenMessage: SomeClass, + ) {} + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + + expect(jsContents) + .toContain( + `MainModule.ɵfac = function MainModule_Factory(t) { return new (t || MainModule)(i0.ɵɵinject(SomeService1), i0.ɵɵinject(SomeService2), i0.ɵɵinject(SomeWhere3.SomeService3), i0.ɵɵinjectAttribute('title'), i0.ɵɵinject(MESSAGE_TOKEN)); };`); + }); + }); }); });