2016-06-23 12:47:54 -04:00
|
|
|
/**
|
|
|
|
* @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
|
|
|
|
*/
|
|
|
|
|
2016-11-15 13:14:01 -05:00
|
|
|
import {AotCompilerHost, StaticSymbol} from '@angular/compiler';
|
2017-03-01 16:23:34 -05:00
|
|
|
import {AngularCompilerOptions, CollectorOptions, MetadataCollector, ModuleMetadata} from '@angular/tsc-wrapped';
|
2016-04-29 00:57:16 -04:00
|
|
|
import * as fs from 'fs';
|
|
|
|
import * as path from 'path';
|
2016-06-08 19:38:52 -04:00
|
|
|
import * as ts from 'typescript';
|
|
|
|
|
2016-04-29 00:57:16 -04:00
|
|
|
const EXT = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
|
|
|
const DTS = /\.d\.ts$/;
|
2016-08-26 18:41:50 -04:00
|
|
|
const NODE_MODULES = '/node_modules/';
|
2017-04-26 12:24:42 -04:00
|
|
|
const IS_GENERATED = /\.(ngfactory|ngstyle|ngsummary)$/;
|
|
|
|
const GENERATED_FILES = /\.ngfactory\.ts$|\.ngstyle\.ts$|\.ngsummary\.ts$/;
|
|
|
|
const GENERATED_OR_DTS_FILES = /\.d\.ts$|\.ngfactory\.ts$|\.ngstyle\.ts$|\.ngsummary\.ts$/;
|
2017-02-22 20:23:08 -05:00
|
|
|
const SHALLOW_IMPORT = /^((\w|-)+|(@(\w|-)+(\/(\w|-)+)+))$/;
|
2016-04-29 00:57:16 -04:00
|
|
|
|
2016-12-01 16:24:51 -05:00
|
|
|
export interface CompilerHostContext extends ts.ModuleResolutionHost {
|
2016-11-15 14:13:20 -05:00
|
|
|
readResource(fileName: string): Promise<string>;
|
2016-07-13 14:15:23 -04:00
|
|
|
assumeFileExists(fileName: string): void;
|
2016-06-09 17:51:53 -04:00
|
|
|
}
|
|
|
|
|
2016-11-17 15:24:33 -05:00
|
|
|
export class CompilerHost implements AotCompilerHost {
|
2016-08-22 14:48:33 -04:00
|
|
|
protected metadataCollector = new MetadataCollector();
|
2016-08-04 00:34:03 -04:00
|
|
|
private isGenDirChildOfRootDir: boolean;
|
2016-08-22 14:48:33 -04:00
|
|
|
protected basePath: string;
|
2016-08-05 14:11:24 -04:00
|
|
|
private genDir: string;
|
2016-11-17 12:52:38 -05:00
|
|
|
private resolverCache = new Map<string, ModuleMetadata[]>();
|
2017-02-19 00:28:27 -05:00
|
|
|
private bundleIndexCache = new Map<string, boolean>();
|
|
|
|
private bundleIndexNames = new Set<string>();
|
2017-03-24 12:59:58 -04:00
|
|
|
private moduleFileNames = new Map<string, string|null>();
|
2016-12-15 12:12:40 -05:00
|
|
|
protected resolveModuleNameHost: CompilerHostContext;
|
2016-11-17 12:52:38 -05:00
|
|
|
|
2016-06-08 19:38:52 -04:00
|
|
|
constructor(
|
2016-12-01 16:24:51 -05:00
|
|
|
protected program: ts.Program, protected options: AngularCompilerOptions,
|
2017-03-01 16:23:34 -05:00
|
|
|
protected context: CompilerHostContext, collectorOptions?: CollectorOptions) {
|
2016-08-05 14:11:24 -04:00
|
|
|
// normalize the path so that it never ends with '/'.
|
2016-08-26 18:41:50 -04:00
|
|
|
this.basePath = path.normalize(path.join(this.options.basePath, '.')).replace(/\\/g, '/');
|
|
|
|
this.genDir = path.normalize(path.join(this.options.genDir, '.')).replace(/\\/g, '/');
|
2016-08-05 14:11:24 -04:00
|
|
|
|
2016-11-12 08:08:58 -05:00
|
|
|
const genPath: string = path.relative(this.basePath, this.genDir);
|
2016-08-04 00:34:03 -04:00
|
|
|
this.isGenDirChildOfRootDir = genPath === '' || !genPath.startsWith('..');
|
2016-12-15 12:12:40 -05:00
|
|
|
this.resolveModuleNameHost = Object.create(this.context);
|
|
|
|
|
|
|
|
// When calling ts.resolveModuleName,
|
|
|
|
// additional allow checks for .d.ts files to be done based on
|
|
|
|
// checks for .ngsummary.json files,
|
|
|
|
// so that our codegen depends on fewer inputs and requires to be called
|
|
|
|
// less often.
|
|
|
|
// This is needed as we use ts.resolveModuleName in reflector_host
|
|
|
|
// and it should be able to resolve summary file names.
|
|
|
|
this.resolveModuleNameHost.fileExists = (fileName: string): boolean => {
|
|
|
|
if (this.context.fileExists(fileName)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (DTS.test(fileName)) {
|
|
|
|
const base = fileName.substring(0, fileName.length - 5);
|
|
|
|
return this.context.fileExists(base + '.ngsummary.json');
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
2016-06-09 17:51:53 -04:00
|
|
|
}
|
2016-05-01 14:22:39 -04:00
|
|
|
|
2016-08-22 14:48:33 -04:00
|
|
|
// We use absolute paths on disk as canonical.
|
|
|
|
getCanonicalFileName(fileName: string): string { return fileName; }
|
|
|
|
|
2016-12-15 12:12:40 -05:00
|
|
|
moduleNameToFileName(m: string, containingFile: string): string|null {
|
2017-03-28 16:32:46 -04:00
|
|
|
const key = m + ':' + (containingFile || '');
|
2017-03-24 12:59:58 -04:00
|
|
|
let result: string|null = this.moduleFileNames.get(key) || null;
|
2017-03-28 16:32:46 -04:00
|
|
|
if (!result) {
|
|
|
|
if (!containingFile || !containingFile.length) {
|
|
|
|
if (m.indexOf('.') === 0) {
|
|
|
|
throw new Error('Resolution of relative paths requires a containing file.');
|
|
|
|
}
|
|
|
|
// Any containing file gives the same result for absolute imports
|
|
|
|
containingFile = this.getCanonicalFileName(path.join(this.basePath, 'index.ts'));
|
2016-11-15 11:49:23 -05:00
|
|
|
}
|
2017-03-28 16:32:46 -04:00
|
|
|
m = m.replace(EXT, '');
|
|
|
|
const resolved =
|
|
|
|
ts.resolveModuleName(
|
|
|
|
m, containingFile.replace(/\\/g, '/'), this.options, this.resolveModuleNameHost)
|
|
|
|
.resolvedModule;
|
|
|
|
result = resolved ? this.getCanonicalFileName(resolved.resolvedFileName) : null;
|
|
|
|
this.moduleFileNames.set(key, result);
|
2016-11-15 11:49:23 -05:00
|
|
|
}
|
2017-03-28 16:32:46 -04:00
|
|
|
return result;
|
2016-04-29 00:57:16 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* We want a moduleId that will appear in import statements in the generated code.
|
|
|
|
* These need to be in a form that system.js can load, so absolute file paths don't work.
|
2016-08-04 00:34:03 -04:00
|
|
|
*
|
|
|
|
* The `containingFile` is always in the `genDir`, where as the `importedFile` can be in
|
2016-08-12 20:38:29 -04:00
|
|
|
* `genDir`, `node_module` or `basePath`. The `importedFile` is either a generated file or
|
|
|
|
* existing file.
|
2016-08-04 00:34:03 -04:00
|
|
|
*
|
|
|
|
* | genDir | node_module | rootDir
|
|
|
|
* --------------+----------+-------------+----------
|
|
|
|
* generated | relative | relative | n/a
|
|
|
|
* existing file | n/a | absolute | relative(*)
|
|
|
|
*
|
|
|
|
* NOTE: (*) the relative path is computed depending on `isGenDirChildOfRootDir`.
|
2016-04-29 00:57:16 -04:00
|
|
|
*/
|
2016-11-17 15:24:33 -05:00
|
|
|
fileNameToModuleName(importedFile: string, containingFile: string): string {
|
2016-08-02 14:45:14 -04:00
|
|
|
// 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!
|
2016-12-01 16:24:51 -05:00
|
|
|
if (!this.context.fileExists(importedFile)) {
|
2016-07-13 14:15:23 -04:00
|
|
|
this.context.assumeFileExists(importedFile);
|
2016-04-29 00:57:16 -04:00
|
|
|
}
|
2016-05-02 12:38:46 -04:00
|
|
|
|
2016-08-12 20:38:29 -04:00
|
|
|
containingFile = this.rewriteGenDirPath(containingFile);
|
|
|
|
const containingDir = path.dirname(containingFile);
|
|
|
|
// drop extension
|
|
|
|
importedFile = importedFile.replace(EXT, '');
|
|
|
|
|
2016-11-12 08:08:58 -05:00
|
|
|
const nodeModulesIndex = importedFile.indexOf(NODE_MODULES);
|
2016-08-12 20:38:29 -04:00
|
|
|
const importModule = nodeModulesIndex === -1 ?
|
|
|
|
null :
|
|
|
|
importedFile.substring(nodeModulesIndex + NODE_MODULES.length);
|
|
|
|
const isGeneratedFile = IS_GENERATED.test(importedFile);
|
|
|
|
|
|
|
|
if (isGeneratedFile) {
|
|
|
|
// rewrite to genDir path
|
|
|
|
if (importModule) {
|
|
|
|
// it is generated, therefore we do a relative path to the factory
|
|
|
|
return this.dotRelative(containingDir, this.genDir + NODE_MODULES + importModule);
|
|
|
|
} else {
|
|
|
|
// assume that import is also in `genDir`
|
|
|
|
importedFile = this.rewriteGenDirPath(importedFile);
|
|
|
|
return this.dotRelative(containingDir, importedFile);
|
2016-05-02 12:38:46 -04:00
|
|
|
}
|
2016-08-12 20:38:29 -04:00
|
|
|
} else {
|
|
|
|
// user code import
|
|
|
|
if (importModule) {
|
|
|
|
return importModule;
|
|
|
|
} else {
|
|
|
|
if (!this.isGenDirChildOfRootDir) {
|
|
|
|
// assume that they are on top of each other.
|
|
|
|
importedFile = importedFile.replace(this.basePath, this.genDir);
|
2016-08-04 00:34:03 -04:00
|
|
|
}
|
2017-02-14 16:33:06 -05:00
|
|
|
if (SHALLOW_IMPORT.test(importedFile)) {
|
|
|
|
return importedFile;
|
|
|
|
}
|
2016-08-12 20:38:29 -04:00
|
|
|
return this.dotRelative(containingDir, importedFile);
|
2016-04-29 00:57:16 -04:00
|
|
|
}
|
|
|
|
}
|
2016-08-04 00:34:03 -04:00
|
|
|
}
|
2016-06-03 15:23:35 -04:00
|
|
|
|
2016-08-04 00:34:03 -04:00
|
|
|
private dotRelative(from: string, to: string): string {
|
2016-11-12 08:08:58 -05:00
|
|
|
const rPath: string = path.relative(from, to).replace(/\\/g, '/');
|
2016-08-04 00:34:03 -04:00
|
|
|
return rPath.startsWith('.') ? rPath : './' + rPath;
|
|
|
|
}
|
2016-06-03 15:23:35 -04:00
|
|
|
|
2016-08-12 20:38:29 -04:00
|
|
|
/**
|
|
|
|
* Moves the path into `genDir` folder while preserving the `node_modules` directory.
|
|
|
|
*/
|
|
|
|
private rewriteGenDirPath(filepath: string) {
|
2016-11-12 08:08:58 -05:00
|
|
|
const nodeModulesIndex = filepath.indexOf(NODE_MODULES);
|
2016-08-12 20:38:29 -04:00
|
|
|
if (nodeModulesIndex !== -1) {
|
|
|
|
// If we are in node_modulse, transplant them into `genDir`.
|
|
|
|
return path.join(this.genDir, filepath.substring(nodeModulesIndex));
|
|
|
|
} else {
|
|
|
|
// pretend that containing file is on top of the `genDir` to normalize the paths.
|
|
|
|
// we apply the `genDir` => `rootDir` delta through `rootDirPrefix` later.
|
|
|
|
return filepath.replace(this.basePath, this.genDir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-17 12:52:38 -05:00
|
|
|
protected getSourceFile(filePath: string): ts.SourceFile {
|
|
|
|
const sf = this.program.getSourceFile(filePath);
|
|
|
|
if (!sf) {
|
|
|
|
if (this.context.fileExists(filePath)) {
|
|
|
|
const sourceText = this.context.readFile(filePath);
|
|
|
|
return ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true);
|
|
|
|
}
|
|
|
|
throw new Error(`Source file ${filePath} not present in program.`);
|
|
|
|
}
|
2016-11-17 15:24:33 -05:00
|
|
|
return sf;
|
2016-11-17 12:52:38 -05:00
|
|
|
}
|
2016-04-29 00:57:16 -04:00
|
|
|
|
2017-03-24 12:59:58 -04:00
|
|
|
getMetadataFor(filePath: string): ModuleMetadata[]|undefined {
|
2016-07-13 14:15:23 -04:00
|
|
|
if (!this.context.fileExists(filePath)) {
|
2016-07-11 20:26:35 -04:00
|
|
|
// 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).
|
|
|
|
return;
|
2016-04-29 00:57:16 -04:00
|
|
|
}
|
|
|
|
if (DTS.test(filePath)) {
|
|
|
|
const metadataPath = filePath.replace(DTS, '.metadata.json');
|
2016-07-13 14:15:23 -04:00
|
|
|
if (this.context.fileExists(metadataPath)) {
|
2016-11-17 12:52:38 -05:00
|
|
|
return this.readMetadata(metadataPath, filePath);
|
2016-12-16 18:33:47 -05:00
|
|
|
} else {
|
|
|
|
// If there is a .d.ts file but no metadata file we need to produce a
|
|
|
|
// v3 metadata from the .d.ts file as v3 includes the exports we need
|
|
|
|
// to resolve symbols.
|
|
|
|
return [this.upgradeVersion1Metadata(
|
|
|
|
{'__symbolic': 'module', 'version': 1, 'metadata': {}}, filePath)];
|
2016-04-29 00:57:16 -04:00
|
|
|
}
|
2016-07-06 17:26:31 -04:00
|
|
|
} else {
|
2016-11-17 12:52:38 -05:00
|
|
|
const sf = this.getSourceFile(filePath);
|
|
|
|
const metadata = this.metadataCollector.getMetadata(sf);
|
|
|
|
return metadata ? [metadata] : [];
|
2016-04-29 00:57:16 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-17 12:52:38 -05:00
|
|
|
readMetadata(filePath: string, dtsFilePath: string): ModuleMetadata[] {
|
|
|
|
let metadatas = this.resolverCache.get(filePath);
|
|
|
|
if (metadatas) {
|
|
|
|
return metadatas;
|
|
|
|
}
|
2016-04-29 00:57:16 -04:00
|
|
|
try {
|
2017-03-20 22:43:39 -04:00
|
|
|
const metadataOrMetadatas = JSON.parse(this.context.readFile(filePath));
|
2016-12-16 18:33:47 -05:00
|
|
|
const metadatas: ModuleMetadata[] = metadataOrMetadatas ?
|
2016-11-17 12:52:38 -05:00
|
|
|
(Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) :
|
|
|
|
[];
|
2016-12-16 18:33:47 -05:00
|
|
|
const v1Metadata = metadatas.find(m => m.version === 1);
|
|
|
|
let v3Metadata = metadatas.find(m => m.version === 3);
|
2016-12-14 18:28:51 -05:00
|
|
|
if (!v3Metadata && v1Metadata) {
|
2016-12-16 18:33:47 -05:00
|
|
|
metadatas.push(this.upgradeVersion1Metadata(v1Metadata, dtsFilePath));
|
2016-11-17 12:52:38 -05:00
|
|
|
}
|
|
|
|
this.resolverCache.set(filePath, metadatas);
|
|
|
|
return metadatas;
|
2016-04-29 00:57:16 -04:00
|
|
|
} catch (e) {
|
|
|
|
console.error(`Failed to read JSON file ${filePath}`);
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
2016-08-02 14:45:14 -04:00
|
|
|
|
2016-12-16 18:33:47 -05:00
|
|
|
private upgradeVersion1Metadata(v1Metadata: ModuleMetadata, dtsFilePath: string): ModuleMetadata {
|
|
|
|
// patch up v1 to v3 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
|
|
|
|
// the metadata and the `extends` clause.
|
|
|
|
let v3Metadata: ModuleMetadata = {'__symbolic': 'module', 'version': 3, 'metadata': {}};
|
|
|
|
if (v1Metadata.exports) {
|
|
|
|
v3Metadata.exports = v1Metadata.exports;
|
|
|
|
}
|
|
|
|
for (let prop in v1Metadata.metadata) {
|
|
|
|
v3Metadata.metadata[prop] = v1Metadata.metadata[prop];
|
|
|
|
}
|
|
|
|
|
|
|
|
const exports = this.metadataCollector.getMetadata(this.getSourceFile(dtsFilePath));
|
|
|
|
if (exports) {
|
|
|
|
for (let prop in exports.metadata) {
|
|
|
|
if (!v3Metadata.metadata[prop]) {
|
|
|
|
v3Metadata.metadata[prop] = exports.metadata[prop];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (exports.exports) {
|
|
|
|
v3Metadata.exports = exports.exports;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return v3Metadata;
|
|
|
|
}
|
|
|
|
|
2016-11-15 14:13:20 -05:00
|
|
|
loadResource(filePath: string): Promise<string> { return this.context.readResource(filePath); }
|
2016-11-29 18:36:33 -05:00
|
|
|
|
2016-12-15 12:12:40 -05:00
|
|
|
loadSummary(filePath: string): string|null {
|
|
|
|
if (this.context.fileExists(filePath)) {
|
|
|
|
return this.context.readFile(filePath);
|
|
|
|
}
|
2017-03-24 12:59:58 -04:00
|
|
|
return null;
|
2016-12-15 12:12:40 -05:00
|
|
|
}
|
2016-11-29 18:36:33 -05:00
|
|
|
|
|
|
|
getOutputFileName(sourceFilePath: string): string {
|
|
|
|
return sourceFilePath.replace(EXT, '') + '.d.ts';
|
|
|
|
}
|
2016-12-15 12:12:40 -05:00
|
|
|
|
|
|
|
isSourceFile(filePath: string): boolean {
|
|
|
|
const excludeRegex =
|
|
|
|
this.options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
|
2017-02-19 00:28:27 -05:00
|
|
|
if (excludeRegex.test(filePath)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (DTS.test(filePath)) {
|
|
|
|
// Check for a bundle index.
|
|
|
|
if (this.hasBundleIndex(filePath)) {
|
2017-02-24 14:52:18 -05:00
|
|
|
const normalFilePath = path.normalize(filePath);
|
|
|
|
return this.bundleIndexNames.has(normalFilePath);
|
2017-02-19 00:28:27 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
2016-12-15 12:12:40 -05:00
|
|
|
}
|
2017-01-12 18:31:09 -05:00
|
|
|
|
|
|
|
calculateEmitPath(filePath: string): string {
|
|
|
|
// Write codegen in a directory structure matching the sources.
|
2017-03-24 12:59:58 -04:00
|
|
|
let root = this.options.basePath !;
|
2017-01-12 18:31:09 -05:00
|
|
|
for (const eachRootDir of this.options.rootDirs || []) {
|
|
|
|
if (this.options.trace) {
|
|
|
|
console.error(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
|
|
|
|
}
|
|
|
|
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
|
|
|
|
root = eachRootDir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// transplant the codegen path to be inside the `genDir`
|
|
|
|
let relativePath: string = path.relative(root, filePath);
|
|
|
|
while (relativePath.startsWith('..' + path.sep)) {
|
|
|
|
// Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
|
|
|
|
// into `genDir`.
|
|
|
|
relativePath = relativePath.substr(3);
|
|
|
|
}
|
|
|
|
|
|
|
|
return path.join(this.options.genDir, relativePath);
|
|
|
|
}
|
2017-02-19 00:28:27 -05:00
|
|
|
|
|
|
|
private hasBundleIndex(filePath: string): boolean {
|
|
|
|
const checkBundleIndex = (directory: string): boolean => {
|
|
|
|
let result = this.bundleIndexCache.get(directory);
|
|
|
|
if (result == null) {
|
|
|
|
if (path.basename(directory) == 'node_module') {
|
|
|
|
// Don't look outside the node_modules this package is installed in.
|
|
|
|
result = false;
|
|
|
|
} else {
|
|
|
|
// A bundle index exists if the typings .d.ts file has a metadata.json that has an
|
|
|
|
// importAs.
|
|
|
|
try {
|
|
|
|
const packageFile = path.join(directory, 'package.json');
|
|
|
|
if (this.context.fileExists(packageFile)) {
|
|
|
|
// Once we see a package.json file, assume false until it we find the bundle index.
|
|
|
|
result = false;
|
|
|
|
const packageContent: any = JSON.parse(this.context.readFile(packageFile));
|
|
|
|
if (packageContent.typings) {
|
|
|
|
const typings = path.normalize(path.join(directory, packageContent.typings));
|
|
|
|
if (DTS.test(typings)) {
|
|
|
|
const metadataFile = typings.replace(DTS, '.metadata.json');
|
|
|
|
if (this.context.fileExists(metadataFile)) {
|
|
|
|
const metadata = JSON.parse(this.context.readFile(metadataFile));
|
|
|
|
if (metadata.importAs) {
|
|
|
|
this.bundleIndexNames.add(typings);
|
|
|
|
result = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const parent = path.dirname(directory);
|
|
|
|
if (parent != directory) {
|
|
|
|
// Try the parent directory.
|
|
|
|
result = checkBundleIndex(parent);
|
|
|
|
} else {
|
|
|
|
result = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
// If we encounter any errors assume we this isn't a bundle index.
|
|
|
|
result = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.bundleIndexCache.set(directory, result);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
|
|
|
|
return checkBundleIndex(path.dirname(filePath));
|
|
|
|
}
|
2016-04-29 00:57:16 -04:00
|
|
|
}
|
2016-06-09 17:51:53 -04:00
|
|
|
|
2016-12-01 16:24:51 -05:00
|
|
|
export class CompilerHostContextAdapter {
|
|
|
|
protected assumedExists: {[fileName: string]: boolean} = {};
|
2016-09-07 19:24:52 -04:00
|
|
|
|
2016-12-01 16:24:51 -05:00
|
|
|
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) {
|
2017-03-24 12:59:58 -04:00
|
|
|
this.directoryExists = (directoryName: string) => host.directoryExists !(directoryName);
|
2016-12-01 16:24:51 -05:00
|
|
|
}
|
|
|
|
}
|
2016-06-09 17:51:53 -04:00
|
|
|
|
2016-07-13 14:15:23 -04:00
|
|
|
fileExists(fileName: string): boolean {
|
2016-09-07 19:24:52 -04:00
|
|
|
return this.assumedExists[fileName] || this.host.fileExists(fileName);
|
2016-07-13 14:15:23 -04:00
|
|
|
}
|
|
|
|
|
2016-12-01 16:24:51 -05:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-07-13 14:15:23 -04:00
|
|
|
directoryExists(directoryName: string): boolean {
|
|
|
|
try {
|
|
|
|
return fs.statSync(directoryName).isDirectory();
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
readFile(fileName: string): string { return fs.readFileSync(fileName, 'utf8'); }
|
2016-06-09 17:51:53 -04:00
|
|
|
|
2016-11-15 14:13:20 -05:00
|
|
|
readResource(s: string) {
|
2016-12-01 16:24:51 -05:00
|
|
|
if (!this.fileExists(s)) {
|
2016-11-15 14:13:20 -05:00
|
|
|
// TODO: We should really have a test for error cases like this!
|
|
|
|
throw new Error(`Compilation failed. Resource file not found: ${s}`);
|
|
|
|
}
|
2016-12-01 16:24:51 -05:00
|
|
|
return Promise.resolve(this.readFile(s));
|
2016-11-15 14:13:20 -05:00
|
|
|
}
|
2016-06-09 17:51:53 -04:00
|
|
|
}
|