fix(ivy): deps are actually supported (#28076)

This code was throwing if the `deps` array of a provider has several elements, but at the next line it resolves them... With this check `ngtsc` couldn’t compile `ng-bootstrap` for example.

PR Close #28076
This commit is contained in:
cexbrayat 2019-01-11 14:50:26 +01:00 committed by Andrew Kushnir
parent 9460218f36
commit 6072ca87e1
2 changed files with 169 additions and 4 deletions

View File

@ -109,9 +109,6 @@ function extractInjectableMetadata(
ErrorCode.VALUE_NOT_LITERAL, depsExpr, ErrorCode.VALUE_NOT_LITERAL, depsExpr,
`In Ivy, deps metadata must be an inline array.`); `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)); userDeps = depsExpr.elements.map(dep => getDep(dep, reflector));
} }

View File

@ -66,6 +66,88 @@ describe('ngtsc behavioral tests', () => {
expect(dtsContents).toContain('static ngInjectableDef: i0.ɵInjectableDef<Store<any>>;'); expect(dtsContents).toContain('static ngInjectableDef: i0.ɵInjectableDef<Store<any>>;');
}); });
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<Dep>;');
expect(dtsContents).toContain('static ngInjectableDef: i0.ɵInjectableDef<Service>;');
});
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<Service>;');
});
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<Service>;');
});
it('should compile Components without errors', () => { it('should compile Components without errors', () => {
env.tsconfig(); env.tsconfig();
env.write('test.ts', ` env.write('test.ts', `
@ -246,6 +328,92 @@ describe('ngtsc behavioral tests', () => {
expect(dtsContents).toContain('static ngInjectorDef: i0.ɵInjectorDef'); 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<TestModule, [typeof TestCmp], [typeof OtherModule], never>');
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<TestModule, [typeof TestCmp], [typeof OtherModule], never>');
expect(dtsContents).toContain('static ngInjectorDef: i0.ɵInjectorDef');
});
it('should compile NgModules with references to local components', () => { it('should compile NgModules with references to local components', () => {
env.tsconfig(); env.tsconfig();
env.write('test.ts', ` env.write('test.ts', `
@ -1782,4 +1950,4 @@ function expectTokenAtPosition<T extends ts.Node>(
function normalize(input: string): string { function normalize(input: string): string {
return input.replace(/\s+/g, ' ').trim(); return input.replace(/\s+/g, ' ').trim();
} }