mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(core): verify standalone component imports in JiT (#45777)
This commits adds verifications assuring that items imported into standalone components are one of: - standalone component / directive / pipe; - NgModule; - forwardRef resolving to one of the above. It explicitly disallows modules with providers. PR Close #45777
This commit is contained in:
committed by
Dylan Hunn
parent
58e8f4b708
commit
aafac7228f
@@ -8,7 +8,7 @@
|
||||
|
||||
import {getCompilerFacade, JitCompilerUsage, R3DirectiveMetadataFacade} from '../../compiler/compiler_facade';
|
||||
import {R3ComponentMetadataFacade, R3QueryMetadataFacade} from '../../compiler/compiler_facade_interface';
|
||||
import {resolveForwardRef} from '../../di/forward_ref';
|
||||
import {isForwardRef, resolveForwardRef} from '../../di/forward_ref';
|
||||
import {getReflect, reflectDependencies} from '../../di/jit/util';
|
||||
import {Type} from '../../interface/type';
|
||||
import {Query} from '../../metadata/di';
|
||||
@@ -26,6 +26,7 @@ import {stringifyForError} from '../util/stringify_utils';
|
||||
import {angularCoreEnv} from './environment';
|
||||
import {getJitOptions} from './jit_options';
|
||||
import {flushModuleScopingQueueAsMuchAsPossible, patchComponentDefWithScope, transitiveScopesFor} from './module';
|
||||
import {isModuleWithProviders} from './util';
|
||||
|
||||
/**
|
||||
* Keep track of the compilation depth to avoid reentrancy issues during JIT compilation. This
|
||||
@@ -186,6 +187,48 @@ export function compileComponent(type: Type<any>, metadata: Component): void {
|
||||
});
|
||||
}
|
||||
|
||||
function getDependencyTypeForError(type: Type<any>) {
|
||||
if (getComponentDef(type)) return 'component';
|
||||
if (getDirectiveDef(type)) return 'directive';
|
||||
if (getPipeDef(type)) return 'pipe';
|
||||
return 'type';
|
||||
}
|
||||
|
||||
function verifyStandaloneImport(depType: Type<unknown>, importingType: Type<unknown>) {
|
||||
if (isForwardRef(depType)) {
|
||||
depType = resolveForwardRef(depType);
|
||||
if (!depType) {
|
||||
throw new Error(`Expected forwardRef function, imported from "${
|
||||
stringifyForError(importingType)}", to return a standalone entity or NgModule but got "${
|
||||
stringifyForError(depType) || depType}".`);
|
||||
}
|
||||
}
|
||||
|
||||
if (getNgModuleDef(depType) == null) {
|
||||
const def = getComponentDef(depType) || getDirectiveDef(depType) || getPipeDef(depType);
|
||||
if (def != null) {
|
||||
// if a component, directive or pipe is imported make sure that it is standalone
|
||||
if (!def.standalone) {
|
||||
throw new Error(`The "${stringifyForError(depType)}" ${
|
||||
getDependencyTypeForError(depType)}, imported from "${
|
||||
stringifyForError(
|
||||
importingType)}", is not standalone. Did you forget to add the standalone: true flag?`);
|
||||
}
|
||||
} else {
|
||||
// it can be either a module with provider or an unknown (not annotated) type
|
||||
if (isModuleWithProviders(depType)) {
|
||||
throw new Error(`A module with providers was imported from "${
|
||||
stringifyForError(
|
||||
importingType)}". Modules with providers are not supported in standalone components imports.`);
|
||||
} else {
|
||||
throw new Error(`The "${stringifyForError(depType)}" type, imported from "${
|
||||
stringifyForError(
|
||||
importingType)}", must be a standalone component / directive / pipe or an NgModule. Did you forget to add the required @Component / @Directive / @Pipe or @NgModule annotation?`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build memoized `directiveDefs` and `pipeDefs` functions for the component definition of a
|
||||
* standalone component, which process `imports` and filter out directives and pipes. The use of
|
||||
@@ -205,8 +248,9 @@ function getStandaloneDefFunctions(type: Type<any>, imports: Type<any>[]): {
|
||||
cachedDirectiveDefs = [getComponentDef(type)!];
|
||||
|
||||
for (const rawDep of imports) {
|
||||
const dep = resolveForwardRef(rawDep);
|
||||
ngDevMode && verifyStandaloneImport(rawDep, type);
|
||||
|
||||
const dep = resolveForwardRef(rawDep);
|
||||
if (!!getNgModuleDef(dep)) {
|
||||
const scope = transitiveScopesFor(dep);
|
||||
for (const dir of scope.exported.directives) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import {stringifyForError} from '../util/stringify_utils';
|
||||
|
||||
import {angularCoreEnv} from './environment';
|
||||
import {patchModuleCompilation} from './module_patch';
|
||||
import {isModuleWithProviders, isNgModule} from './util';
|
||||
|
||||
interface ModuleQueueItem {
|
||||
moduleType: Type<any>;
|
||||
@@ -610,11 +611,3 @@ function expandModuleWithProviders(value: Type<any>|ModuleWithProviders<{}>): Ty
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function isModuleWithProviders(value: any): value is ModuleWithProviders<{}> {
|
||||
return (value as {ngModule?: any}).ngModule !== undefined;
|
||||
}
|
||||
|
||||
function isNgModule<T>(value: Type<T>): value is Type<T>&{ɵmod: NgModuleDef<T>} {
|
||||
return !!getNgModuleDef(value);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @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 {ModuleWithProviders} from '../../di/interface/provider';
|
||||
import {Type} from '../../interface/type';
|
||||
import {NgModuleDef} from '../../metadata/ng_module_def';
|
||||
import {getNgModuleDef} from '../definition';
|
||||
|
||||
export function isModuleWithProviders(value: any): value is ModuleWithProviders<{}> {
|
||||
return (value as {ngModule?: any}).ngModule !== undefined;
|
||||
}
|
||||
|
||||
export function isNgModule<T>(value: Type<T>): value is Type<T>&{ɵmod: NgModuleDef<T>} {
|
||||
return !!getNgModuleDef(value);
|
||||
}
|
||||
Reference in New Issue
Block a user