2015-05-04 11:27:14 -04:00
|
|
|
/// <reference path="../typings/node/node.d.ts" />
|
|
|
|
|
|
|
|
import fs = require('fs');
|
|
|
|
import fse = require('fs-extra');
|
|
|
|
import path = require('path');
|
2015-09-23 18:02:37 -04:00
|
|
|
import * as ts from 'typescript';
|
2015-05-04 11:27:14 -04:00
|
|
|
import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-broccoli-plugin';
|
2016-03-22 20:11:42 -04:00
|
|
|
import {MetadataCollector} from '../metadata';
|
2015-05-04 11:27:14 -04:00
|
|
|
|
|
|
|
type FileRegistry = ts.Map<{version: number}>;
|
|
|
|
|
2015-06-12 10:50:45 -04:00
|
|
|
const FS_OPTS = {
|
|
|
|
encoding: 'utf-8'
|
|
|
|
};
|
2015-05-04 11:27:14 -04:00
|
|
|
|
2015-12-22 19:24:35 -05:00
|
|
|
// Sub-directory where the @internal typing files (.d.ts) are stored
|
|
|
|
export const INTERNAL_TYPINGS_PATH: string = 'internal_typings';
|
|
|
|
|
|
|
|
// Monkey patch the TS compiler to be able to re-emit files with @internal symbols
|
|
|
|
let tsEmitInternal: boolean = false;
|
|
|
|
|
|
|
|
const originalEmitFiles: Function = (<any>ts).emitFiles;
|
|
|
|
|
|
|
|
(<any>ts).emitFiles = function(resolver: any, host: any, targetSourceFile: any): any {
|
|
|
|
if (tsEmitInternal) {
|
|
|
|
const orignalgetCompilerOptions = host.getCompilerOptions;
|
|
|
|
host.getCompilerOptions = () => {
|
|
|
|
let options = clone(orignalgetCompilerOptions.call(host));
|
|
|
|
options.stripInternal = false;
|
|
|
|
options.outDir = `${options.outDir}/${INTERNAL_TYPINGS_PATH}`;
|
|
|
|
return options;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return originalEmitFiles(resolver, host, targetSourceFile);
|
|
|
|
};
|
2015-05-04 11:27:14 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Broccoli plugin that implements incremental Typescript compiler.
|
|
|
|
*
|
|
|
|
* It instantiates a typescript compiler instance that keeps all the state about the project and
|
2015-09-22 15:03:39 -04:00
|
|
|
* can re-emit only the files that actually changed.
|
2015-05-04 11:27:14 -04:00
|
|
|
*
|
|
|
|
* Limitations: only files that map directly to the changed source file via naming conventions are
|
2015-09-22 15:03:39 -04:00
|
|
|
* re-emitted. This primarily affects code that uses `const enum`s, because changing the enum value
|
2015-05-04 11:27:14 -04:00
|
|
|
* requires global emit, which can affect many files.
|
|
|
|
*/
|
|
|
|
class DiffingTSCompiler implements DiffingBroccoliPlugin {
|
|
|
|
private tsOpts: ts.CompilerOptions;
|
|
|
|
private fileRegistry: FileRegistry = Object.create(null);
|
|
|
|
private rootFilePaths: string[];
|
|
|
|
private tsServiceHost: ts.LanguageServiceHost;
|
|
|
|
private tsService: ts.LanguageService;
|
2016-03-22 20:11:42 -04:00
|
|
|
private metadataCollector: MetadataCollector;
|
2015-05-14 13:35:01 -04:00
|
|
|
private firstRun: boolean = true;
|
2015-05-20 14:26:58 -04:00
|
|
|
private previousRunFailed: boolean = false;
|
2015-12-22 19:24:35 -05:00
|
|
|
// Whether to generate the @internal typing files (they are only generated when `stripInternal` is
|
|
|
|
// true)
|
|
|
|
private genInternalTypings: boolean = false;
|
2015-05-04 11:27:14 -04:00
|
|
|
|
2015-05-06 19:24:10 -04:00
|
|
|
static includeExtensions = ['.ts'];
|
2015-05-04 11:27:14 -04:00
|
|
|
|
|
|
|
constructor(public inputPath: string, public cachePath: string, public options) {
|
2015-10-15 19:29:24 -04:00
|
|
|
if (options.rootFilePaths) {
|
|
|
|
this.rootFilePaths = options.rootFilePaths.splice(0);
|
|
|
|
delete options.rootFilePaths;
|
|
|
|
} else {
|
|
|
|
this.rootFilePaths = [];
|
|
|
|
}
|
|
|
|
|
2015-12-22 19:24:35 -05:00
|
|
|
if (options.internalTypings) {
|
|
|
|
this.genInternalTypings = true;
|
|
|
|
delete options.internalTypings;
|
|
|
|
}
|
|
|
|
|
2015-10-15 19:29:24 -04:00
|
|
|
// the conversion is a bit awkward, see https://github.com/Microsoft/TypeScript/issues/5276
|
2015-12-09 16:42:36 -05:00
|
|
|
// in 1.8 use convertCompilerOptionsFromJson
|
|
|
|
this.tsOpts =
|
|
|
|
ts.parseJsonConfigFileContent({compilerOptions: options, files: []}, null, null).options;
|
2015-11-13 04:58:17 -05:00
|
|
|
|
2015-12-22 19:24:35 -05:00
|
|
|
if ((<any>this.tsOpts).stripInternal === false) {
|
|
|
|
// @internal are included in the generated .d.ts, do not generate them separately
|
|
|
|
this.genInternalTypings = false;
|
|
|
|
}
|
|
|
|
|
2016-04-06 19:10:45 -04:00
|
|
|
this.tsOpts.rootDir = inputPath;
|
2015-05-04 11:27:14 -04:00
|
|
|
this.tsOpts.outDir = this.cachePath;
|
2015-10-15 19:29:24 -04:00
|
|
|
|
2016-04-12 12:40:37 -04:00
|
|
|
this.tsServiceHost = new CustomLanguageServiceHost(this.tsOpts, this.rootFilePaths,
|
|
|
|
this.fileRegistry, this.inputPath);
|
2015-05-14 13:36:17 -04:00
|
|
|
this.tsService = ts.createLanguageService(this.tsServiceHost, ts.createDocumentRegistry());
|
2016-04-26 00:29:06 -04:00
|
|
|
this.metadataCollector = new MetadataCollector();
|
2015-05-04 11:27:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rebuild(treeDiff: DiffResult) {
|
|
|
|
let pathsToEmit = [];
|
|
|
|
let pathsWithErrors = [];
|
2015-07-03 00:53:12 -04:00
|
|
|
let errorMessages = [];
|
2015-05-04 11:27:14 -04:00
|
|
|
|
2016-04-12 12:40:37 -04:00
|
|
|
treeDiff.addedPaths.concat(treeDiff.changedPaths)
|
|
|
|
.forEach((tsFilePath) => {
|
|
|
|
if (!this.fileRegistry[tsFilePath]) {
|
|
|
|
this.fileRegistry[tsFilePath] = {version: 0};
|
|
|
|
this.rootFilePaths.push(tsFilePath);
|
|
|
|
} else {
|
|
|
|
this.fileRegistry[tsFilePath].version++;
|
|
|
|
}
|
2015-05-04 11:27:14 -04:00
|
|
|
|
2016-04-12 12:40:37 -04:00
|
|
|
pathsToEmit.push(path.join(this.inputPath, tsFilePath));
|
|
|
|
});
|
2015-05-04 11:27:14 -04:00
|
|
|
|
2015-06-03 16:42:57 -04:00
|
|
|
treeDiff.removedPaths.forEach((tsFilePath) => {
|
|
|
|
console.log('removing outputs for', tsFilePath);
|
2015-05-04 11:27:14 -04:00
|
|
|
|
2015-06-03 16:42:57 -04:00
|
|
|
this.rootFilePaths.splice(this.rootFilePaths.indexOf(tsFilePath), 1);
|
|
|
|
this.fileRegistry[tsFilePath] = null;
|
|
|
|
this.removeOutputFor(tsFilePath);
|
|
|
|
});
|
2015-05-04 11:27:14 -04:00
|
|
|
|
2015-05-14 13:35:01 -04:00
|
|
|
if (this.firstRun) {
|
|
|
|
this.firstRun = false;
|
2015-05-20 14:26:58 -04:00
|
|
|
this.doFullBuild();
|
2015-05-14 13:35:01 -04:00
|
|
|
} else {
|
2016-03-08 13:27:54 -05:00
|
|
|
let program = this.tsService.getProgram();
|
|
|
|
let typeChecker = program.getTypeChecker();
|
2015-12-22 19:24:35 -05:00
|
|
|
tsEmitInternal = false;
|
2015-05-14 13:35:01 -04:00
|
|
|
pathsToEmit.forEach((tsFilePath) => {
|
|
|
|
let output = this.tsService.getEmitOutput(tsFilePath);
|
|
|
|
|
|
|
|
if (output.emitSkipped) {
|
2015-07-03 00:53:12 -04:00
|
|
|
let errorFound = this.collectErrors(tsFilePath);
|
2015-05-14 13:35:01 -04:00
|
|
|
if (errorFound) {
|
|
|
|
pathsWithErrors.push(tsFilePath);
|
2015-07-03 00:53:12 -04:00
|
|
|
errorMessages.push(errorFound);
|
2015-05-14 13:35:01 -04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
output.outputFiles.forEach(o => {
|
|
|
|
let destDirPath = path.dirname(o.name);
|
|
|
|
fse.mkdirsSync(destDirPath);
|
2015-11-23 17:58:18 -05:00
|
|
|
fs.writeFileSync(o.name, this.fixSourceMapSources(o.text), FS_OPTS);
|
2016-03-08 13:27:54 -05:00
|
|
|
if (endsWith(o.name, '.d.ts')) {
|
|
|
|
const sourceFile = program.getSourceFile(tsFilePath);
|
|
|
|
this.emitMetadata(o.name, sourceFile, typeChecker);
|
|
|
|
}
|
2015-05-14 13:35:01 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2015-05-04 11:27:14 -04:00
|
|
|
|
2015-05-14 13:35:01 -04:00
|
|
|
if (pathsWithErrors.length) {
|
2015-05-20 14:26:58 -04:00
|
|
|
this.previousRunFailed = true;
|
2015-07-03 00:53:12 -04:00
|
|
|
var error =
|
|
|
|
new Error('Typescript found the following errors:\n' + errorMessages.join('\n'));
|
2015-05-19 19:51:57 -04:00
|
|
|
error['showStack'] = false;
|
|
|
|
throw error;
|
2015-05-20 14:26:58 -04:00
|
|
|
} else if (this.previousRunFailed) {
|
|
|
|
this.doFullBuild();
|
2015-12-22 19:24:35 -05:00
|
|
|
} else if (this.genInternalTypings) {
|
|
|
|
// serialize the .d.ts files containing @internal symbols
|
|
|
|
tsEmitInternal = true;
|
|
|
|
pathsToEmit.forEach((tsFilePath) => {
|
|
|
|
let output = this.tsService.getEmitOutput(tsFilePath);
|
|
|
|
if (!output.emitSkipped) {
|
|
|
|
output.outputFiles.forEach(o => {
|
|
|
|
if (endsWith(o.name, '.d.ts')) {
|
|
|
|
let destDirPath = path.dirname(o.name);
|
|
|
|
fse.mkdirsSync(destDirPath);
|
|
|
|
fs.writeFileSync(o.name, this.fixSourceMapSources(o.text), FS_OPTS);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
tsEmitInternal = false;
|
2015-05-14 13:35:01 -04:00
|
|
|
}
|
2015-05-04 11:27:14 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-03 00:53:12 -04:00
|
|
|
private collectErrors(tsFilePath): String {
|
2015-05-04 11:27:14 -04:00
|
|
|
let allDiagnostics = this.tsService.getCompilerOptionsDiagnostics()
|
|
|
|
.concat(this.tsService.getSyntacticDiagnostics(tsFilePath))
|
|
|
|
.concat(this.tsService.getSemanticDiagnostics(tsFilePath));
|
2015-07-03 00:53:12 -04:00
|
|
|
let errors = [];
|
2015-05-04 11:27:14 -04:00
|
|
|
|
|
|
|
allDiagnostics.forEach(diagnostic => {
|
2016-04-12 12:40:37 -04:00
|
|
|
let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
2015-05-04 11:27:14 -04:00
|
|
|
if (diagnostic.file) {
|
2015-10-28 03:59:19 -04:00
|
|
|
let {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
2015-07-03 00:53:12 -04:00
|
|
|
errors.push(` ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
|
2015-05-04 11:27:14 -04:00
|
|
|
} else {
|
2015-07-03 00:53:12 -04:00
|
|
|
errors.push(` Error: ${message}`);
|
2015-05-04 11:27:14 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-07-03 00:53:12 -04:00
|
|
|
if (errors.length) {
|
|
|
|
return errors.join('\n');
|
|
|
|
}
|
2015-05-04 11:27:14 -04:00
|
|
|
}
|
2015-05-20 14:26:58 -04:00
|
|
|
|
|
|
|
private doFullBuild() {
|
|
|
|
let program = this.tsService.getProgram();
|
2016-03-08 13:27:54 -05:00
|
|
|
let typeChecker = program.getTypeChecker();
|
|
|
|
let diagnostics: ts.Diagnostic[] = [];
|
2015-12-22 19:24:35 -05:00
|
|
|
tsEmitInternal = false;
|
2016-03-08 13:27:54 -05:00
|
|
|
|
2015-11-23 17:58:18 -05:00
|
|
|
let emitResult = program.emit(undefined, (absoluteFilePath, fileContent) => {
|
2015-05-20 14:26:58 -04:00
|
|
|
fse.mkdirsSync(path.dirname(absoluteFilePath));
|
2015-11-23 17:58:18 -05:00
|
|
|
fs.writeFileSync(absoluteFilePath, this.fixSourceMapSources(fileContent), FS_OPTS);
|
2016-03-08 13:27:54 -05:00
|
|
|
if (endsWith(absoluteFilePath, '.d.ts')) {
|
|
|
|
// TODO: Use sourceFile from the callback if
|
|
|
|
// https://github.com/Microsoft/TypeScript/issues/7438
|
|
|
|
// is taken
|
|
|
|
const originalFile = absoluteFilePath.replace(this.tsOpts.outDir, this.tsOpts.rootDir)
|
|
|
|
.replace(/\.d\.ts$/, '.ts');
|
|
|
|
const sourceFile = program.getSourceFile(originalFile);
|
|
|
|
this.emitMetadata(absoluteFilePath, sourceFile, typeChecker);
|
|
|
|
}
|
2015-05-20 14:26:58 -04:00
|
|
|
});
|
|
|
|
|
2015-12-22 19:24:35 -05:00
|
|
|
if (this.genInternalTypings) {
|
|
|
|
// serialize the .d.ts files containing @internal symbols
|
|
|
|
tsEmitInternal = true;
|
|
|
|
program.emit(undefined, (absoluteFilePath, fileContent) => {
|
|
|
|
if (endsWith(absoluteFilePath, '.d.ts')) {
|
|
|
|
fse.mkdirsSync(path.dirname(absoluteFilePath));
|
|
|
|
fs.writeFileSync(absoluteFilePath, fileContent, FS_OPTS);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
tsEmitInternal = false;
|
|
|
|
}
|
|
|
|
|
2015-05-20 14:26:58 -04:00
|
|
|
if (emitResult.emitSkipped) {
|
|
|
|
let allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics);
|
|
|
|
let errorMessages = [];
|
|
|
|
|
|
|
|
allDiagnostics.forEach(diagnostic => {
|
2015-06-26 17:43:51 -04:00
|
|
|
var pos = '';
|
|
|
|
if (diagnostic.file) {
|
|
|
|
var {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
|
|
|
pos = `${diagnostic.file.fileName} (${line + 1}, ${character + 1}): `
|
|
|
|
}
|
2015-05-20 14:26:58 -04:00
|
|
|
var message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
2015-06-26 17:43:51 -04:00
|
|
|
errorMessages.push(` ${pos}${message}`);
|
2015-05-20 14:26:58 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
if (errorMessages.length) {
|
|
|
|
this.previousRunFailed = true;
|
2015-07-03 00:53:12 -04:00
|
|
|
var error =
|
|
|
|
new Error('Typescript found the following errors:\n' + errorMessages.join('\n'));
|
2015-05-19 19:51:57 -04:00
|
|
|
error['showStack'] = false;
|
|
|
|
throw error;
|
2015-05-20 14:26:58 -04:00
|
|
|
} else {
|
|
|
|
this.previousRunFailed = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-05-20 14:28:39 -04:00
|
|
|
|
2016-03-08 13:27:54 -05:00
|
|
|
/**
|
|
|
|
* Emit a .metadata.json file to correspond to the .d.ts file if the module contains classes that
|
|
|
|
* use decorators or exported constants.
|
|
|
|
*/
|
2016-04-12 12:40:37 -04:00
|
|
|
private emitMetadata(dtsFileName: string, sourceFile: ts.SourceFile,
|
|
|
|
typeChecker: ts.TypeChecker) {
|
2016-03-08 13:27:54 -05:00
|
|
|
if (sourceFile) {
|
2016-03-22 20:11:42 -04:00
|
|
|
const metadata = this.metadataCollector.getMetadata(sourceFile, typeChecker);
|
2016-03-08 13:27:54 -05:00
|
|
|
if (metadata && metadata.metadata) {
|
|
|
|
const metadataText = JSON.stringify(metadata);
|
|
|
|
const metadataFileName = dtsFileName.replace(/\.d.ts$/, '.metadata.json');
|
|
|
|
fs.writeFileSync(metadataFileName, metadataText, FS_OPTS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-23 17:58:18 -05:00
|
|
|
/**
|
|
|
|
* There is a bug in TypeScript 1.6, where the sourceRoot and inlineSourceMap properties
|
|
|
|
* are exclusive. This means that the sources property always contains relative paths
|
|
|
|
* (e.g, ../../../../angular2/src/di/injector.ts).
|
|
|
|
*
|
|
|
|
* Here, we normalize the sources property and remove the ../../../
|
|
|
|
*
|
|
|
|
* This issue is fixed in https://github.com/Microsoft/TypeScript/pull/5620.
|
|
|
|
* Once we switch to TypeScript 1.8, we can remove this method.
|
|
|
|
*/
|
|
|
|
private fixSourceMapSources(content: string): string {
|
|
|
|
try {
|
2016-04-12 12:40:37 -04:00
|
|
|
const marker = "//# sourceMappingURL=data:application/json;base64,";
|
2015-11-23 17:58:18 -05:00
|
|
|
const index = content.indexOf(marker);
|
|
|
|
if (index == -1) return content;
|
|
|
|
|
|
|
|
const base = content.substring(0, index + marker.length);
|
|
|
|
const sourceMapBit =
|
2016-04-12 12:40:37 -04:00
|
|
|
new Buffer(content.substring(index + marker.length), 'base64').toString("utf8");
|
2015-11-23 17:58:18 -05:00
|
|
|
const sourceMaps = JSON.parse(sourceMapBit);
|
|
|
|
const source = sourceMaps.sources[0];
|
2016-04-12 12:40:37 -04:00
|
|
|
sourceMaps.sources = [source.substring(source.lastIndexOf("../") + 3)];
|
2015-11-23 17:58:18 -05:00
|
|
|
return `${base}${new Buffer(JSON.stringify(sourceMaps)).toString('base64')}`;
|
|
|
|
} catch (e) {
|
|
|
|
return content;
|
|
|
|
}
|
|
|
|
}
|
2015-05-20 14:28:39 -04:00
|
|
|
|
|
|
|
private removeOutputFor(tsFilePath: string) {
|
|
|
|
let absoluteJsFilePath = path.join(this.cachePath, tsFilePath.replace(/\.ts$/, '.js'));
|
|
|
|
let absoluteMapFilePath = path.join(this.cachePath, tsFilePath.replace(/.ts$/, '.js.map'));
|
|
|
|
let absoluteDtsFilePath = path.join(this.cachePath, tsFilePath.replace(/\.ts$/, '.d.ts'));
|
|
|
|
|
|
|
|
if (fs.existsSync(absoluteJsFilePath)) {
|
|
|
|
fs.unlinkSync(absoluteJsFilePath);
|
2015-12-21 17:41:18 -05:00
|
|
|
if (fs.existsSync(absoluteMapFilePath)) {
|
|
|
|
// source map could be inline or not generated
|
|
|
|
fs.unlinkSync(absoluteMapFilePath);
|
|
|
|
}
|
2015-05-20 14:28:39 -04:00
|
|
|
fs.unlinkSync(absoluteDtsFilePath);
|
|
|
|
}
|
|
|
|
}
|
2015-05-04 11:27:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class CustomLanguageServiceHost implements ts.LanguageServiceHost {
|
|
|
|
private currentDirectory: string;
|
|
|
|
private defaultLibFilePath: string;
|
|
|
|
|
|
|
|
|
2016-04-12 12:40:37 -04:00
|
|
|
constructor(private compilerOptions: ts.CompilerOptions, private fileNames: string[],
|
|
|
|
private fileRegistry: FileRegistry, private treeInputPath: string) {
|
2015-05-04 11:27:14 -04:00
|
|
|
this.currentDirectory = process.cwd();
|
2015-05-13 06:04:10 -04:00
|
|
|
this.defaultLibFilePath = ts.getDefaultLibFilePath(compilerOptions).replace(/\\/g, '/');
|
2015-05-04 11:27:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-06 19:10:45 -04:00
|
|
|
getScriptFileNames(): string[] {
|
|
|
|
return this.fileNames.map(f => path.join(this.treeInputPath, f));
|
|
|
|
}
|
2015-05-04 11:27:14 -04:00
|
|
|
|
|
|
|
|
|
|
|
getScriptVersion(fileName: string): string {
|
2016-04-08 19:53:35 -04:00
|
|
|
if (startsWith(fileName, this.treeInputPath)) {
|
|
|
|
const key = fileName.substr(this.treeInputPath.length + 1);
|
|
|
|
return this.fileRegistry[key] && this.fileRegistry[key].version.toString();
|
|
|
|
}
|
2015-05-04 11:27:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getScriptSnapshot(tsFilePath: string): ts.IScriptSnapshot {
|
2016-04-06 19:10:45 -04:00
|
|
|
// TypeScript seems to request lots of bogus paths during import path lookup and resolution,
|
|
|
|
// so we we just return undefined when the path is not correct.
|
2015-10-16 00:27:58 -04:00
|
|
|
|
2016-04-06 19:10:45 -04:00
|
|
|
// Ensure it is in the input tree or a lib.d.ts file.
|
|
|
|
if (!startsWith(tsFilePath, this.treeInputPath) && !tsFilePath.match(/\/lib(\..*)*.d\.ts$/)) {
|
|
|
|
if (fs.existsSync(tsFilePath)) {
|
|
|
|
console.log('Rejecting', tsFilePath, '. File is not in the input tree.');
|
|
|
|
}
|
|
|
|
return undefined;
|
|
|
|
}
|
2015-05-04 11:27:14 -04:00
|
|
|
|
2016-04-06 19:10:45 -04:00
|
|
|
// Ensure it exists
|
|
|
|
if (!fs.existsSync(tsFilePath)) {
|
2015-05-04 11:27:14 -04:00
|
|
|
return undefined;
|
|
|
|
}
|
2016-04-06 19:10:45 -04:00
|
|
|
|
|
|
|
return ts.ScriptSnapshot.fromString(fs.readFileSync(tsFilePath, FS_OPTS));
|
2015-05-04 11:27:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getCurrentDirectory(): string { return this.currentDirectory; }
|
|
|
|
|
|
|
|
getCompilationSettings(): ts.CompilerOptions { return this.compilerOptions; }
|
|
|
|
|
|
|
|
getDefaultLibFileName(options: ts.CompilerOptions): string {
|
|
|
|
// ignore options argument, options should not change during the lifetime of the plugin
|
|
|
|
return this.defaultLibFilePath;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default wrapDiffingPlugin(DiffingTSCompiler);
|
2015-12-22 19:24:35 -05:00
|
|
|
|
|
|
|
function clone<T>(object: T): T {
|
|
|
|
const result: any = {};
|
|
|
|
for (const id in object) {
|
|
|
|
result[id] = (<any>object)[id];
|
|
|
|
}
|
|
|
|
return <T>result;
|
|
|
|
}
|
|
|
|
|
2016-04-06 19:10:45 -04:00
|
|
|
function startsWith(str: string, substring: string): boolean {
|
|
|
|
return str.substring(0, substring.length) === substring;
|
|
|
|
}
|
|
|
|
|
2015-12-22 19:24:35 -05:00
|
|
|
function endsWith(str: string, substring: string): boolean {
|
|
|
|
return str.indexOf(substring, str.length - substring.length) !== -1;
|
|
|
|
}
|