diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts b/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts index 638523efa9..9f72891cb8 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts @@ -109,9 +109,6 @@ function extractInjectableMetadata( ErrorCode.VALUE_NOT_LITERAL, depsExpr, `In Ivy, deps metadata must be an inline array.`); } - if (depsExpr.elements.length > 0) { - throw new Error(`deps not yet supported`); - } userDeps = depsExpr.elements.map(dep => getDep(dep, reflector)); } diff --git a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts index 1df79883b7..befe6e253a 100644 --- a/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts +++ b/packages/compiler-cli/test/ngtsc/ngtsc_spec.ts @@ -66,6 +66,88 @@ describe('ngtsc behavioral tests', () => { expect(dtsContents).toContain('static ngInjectableDef: i0.ɵInjectableDef>;'); }); + it('should compile Injectables with providedIn without errors', () => { + env.tsconfig(); + env.write('test.ts', ` + import {Injectable} from '@angular/core'; + + @Injectable() + export class Dep {} + + @Injectable({ providedIn: 'root' }) + export class Service { + constructor(dep: Dep) {} + } + `); + + env.driveMain(); + + + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('Dep.ngInjectableDef ='); + expect(jsContents).toContain('Service.ngInjectableDef ='); + expect(jsContents) + .toContain('return new (t || Service)(i0.inject(Dep)); }, providedIn: \'root\' });'); + expect(jsContents).not.toContain('__decorate'); + const dtsContents = env.getContents('test.d.ts'); + expect(dtsContents).toContain('static ngInjectableDef: i0.ɵInjectableDef;'); + expect(dtsContents).toContain('static ngInjectableDef: i0.ɵInjectableDef;'); + }); + + it('should compile Injectables with providedIn and factory without errors', () => { + env.tsconfig(); + env.write('test.ts', ` + import {Injectable} from '@angular/core'; + + @Injectable({ providedIn: 'root', useFactory: () => new Service() }) + export class Service { + constructor() {} + } + `); + + env.driveMain(); + + + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('Service.ngInjectableDef ='); + expect(jsContents).toContain('(r = new t());'); + expect(jsContents).toContain('(r = (function () { return new Service(); })());'); + expect(jsContents).toContain('factory: function Service_Factory(t) { var r = null; if (t) {'); + expect(jsContents).toContain('return r; }, providedIn: \'root\' });'); + expect(jsContents).not.toContain('__decorate'); + const dtsContents = env.getContents('test.d.ts'); + expect(dtsContents).toContain('static ngInjectableDef: i0.ɵInjectableDef;'); + }); + + it('should compile Injectables with providedIn and factory with deps without errors', () => { + env.tsconfig(); + env.write('test.ts', ` + import {Injectable} from '@angular/core'; + + @Injectable() + export class Dep {} + + @Injectable({ providedIn: 'root', useFactory: (dep: Dep) => new Service(dep), deps: [Dep] }) + export class Service { + constructor(dep: Dep) {} + } + `); + + env.driveMain(); + + + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('Service.ngInjectableDef ='); + expect(jsContents).toContain('factory: function Service_Factory(t) { var r = null; if (t) {'); + expect(jsContents).toContain('(r = new t(i0.inject(Dep)));'); + expect(jsContents) + .toContain('(r = (function (dep) { return new Service(dep); })(i0.inject(Dep)));'); + expect(jsContents).toContain('return r; }, providedIn: \'root\' });'); + expect(jsContents).not.toContain('__decorate'); + const dtsContents = env.getContents('test.d.ts'); + expect(dtsContents).toContain('static ngInjectableDef: i0.ɵInjectableDef;'); + }); + it('should compile Components without errors', () => { env.tsconfig(); env.write('test.ts', ` @@ -246,6 +328,92 @@ describe('ngtsc behavioral tests', () => { expect(dtsContents).toContain('static ngInjectorDef: i0.ɵInjectorDef'); }); + it('should compile NgModules with factory providers without errors', () => { + env.tsconfig(); + env.write('test.ts', ` + import {Component, NgModule} from '@angular/core'; + + export class Token {} + + @NgModule({}) + export class OtherModule {} + + @Component({ + selector: 'test-cmp', + template: 'this is a test', + }) + export class TestCmp {} + + @NgModule({ + declarations: [TestCmp], + providers: [{provide: Token, useFactory: () => new Token()}], + imports: [OtherModule], + }) + export class TestModule {} + `); + + env.driveMain(); + + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('i0.ɵdefineNgModule({ type: TestModule,'); + expect(jsContents) + .toContain( + `TestModule.ngInjectorDef = i0.defineInjector({ factory: ` + + `function TestModule_Factory(t) { return new (t || TestModule)(); }, providers: [{ provide: ` + + `Token, useFactory: function () { return new Token(); } }], imports: [[OtherModule]] });`); + + const dtsContents = env.getContents('test.d.ts'); + expect(dtsContents) + .toContain( + 'static ngModuleDef: i0.ɵNgModuleDefWithMeta'); + expect(dtsContents).toContain('static ngInjectorDef: i0.ɵInjectorDef'); + }); + + it('should compile NgModules with factory providers and deps without errors', () => { + env.tsconfig(); + env.write('test.ts', ` + import {Component, NgModule} from '@angular/core'; + + export class Dep {} + + export class Token { + constructor(dep: Dep) {} + } + + @NgModule({}) + export class OtherModule {} + + @Component({ + selector: 'test-cmp', + template: 'this is a test', + }) + export class TestCmp {} + + @NgModule({ + declarations: [TestCmp], + providers: [{provide: Token, useFactory: (dep: Dep) => new Token(dep), deps: [Dep]}], + imports: [OtherModule], + }) + export class TestModule {} + `); + + env.driveMain(); + + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('i0.ɵdefineNgModule({ type: TestModule,'); + expect(jsContents) + .toContain( + `TestModule.ngInjectorDef = i0.defineInjector({ factory: ` + + `function TestModule_Factory(t) { return new (t || TestModule)(); }, providers: [{ provide: ` + + `Token, useFactory: function (dep) { return new Token(dep); }, deps: [Dep] }], imports: [[OtherModule]] });`); + + const dtsContents = env.getContents('test.d.ts'); + expect(dtsContents) + .toContain( + 'static ngModuleDef: i0.ɵNgModuleDefWithMeta'); + expect(dtsContents).toContain('static ngInjectorDef: i0.ɵInjectorDef'); + }); + it('should compile NgModules with references to local components', () => { env.tsconfig(); env.write('test.ts', ` @@ -1782,4 +1950,4 @@ function expectTokenAtPosition( function normalize(input: string): string { return input.replace(/\s+/g, ' ').trim(); -} \ No newline at end of file +}