Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(compiler-cli): refactor compiler host parameters #13147

Merged
merged 1 commit into from Dec 1, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion modules/@angular/compiler-cli/index.ts
Expand Up @@ -8,7 +8,7 @@

export {AotCompilerHost, AotCompilerHost as StaticReflectorHost, StaticReflector, StaticSymbol} from '@angular/compiler';
export {CodeGenerator} from './src/codegen';
export {CompilerHost, CompilerHostContext, NodeCompilerHostContext} from './src/compiler_host';
export {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter, NodeCompilerHostContext} from './src/compiler_host';
export {Extractor} from './src/extractor';

export * from '@angular/tsc-wrapped';
8 changes: 4 additions & 4 deletions modules/@angular/compiler-cli/src/codegen.ts
Expand Up @@ -17,7 +17,7 @@ import {readFileSync} from 'fs';
import * as path from 'path';
import * as ts from 'typescript';

import {CompilerHost, CompilerHostContext} from './compiler_host';
import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './compiler_host';
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
import {Console} from './private_import_core';

Expand Down Expand Up @@ -82,9 +82,9 @@ export class CodeGenerator {
ngCompilerHost?: CompilerHost): CodeGenerator {
if (!ngCompilerHost) {
const usePathMapping = !!options.rootDirs && options.rootDirs.length > 0;
ngCompilerHost = usePathMapping ?
new PathMappedCompilerHost(program, tsCompilerHost, options, compilerHostContext) :
new CompilerHost(program, tsCompilerHost, options, compilerHostContext);
const context = compilerHostContext || new ModuleResolutionHostAdapter(tsCompilerHost);
ngCompilerHost = usePathMapping ? new PathMappedCompilerHost(program, options, context) :
new CompilerHost(program, options, context);
}
const transFile = cliOptions.i18nFile;
const locale = cliOptions.locale;
Expand Down
58 changes: 40 additions & 18 deletions modules/@angular/compiler-cli/src/compiler_host.ts
Expand Up @@ -17,30 +17,25 @@ const DTS = /\.d\.ts$/;
const NODE_MODULES = '/node_modules/';
const IS_GENERATED = /\.(ngfactory|css(\.shim)?)$/;

export interface CompilerHostContext {
fileExists(fileName: string): boolean;
directoryExists(directoryName: string): boolean;
readFile(fileName: string): string;
export interface CompilerHostContext extends ts.ModuleResolutionHost {
readResource(fileName: string): Promise<string>;
assumeFileExists(fileName: string): void;
}

export class CompilerHost implements AotCompilerHost {
protected metadataCollector = new MetadataCollector();
protected context: CompilerHostContext;
private isGenDirChildOfRootDir: boolean;
protected basePath: string;
private genDir: string;
private resolverCache = new Map<string, ModuleMetadata[]>();

constructor(
protected program: ts.Program, protected compilerHost: ts.CompilerHost,
protected options: AngularCompilerOptions, context?: CompilerHostContext) {
protected program: ts.Program, protected options: AngularCompilerOptions,
protected context: CompilerHostContext) {
// normalize the path so that it never ends with '/'.
this.basePath = path.normalize(path.join(this.options.basePath, '.')).replace(/\\/g, '/');
this.genDir = path.normalize(path.join(this.options.genDir, '.')).replace(/\\/g, '/');

this.context = context || new NodeCompilerHostContext(compilerHost);
const genPath: string = path.relative(this.basePath, this.genDir);
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
}
Expand Down Expand Up @@ -81,7 +76,7 @@ export class CompilerHost implements AotCompilerHost {
fileNameToModuleName(importedFile: string, containingFile: string): string {
// 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.context.fileExists(importedFile)) {
this.context.assumeFileExists(importedFile);
}

Expand Down Expand Up @@ -181,8 +176,8 @@ export class CompilerHost implements AotCompilerHost {
const metadatas = metadataOrMetadatas ?
(Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) :
[];
const v1Metadata = metadatas.find(m => m['version'] === 1);
let v2Metadata = metadatas.find(m => m['version'] === 2);
const v1Metadata = metadatas.find((m: any) => m['version'] === 1);
let v2Metadata = metadatas.find((m: any) => m['version'] === 2);
if (!v2Metadata && v1Metadata) {
// patch up v1 to v2 by merging the metadata with metadata collected from the d.ts file
// as the only difference between the versions is whether all exports are contained in
Expand Down Expand Up @@ -216,15 +211,44 @@ export class CompilerHost implements AotCompilerHost {
loadResource(filePath: string): Promise<string> { return this.context.readResource(filePath); }
}

export class NodeCompilerHostContext implements CompilerHostContext {
constructor(private host: ts.CompilerHost) {}
export class CompilerHostContextAdapter {
protected assumedExists: {[fileName: string]: boolean} = {};

private assumedExists: {[fileName: string]: boolean} = {};
assumeFileExists(fileName: string): void { this.assumedExists[fileName] = true; }
}

export class ModuleResolutionHostAdapter extends CompilerHostContextAdapter implements
CompilerHostContext {
public directoryExists: ((directoryName: string) => boolean)|undefined;

constructor(private host: ts.ModuleResolutionHost) {
super();
if (host.directoryExists) {
this.directoryExists = (directoryName: string) => host.directoryExists(directoryName);
}
}

fileExists(fileName: string): boolean {
return this.assumedExists[fileName] || this.host.fileExists(fileName);
}

readFile(fileName: string): string { return this.host.readFile(fileName); }

readResource(s: string) {
if (!this.host.fileExists(s)) {
// TODO: We should really have a test for error cases like this!
throw new Error(`Compilation failed. Resource file not found: ${s}`);
}
return Promise.resolve(this.host.readFile(s));
}
}

export class NodeCompilerHostContext extends CompilerHostContextAdapter implements
CompilerHostContext {
fileExists(fileName: string): boolean {
return this.assumedExists[fileName] || fs.existsSync(fileName);
}

directoryExists(directoryName: string): boolean {
try {
return fs.statSync(directoryName).isDirectory();
Expand All @@ -236,12 +260,10 @@ export class NodeCompilerHostContext implements CompilerHostContext {
readFile(fileName: string): string { return fs.readFileSync(fileName, 'utf8'); }

readResource(s: string) {
if (!this.host.fileExists(s)) {
if (!this.fileExists(s)) {
// TODO: We should really have a test for error cases like this!
throw new Error(`Compilation failed. Resource file not found: ${s}`);
}
return Promise.resolve(this.host.readFile(s));
return Promise.resolve(this.readFile(s));
}

assumeFileExists(fileName: string): void { this.assumedExists[fileName] = true; }
}
8 changes: 5 additions & 3 deletions modules/@angular/compiler-cli/src/extractor.ts
Expand Up @@ -18,7 +18,7 @@ import * as tsc from '@angular/tsc-wrapped';
import * as ts from 'typescript';

import {excludeFilePattern} from './codegen';
import {CompilerHost} from './compiler_host';
import {CompilerHost, ModuleResolutionHostAdapter} from './compiler_host';

export class Extractor {
constructor(
Expand All @@ -32,8 +32,10 @@ export class Extractor {

static create(
options: tsc.AngularCompilerOptions, translationsFormat: string, program: ts.Program,
tsCompilerHost: ts.CompilerHost, ngCompilerHost?: CompilerHost): Extractor {
if (!ngCompilerHost) ngCompilerHost = new CompilerHost(program, tsCompilerHost, options);
moduleResolverHost: ts.ModuleResolutionHost, ngCompilerHost?: CompilerHost): Extractor {
if (!ngCompilerHost)
ngCompilerHost =
new CompilerHost(program, options, new ModuleResolutionHostAdapter(moduleResolverHost));
const {extractor: ngExtractor} = compiler.Extractor.create(
ngCompilerHost, {excludeFilePattern: excludeFilePattern(options)});
return new Extractor(ngExtractor, ngCompilerHost, program);
Expand Down
Expand Up @@ -25,10 +25,8 @@ const DTS = /\.d\.ts$/;
* loader what to do.
*/
export class PathMappedCompilerHost extends CompilerHost {
constructor(
program: ts.Program, compilerHost: ts.CompilerHost, options: AngularCompilerOptions,
context?: CompilerHostContext) {
super(program, compilerHost, options, context);
constructor(program: ts.Program, options: AngularCompilerOptions, context: CompilerHostContext) {
super(program, options, context);
}

getCanonicalFileName(fileName: string): string {
Expand Down Expand Up @@ -119,7 +117,7 @@ export class PathMappedCompilerHost extends CompilerHost {
getMetadataFor(filePath: string): ModuleMetadata[] {
for (const root of this.options.rootDirs || []) {
const rootedPath = path.join(root, filePath);
if (!this.compilerHost.fileExists(rootedPath)) {
if (!this.context.fileExists(rootedPath)) {
// If the file doesn't exists then we cannot return metadata for the file.
// This will occur if the user refernced a declared module for which no file
// exists for the module (i.e. jQuery or angularjs).
Expand Down
7 changes: 3 additions & 4 deletions modules/@angular/compiler-cli/test/aot_host_spec.ts
Expand Up @@ -14,14 +14,13 @@ import {Directory, Entry, MockAotContext, MockCompilerHost} from './mocks';

describe('CompilerHost', () => {
let context: MockAotContext;
let host: ts.CompilerHost;
let program: ts.Program;
let hostNestedGenDir: CompilerHost;
let hostSiblingGenDir: CompilerHost;

beforeEach(() => {
context = new MockAotContext('/tmp/src', clone(FILES));
host = new MockCompilerHost(context);
const host = new MockCompilerHost(context);
program = ts.createProgram(
['main.ts'], {
module: ts.ModuleKind.CommonJS,
Expand All @@ -33,7 +32,7 @@ describe('CompilerHost', () => {
throw new Error('Expected no errors');
}
hostNestedGenDir = new CompilerHost(
program, host, {
program, {
genDir: '/tmp/project/src/gen/',
basePath: '/tmp/project/src',
skipMetadataEmit: false,
Expand All @@ -43,7 +42,7 @@ describe('CompilerHost', () => {
},
context);
hostSiblingGenDir = new CompilerHost(
program, host, {
program, {
genDir: '/tmp/project/gen',
basePath: '/tmp/project/src/',
skipMetadataEmit: false,
Expand Down