Skip to content

Commit

Permalink
refactor(core): better typing for decorator scope info to handle nest…
Browse files Browse the repository at this point in the history
…ed array and forward refs

TODO
  • Loading branch information
pmvald committed Sep 4, 2023
1 parent 690c832 commit 8a9eea3
Show file tree
Hide file tree
Showing 11 changed files with 61 additions and 46 deletions.
4 changes: 2 additions & 2 deletions packages/common/src/directives/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Provider} from '@angular/core';
import {Type} from '@angular/core';

import {NgClass} from './ng_class';
import {NgComponentOutlet} from './ng_component_outlet';
Expand Down Expand Up @@ -40,7 +40,7 @@ export {
* A collection of Angular directives that are likely to be used in each and every Angular
* application.
*/
export const COMMON_DIRECTIVES: Provider[] = [
export const COMMON_DIRECTIVES: Array<Type<any>> = [
NgClass,
NgComponentOutlet,
NgForOf,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/di/forward_ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function forwardRef(forwardRefFn: ForwardRefFn): Type<any> {
* @see {@link forwardRef}
* @publicApi
*/
export function resolveForwardRef<T>(type: T): T {
export function resolveForwardRef<T>(type: T|(() => T)): T {
return isForwardRef(type) ? type() : type;
}

Expand Down
6 changes: 4 additions & 2 deletions packages/core/src/metadata/directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
*/

import {ChangeDetectionStrategy} from '../change_detection/constants';
import {Provider} from '../di/interface/provider';
import {ModuleWithProviders, Provider} from '../di/interface/provider';
import {Type} from '../interface/type';
import {compileComponent, compileDirective} from '../render3/jit/directive';
import {compilePipe} from '../render3/jit/pipe';
import {NestedArray} from '../util/array_utils';
import {makeDecorator, makePropDecorator, TypeDecorator} from '../util/decorators';

import {SchemaMetadata} from './schema';
Expand Down Expand Up @@ -650,7 +651,8 @@ export interface Component extends Directive {
* More information about standalone components, directives, and pipes can be found in [this
* guide](guide/standalone-components).
*/
imports?: (Type<any>|ReadonlyArray<any>)[];
imports?: NestedArray<Type<any>|ModuleWithProviders<any>|(() => Type<any>)|
(() => ModuleWithProviders<any>)>;

/**
* The set of schemas that declare elements to be allowed in a standalone component. Elements and
Expand Down
11 changes: 6 additions & 5 deletions packages/core/src/metadata/ng_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {EnvironmentProviders, ModuleWithProviders, Provider} from '../di/interfa
import {Type} from '../interface/type';
import {SchemaMetadata} from '../metadata/schema';
import {compileNgModule} from '../render3/jit/module';
import {NestedArray} from '../util/array_utils';
import {makeDecorator, TypeDecorator} from '../util/decorators';


/**
* Type of the NgModule decorator / constructor function.
*
Expand Down Expand Up @@ -107,7 +107,7 @@ export interface NgModule {
* }
* ```
*/
declarations?: Array<Type<any>|any[]>;
declarations?: NestedArray<Type<any>|(() => Type<any>)>;

/**
* The set of NgModules whose exported [declarables](guide/glossary#declarable)
Expand Down Expand Up @@ -136,7 +136,8 @@ export interface NgModule {
* ```
*
*/
imports?: Array<Type<any>|ModuleWithProviders<{}>|any[]>;
imports?: NestedArray<Type<any>|ModuleWithProviders<any>|(() => Type<any>)|
(() => ModuleWithProviders<any>)>;

/**
* The set of components, directives, and pipes declared in this
Expand Down Expand Up @@ -168,12 +169,12 @@ export interface NgModule {
* }
* ```
*/
exports?: Array<Type<any>|any[]>;
exports?: NestedArray<Type<any>|(() => Type<any>)>;

/**
* The set of components that are bootstrapped when this module is bootstrapped.
*/
bootstrap?: Array<Type<any>|any[]>;
bootstrap?: NestedArray<Type<any>|(() => Type<any>)>;

/**
* The set of schemas that declare elements to be allowed in the NgModule.
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/render3/deps_tracker/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import {Type} from '../../interface/type';
import {Component} from '../../metadata';
import {NgModuleType} from '../../metadata/ng_module_def';
import {ComponentType, DependencyTypeList, DirectiveType, NgModuleScopeInfoFromDecorator, PipeType} from '../interfaces/definition';

Expand Down Expand Up @@ -98,13 +99,12 @@ export interface DepsTrackerApi {
* Returns the scope of standalone component. Mainly to be used by JIT. This method should be
* called lazily after the initial parsing so that all the forward refs can be resolved.
*
* @param rawImports the imports statement as appears on the component decorate which consists of
* @param rawImports the imports statement as appears on the component decorator which consists of
* Type as well as forward refs.
*
* The scope value here is memoized. To enforce a new calculation bust the cache by using
* `clearScopeCacheFor` method.
*/
getStandaloneComponentScope(
type: ComponentType<any>,
rawImports: (Type<any>|(() => Type<any>))[]): StandaloneComponentScope;
getStandaloneComponentScope(type: ComponentType<any>, rawImports: Component['imports']):
StandaloneComponentScope;
}
10 changes: 5 additions & 5 deletions packages/core/src/render3/deps_tracker/deps_tracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
import {resolveForwardRef} from '../../di';
import {RuntimeError, RuntimeErrorCode} from '../../errors';
import {Type} from '../../interface/type';
import {Component} from '../../metadata';
import {NgModuleType} from '../../metadata/ng_module_def';
import {getComponentDef, getNgModuleDef, isStandalone} from '../definition';
import {ComponentType, NgModuleScopeInfoFromDecorator, RawScopeInfoFromDecorator} from '../interfaces/definition';
import {ComponentType, NgModuleScopeInfoFromDecorator} from '../interfaces/definition';
import {isComponent, isDirective, isNgModule, isPipe, verifyStandaloneImport} from '../jit/util';
import {maybeUnwrapFn} from '../util/misc_utils';

Expand Down Expand Up @@ -60,7 +61,7 @@ class DepsTracker implements DepsTrackerApi {
}

/** @override */
getComponentDependencies(type: ComponentType<any>, rawImports?: RawScopeInfoFromDecorator[]):
getComponentDependencies(type: ComponentType<any>, rawImports: Component['imports']):
ComponentDependencies {
this.resolveNgModulesDecls();

Expand Down Expand Up @@ -216,7 +217,7 @@ class DepsTracker implements DepsTrackerApi {
}

/** @override */
getStandaloneComponentScope(type: ComponentType<any>, rawImports?: RawScopeInfoFromDecorator[]):
getStandaloneComponentScope(type: ComponentType<any>, rawImports?: Component['imports']):
StandaloneComponentScope {
if (this.standaloneComponentsScopeCache.has(type)) {
return this.standaloneComponentsScopeCache.get(type)!;
Expand All @@ -229,8 +230,7 @@ class DepsTracker implements DepsTrackerApi {
}

private computeStandaloneComponentScope(
type: ComponentType<any>,
rawImports?: RawScopeInfoFromDecorator[]): StandaloneComponentScope {
type: ComponentType<any>, rawImports?: Component['imports']): StandaloneComponentScope {
const ans: StandaloneComponentScope = {
compilation: {
// Standalone components are always able to self-reference.
Expand Down
37 changes: 19 additions & 18 deletions packages/core/src/render3/interfaces/definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import {ModuleWithProviders, ProcessProvidersFunction} from '../../di/interface/provider';
import {EnvironmentInjector} from '../../di/r3_injector';
import {Type} from '../../interface/type';
import {NgModule} from '../../metadata';
import {SchemaMetadata} from '../../metadata/schema';
import {ViewEncapsulation} from '../../metadata/view';
import {FactoryFn} from '../definition_factory';
Expand Down Expand Up @@ -519,34 +520,34 @@ export type PipeTypeList =
export const unusedValueExportToPlacateAjd = 1;

/**
* NgModule scope info as provided by AoT compiler
* Scope info (e.g., imports, exports, etc.) as calculated by AoT compiler in full mode and passed
* to downstream tools (e.g., `ɵɵsetNgModuleScope`)
*
* In full compilation Ivy resolved all the "module with providers" and forward refs the whole array
* if at least one element is forward refed. So we end up with type `Type<any>[]|(() =>
* Type<any>[])`.
* In full compilation mode the AoT compiler flatten the nested array and resolves all the "module
* with providers" and forward refs the whole array if at least one element is forward refed. We we
* ended up with a flat array of Types or a factory thereof.
*/
export type FullAotScopeInfoFromDecorator = Type<any>[]|(() => Type<any>[]);

/**
* NgModule scope info as provided by AoT compiler in full and local compilation mode.
*
* In full compilation the compiler resolves all the types and so they'll have the type
* `FullAotScopeInfoFromDecorator`
*
* In local mode the compiler passes the raw info as they are to the runtime functions as it is not
* possible to resolve them any further due to limited info at compile time. So we end up with type
* `RawScopeInfoFromDecorator[]`.
* In local mode the compiler passes the raw info from the decorator to the runtime functions, and
* so teh type will be the same as defined in the NgModule metadata.
*/
export interface NgModuleScopeInfoFromDecorator {
/** List of components, directives, and pipes declared by this module. */
declarations?: Type<any>[]|(() => Type<any>[])|RawScopeInfoFromDecorator[];
declarations?: FullAotScopeInfoFromDecorator|NgModule['declarations'];

/** List of modules or `ModuleWithProviders` or standalone components imported by this module. */
imports?: Type<any>[]|(() => Type<any>[])|RawScopeInfoFromDecorator[];
imports?: FullAotScopeInfoFromDecorator|NgModule['imports'];

/**
* List of modules, `ModuleWithProviders`, components, directives, or pipes exported by this
* module.
*/
exports?: Type<any>[]|(() => Type<any>[])|RawScopeInfoFromDecorator[];
exports?: FullAotScopeInfoFromDecorator|NgModule['exports'];
}

/**
* The array element type passed to:
* - NgModule's annotation imports/exports/declarations fields
* - standalone component annotation imports field
*/
export type RawScopeInfoFromDecorator =
Type<any>|ModuleWithProviders<any>|(() => Type<any>)|(() => ModuleWithProviders<any>);
6 changes: 3 additions & 3 deletions packages/core/src/render3/jit/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export function flushModuleScopingQueueAsMuchAsPossible() {
* an array of declarations, it will recurse to check each declaration in that array
* (which may also be arrays).
*/
function isResolvedDeclaration(declaration: any[]|Type<any>): boolean {
function isResolvedDeclaration(declaration: any[]|Type<any>|(() => Type<any>)): boolean {
if (Array.isArray(declaration)) {
return declaration.every(isResolvedDeclaration);
}
Expand Down Expand Up @@ -319,7 +319,7 @@ function verifySemanticsOfNgModuleDef(
}
}

function verifyComponentIsPartOfNgModule(type: Type<any>) {
function verifyComponentIsPartOfNgModule(type: Type<any>|(() => Type<any>)) {
type = resolveForwardRef(type);
const existingModule = ownerNgModule.get(type);
if (!existingModule && !isStandalone(type)) {
Expand All @@ -329,7 +329,7 @@ function verifySemanticsOfNgModuleDef(
}
}

function verifyCorrectBootstrapType(type: Type<any>) {
function verifyCorrectBootstrapType(type: Type<any>|(() => Type<any>)) {
type = resolveForwardRef(type);
if (!getComponentDef(type)) {
errors.push(`${stringifyForError(type)} cannot be used as an entry component.`);
Expand Down
6 changes: 4 additions & 2 deletions packages/core/src/render3/local_compilation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Component} from '../metadata';

import {depsTracker} from './deps_tracker/deps_tracker';
import {ComponentType, DependencyTypeList, RawScopeInfoFromDecorator} from './interfaces/definition';
import {ComponentType, DependencyTypeList} from './interfaces/definition';

export function ɵɵgetComponentDepsFactory(
type: ComponentType<any>, rawImports?: RawScopeInfoFromDecorator[]): () => DependencyTypeList {
type: ComponentType<any>, rawImports: Component['imports']): () => DependencyTypeList {
return () => {
return depsTracker.getComponentDependencies(type, rawImports).dependencies;
};
Expand Down
12 changes: 9 additions & 3 deletions packages/core/src/render3/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@

import {isForwardRef, resolveForwardRef} from '../di/forward_ref';
import {Type} from '../interface/type';
import {NgModule} from '../metadata';
import {noSideEffects} from '../util/closure';
import {EMPTY_ARRAY} from '../util/empty';

import {extractDefListOrFactory, getNgModuleDef} from './definition';
import {depsTracker} from './deps_tracker/deps_tracker';
import {ComponentDef, ComponentType, NgModuleScopeInfoFromDecorator, RawScopeInfoFromDecorator} from './interfaces/definition';
import {ComponentDef, ComponentType, FullAotScopeInfoFromDecorator, NgModuleScopeInfoFromDecorator} from './interfaces/definition';
import {isModuleWithProviders} from './jit/util';

/**
Expand Down Expand Up @@ -54,8 +55,13 @@ export function ɵɵsetNgModuleScope(type: any, scope: NgModuleScopeInfoFromDeco
});
}

function convertToTypeArray(values: Type<any>[]|(() => Type<any>[])|
RawScopeInfoFromDecorator[]): Type<any>[]|(() => Type<any>[]) {
function convertToTypeArray(values: FullAotScopeInfoFromDecorator|NgModule['declarations']|
NgModule['imports']|NgModule['exports']): Type<any>[]|
(() => Type<any>[]) {
if (typeof values === 'undefined') {
return EMPTY_ARRAY;
}

if (typeof values === 'function') {
return values;
}
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/util/array_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function flatten(list: any[]): any[] {
return list.flat(Number.POSITIVE_INFINITY);
}

export function deepForEach<T>(input: (T|any[])[], fn: (value: T) => void): void {
export function deepForEach<T>(input: NestedArray<T>, fn: (value: T) => void): void {
input.forEach(value => Array.isArray(value) ? deepForEach(value, fn) : fn(value));
}

Expand Down Expand Up @@ -292,3 +292,6 @@ function _arrayIndexOfSorted(array: string[], value: string, shift: number): num
}
return ~(end << shift);
}

/** Represents a nested array with entries of type T. */
export type NestedArray<T> = Array<T>|Array<T|NestedArray<T>>;

0 comments on commit 8a9eea3

Please sign in to comment.