fix(compiler): Added unit test to ReflectorHost and fixed issues (#9052)
Refactored ReflectorHost to allow it to be tested. Fixed an issue where the .d.ts was findable but it wasn't used by the project (This happens when the .metadata.json file references a module that was not needed, such as it doesn't declare any types, and the reference to it was elided by TypeScript when writing the .d.ts file). Added tests for ReflectorHost
This commit is contained in:
@ -24,7 +24,7 @@ import {
|
||||
|
||||
import {Parse5DomAdapter} from '@angular/platform-server';
|
||||
|
||||
import {NodeReflectorHost} from './reflector_host';
|
||||
import {ReflectorHost, ReflectorHostContext} from './reflector_host';
|
||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||
|
||||
const GENERATED_FILES = /\.ngfactory\.ts$|\.css\.ts$|\.css\.shim\.ts$/;
|
||||
@ -42,7 +42,7 @@ export class CodeGenerator {
|
||||
private program: ts.Program, public host: ts.CompilerHost,
|
||||
private staticReflector: StaticReflector, private resolver: CompileMetadataResolver,
|
||||
private compiler: compiler.OfflineCompiler,
|
||||
private reflectorHost: NodeReflectorHost) {}
|
||||
private reflectorHost: ReflectorHost) {}
|
||||
|
||||
private generateSource(metadatas: compiler.CompileDirectiveMetadata[]) {
|
||||
const normalize = (metadata: compiler.CompileDirectiveMetadata) => {
|
||||
@ -155,10 +155,11 @@ export class CodeGenerator {
|
||||
}
|
||||
|
||||
static create(options: AngularCompilerOptions, program: ts.Program,
|
||||
compilerHost: ts.CompilerHost): CodeGenerator {
|
||||
compilerHost: ts.CompilerHost,
|
||||
reflectorHostContext?: ReflectorHostContext): CodeGenerator {
|
||||
const xhr: compiler.XHR = {get: (s: string) => Promise.resolve(compilerHost.readFile(s))};
|
||||
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
||||
const reflectorHost = new NodeReflectorHost(program, compilerHost, options);
|
||||
const reflectorHost = new ReflectorHost(program, compilerHost, options, reflectorHostContext);
|
||||
const staticReflector = new StaticReflector(reflectorHost);
|
||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||
const htmlParser = new HtmlParser();
|
||||
|
@ -34,7 +34,7 @@ import {
|
||||
|
||||
import {Parse5DomAdapter} from '@angular/platform-server';
|
||||
|
||||
import {NodeReflectorHost} from './reflector_host';
|
||||
import {ReflectorHost} from './reflector_host';
|
||||
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ class Extractor {
|
||||
private program: ts.Program, public host: ts.CompilerHost,
|
||||
private staticReflector: StaticReflector, private resolver: CompileMetadataResolver,
|
||||
private compiler: compiler.OfflineCompiler,
|
||||
private reflectorHost: NodeReflectorHost, private _extractor: MessageExtractor) {}
|
||||
private reflectorHost: ReflectorHost, private _extractor: MessageExtractor) {}
|
||||
|
||||
private extractCmpMessages(metadatas: compiler.CompileDirectiveMetadata[]): Promise<ExtractionResult> {
|
||||
if (!metadatas || !metadatas.length) {
|
||||
@ -151,7 +151,7 @@ class Extractor {
|
||||
compilerHost: ts.CompilerHost): Extractor {
|
||||
const xhr: compiler.XHR = {get: (s: string) => Promise.resolve(compilerHost.readFile(s))};
|
||||
const urlResolver: compiler.UrlResolver = compiler.createOfflineCompileUrlResolver();
|
||||
const reflectorHost = new NodeReflectorHost(program, compilerHost, options);
|
||||
const reflectorHost = new ReflectorHost(program, compilerHost, options);
|
||||
const staticReflector = new StaticReflector(reflectorHost);
|
||||
StaticAndDynamicReflectionCapabilities.install(staticReflector);
|
||||
const htmlParser = new HtmlParser();
|
||||
|
@ -5,14 +5,23 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import {ImportGenerator, AssetUrl} from './compiler_private';
|
||||
|
||||
|
||||
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
||||
const DTS = /\.d\.ts$/;
|
||||
|
||||
export class NodeReflectorHost implements StaticReflectorHost, ImportGenerator {
|
||||
export interface ReflectorHostContext {
|
||||
exists(fileName: string): boolean;
|
||||
read(fileName: string): string;
|
||||
write(fileName: string, data: string): void;
|
||||
}
|
||||
|
||||
export class ReflectorHost implements StaticReflectorHost, ImportGenerator {
|
||||
private metadataCollector = new MetadataCollector();
|
||||
private context: ReflectorHostContext;
|
||||
constructor(private program: ts.Program, private compilerHost: ts.CompilerHost,
|
||||
private options: AngularCompilerOptions) {}
|
||||
private options: AngularCompilerOptions,
|
||||
context?: ReflectorHostContext) {
|
||||
this.context = context || new NodeReflectorHostContext();
|
||||
}
|
||||
|
||||
angularImportLocations() {
|
||||
return {
|
||||
@ -59,7 +68,7 @@ export class NodeReflectorHost implements StaticReflectorHost, ImportGenerator {
|
||||
console.log(`Generating empty file ${importedFile} to allow resolution of import`);
|
||||
}
|
||||
this.compilerHost.writeFile(importedFile, '', false);
|
||||
fs.writeFileSync(importedFile, '');
|
||||
this.context.write(importedFile, '');
|
||||
}
|
||||
|
||||
const importModuleName = importedFile.replace(EXT, '');
|
||||
@ -108,6 +117,14 @@ export class NodeReflectorHost implements StaticReflectorHost, ImportGenerator {
|
||||
|
||||
const tc = this.program.getTypeChecker();
|
||||
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);
|
||||
}
|
||||
|
||||
let symbol = tc.getExportsOfModule((<any>sf).symbol).find(m => m.name === symbolName);
|
||||
if (!symbol) {
|
||||
@ -148,12 +165,12 @@ export class NodeReflectorHost implements StaticReflectorHost, ImportGenerator {
|
||||
|
||||
// TODO(alexeagle): take a statictype
|
||||
getMetadataFor(filePath: string): ModuleMetadata {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
if (!this.context.exists(filePath)) {
|
||||
throw new Error(`No such file '${filePath}'`);
|
||||
}
|
||||
if (DTS.test(filePath)) {
|
||||
const metadataPath = filePath.replace(DTS, '.metadata.json');
|
||||
if (fs.existsSync(metadataPath)) {
|
||||
if (this.context.exists(metadataPath)) {
|
||||
return this.readMetadata(metadataPath);
|
||||
}
|
||||
}
|
||||
@ -168,7 +185,7 @@ export class NodeReflectorHost implements StaticReflectorHost, ImportGenerator {
|
||||
|
||||
readMetadata(filePath: string) {
|
||||
try {
|
||||
const result = JSON.parse(fs.readFileSync(filePath, {encoding: 'utf-8'}));
|
||||
const result = JSON.parse(this.context.read(filePath));
|
||||
return result;
|
||||
} catch (e) {
|
||||
console.error(`Failed to read JSON file ${filePath}`);
|
||||
@ -176,3 +193,17 @@ export class NodeReflectorHost implements StaticReflectorHost, ImportGenerator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class NodeReflectorHostContext implements ReflectorHostContext {
|
||||
exists(fileName: string): boolean {
|
||||
return fs.existsSync(fileName);
|
||||
}
|
||||
|
||||
read(fileName: string): string {
|
||||
return fs.readFileSync(fileName, 'utf8');
|
||||
}
|
||||
|
||||
write(fileName: string, data: string): void {
|
||||
fs.writeFileSync(fileName, data, 'utf8');
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user