fix(compiler-cli): Use typescript to resolve modules for metadata (#22856)

The current module resolution simply attaches .ts to the import/export path, which does
not work if the path is using Node / CommonJS behavior to resolve to an index.ts file.
This patch uses typescript's module resolution logic, and will attempt to load the original
typescript file if this resolution returns a .js or .d.ts file

PR Close #22856
This commit is contained in:
Jeff Burn
2018-07-07 10:02:08 +10:00
committed by Miško Hevery
parent a167bca927
commit 0d5f2d3c7e
5 changed files with 245 additions and 24 deletions

View File

@ -103,7 +103,7 @@ export function createBundleIndexHost<H extends ts.CompilerHost>(
// etc.
const getMetadataBundle = (cache: MetadataCache | null) => {
const bundler = new MetadataBundler(
indexModule, ngOptions.flatModuleId, new CompilerHostAdapter(host, cache),
indexModule, ngOptions.flatModuleId, new CompilerHostAdapter(host, cache, ngOptions),
ngOptions.flatModulePrivateSymbolPrefix);
return bundler.getMetadataBundle();
};

View File

@ -72,7 +72,7 @@ export interface BundledModule {
}
export interface MetadataBundlerHost {
getMetadataFor(moduleName: string): ModuleMetadata|undefined;
getMetadataFor(moduleName: string, containingFile: string): ModuleMetadata|undefined;
}
type StaticsMetadata = {
@ -136,7 +136,7 @@ export class MetadataBundler {
if (!result) {
if (moduleName.startsWith('.')) {
const fullModuleName = resolveModule(moduleName, this.root);
result = this.host.getMetadataFor(fullModuleName);
result = this.host.getMetadataFor(fullModuleName, this.root);
}
this.metadataCache.set(moduleName, result);
}
@ -598,11 +598,27 @@ export class MetadataBundler {
export class CompilerHostAdapter implements MetadataBundlerHost {
private collector = new MetadataCollector();
constructor(private host: ts.CompilerHost, private cache: MetadataCache|null) {}
constructor(
private host: ts.CompilerHost, private cache: MetadataCache|null,
private options: ts.CompilerOptions) {}
getMetadataFor(fileName: string, containingFile: string): ModuleMetadata|undefined {
const {resolvedModule} =
ts.resolveModuleName(fileName, containingFile, this.options, this.host);
let sourceFile: ts.SourceFile|undefined;
if (resolvedModule) {
let {resolvedFileName} = resolvedModule;
if (resolvedModule.extension !== '.ts') {
resolvedFileName = resolvedFileName.replace(/(\.d\.ts|\.js)$/, '.ts');
}
sourceFile = this.host.getSourceFile(resolvedFileName, ts.ScriptTarget.Latest);
} else {
// If typescript is unable to resolve the file, fallback on old behavior
if (!this.host.fileExists(fileName + '.ts')) return undefined;
sourceFile = this.host.getSourceFile(fileName + '.ts', ts.ScriptTarget.Latest);
}
getMetadataFor(fileName: string): ModuleMetadata|undefined {
if (!this.host.fileExists(fileName + '.ts')) return undefined;
const sourceFile = this.host.getSourceFile(fileName + '.ts', ts.ScriptTarget.Latest);
// If there is a metadata cache, use it to get the metadata for this source file. Otherwise,
// fall back on the locally created MetadataCollector.
if (!sourceFile) {