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:
parent
9460218f36
commit
6072ca87e1
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue