diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 9852b2354e2e5..0f98b46d933c2 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,50 @@ namespace ts { } } + let outputFingerprints: Map; + + function writeFileIfUpdated(fileName: string, data: string, writeByteOrderMark: boolean): void { + if (!outputFingerprints) { + outputFingerprints = {}; + } + + const hash = sys.createHash(data); + const mtimeBefore = sys.getModifiedTime(fileName); + + if (mtimeBefore && hasProperty(outputFingerprints, 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 + }; + } + 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); + + if (options.watch && sys.createHash && sys.getModifiedTime) { + writeFileIfUpdated(fileName, data, writeByteOrderMark); + } + else { + sys.writeFile(fileName, data, writeByteOrderMark); + } + ioWriteTime += new Date().getTime() - start; } catch (e) { diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 9c3972b27566a..99008d4cc7e8d 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; } @@ -226,6 +228,7 @@ namespace ts { const _fs = require("fs"); const _path = require("path"); const _os = require("os"); + const _crypto = require("crypto"); // average async stat takes about 30 microseconds // set chunk size to do 30 files in < 1 millisecond @@ -556,6 +559,19 @@ namespace ts { return process.cwd(); }, readDirectory, + getModifiedTime(path) { + try { + return _fs.statSync(path).mtime; + } + catch (e) { + return undefined; + } + }, + createHash(data) { + const hash = _crypto.createHash("md5"); + hash.update(data); + return hash.digest("hex"); + }, getMemoryUsage() { if (global.gc) { global.gc();