mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
refactor(core): implement runtime logic to compute component dependencies in local compilation mode (#51309)
The runtime `ɵɵsetNgModuleScope` is modified to accept raw scope info as passed to it in local compilation mode. The runtime further registers the ng-module in the deps tracker. Then the runtime `ɵɵgetComponentDepsFactory` is implemented to use the deps tracker to get the component dependencies which leads to a valid and working Angular code. PR Close #51309
This commit is contained in:
parent
e7ea016e00
commit
f7cfc3b8fa
4 changed files with 168 additions and 15 deletions
|
|
@ -517,21 +517,34 @@ export type PipeTypeList =
|
|||
export const unusedValueExportToPlacateAjd = 1;
|
||||
|
||||
/**
|
||||
* NgModule scope info as provided by NgModule decorator.
|
||||
* NgModule scope info as provided by AoT compiler
|
||||
*
|
||||
* 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 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[]`.
|
||||
*/
|
||||
export interface NgModuleScopeInfoFromDecorator {
|
||||
/** List of components, directives, and pipes declared by this module. */
|
||||
declarations?: Type<any>[]|(() => Type<any>[]);
|
||||
declarations?: Type<any>[]|(() => Type<any>[])|RawScopeInfoFromDecorator[];
|
||||
|
||||
/** List of modules or `ModuleWithProviders` imported by this module. */
|
||||
imports?: Type<any>[]|(() => Type<any>[]);
|
||||
/** List of modules or `ModuleWithProviders` or standalone components imported by this module. */
|
||||
imports?: Type<any>[]|(() => Type<any>[])|RawScopeInfoFromDecorator[];
|
||||
|
||||
/**
|
||||
* List of modules, `ModuleWithProviders`, components, directives, or pipes exported by this
|
||||
* module.
|
||||
*/
|
||||
exports?: Type<any>[]|(() => Type<any>[]);
|
||||
exports?: Type<any>[]|(() => Type<any>[])|RawScopeInfoFromDecorator[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,12 +6,12 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Type} from '../interface/type';
|
||||
|
||||
import {DependencyTypeList, RawScopeInfoFromDecorator} from './interfaces/definition';
|
||||
import {depsTracker} from './deps_tracker/deps_tracker';
|
||||
import {ComponentType, DependencyTypeList, RawScopeInfoFromDecorator} from './interfaces/definition';
|
||||
|
||||
export function ɵɵgetComponentDepsFactory(
|
||||
type: Type<any>, rawImports?: RawScopeInfoFromDecorator): () => DependencyTypeList {
|
||||
// TODO(pmvald): Implement this runtime using deps tracker.
|
||||
return () => [];
|
||||
type: ComponentType<any>, rawImports?: RawScopeInfoFromDecorator[]): () => DependencyTypeList {
|
||||
return () => {
|
||||
return depsTracker.getComponentDependencies(type, rawImports).dependencies;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,15 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {isForwardRef, resolveForwardRef} from '../di/forward_ref';
|
||||
import {Type} from '../interface/type';
|
||||
import {noSideEffects} from '../util/closure';
|
||||
import {EMPTY_ARRAY} from '../util/empty';
|
||||
|
||||
import {extractDefListOrFactory, getNgModuleDef} from './definition';
|
||||
import {ComponentDef, ComponentType, NgModuleScopeInfoFromDecorator} from './interfaces/definition';
|
||||
import {depsTracker} from './deps_tracker/deps_tracker';
|
||||
import {ComponentDef, ComponentType, NgModuleScopeInfoFromDecorator, RawScopeInfoFromDecorator} from './interfaces/definition';
|
||||
import {isModuleWithProviders} from './jit/util';
|
||||
|
||||
/**
|
||||
* Generated next to NgModules to monkey-patch directive and pipe references onto a component's
|
||||
|
|
@ -43,8 +46,27 @@ export function ɵɵsetComponentScope(
|
|||
export function ɵɵsetNgModuleScope(type: any, scope: NgModuleScopeInfoFromDecorator): unknown {
|
||||
return noSideEffects(() => {
|
||||
const ngModuleDef = getNgModuleDef(type, true);
|
||||
ngModuleDef.declarations = scope.declarations || EMPTY_ARRAY;
|
||||
ngModuleDef.imports = scope.imports || EMPTY_ARRAY;
|
||||
ngModuleDef.exports = scope.exports || EMPTY_ARRAY;
|
||||
ngModuleDef.declarations = convertToTypeArray(scope.declarations || EMPTY_ARRAY);
|
||||
ngModuleDef.imports = convertToTypeArray(scope.imports || EMPTY_ARRAY);
|
||||
ngModuleDef.exports = convertToTypeArray(scope.exports || EMPTY_ARRAY);
|
||||
|
||||
depsTracker.registerNgModule(type, scope);
|
||||
});
|
||||
}
|
||||
|
||||
function convertToTypeArray(values: Type<any>[]|(() => Type<any>[])|
|
||||
RawScopeInfoFromDecorator[]): Type<any>[]|(() => Type<any>[]) {
|
||||
if (typeof values === 'function') {
|
||||
return values;
|
||||
}
|
||||
|
||||
if (values.some(isForwardRef)) {
|
||||
return () => values.map(resolveForwardRef).map(maybeUnwrapModuleWithProviders);
|
||||
} else {
|
||||
return values.map(maybeUnwrapModuleWithProviders);
|
||||
}
|
||||
}
|
||||
|
||||
function maybeUnwrapModuleWithProviders(value: any): Type<any> {
|
||||
return isModuleWithProviders(value) ? value.ngModule : value as Type<any>;
|
||||
}
|
||||
|
|
|
|||
118
packages/core/test/acceptance/local_compilation_spec.ts
Normal file
118
packages/core/test/acceptance/local_compilation_spec.ts
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component, forwardRef, ɵɵdefineNgModule, ɵɵgetComponentDepsFactory, ɵɵsetNgModuleScope} from '@angular/core';
|
||||
import {ComponentType} from '@angular/core/src/render3';
|
||||
|
||||
describe('component dependencies in local compilation', () => {
|
||||
it('should compute correct set of dependencies when importing ng-modules directly', () => {
|
||||
@Component({selector: 'sub'})
|
||||
class SubComponent {
|
||||
}
|
||||
|
||||
class SubModule {
|
||||
static ɵmod = ɵɵdefineNgModule({type: SubModule});
|
||||
}
|
||||
ɵɵsetNgModuleScope(SubModule, {exports: [SubComponent]});
|
||||
|
||||
@Component({})
|
||||
class MainComponent {
|
||||
}
|
||||
|
||||
class MainModule {
|
||||
static ɵmod = ɵɵdefineNgModule({type: MainModule});
|
||||
}
|
||||
ɵɵsetNgModuleScope(MainModule, {imports: [SubModule], declarations: [MainComponent]});
|
||||
|
||||
const deps = ɵɵgetComponentDepsFactory(MainComponent as ComponentType<any>)();
|
||||
|
||||
expect(deps).toEqual(jasmine.arrayWithExactContents([SubComponent, MainComponent]));
|
||||
});
|
||||
|
||||
it('should compute correct set of dependencies when importing ng-modules with providers', () => {
|
||||
@Component({selector: 'sub'})
|
||||
class SubComponent {
|
||||
}
|
||||
|
||||
class SubModule {
|
||||
static ɵmod = ɵɵdefineNgModule({type: SubModule});
|
||||
}
|
||||
ɵɵsetNgModuleScope(SubModule, {exports: [SubComponent]});
|
||||
|
||||
@Component({})
|
||||
class MainComponent {
|
||||
}
|
||||
|
||||
class MainModule {
|
||||
static ɵmod = ɵɵdefineNgModule({type: MainModule});
|
||||
}
|
||||
ɵɵsetNgModuleScope(
|
||||
MainModule,
|
||||
{imports: [{ngModule: SubModule, providers: []}], declarations: [MainComponent]});
|
||||
|
||||
const deps = ɵɵgetComponentDepsFactory(MainComponent as ComponentType<any>)();
|
||||
|
||||
expect(deps).toEqual(jasmine.arrayWithExactContents([SubComponent, MainComponent]));
|
||||
});
|
||||
|
||||
it('should compute correct set of dependencies when importing ng-modules using forward ref',
|
||||
() => {
|
||||
@Component({selector: 'sub'})
|
||||
class SubComponent {
|
||||
}
|
||||
|
||||
class SubModule {
|
||||
static ɵmod = ɵɵdefineNgModule({type: SubModule});
|
||||
}
|
||||
ɵɵsetNgModuleScope(SubModule, {exports: [forwardRef(() => SubComponent)]});
|
||||
|
||||
@Component({})
|
||||
class MainComponent {
|
||||
}
|
||||
|
||||
class MainModule {
|
||||
static ɵmod = ɵɵdefineNgModule({type: MainModule});
|
||||
}
|
||||
ɵɵsetNgModuleScope(MainModule, {
|
||||
imports: [forwardRef(() => SubModule)],
|
||||
declarations: [forwardRef(() => MainComponent)]
|
||||
});
|
||||
|
||||
const deps = ɵɵgetComponentDepsFactory(MainComponent as ComponentType<any>)();
|
||||
|
||||
expect(deps).toEqual(jasmine.arrayWithExactContents([SubComponent, MainComponent]));
|
||||
});
|
||||
|
||||
it('should compute correct set of dependencies when importing ng-modules with providers using forward ref',
|
||||
() => {
|
||||
@Component({selector: 'sub'})
|
||||
class SubComponent {
|
||||
}
|
||||
|
||||
class SubModule {
|
||||
static ɵmod = ɵɵdefineNgModule({type: SubModule});
|
||||
}
|
||||
ɵɵsetNgModuleScope(SubModule, {exports: [SubComponent]});
|
||||
|
||||
@Component({})
|
||||
class MainComponent {
|
||||
}
|
||||
|
||||
class MainModule {
|
||||
static ɵmod = ɵɵdefineNgModule({type: MainModule});
|
||||
}
|
||||
ɵɵsetNgModuleScope(MainModule, {
|
||||
imports: [forwardRef(() => ({ngModule: SubModule, providers: []}))],
|
||||
declarations: [MainComponent]
|
||||
});
|
||||
|
||||
const deps = ɵɵgetComponentDepsFactory(MainComponent as ComponentType<any>)();
|
||||
|
||||
expect(deps).toEqual(jasmine.arrayWithExactContents([SubComponent, MainComponent]));
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue