fix(ivy): use `ReflectionHost` in `AbsoluteModuleStrategy` (#30200)

The AbsoluteModuleStrategy in ngtsc assumed that the source code is
formatted as TypeScript with regards to module exports.

In ngcc this is not always the case, so this commit changes
`AbsoluteModuleStrategy` so that it relies upon a `ReflectionHost`  to
compute the exports of a module.

PR Close #30200
This commit is contained in:
Pete Bacon Darwin 2019-05-01 14:58:59 +01:00 committed by Jason Aden
parent 02523debe5
commit 76391f8999
4 changed files with 18 additions and 33 deletions

View File

@ -70,7 +70,8 @@ export class DecorationAnalyzer {
fullMetaReader = new CompoundMetadataReader([this.metaRegistry, this.dtsMetaReader]);
refEmitter = new ReferenceEmitter([
new LocalIdentifierStrategy(),
new AbsoluteModuleStrategy(this.program, this.typeChecker, this.options, this.host),
new AbsoluteModuleStrategy(
this.program, this.typeChecker, this.options, this.host, this.reflectionHost),
// TODO(alxhub): there's no reason why ngcc needs the "logical file system" logic here, as ngcc
// projects only ever have one rootDir. Instead, ngcc should just switch its emitted import
// based on whether a bestGuessOwningModule is present in the Reference.

View File

@ -11,11 +11,13 @@ import {ExternalReference} from '@angular/compiler/src/compiler';
import * as ts from 'typescript';
import {LogicalFileSystem, LogicalProjectPath} from '../../path';
import {ReflectionHost} from '../../reflection';
import {getSourceFile, isDeclaration, nodeNameForError, resolveModuleName} from '../../util/src/typescript';
import {findExportedNameOfNode} from './find_export';
import {ImportMode, Reference} from './references';
/**
* A host which supports an operation to convert a file name into a module name.
*
@ -115,8 +117,9 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
private moduleExportsCache = new Map<string, Map<ts.Declaration, string>|null>();
constructor(
private program: ts.Program, private checker: ts.TypeChecker,
private options: ts.CompilerOptions, private host: ts.CompilerHost) {}
protected program: ts.Program, protected checker: ts.TypeChecker,
protected options: ts.CompilerOptions, protected host: ts.CompilerHost,
private reflectionHost: ReflectionHost) {}
emit(ref: Reference<ts.Node>, context: ts.SourceFile, importMode: ImportMode): Expression|null {
if (ref.bestGuessOwningModule === null) {
@ -159,7 +162,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
return this.moduleExportsCache.get(moduleName) !;
}
private enumerateExportsOfModule(specifier: string, fromFile: string):
protected enumerateExportsOfModule(specifier: string, fromFile: string):
Map<ts.Declaration, string>|null {
// First, resolve the module specifier to its entry point, and get the ts.Symbol for it.
const resolvedModule = resolveModuleName(specifier, fromFile, this.options, this.host);
@ -172,34 +175,12 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
return null;
}
const entryPointSymbol = this.checker.getSymbolAtLocation(entryPointFile);
if (entryPointSymbol === undefined) {
const exports = this.reflectionHost.getExportsOfModule(entryPointFile);
if (exports === null) {
return null;
}
// Next, build a Map of all the ts.Declarations exported via the specifier and their exported
// names.
const exportMap = new Map<ts.Declaration, string>();
const exports = this.checker.getExportsOfModule(entryPointSymbol);
for (const expSymbol of exports) {
// Resolve export symbols to their actual declarations.
const declSymbol = expSymbol.flags & ts.SymbolFlags.Alias ?
this.checker.getAliasedSymbol(expSymbol) :
expSymbol;
// At this point the valueDeclaration of the symbol should be defined.
const decl = declSymbol.valueDeclaration;
if (decl === undefined) {
continue;
}
// Prefer importing the symbol via its declared name, but take any export of it otherwise.
if (declSymbol.name === expSymbol.name || !exportMap.has(decl)) {
exportMap.set(decl, expSymbol.name);
}
}
exports.forEach((declaration, name) => { exportMap.set(declaration.node, name); });
return exportMap;
}
}

View File

@ -438,7 +438,8 @@ export class NgtscProgram implements api.Program {
// First, try to use local identifiers if available.
new LocalIdentifierStrategy(),
// Next, attempt to use an absolute import.
new AbsoluteModuleStrategy(this.tsProgram, checker, this.options, this.host),
new AbsoluteModuleStrategy(
this.tsProgram, checker, this.options, this.host, this.reflector),
// Finally, check if the reference is being written into a file within the project's logical
// file system, and use a relative import if so. If this fails, ReferenceEmitter will throw
// an error.

View File

@ -10,7 +10,7 @@ import * as ts from 'typescript';
import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, Reference, ReferenceEmitter} from '../../imports';
import {AbsoluteFsPath, LogicalFileSystem} from '../../path';
import {isNamedClassDeclaration} from '../../reflection';
import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection';
import {getDeclaration, makeProgram} from '../../testing/in_memory_typescript';
import {getRootDirs} from '../../util/src/typescript';
import {TypeCheckingConfig} from '../src/api';
@ -54,7 +54,8 @@ TestClass.ngTypeCtor({value: 'test'});
const logicalFs = new LogicalFileSystem(getRootDirs(host, options));
const emitter = new ReferenceEmitter([
new LocalIdentifierStrategy(),
new AbsoluteModuleStrategy(program, checker, options, host),
new AbsoluteModuleStrategy(
program, checker, options, host, new TypeScriptReflectionHost(checker)),
new LogicalProjectStrategy(checker, logicalFs),
]);
const ctx = new TypeCheckContext(
@ -84,7 +85,8 @@ TestClass.ngTypeCtor({value: 'test'});
const logicalFs = new LogicalFileSystem(getRootDirs(host, options));
const emitter = new ReferenceEmitter([
new LocalIdentifierStrategy(),
new AbsoluteModuleStrategy(program, checker, options, host),
new AbsoluteModuleStrategy(
program, checker, options, host, new TypeScriptReflectionHost(checker)),
new LogicalProjectStrategy(checker, logicalFs),
]);
const ctx = new TypeCheckContext(