fix(compiler): StaticReflect now resolves re-exported symbols (#10453)

Fixes: #10451
This commit is contained in:
Chuck Jazdzewski
2016-08-02 11:45:14 -07:00
committed by GitHub
parent 3d53b33391
commit 82e7ecd611
5 changed files with 208 additions and 14 deletions

View File

@ -43,6 +43,7 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
provider: '@angular/core/src/di/provider'
};
}
private resolve(m: string, containingFile: string) {
const resolved =
ts.resolveModuleName(m, containingFile, this.options, this.context).resolvedModule;
@ -72,12 +73,9 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
importedFile = this.resolveAssetUrl(importedFile, containingFile);
containingFile = this.resolveAssetUrl(containingFile, '');
// TODO(tbosch): if a file does not yet exist (because we compile it later),
// we still need to create it so that the `resolve` method works!
// If a file does not yet exist (because we compile it later), we still need to
// assume it exists it so that the `resolve` method works!
if (!this.compilerHost.fileExists(importedFile)) {
if (this.options.trace) {
console.log(`Generating empty file ${importedFile} to allow resolution of import`);
}
this.context.assumeFileExists(importedFile);
}
@ -133,11 +131,10 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
const sf = this.program.getSourceFile(filePath);
if (!sf || !(<any>sf).symbol) {
// The source file was not needed in the compile but we do need the values from
// the corresponding .ts files stored in the .metadata.json file. Just assume the
// symbol and file we resolved to be correct as we don't need this to be the
// cannonical reference as this reference could have only been generated by a
// .metadata.json file resolving values.
return this.getStaticSymbol(filePath, symbolName);
// the corresponding .ts files stored in the .metadata.json file. Check the file
// for exports to see if the file is exported.
return this.resolveExportedSymbol(filePath, symbolName) ||
this.getStaticSymbol(filePath, symbolName);
}
let symbol = tc.getExportsOfModule((<any>sf).symbol).find(m => m.name === symbolName);
@ -159,6 +156,7 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
}
private typeCache = new Map<string, StaticSymbol>();
private resolverCache = new Map<string, ModuleMetadata>();
/**
* getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded.
@ -200,13 +198,71 @@ export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
readMetadata(filePath: string) {
try {
const result = JSON.parse(this.context.readFile(filePath));
return result;
return this.resolverCache.get(filePath) || JSON.parse(this.context.readFile(filePath));
} catch (e) {
console.error(`Failed to read JSON file ${filePath}`);
throw e;
}
}
private getResolverMetadata(filePath: string): ModuleMetadata {
let metadata = this.resolverCache.get(filePath);
if (!metadata) {
metadata = this.getMetadataFor(filePath);
this.resolverCache.set(filePath, metadata);
}
return metadata;
}
private resolveExportedSymbol(filePath: string, symbolName: string): StaticSymbol {
const resolveModule = (moduleName: string): string => {
const resolvedModulePath = this.resolve(moduleName, filePath);
if (!resolvedModulePath) {
throw new Error(`Could not resolve module '${moduleName}' relative to file ${filePath}`);
}
return resolvedModulePath;
};
let metadata = this.getResolverMetadata(filePath);
if (metadata) {
// If we have metadata for the symbol, this is the original exporting location.
if (metadata.metadata[symbolName]) {
return this.getStaticSymbol(filePath, symbolName);
}
// If no, try to find the symbol in one of the re-export location
if (metadata.exports) {
// Try and find the symbol in the list of explicitly re-exported symbols.
for (const moduleExport of metadata.exports) {
if (moduleExport.export) {
const exportSymbol = moduleExport.export.find(symbol => {
if (typeof symbol === 'string') {
return symbol == symbolName;
} else {
return symbol.as == symbolName;
}
});
if (exportSymbol) {
let symName = symbolName;
if (typeof exportSymbol !== 'string') {
symName = exportSymbol.name;
}
return this.resolveExportedSymbol(resolveModule(moduleExport.from), symName);
}
}
}
// Try to find the symbol via export * directives.
for (const moduleExport of metadata.exports) {
if (!moduleExport.export) {
const resolvedModule = resolveModule(moduleExport.from);
const candidateSymbol = this.resolveExportedSymbol(resolvedModule, symbolName);
if (candidateSymbol) return candidateSymbol;
}
}
}
}
return null;
}
}
export class NodeReflectorHostContext implements ReflectorHostContext {