From 18db4ef7ffa0ddb0c1b5b248f85fa8647ecea877 Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Wed, 20 Apr 2022 11:03:34 -0700 Subject: [PATCH] refactor(compiler-cli): introduce ResolvedModuleWithProviders (#45701) This commit updates the `ForeignFunctionResolver` used by the NgModule handler to resolve `ModuleWithProvider` types. Previously, this resolver returned the NgModule `Reference` directly, but there are two problems with this: * It's not completely accurate, as the expression returned by the MWP call will not return the NgModule at runtime. * We need the ability to distinguish the MWP call itself from an ordinary NgModule reference in future optimizations. PR Close #45701 --- .../annotations/ng_module/src/handler.ts | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/ng_module/src/handler.ts b/packages/compiler-cli/src/ngtsc/annotations/ng_module/src/handler.ts index a1ec358f550..c528dcda03c 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/ng_module/src/handler.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/ng_module/src/handler.ts @@ -13,7 +13,7 @@ import {ErrorCode, FatalDiagnosticError, makeDiagnostic, makeRelatedInformation} import {assertSuccessfulReferenceEmit, Reference, ReferenceEmitter} from '../../../imports'; import {isArrayEqual, isReferenceEqual, isSymbolEqual, SemanticReference, SemanticSymbol} from '../../../incremental/semantic_graph'; import {InjectableClassRegistry, MetadataReader, MetadataRegistry, MetaKind} from '../../../metadata'; -import {DynamicValue, PartialEvaluator, ResolvedValue} from '../../../partial_evaluator'; +import {DynamicValue, PartialEvaluator, ResolvedValue, SyntheticValue} from '../../../partial_evaluator'; import {PerfEvent, PerfRecorder} from '../../../perf'; import {ClassDeclaration, Decorator, isNamedClassDeclaration, ReflectionHost, reflectObjectLiteral, typeNodeToValueExpr} from '../../../reflection'; import {LocalModuleScopeRegistry, ScopeData} from '../../../scope'; @@ -606,7 +606,7 @@ export class NgModuleDecoratorHandler implements private _extractModuleFromModuleWithProvidersFn( fn: Reference, node: ts.CallExpression, resolve: (expr: ts.Expression) => ResolvedValue, - unresolvable: DynamicValue): Reference|DynamicValue { + unresolvable: DynamicValue): SyntheticValue|DynamicValue { const rawType = fn.node.type || null; const type = rawType && @@ -620,7 +620,10 @@ export class NgModuleDecoratorHandler implements return unresolvable; } - return ngModule as Reference; + return new SyntheticValue({ + ngModule: ngModule as Reference, + mwpCall: node, + }); } /** @@ -720,10 +723,13 @@ export class NgModuleDecoratorHandler implements `Expected array when reading the NgModule.${arrayName} of ${className}`); } - resolvedList.forEach((entry, idx) => { + for (let idx = 0; idx < resolvedList.length; idx++) { + let entry = resolvedList[idx]; // Unwrap ModuleWithProviders for modules that are locally declared (and thus static // resolution was able to descend into the function and return an object literal, a Map). - if (entry instanceof Map && entry.has('ngModule')) { + if (entry instanceof SyntheticValue && isResolvedModuleWithProviders(entry)) { + entry = entry.value.ngModule; + } else if (entry instanceof Map && entry.has('ngModule')) { entry = entry.get('ngModule')!; } @@ -745,7 +751,7 @@ export class NgModuleDecoratorHandler implements `Value at position ${idx} in the NgModule.${arrayName} of ${ className} is not a reference`); } - }); + } return refList; } @@ -762,3 +768,15 @@ function isModuleIdExpression(expr: ts.Expression): boolean { return ts.isPropertyAccessExpression(expr) && ts.isIdentifier(expr.expression) && expr.expression.text === 'module' && expr.name.text === 'id'; } + +interface ResolvedModuleWithProviders { + ngModule: Reference; + mwpCall: ts.CallExpression; +} + +function isResolvedModuleWithProviders(sv: SyntheticValue): + sv is SyntheticValue { + return typeof sv.value === 'object' && sv.value != null && + sv.value.hasOwnProperty('ngModule' as keyof ResolvedModuleWithProviders) && + sv.value.hasOwnProperty('mwpCall' as keyof ResolvedModuleWithProviders); +}