/** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import * as ts from 'typescript'; export type Entry = string | Directory; export interface Directory { [name: string]: Entry; } export class MockAotContext { private files: Entry[]; constructor(public currentDirectory: string, ...files: Entry[]) { this.files = files; } fileExists(fileName: string): boolean { return typeof this.getEntry(fileName) === 'string'; } directoryExists(path: string): boolean { return path === this.currentDirectory || typeof this.getEntry(path) === 'object'; } readFile(fileName: string): string { const data = this.getEntry(fileName); if (typeof data === 'string') { return data; } return undefined !; } readResource(fileName: string): Promise { const result = this.readFile(fileName); if (result == null) { return Promise.reject(new Error(`Resource not found: ${fileName}`)); } return Promise.resolve(result); } writeFile(fileName: string, data: string): void { const parts = fileName.split('/'); const name = parts.pop() !; const entry = this.getEntry(parts); if (entry && typeof entry !== 'string') { entry[name] = data; } } assumeFileExists(fileName: string): void { this.writeFile(fileName, ''); } getEntry(fileName: string|string[]): Entry|undefined { let parts = typeof fileName === 'string' ? fileName.split('/') : fileName; if (parts[0]) { parts = this.currentDirectory.split('/').concat(parts); } parts.shift(); parts = normalize(parts); return first(this.files, files => getEntryFromFiles(parts, files)); } getDirectories(path: string): string[] { const dir = this.getEntry(path); if (typeof dir !== 'object') { return []; } else { return Object.keys(dir).filter(key => typeof dir[key] === 'object'); } } override(files: Entry) { return new MockAotContext(this.currentDirectory, files, ...this.files); } } function first(a: T[], cb: (value: T) => T | undefined): T|undefined { for (const value of a) { const result = cb(value); if (result != null) return result; } } function getEntryFromFiles(parts: string[], files: Entry) { let current = files; while (parts.length) { const part = parts.shift() !; if (typeof current === 'string') { return undefined; } const next = (current)[part]; if (next === undefined) { return undefined; } current = next; } return current; } function normalize(parts: string[]): string[] { const result: string[] = []; while (parts.length) { const part = parts.shift() !; switch (part) { case '.': break; case '..': result.pop(); break; default: result.push(part); } } return result; } export class MockCompilerHost implements ts.CompilerHost { constructor(private context: MockAotContext) {} fileExists(fileName: string): boolean { return this.context.fileExists(fileName); } readFile(fileName: string): string { return this.context.readFile(fileName); } directoryExists(directoryName: string): boolean { return this.context.directoryExists(directoryName); } getSourceFile( fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void): ts.SourceFile { const sourceText = this.context.readFile(fileName); if (sourceText != null) { return ts.createSourceFile(fileName, sourceText, languageVersion); } else { return undefined !; } } getDefaultLibFileName(options: ts.CompilerOptions): string { return ts.getDefaultLibFileName(options); } writeFile: ts.WriteFileCallback = (fileName, text) => { this.context.writeFile(fileName, text); }; getCurrentDirectory(): string { return this.context.currentDirectory; } getCanonicalFileName(fileName: string): string { return fileName; } useCaseSensitiveFileNames(): boolean { return false; } getNewLine(): string { return '\n'; } getDirectories(path: string): string[] { return this.context.getDirectories(path); } }