refactor(core): rename ngInjectableDef to ɵprov (#33151)

Injectable defs are not considered public API, so the property
that contains them should be prefixed with Angular's marker
for "private" ('ɵ') to discourage apps from relying on def
APIs directly.

This commit adds the prefix and shortens the name from
ngInjectableDef to "prov" (for "provider", since injector defs
are known as "inj"). This is because property names cannot
be minified by Uglify without turning on property mangling
(which most apps have turned off) and are thus size-sensitive.

PR Close #33151
This commit is contained in:
Kara Erickson
2019-10-15 12:41:30 -07:00
committed by Matias Niemelä
parent cda9248b33
commit 86104b82b8
38 changed files with 168 additions and 154 deletions

View File

@ -125,14 +125,14 @@ describe('ngInjectableDef Bazel Integration', () => {
expect(TestBed.inject(Service).value).toEqual('overridden');
});
it('does not override existing ngInjectableDef', () => {
it('does not override existing ɵprov', () => {
@Injectable({
providedIn: 'root',
useValue: new Service(false),
})
class Service {
constructor(public value: boolean) {}
static ngInjectableDef = {
static ɵprov = {
providedIn: 'root',
factory: () => new Service(true),
token: Service,
@ -143,7 +143,7 @@ describe('ngInjectableDef Bazel Integration', () => {
expect(TestBed.inject(Service).value).toEqual(true);
});
it('does not override existing ngInjectableDef in case of inheritance', () => {
it('does not override existing ɵprov in case of inheritance', () => {
@Injectable({
providedIn: 'root',
useValue: new ParentService(false),

View File

@ -423,7 +423,7 @@ export class MetadataBundler {
result[key] = this.convertFunction(moduleName, value);
} else if (isMetadataSymbolicCallExpression(value)) {
// Class members can also contain static members that call a function with module
// references. e.g. "static ngInjectableDef = ɵɵdefineInjectable(..)". We also need to
// references. e.g. "static ɵprov = ɵɵdefineInjectable(..)". We also need to
// convert these module references because otherwise these resolve to non-existent files.
result[key] = this.convertValue(moduleName, value);
} else {

View File

@ -92,7 +92,7 @@ export class InjectableDecoratorHandler implements
}
results.push({
name: 'ngInjectableDef',
name: 'ɵprov',
initializer: res.expression, statements,
type: res.type,
});
@ -214,8 +214,7 @@ function extractInjectableCtorDeps(
// Angular's DI.
//
// To deal with this, @Injectable() without an argument is more lenient, and if the
// constructor
// signature does not work for DI then an ngInjectableDef that throws.
// constructor signature does not work for DI then a provider def (ɵprov) that throws.
if (strictCtorDeps) {
ctorDeps = getValidConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
} else {

View File

@ -6,11 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
// Closure compiler transforms the form `Service.ngInjectableDef = X` into
// `Service$ngInjectableDef = X`. To prevent this transformation, such assignments need to be
// Closure compiler transforms the form `Service.ɵprov = X` into
// `Service$ɵprov = X`. To prevent this transformation, such assignments need to be
// annotated with @nocollapse. Unfortunately, a bug in Typescript where comments aren't propagated
// through the TS transformations precludes adding the comment via the AST. This workaround detects
// the static assignments to R3 properties such as ngInjectableDef using a regex, as output files
// the static assignments to R3 properties such as ɵprov using a regex, as output files
// are written, and applies the annotation through regex replacement.
//
// TODO(alxhub): clean up once fix for TS transformers lands in upstream
@ -22,7 +22,7 @@ const R3_DEF_NAME_PATTERN = [
'ngBaseDef',
'ɵcmp',
'ɵdir',
'ngInjectableDef',
'ɵprov',
'ɵinj',
'ɵmod',
'ɵpipe',

View File

@ -88,7 +88,7 @@ describe('compiler compliance: dependency injection', () => {
}`;
const def = `
MyService.ngInjectableDef = $r3$.ɵɵdefineInjectable({
MyService.ɵprov = $r3$.ɵɵdefineInjectable({
token: MyService,
factory: function(t) {
return MyService.ɵfac(t);
@ -144,7 +144,7 @@ describe('compiler compliance: dependency injection', () => {
};
const def = `
MyService.ngInjectableDef = $r3$.ɵɵdefineInjectable({
MyService.ɵprov = $r3$.ɵɵdefineInjectable({
token: MyService,
factory: function() {
return alternateFactory();
@ -178,7 +178,7 @@ describe('compiler compliance: dependency injection', () => {
};
const def = `
MyService.ngInjectableDef = $r3$.ɵɵdefineInjectable({
MyService.ɵprov = $r3$.ɵɵdefineInjectable({
token: MyService,
factory: function MyService_Factory(t) {
var r = null;
@ -217,7 +217,7 @@ describe('compiler compliance: dependency injection', () => {
};
const factory = `
MyService.ngInjectableDef = $r3$.ɵɵdefineInjectable({
MyService.ɵprov = $r3$.ɵɵdefineInjectable({
token: MyService,
factory: function(t) {
return MyAlternateService.ɵfac(t);
@ -253,7 +253,7 @@ describe('compiler compliance: dependency injection', () => {
};
const factory = `
MyService.ngInjectableDef = $r3$.ɵɵdefineInjectable({
MyService.ɵprov = $r3$.ɵɵdefineInjectable({
token: MyService,
factory: function MyService_Factory(t) {
var r = null;
@ -290,7 +290,7 @@ describe('compiler compliance: dependency injection', () => {
};
const factory = `
SomeProvider.ngInjectableDef = $r3$.ɵɵdefineInjectable({
SomeProvider.ɵprov = $r3$.ɵɵdefineInjectable({
token: SomeProvider,
factory: function(t) {
return SomeProviderImpl.ɵfac(t);

View File

@ -231,7 +231,7 @@ describe('metadata bundler', () => {
import {sharedFn} from '../shared';
export class MyClass {
static ngInjectableDef = sharedFn();
static ɵprov = sharedFn();
}
`,
}
@ -244,7 +244,7 @@ describe('metadata bundler', () => {
// The unbundled metadata should reference symbols using the relative module path.
expect(deepIndexMetadata.metadata['MyClass']).toEqual(jasmine.objectContaining<MetadataEntry>({
statics: {
ngInjectableDef: {
ɵprov: {
__symbolic: 'call',
expression: {
__symbolic: 'reference',
@ -260,7 +260,7 @@ describe('metadata bundler', () => {
// anywhere and it's not guaranteed that the relatively referenced files are present.
expect(bundledMetadata.metadata['MyClass']).toEqual(jasmine.objectContaining<MetadataEntry>({
statics: {
ngInjectableDef: {
ɵprov: {
__symbolic: 'call',
expression: {
__symbolic: 'reference',

View File

@ -2082,7 +2082,7 @@ describe('ngc transformer command-line', () => {
})
export class ServiceModule {}
`);
expect(source).not.toMatch(/ngInjectableDef/);
expect(source).not.toMatch(/ɵprov/);
});
it('on a service with a base class service', () => {
const source = compileService(`
@ -2102,7 +2102,7 @@ describe('ngc transformer command-line', () => {
})
export class ServiceModule {}
`);
expect(source).not.toMatch(/ngInjectableDef/);
expect(source).not.toMatch(/ɵprov/);
});
});
@ -2116,21 +2116,20 @@ describe('ngc transformer command-line', () => {
})
export class Service {}
`);
expect(source).toMatch(/ngInjectableDef = .+\.ɵɵdefineInjectable\(/);
expect(source).toMatch(/ngInjectableDef.*token: Service/);
expect(source).toMatch(/ngInjectableDef.*providedIn: .+\.Module/);
expect(source).toMatch(/ɵprov = .+\.ɵɵdefineInjectable\(/);
expect(source).toMatch(/ɵprov.*token: Service/);
expect(source).toMatch(/ɵprov.*providedIn: .+\.Module/);
});
it('ngInjectableDef in es5 mode is annotated @nocollapse when closure options are enabled',
() => {
writeConfig(`{
it('ɵprov in es5 mode is annotated @nocollapse when closure options are enabled', () => {
writeConfig(`{
"extends": "./tsconfig-base.json",
"angularCompilerOptions": {
"annotateForClosureCompiler": true
},
"files": ["service.ts"]
}`);
const source = compileService(`
const source = compileService(`
import {Injectable} from '@angular/core';
import {Module} from './module';
@ -2139,8 +2138,8 @@ describe('ngc transformer command-line', () => {
})
export class Service {}
`);
expect(source).toMatch(/\/\*\* @nocollapse \*\/ Service\.ngInjectableDef =/);
});
expect(source).toMatch(/\/\*\* @nocollapse \*\/ Service\.ɵprov =/);
});
it('compiles a useValue InjectableDef', () => {
const source = compileService(`
@ -2155,7 +2154,7 @@ describe('ngc transformer command-line', () => {
})
export class Service {}
`);
expect(source).toMatch(/ngInjectableDef.*return CONST_SERVICE/);
expect(source).toMatch(/ɵprov.*return CONST_SERVICE/);
});
it('compiles a useExisting InjectableDef', () => {
@ -2172,7 +2171,7 @@ describe('ngc transformer command-line', () => {
})
export class Service {}
`);
expect(source).toMatch(/ngInjectableDef.*return ..\.ɵɵinject\(Existing\)/);
expect(source).toMatch(/ɵprov.*return ..\.ɵɵinject\(Existing\)/);
});
it('compiles a useFactory InjectableDef with optional dep', () => {
@ -2192,7 +2191,7 @@ describe('ngc transformer command-line', () => {
constructor(e: Existing|null) {}
}
`);
expect(source).toMatch(/ngInjectableDef.*return ..\(..\.ɵɵinject\(Existing, 8\)/);
expect(source).toMatch(/ɵprov.*return ..\(..\.ɵɵinject\(Existing, 8\)/);
});
it('compiles a useFactory InjectableDef with skip-self dep', () => {
@ -2212,7 +2211,7 @@ describe('ngc transformer command-line', () => {
constructor(e: Existing) {}
}
`);
expect(source).toMatch(/ngInjectableDef.*return ..\(..\.ɵɵinject\(Existing, 4\)/);
expect(source).toMatch(/ɵprov.*return ..\(..\.ɵɵinject\(Existing, 4\)/);
});
it('compiles a service that depends on a token', () => {
@ -2229,9 +2228,9 @@ describe('ngc transformer command-line', () => {
constructor(@Inject(TOKEN) value: boolean) {}
}
`);
expect(source).toMatch(/ngInjectableDef = .+\.ɵɵdefineInjectable\(/);
expect(source).toMatch(/ngInjectableDef.*token: Service/);
expect(source).toMatch(/ngInjectableDef.*providedIn: .+\.Module/);
expect(source).toMatch(/ɵprov = .+\.ɵɵdefineInjectable\(/);
expect(source).toMatch(/ɵprov.*token: Service/);
expect(source).toMatch(/ɵprov.*providedIn: .+\.Module/);
});
it('generates exports.* references when outputting commonjs', () => {

View File

@ -61,12 +61,12 @@ runInEachFileSystem(os => {
const jsContents = env.getContents('test.js');
expect(jsContents).toContain('Dep.ngInjectableDef =');
expect(jsContents).toContain('Service.ngInjectableDef =');
expect(jsContents).toContain('Dep.ɵprov =');
expect(jsContents).toContain('Service.ɵprov =');
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>;');
expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef<Dep>;');
expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef<Service>;');
expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Dep>;');
expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Service>;');
});
@ -83,10 +83,10 @@ runInEachFileSystem(os => {
const jsContents = env.getContents('test.js');
expect(jsContents).toContain('Store.ngInjectableDef =');
expect(jsContents).toContain('Store.ɵprov =');
const dtsContents = env.getContents('test.d.ts');
expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Store<any>>;');
expect(dtsContents).toContain('static ngInjectableDef: i0.ɵɵInjectableDef<Store<any>>;');
expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef<Store<any>>;');
});
it('should compile Injectables with providedIn without errors', () => {
@ -106,16 +106,16 @@ runInEachFileSystem(os => {
const jsContents = env.getContents('test.js');
expect(jsContents).toContain('Dep.ngInjectableDef =');
expect(jsContents).toContain('Service.ngInjectableDef =');
expect(jsContents).toContain('Dep.ɵprov =');
expect(jsContents).toContain('Service.ɵprov =');
expect(jsContents)
.toContain(
'Service.ɵfac = function Service_Factory(t) { return new (t || Service)(i0.ɵɵinject(Dep)); };');
expect(jsContents).toContain('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>;');
expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef<Dep>;');
expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef<Service>;');
expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Dep>;');
expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Service>;');
});
@ -134,14 +134,14 @@ runInEachFileSystem(os => {
const jsContents = env.getContents('test.js');
expect(jsContents).toContain('Service.ngInjectableDef =');
expect(jsContents).toContain('Service.ɵprov =');
expect(jsContents)
.toContain('factory: function () { return (function () { return new Service(); })(); }');
expect(jsContents).toContain('Service_Factory(t) { return new (t || Service)(); }');
expect(jsContents).toContain(', providedIn: \'root\' });');
expect(jsContents).not.toContain('__decorate');
const dtsContents = env.getContents('test.d.ts');
expect(dtsContents).toContain('static ngInjectableDef: i0.ɵɵInjectableDef<Service>;');
expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef<Service>;');
expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Service>;');
});
@ -162,7 +162,7 @@ runInEachFileSystem(os => {
const jsContents = env.getContents('test.js');
expect(jsContents).toContain('Service.ngInjectableDef =');
expect(jsContents).toContain('Service.ɵprov =');
expect(jsContents).toContain('factory: function Service_Factory(t) { var r = null; if (t) {');
expect(jsContents).toContain('return new (t || Service)(i0.ɵɵinject(Dep));');
expect(jsContents)
@ -170,7 +170,7 @@ runInEachFileSystem(os => {
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>;');
expect(dtsContents).toContain('static ɵprov: i0.ɵɵInjectableDef<Service>;');
expect(dtsContents).toContain('static ɵfac: i0.ɵɵFactoryDef<Service>;');
});
@ -375,7 +375,7 @@ runInEachFileSystem(os => {
expect(jsContents).toContain('TestComponent.ɵcmp = i0.ɵɵdefineComponent');
expect(jsContents).toContain('TestDirective.ɵdir = i0.ɵɵdefineDirective');
expect(jsContents).toContain('TestPipe.ɵpipe = i0.ɵɵdefinePipe');
expect(jsContents).toContain('TestInjectable.ngInjectableDef = i0.ɵɵdefineInjectable');
expect(jsContents).toContain('TestInjectable.ɵprov = i0.ɵɵdefineInjectable');
expect(jsContents).toContain('MyModule.ɵmod = i0.ɵɵdefineNgModule');
expect(jsContents).toContain('MyModule.ɵinj = i0.ɵɵdefineInjector');
expect(jsContents).toContain('inputs: { input: "input" }');
@ -1183,10 +1183,10 @@ runInEachFileSystem(os => {
expect(jsContents).toContain('TestNgModule.ɵmod =');
// Validate that each class also has an injectable definition.
expect(jsContents).toContain('TestCmp.ngInjectableDef =');
expect(jsContents).toContain('TestDir.ngInjectableDef =');
expect(jsContents).toContain('TestPipe.ngInjectableDef =');
expect(jsContents).toContain('TestNgModule.ngInjectableDef =');
expect(jsContents).toContain('TestCmp.ɵprov =');
expect(jsContents).toContain('TestDir.ɵprov =');
expect(jsContents).toContain('TestPipe.ɵprov =');
expect(jsContents).toContain('TestNgModule.ɵprov =');
// Validate that each class's .d.ts declaration has the primary definition.
expect(dtsContents).toContain('ComponentDefWithMeta<TestCmp');