Skip to content

Commit

Permalink
refactor(core): support EnvironmentProviders types internally (#47669)
Browse files Browse the repository at this point in the history
This commit modifies `R3Injector` and other code in Angular that deals with
providers, to handle `EnvironmentProviders` objects as well as normal
`Provider` types. There is no user-visible impact to this change, but it
prepares the core of the DI system for the introduction of
`EnvironmentProviders` as a public feature.

PR Close #47669
  • Loading branch information
alxhub authored and thePunderWoman committed Oct 7, 2022
1 parent 3133351 commit c5a1b90
Show file tree
Hide file tree
Showing 13 changed files with 143 additions and 36 deletions.
30 changes: 23 additions & 7 deletions packages/core/src/di/provider_collection.ts
Expand Up @@ -21,7 +21,7 @@ import {resolveForwardRef} from './forward_ref';
import {ENVIRONMENT_INITIALIZER} from './initializer_token';
import {ɵɵinject as inject} from './injector_compatibility';
import {getInjectorDef, InjectorType, InjectorTypeWithProviders} from './interface/defs';
import {ClassProvider, ConstructorProvider, EnvironmentProviders, ExistingProvider, FactoryProvider, ImportedNgModuleProviders, ModuleWithProviders, Provider, StaticClassProvider, TypeProvider, ValueProvider} from './interface/provider';
import {ClassProvider, ConstructorProvider, EnvironmentProviders, ExistingProvider, FactoryProvider, ImportedNgModuleProviders, InternalEnvironmentProviders, isEnvironmentProviders, ModuleWithProviders, Provider, StaticClassProvider, TypeProvider, ValueProvider} from './interface/provider';
import {INJECTOR_DEF_TYPES} from './internal_tokens';

/**
Expand Down Expand Up @@ -128,7 +128,7 @@ function processInjectorTypesWithProviders(
typesWithProviders: InjectorTypeWithProviders<unknown>[], providersOut: Provider[]): void {
for (let i = 0; i < typesWithProviders.length; i++) {
const {ngModule, providers} = typesWithProviders[i];
deepForEach(providers!, provider => {
deepForEachProvider(providers! as Array<Provider|InternalEnvironmentProviders>, provider => {
ngDevMode && validateProvider(provider, providers || EMPTY_ARRAY, ngModule);
providersOut.push(provider);
});
Expand Down Expand Up @@ -261,12 +261,12 @@ export function walkProviderTree(
}

// Next, include providers listed on the definition itself.
const defProviders = injDef.providers;
const defProviders = injDef.providers as Array<SingleProvider|InternalEnvironmentProviders>;
if (defProviders != null && !isDuplicate) {
const injectorType = container as InjectorType<any>;
deepForEach(defProviders, provider => {
ngDevMode && validateProvider(provider, defProviders as SingleProvider[], injectorType);
providersOut.push(provider);
deepForEachProvider(defProviders, provider => {
ngDevMode && validateProvider(provider as SingleProvider, defProviders, injectorType);
providersOut.push(provider as SingleProvider);
});
}
} else {
Expand All @@ -280,7 +280,8 @@ export function walkProviderTree(
}

function validateProvider(
provider: SingleProvider, providers: SingleProvider[], containerType: Type<unknown>): void {
provider: SingleProvider, providers: Array<SingleProvider|InternalEnvironmentProviders>,
containerType: Type<unknown>): void {
if (isTypeProvider(provider) || isValueProvider(provider) || isFactoryProvider(provider) ||
isExistingProvider(provider)) {
return;
Expand All @@ -294,6 +295,21 @@ function validateProvider(
}
}

function deepForEachProvider(
providers: Array<Provider|InternalEnvironmentProviders>,
fn: (provider: SingleProvider) => void): void {
for (let provider of providers) {
if (isEnvironmentProviders(provider)) {
provider = provider.ɵproviders;
}
if (Array.isArray(provider)) {
deepForEachProvider(provider, fn);
} else {
fn(provider);
}
}
}

export const USE_VALUE =
getClosureSafeProperty<ValueProvider>({provide: String, useValue: getClosureSafeProperty});

Expand Down
22 changes: 13 additions & 9 deletions packages/core/src/di/r3_injector.ts
Expand Up @@ -27,7 +27,7 @@ import {catchInjectorError, convertToBitFlags, injectArgs, NG_TEMP_TOKEN_PATH, s
import {INJECTOR} from './injector_token';
import {getInheritedInjectableDef, getInjectableDef, InjectorType, ɵɵInjectableDeclaration} from './interface/defs';
import {InjectFlags, InjectOptions} from './interface/injector';
import {ClassProvider, ConstructorProvider, ImportedNgModuleProviders, Provider, StaticClassProvider} from './interface/provider';
import {ClassProvider, ConstructorProvider, EnvironmentProviders, ImportedNgModuleProviders, InternalEnvironmentProviders, isEnvironmentProviders, Provider, StaticClassProvider} from './interface/provider';
import {INJECTOR_DEF_TYPES} from './internal_tokens';
import {NullInjector} from './null_injector';
import {isExistingProvider, isFactoryProvider, isTypeProvider, isValueProvider, SingleProvider} from './provider_collection';
Expand Down Expand Up @@ -157,11 +157,14 @@ export class R3Injector extends EnvironmentInjector {
private injectorDefTypes: Set<Type<unknown>>;

constructor(
providers: Array<Provider|ImportedNgModuleProviders>, readonly parent: Injector,
readonly source: string|null, readonly scopes: Set<InjectorScope>) {
providers: Array<Provider|ImportedNgModuleProviders|EnvironmentProviders>,
readonly parent: Injector, readonly source: string|null,
readonly scopes: Set<InjectorScope>) {
super();
// Start off by creating Records for every provider.
forEachSingleProvider(providers, provider => this.processProvider(provider));
forEachSingleProvider(
providers as Array<Provider|ImportedNgModuleProviders|InternalEnvironmentProviders>,
provider => this.processProvider(provider));

// Make sure the INJECTOR token provides this injector.
this.records.set(INJECTOR, makeRecord(undefined, this));
Expand Down Expand Up @@ -460,7 +463,7 @@ function providerToRecord(provider: SingleProvider): Record<any> {
export function providerToFactory(
provider: SingleProvider, ngModuleType?: InjectorType<any>, providers?: any[]): () => any {
let factory: (() => any)|undefined = undefined;
if (ngDevMode && isImportedNgModuleProviders(provider)) {
if (ngDevMode && (isImportedNgModuleProviders(provider) || isEnvironmentProviders(provider))) {
throwInvalidProviderError(undefined, providers, provider);
}

Expand Down Expand Up @@ -517,19 +520,20 @@ function couldBeInjectableType(value: any): value is ProviderToken<any> {

function isImportedNgModuleProviders(provider: Provider|ImportedNgModuleProviders):
provider is ImportedNgModuleProviders {
return !!(provider as ImportedNgModuleProviders).ɵproviders;
return provider && !!(provider as ImportedNgModuleProviders).ɵproviders;
}

function forEachSingleProvider(
providers: Array<Provider|ImportedNgModuleProviders>,
providers: Array<Provider|ImportedNgModuleProviders|InternalEnvironmentProviders>,
fn: (provider: SingleProvider) => void): void {
for (const provider of providers) {
if (Array.isArray(provider)) {
forEachSingleProvider(provider, fn);
} else if (isImportedNgModuleProviders(provider)) {
} else if (
provider && (isImportedNgModuleProviders(provider) || isEnvironmentProviders(provider))) {
forEachSingleProvider(provider.ɵproviders, fn);
} else {
fn(provider);
fn(provider as SingleProvider);
}
}
}
16 changes: 11 additions & 5 deletions packages/core/src/render3/errors_di.ts
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {ImportedNgModuleProviders} from '../di/interface/provider';
import {isEnvironmentProviders} from '../di/interface/provider';
import {RuntimeError, RuntimeErrorCode} from '../errors';
import {Type} from '../interface/type';
import {stringify} from '../util/stringify';
Expand All @@ -33,10 +33,16 @@ export function throwInvalidProviderError(
throw new Error(`Invalid provider for the NgModule '${
stringify(ngModuleType)}' - only instances of Provider and Type are allowed, got: [${
providerDetail.join(', ')}]`);
} else if ((provider as ImportedNgModuleProviders).ɵproviders) {
throw new RuntimeError(
RuntimeErrorCode.PROVIDER_IN_WRONG_CONTEXT,
`Invalid providers from 'importProvidersFrom' present in a non-environment injector. 'importProvidersFrom' can't be used for component providers.`);
} else if (isEnvironmentProviders(provider)) {
if (provider.ɵfromNgModule) {
throw new RuntimeError(
RuntimeErrorCode.PROVIDER_IN_WRONG_CONTEXT,
`Invalid providers from 'importProvidersFrom' present in a non-environment injector. 'importProvidersFrom' can't be used for component providers.`);
} else {
throw new RuntimeError(
RuntimeErrorCode.PROVIDER_IN_WRONG_CONTEXT,
`Invalid providers present in a non-environment injector. 'EnvironmentProviders' can't be used for component providers.`);
}
} else {
throw new Error('Invalid provider');
}
Expand Down
Expand Up @@ -710,6 +710,9 @@
{
"name": "deepForEach"
},
{
"name": "deepForEachProvider"
},
{
"name": "detachMovedView"
},
Expand Down Expand Up @@ -1001,6 +1004,9 @@
{
"name": "isElementNode"
},
{
"name": "isEnvironmentProviders"
},
{
"name": "isFunction"
},
Expand Down
Expand Up @@ -497,6 +497,9 @@
{
"name": "deepForEach"
},
{
"name": "deepForEachProvider"
},
{
"name": "detachMovedView"
},
Expand Down Expand Up @@ -749,6 +752,9 @@
{
"name": "isCurrentTNodeParent"
},
{
"name": "isEnvironmentProviders"
},
{
"name": "isFunction"
},
Expand Down
Expand Up @@ -722,6 +722,9 @@
{
"name": "deepForEach"
},
{
"name": "deepForEachProvider"
},
{
"name": "defaultIterableDiffersFactory"
},
Expand Down Expand Up @@ -1097,6 +1100,9 @@
{
"name": "isEmptyInputValue"
},
{
"name": "isEnvironmentProviders"
},
{
"name": "isFormControlState"
},
Expand Down
Expand Up @@ -692,6 +692,9 @@
{
"name": "deepForEach"
},
{
"name": "deepForEachProvider"
},
{
"name": "defaultIterableDiffersFactory"
},
Expand Down Expand Up @@ -1055,6 +1058,9 @@
{
"name": "isDirectiveHost"
},
{
"name": "isEnvironmentProviders"
},
{
"name": "isFormControlState"
},
Expand Down
Expand Up @@ -359,6 +359,9 @@
{
"name": "deepForEach"
},
{
"name": "deepForEachProvider"
},
{
"name": "detachMovedView"
},
Expand Down Expand Up @@ -557,6 +560,9 @@
{
"name": "isComponentDef"
},
{
"name": "isEnvironmentProviders"
},
{
"name": "isFunction"
},
Expand Down
Expand Up @@ -83,6 +83,9 @@
{
"name": "deepForEach"
},
{
"name": "deepForEachProvider"
},
{
"name": "forEachSingleProvider"
},
Expand Down Expand Up @@ -122,6 +125,9 @@
{
"name": "internalImportProvidersFrom"
},
{
"name": "isEnvironmentProviders"
},
{
"name": "isImportedNgModuleProviders"
},
Expand Down
6 changes: 6 additions & 0 deletions packages/core/test/bundling/router/bundle.golden_symbols.json
Expand Up @@ -884,6 +884,9 @@
{
"name": "deepForEach"
},
{
"name": "deepForEachProvider"
},
{
"name": "defaultErrorFactory"
},
Expand Down Expand Up @@ -1340,6 +1343,9 @@
{
"name": "isEmptyError"
},
{
"name": "isEnvironmentProviders"
},
{
"name": "isFunction"
},
Expand Down
Expand Up @@ -428,6 +428,9 @@
{
"name": "deepForEach"
},
{
"name": "deepForEachProvider"
},
{
"name": "detachMovedView"
},
Expand Down Expand Up @@ -641,6 +644,9 @@
{
"name": "isComponentDef"
},
{
"name": "isEnvironmentProviders"
},
{
"name": "isFunction"
},
Expand Down
6 changes: 6 additions & 0 deletions packages/core/test/bundling/todo/bundle.golden_symbols.json
Expand Up @@ -602,6 +602,9 @@
{
"name": "deepForEach"
},
{
"name": "deepForEachProvider"
},
{
"name": "defaultIterableDiffersFactory"
},
Expand Down Expand Up @@ -926,6 +929,9 @@
{
"name": "isDirectiveHost"
},
{
"name": "isEnvironmentProviders"
},
{
"name": "isFunction"
},
Expand Down

0 comments on commit c5a1b90

Please sign in to comment.