
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
50 lines
1.9 KiB
TypeScript
50 lines
1.9 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google Inc. 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 * as ts from 'typescript';
|
|
import {ReferencesRegistry} from '../../../src/ngtsc/annotations';
|
|
import {Reference} from '../../../src/ngtsc/imports';
|
|
import {ConcreteDeclaration, ReflectionHost} from '../../../src/ngtsc/reflection';
|
|
import {hasNameIdentifier} from '../utils';
|
|
|
|
/**
|
|
* This is a place for DecoratorHandlers to register references that they
|
|
* find in their analysis of the code.
|
|
*
|
|
* This registry is used to ensure that these references are publicly exported
|
|
* from libraries that are compiled by ngcc.
|
|
*/
|
|
export class NgccReferencesRegistry implements ReferencesRegistry {
|
|
private map = new Map<ts.Identifier, ConcreteDeclaration>();
|
|
|
|
constructor(private host: ReflectionHost) {}
|
|
|
|
/**
|
|
* Register one or more references in the registry.
|
|
* Only `ResolveReference` references are stored. Other types are ignored.
|
|
* @param references A collection of references to register.
|
|
*/
|
|
add(source: ts.Declaration, ...references: Reference<ts.Declaration>[]): void {
|
|
references.forEach(ref => {
|
|
// Only store relative references. We are not interested in literals.
|
|
if (ref.bestGuessOwningModule === null && hasNameIdentifier(ref.node)) {
|
|
const declaration = this.host.getDeclarationOfIdentifier(ref.node.name);
|
|
if (declaration && declaration.node !== null && hasNameIdentifier(declaration.node)) {
|
|
this.map.set(declaration.node.name, declaration);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create and return a mapping for the registered resolved references.
|
|
* @returns A map of reference identifiers to reference declarations.
|
|
*/
|
|
getDeclarationMap(): Map<ts.Identifier, ConcreteDeclaration> { return this.map; }
|
|
}
|