fix(ivy): semantic module check incorrectly handles nested arrays (#30993)

In View Engine, developers can pass bootstrap and entry components
as nested arrays. e.g.

```ts
export const MyOtherEntryComponents = [A, B, C]

@NgModule({
  entryComponents: [MyComp, MyOtherEntryComponents]
})
```

Currently using nested arrays for these properties causes
unexpected errors to be reported in Ivy since the semantic
NgModule checks aren't properly recursing into the nested
entry/bootstrap components. This issue has been unveiled by
enabling the strict function parameter checks.

PR Close #30993
This commit is contained in:
Paul Gschwendtner
2019-06-11 22:53:03 +02:00
committed by Miško Hevery
parent dda781ecce
commit e061e638cb
4 changed files with 89 additions and 10 deletions

View File

@ -11,6 +11,7 @@ import '../util/ng_dev_mode';
import {OnDestroy} from '../interface/lifecycle_hooks';
import {Type} from '../interface/type';
import {throwCyclicDependencyError, throwInvalidProviderError, throwMixedMultiProviderError} from '../render3/errors';
import {deepForEach} from '../util/array_utils';
import {stringify} from '../util/stringify';
import {resolveForwardRef} from './forward_ref';
@ -497,10 +498,6 @@ function makeRecord<T>(
};
}
function deepForEach<T>(input: (T | any[])[], fn: (value: T) => void): void {
input.forEach(value => Array.isArray(value) ? deepForEach(value, fn) : fn(value));
}
function isValueProvider(value: SingleProvider): value is ValueProvider {
return value !== null && typeof value == 'object' && USE_VALUE in value;
}

View File

@ -13,7 +13,7 @@ import {reflectDependencies} from '../../di/jit/util';
import {Type} from '../../interface/type';
import {Component} from '../../metadata';
import {ModuleWithProviders, NgModule, NgModuleDef, NgModuleTransitiveScopes} from '../../metadata/ng_module';
import {flatten} from '../../util/array_utils';
import {deepForEach, flatten} from '../../util/array_utils';
import {assertDefined} from '../../util/assert';
import {getComponentDef, getDirectiveDef, getNgModuleDef, getPipeDef} from '../definition';
import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF, NG_MODULE_DEF, NG_PIPE_DEF} from '../fields';
@ -200,9 +200,10 @@ function verifySemanticsOfNgModuleDef(
verifySemanticsOfNgModuleImport(mod, moduleType);
verifySemanticsOfNgModuleDef(mod, false, moduleType);
});
ngModule.bootstrap && ngModule.bootstrap.forEach(verifyCorrectBootstrapType);
ngModule.bootstrap && ngModule.bootstrap.forEach(verifyComponentIsPartOfNgModule);
ngModule.entryComponents && ngModule.entryComponents.forEach(verifyComponentIsPartOfNgModule);
ngModule.bootstrap && deepForEach(ngModule.bootstrap, verifyCorrectBootstrapType);
ngModule.bootstrap && deepForEach(ngModule.bootstrap, verifyComponentIsPartOfNgModule);
ngModule.entryComponents &&
deepForEach(ngModule.entryComponents, verifyComponentIsPartOfNgModule);
}
// Throw Error if any errors were detected.
@ -273,7 +274,7 @@ function verifySemanticsOfNgModuleDef(
// We know we are component
const component = getAnnotation<Component>(type, 'Component');
if (component && component.entryComponents) {
component.entryComponents.forEach(verifyComponentIsPartOfNgModule);
deepForEach(component.entryComponents, verifyComponentIsPartOfNgModule);
}
}
}

View File

@ -38,4 +38,8 @@ export function flatten(list: any[], dst?: any[]): any[] {
}
}
return dst;
}
}
export function deepForEach<T>(input: (T | any[])[], fn: (value: T) => void): void {
input.forEach(value => Array.isArray(value) ? deepForEach(value, fn) : fn(value));
}