fix(ivy): in ngcc, handle inline exports in commonjs code (#32129)
One of the compiler's tasks is to enumerate the exports of a given ES module. This can happen for example to resolve `foo.bar` where `foo` is a namespace import: ```typescript import * as foo from './foo'; @NgModule({ directives: [foo.DIRECTIVES], }) ``` In this case, the compiler must enumerate the exports of `foo.ts` in order to evaluate the expression `foo.DIRECTIVES`. When this operation occurs under ngcc, it must deal with the different module formats and types of exports that occur. In commonjs code, a problem arises when certain exports are downleveled. ```typescript export const DIRECTIVES = [ FooDir, BarDir, ]; ``` can be downleveled to: ```javascript exports.DIRECTIVES = [ FooDir, BarDir, ``` Previously, ngtsc and ngcc expected that any export would have an associated `ts.Declaration` node. `export class`, `export function`, etc. all retain `ts.Declaration`s even when downleveled. But the `export const` construct above does not. Therefore, ngcc would not detect `DIRECTIVES` as an export of `foo.ts`, and the evaluation of `foo.DIRECTIVES` would therefore fail. To solve this problem, the core concept of an exported `Declaration` according to the `ReflectionHost` API is split into a `ConcreteDeclaration` which has a `ts.Declaration`, and an `InlineDeclaration` which instead has a `ts.Expression`. Differentiating between these allows ngcc to return an `InlineDeclaration` for `DIRECTIVES` and correctly keep track of this export. PR Close #32129
This commit is contained in:

committed by
Andrew Kushnir

parent
69ce1c2d41
commit
02bab8cf90
@ -10,7 +10,7 @@ import * as ts from 'typescript';
|
||||
|
||||
import {Reference} from '../../imports';
|
||||
import {OwningModule} from '../../imports/src/references';
|
||||
import {Declaration, ReflectionHost} from '../../reflection';
|
||||
import {Declaration, InlineDeclaration, ReflectionHost} from '../../reflection';
|
||||
import {isDeclaration} from '../../util/src/typescript';
|
||||
|
||||
import {ArrayConcatBuiltinFn, ArraySliceBuiltinFn} from './builtin';
|
||||
@ -20,6 +20,7 @@ import {BuiltinFn, EnumValue, ResolvedValue, ResolvedValueArray, ResolvedValueMa
|
||||
import {evaluateTsHelperInline} from './ts_helpers';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Tracks the scope of a function body, which includes `ResolvedValue`s for the parameters of that
|
||||
* body.
|
||||
@ -223,8 +224,13 @@ export class StaticInterpreter {
|
||||
return DynamicValue.fromUnknownIdentifier(node);
|
||||
}
|
||||
}
|
||||
const result =
|
||||
this.visitDeclaration(decl.node, {...context, ...joinModuleContext(context, node, decl)});
|
||||
const declContext = {...context, ...joinModuleContext(context, node, decl)};
|
||||
// The identifier's declaration is either concrete (a ts.Declaration exists for it) or inline
|
||||
// (a direct reference to a ts.Expression).
|
||||
// TODO(alxhub): remove cast once TS is upgraded in g3.
|
||||
const result = decl.node !== null ?
|
||||
this.visitDeclaration(decl.node, declContext) :
|
||||
this.visitExpression((decl as InlineDeclaration).expression, declContext);
|
||||
if (result instanceof Reference) {
|
||||
// Only record identifiers to non-synthetic references. Synthetic references may not have the
|
||||
// same value at runtime as they do at compile time, so it's not legal to refer to them by the
|
||||
@ -322,10 +328,14 @@ export class StaticInterpreter {
|
||||
}
|
||||
const map = new Map<string, ResolvedValue>();
|
||||
declarations.forEach((decl, name) => {
|
||||
const value = this.visitDeclaration(
|
||||
decl.node, {
|
||||
...context, ...joinModuleContext(context, node, decl),
|
||||
});
|
||||
const declContext = {
|
||||
...context, ...joinModuleContext(context, node, decl),
|
||||
};
|
||||
// Visit both concrete and inline declarations.
|
||||
// TODO(alxhub): remove cast once TS is upgraded in g3.
|
||||
const value = decl.node !== null ?
|
||||
this.visitDeclaration(decl.node, declContext) :
|
||||
this.visitExpression((decl as InlineDeclaration).expression, declContext);
|
||||
map.set(name, value);
|
||||
});
|
||||
return map;
|
||||
|
Reference in New Issue
Block a user