diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 9852b2354e2..d9d23a92186 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -559,6 +559,12 @@ namespace ts { sourceMap: false, }; + interface OutputFingerprint { + hash: string; + byteOrderMark: boolean; + mtime: Date; + } + export function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost { const existingDirectories: Map = {}; @@ -609,11 +615,42 @@ namespace ts { } } + const outputFingerprints: Map = + options.watch && sys.createHash && sys.getModifiedTime ? {} : undefined; + + const fileWriter: typeof sys.writeFile = outputFingerprints ? + (fileName, data, writeByteOrderMark) => { + const hash = sys.createHash(data); + const mtimeBefore = sys.getModifiedTime(fileName); + + if (mtimeBefore && outputFingerprints.hasOwnProperty(fileName)) { + const fingerprint = outputFingerprints[fileName]; + + // If output has not been changed, and the file has no external modification + if (fingerprint.byteOrderMark === writeByteOrderMark && + fingerprint.hash === hash && + fingerprint.mtime.getTime() === mtimeBefore.getTime()) { + return; + } + } + + sys.writeFile(fileName, data, writeByteOrderMark); + + const mtimeAfter = sys.getModifiedTime(fileName); + + outputFingerprints[fileName] = { + hash, + byteOrderMark: writeByteOrderMark, + mtime: mtimeAfter + }; + } : + (fileName, data, writeByteOrderMark) => sys.writeFile(fileName, data, writeByteOrderMark); + function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) { try { const start = new Date().getTime(); ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); - sys.writeFile(fileName, data, writeByteOrderMark); + fileWriter(fileName, data, writeByteOrderMark); ioWriteTime += new Date().getTime() - start; } catch (e) { diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 69277f65a92..3f33df61a0f 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -20,6 +20,8 @@ namespace ts { getExecutingFilePath(): string; getCurrentDirectory(): string; readDirectory(path: string, extension?: string, exclude?: string[]): string[]; + getModifiedTime?(path: string): Date; + createHash?(data: string): string; getMemoryUsage?(): number; exit(exitCode?: number): void; } @@ -39,15 +41,6 @@ namespace ts { referenceCount: number; } - interface OutputFingerprint { - hash: string; - mtime: Date; - } - - interface OutputFingerprintMap { - [fileName: string]: OutputFingerprint; - } - declare var require: any; declare var module: any; declare var process: any; @@ -449,26 +442,12 @@ namespace ts { return buffer.toString("utf8"); } - const outputFingerprintMap: OutputFingerprintMap = {}; - function writeFile(fileName: string, data: string, writeByteOrderMark?: boolean): void { // If a BOM is required, emit one if (writeByteOrderMark) { data = "\uFEFF" + data; } - const md5 = getMd5(data); - const mtimeBefore = _fs.existsSync(fileName) && _fs.statSync(fileName).mtime; - - if (mtimeBefore && outputFingerprintMap.hasOwnProperty(fileName)) { - const fingerprint = outputFingerprintMap[fileName]; - - // If output has not been changed, and the file has no external modification - if (fingerprint.hash === md5 && fingerprint.mtime.getTime() === mtimeBefore.getTime()) { - return; - } - } - let fd: number; try { @@ -480,19 +459,6 @@ namespace ts { _fs.closeSync(fd); } } - - const mtimeAfter = _fs.statSync(fileName).mtime; - - outputFingerprintMap[fileName] = { - hash: md5, - mtime: mtimeAfter - }; - } - - function getMd5(data: string): string { - const hash = _crypto.createHash("md5"); - hash.update(data); - return hash.digest("hex"); } function getCanonicalPath(path: string): string { @@ -593,6 +559,14 @@ namespace ts { return process.cwd(); }, readDirectory, + getModifiedTime(path) { + return _fs.existsSync(path) && _fs.statSync(path).mtime; + }, + createHash(data) { + const hash = _crypto.createHash("md5"); + hash.update(data); + return hash.digest("hex"); + }, getMemoryUsage() { if (global.gc) { global.gc();