feat: tree-shakeable providers API updates (#22655)

Rename @Injectable({scope -> providedIn}).

Instead of {providedIn: APP_ROOT_SCOPE}, accept {providedIn: 'root'}.
Also, {providedIn: null} implies the injectable should not be added
to any scope.

PR Close #22655
This commit is contained in:
Alex Rickabaugh
2018-03-07 15:10:38 -08:00
committed by Kara Erickson
parent 21e44c6ba9
commit db56836425
29 changed files with 219 additions and 114 deletions

View File

@ -35,7 +35,7 @@ export class AppComponent {
export class DepAppModule {
}
@Injectable({scope: DepAppModule})
@Injectable({providedIn: DepAppModule})
export class ShakeableService {
constructor(readonly normal: NormalService) {}
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {APP_ROOT_SCOPE, Component, Injectable, NgModule, Optional, Self} from '@angular/core';
import {Component, Injectable, NgModule, Optional, Self} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {ServerModule} from '@angular/platform-server';
import {RouterModule} from '@angular/router';

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {APP_ROOT_SCOPE, Injectable} from '@angular/core';
import {Injectable} from '@angular/core';
@Injectable({
scope: APP_ROOT_SCOPE,
providedIn: 'root',
})
export class Service {
}

View File

@ -36,6 +36,6 @@ export class AppComponent {
export class SelfAppModule {
}
@Injectable({scope: SelfAppModule})
@Injectable({providedIn: SelfAppModule})
export class ShakeableService {
}

View File

@ -32,7 +32,7 @@ export class AppComponent {
export class StringAppModule {
}
@Injectable({scope: StringAppModule})
@Injectable({providedIn: StringAppModule})
export class Service {
constructor(@Inject('someStringToken') readonly data: string) {}
}

View File

@ -17,7 +17,7 @@ export class TokenModule {
}
export const TOKEN = new InjectionToken('test', {
scope: TokenModule,
providedIn: TokenModule,
factory: () => new Service(inject(Dep)),
});

View File

@ -14,6 +14,7 @@ ts_library(
deps = [
"//packages/compiler-cli/integrationtest/bazel/injectable_def/app",
"//packages/core",
"//packages/core/testing",
"//packages/platform-server",
],
)

View File

@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {enableProdMode} from '@angular/core';
import {Component, Injectable, NgModule} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {renderModuleFactory} from '@angular/platform-server';
import {BasicAppModuleNgFactory} from 'app_built/src/basic.ngfactory';
import {DepAppModuleNgFactory} from 'app_built/src/dep.ngfactory';
@ -16,8 +17,6 @@ import {SelfAppModuleNgFactory} from 'app_built/src/self.ngfactory';
import {StringAppModuleNgFactory} from 'app_built/src/string.ngfactory';
import {TokenAppModuleNgFactory} from 'app_built/src/token.ngfactory';
enableProdMode();
describe('ngInjectableDef Bazel Integration', () => {
it('works in AOT', done => {
renderModuleFactory(BasicAppModuleNgFactory, {
@ -88,4 +87,41 @@ describe('ngInjectableDef Bazel Integration', () => {
done();
});
});
it('allows provider override in JIT for root-scoped @Injectables', () => {
@Injectable({
providedIn: 'root',
useValue: new Service('default'),
})
class Service {
constructor(readonly value: string) {}
}
TestBed.configureTestingModule({});
TestBed.overrideProvider(Service, {useValue: new Service('overridden')});
expect(TestBed.get(Service).value).toEqual('overridden');
});
it('allows provider override in JIT for module-scoped @Injectables', () => {
@NgModule()
class Module {
}
@Injectable({
providedIn: Module,
useValue: new Service('default'),
})
class Service {
constructor(readonly value: string) {}
}
TestBed.configureTestingModule({
imports: [Module],
});
TestBed.overrideProvider(Service, {useValue: new Service('overridden')});
expect(TestBed.get(Service).value).toEqual('overridden');
});
});

View File

@ -13,7 +13,7 @@ export class Lib1Module {
}
@Injectable({
scope: Lib1Module,
providedIn: Lib1Module,
})
export class Service {
static instanceCount = 0;

View File

@ -2058,13 +2058,13 @@ describe('ngc transformer command-line', () => {
import {Module} from './module';
@Injectable({
scope: Module,
providedIn: Module,
})
export class Service {}
`);
expect(source).toMatch(/ngInjectableDef = .+\.defineInjectable\(/);
expect(source).toMatch(/ngInjectableDef.*token: Service/);
expect(source).toMatch(/ngInjectableDef.*scope: .+\.Module/);
expect(source).toMatch(/ngInjectableDef.*providedIn: .+\.Module/);
});
it('ngInjectableDef in es5 mode is annotated @nocollapse when closure options are enabled',
@ -2081,7 +2081,7 @@ describe('ngc transformer command-line', () => {
import {Module} from './module';
@Injectable({
scope: Module,
providedIn: Module,
})
export class Service {}
`);
@ -2096,7 +2096,7 @@ describe('ngc transformer command-line', () => {
export const CONST_SERVICE: Service = null;
@Injectable({
scope: Module,
providedIn: Module,
useValue: CONST_SERVICE
})
export class Service {}
@ -2113,7 +2113,7 @@ describe('ngc transformer command-line', () => {
export class Existing {}
@Injectable({
scope: Module,
providedIn: Module,
useExisting: Existing,
})
export class Service {}
@ -2130,7 +2130,7 @@ describe('ngc transformer command-line', () => {
export class Existing {}
@Injectable({
scope: Module,
providedIn: Module,
useFactory: (existing: Existing|null) => new Service(existing),
deps: [[new Optional(), Existing]],
})
@ -2150,7 +2150,7 @@ describe('ngc transformer command-line', () => {
export class Existing {}
@Injectable({
scope: Module,
providedIn: Module,
useFactory: (existing: Existing) => new Service(existing),
deps: [[new SkipSelf(), Existing]],
})
@ -2166,10 +2166,10 @@ describe('ngc transformer command-line', () => {
import {Inject, Injectable, InjectionToken} from '@angular/core';
import {Module} from './module';
export const TOKEN = new InjectionToken('desc', {scope: Module, factory: () => true});
export const TOKEN = new InjectionToken('desc', {providedIn: Module, factory: () => true});
@Injectable({
scope: Module,
providedIn: Module,
})
export class Service {
constructor(@Inject(TOKEN) value: boolean) {}
@ -2177,7 +2177,7 @@ describe('ngc transformer command-line', () => {
`);
expect(source).toMatch(/ngInjectableDef = .+\.defineInjectable\(/);
expect(source).toMatch(/ngInjectableDef.*token: Service/);
expect(source).toMatch(/ngInjectableDef.*scope: .+\.Module/);
expect(source).toMatch(/ngInjectableDef.*providedIn: .+\.Module/);
});
it('generates exports.* references when outputting commonjs', () => {
@ -2189,15 +2189,15 @@ describe('ngc transformer command-line', () => {
"files": ["service.ts"]
}`);
const source = compileService(`
import {Inject, Injectable, InjectionToken, APP_ROOT_SCOPE} from '@angular/core';
import {Inject, Injectable, InjectionToken} from '@angular/core';
import {Module} from './module';
export const TOKEN = new InjectionToken<string>('test token', {
scope: APP_ROOT_SCOPE,
providedIn: 'root',
factory: () => 'this is a test',
});
@Injectable({scope: APP_ROOT_SCOPE})
@Injectable({providedIn: 'root'})
export class Service {
constructor(@Inject(TOKEN) token: any) {}
}