/// declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any; declare function clearTimeout(handle: any): void; namespace ts { export type FileWatcherCallback = (fileName: string, removed?: boolean) => void; export type DirectoryWatcherCallback = (fileName: string) => void; export interface WatchedFile { fileName: string; callback: FileWatcherCallback; mtime?: Date; } export interface System { args: string[]; newLine: string; useCaseSensitiveFileNames: boolean; write(s: string): void; readFile(path: string, encoding?: string): string; getFileSize?(path: string): number; writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; /** * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that * use native OS file watching */ watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; resolvePath(path: string): string; fileExists(path: string): boolean; directoryExists(path: string): boolean; createDirectory(path: string): void; getExecutingFilePath(): string; getCurrentDirectory(): string; getDirectories(path: string): string[]; readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[]): string[]; getModifiedTime?(path: string): Date; createHash?(data: string): string; getMemoryUsage?(): number; exit(exitCode?: number): void; realpath?(path: string): string; /*@internal*/ getEnvironmentVariable(name: string): string; /*@internal*/ tryEnableSourceMapsForHost?(): void; setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; clearTimeout?(timeoutId: any): void; } export interface FileWatcher { close(): void; } export interface DirectoryWatcher extends FileWatcher { directoryName: string; referenceCount: number; } declare var require: any; declare var process: any; declare var global: any; declare var __filename: string; export function getNodeMajorVersion() { if (typeof process === "undefined") { return undefined; } const version: string = process.version; if (!version) { return undefined; } const dot = version.indexOf("."); if (dot === -1) { return undefined; } return parseInt(version.substring(1, dot)); } declare var ChakraHost: { args: string[]; currentDirectory: string; executingFile: string; newLine?: string; useCaseSensitiveFileNames?: boolean; echo(s: string): void; quit(exitCode?: number): void; fileExists(path: string): boolean; directoryExists(path: string): boolean; createDirectory(path: string): void; resolvePath(path: string): string; readFile(path: string): string; writeFile(path: string, contents: string): void; getDirectories(path: string): string[]; readDirectory(path: string, extensions?: string[], basePaths?: string[], excludeEx?: string, includeFileEx?: string, includeDirEx?: string): string[]; watchFile?(path: string, callback: FileWatcherCallback): FileWatcher; watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; realpath(path: string): string; getEnvironmentVariable?(name: string): string; }; export let sys: System = (function() { function getNodeSystem(): System { const _fs = require("fs"); const _path = require("path"); const _os = require("os"); const _crypto = require("crypto"); const useNonPollingWatchers = process.env["TSC_NONPOLLING_WATCHER"]; function createWatchedFileSet() { const dirWatchers = createMap(); // One file can have multiple watchers const fileWatcherCallbacks = createMultiMap(); return { addFile, removeFile }; function reduceDirWatcherRefCountForFile(fileName: string) { const dirName = getDirectoryPath(fileName); const watcher = dirWatchers.get(dirName); if (watcher) { watcher.referenceCount -= 1; if (watcher.referenceCount <= 0) { watcher.close(); dirWatchers.delete(dirName); } } } function addDirWatcher(dirPath: string): void { let watcher = dirWatchers.get(dirPath); if (watcher) { watcher.referenceCount += 1; return; } watcher = _fs.watch( dirPath, { persistent: true }, (eventName: string, relativeFileName: string) => fileEventHandler(eventName, relativeFileName, dirPath) ); watcher.referenceCount = 1; dirWatchers.set(dirPath, watcher); return; } function addFileWatcherCallback(filePath: string, callback: FileWatcherCallback): void { fileWatcherCallbacks.add(filePath, callback); } function addFile(fileName: string, callback: FileWatcherCallback): WatchedFile { addFileWatcherCallback(fileName, callback); addDirWatcher(getDirectoryPath(fileName)); return { fileName, callback }; } function removeFile(watchedFile: WatchedFile) { removeFileWatcherCallback(watchedFile.fileName, watchedFile.callback); reduceDirWatcherRefCountForFile(watchedFile.fileName); } function removeFileWatcherCallback(filePath: string, callback: FileWatcherCallback) { fileWatcherCallbacks.remove(filePath, callback); } function fileEventHandler(eventName: string, relativeFileName: string, baseDirPath: string) { // When files are deleted from disk, the triggered "rename" event would have a relativefileName of "undefined" const fileName = typeof relativeFileName !== "string" ? undefined : ts.getNormalizedAbsolutePath(relativeFileName, baseDirPath); // Some applications save a working file via rename operations if ((eventName === "change" || eventName === "rename")) { const callbacks = fileWatcherCallbacks.get(fileName); if (callbacks) { for (const fileCallback of callbacks) { fileCallback(fileName); } } } } } const watchedFileSet = createWatchedFileSet(); const nodeVersion = getNodeMajorVersion(); const isNode4OrLater = nodeVersion >= 4; function isFileSystemCaseSensitive(): boolean { // win32\win64 are case insensitive platforms if (platform === "win32" || platform === "win64") { return false; } // convert current file name to upper case / lower case and check if file exists // (guards against cases when name is already all uppercase or lowercase) return !fileExists(__filename.toUpperCase()) || !fileExists(__filename.toLowerCase()); } const platform: string = _os.platform(); const useCaseSensitiveFileNames = isFileSystemCaseSensitive(); function readFile(fileName: string, _encoding?: string): string { if (!fileExists(fileName)) { return undefined; } const buffer = _fs.readFileSync(fileName); let len = buffer.length; if (len >= 2 && buffer[0] === 0xFE && buffer[1] === 0xFF) { // Big endian UTF-16 byte order mark detected. Since big endian is not supported by node.js, // flip all byte pairs and treat as little endian. len &= ~1; // Round down to a multiple of 2 for (let i = 0; i < len; i += 2) { const temp = buffer[i]; buffer[i] = buffer[i + 1]; buffer[i + 1] = temp; } return buffer.toString("utf16le", 2); } if (len >= 2 && buffer[0] === 0xFF && buffer[1] === 0xFE) { // Little endian UTF-16 byte order mark detected return buffer.toString("utf16le", 2); } if (len >= 3 && buffer[0] === 0xEF && buffer[1] === 0xBB && buffer[2] === 0xBF) { // UTF-8 byte order mark detected return buffer.toString("utf8", 3); } // Default is UTF-8 with no byte order mark return buffer.toString("utf8"); } function writeFile(fileName: string, data: string, writeByteOrderMark?: boolean): void { // If a BOM is required, emit one if (writeByteOrderMark) { data = "\uFEFF" + data; } let fd: number; try { fd = _fs.openSync(fileName, "w"); _fs.writeSync(fd, data, undefined, "utf8"); } finally { if (fd !== undefined) { _fs.closeSync(fd); } } } function getAccessibleFileSystemEntries(path: string): FileSystemEntries { try { const entries = _fs.readdirSync(path || ".").sort(); const files: string[] = []; const directories: string[] = []; for (const entry of entries) { // This is necessary because on some file system node fails to exclude // "." and "..". See https://github.com/nodejs/node/issues/4002 if (entry === "." || entry === "..") { continue; } const name = combinePaths(path, entry); let stat: any; try { stat = _fs.statSync(name); } catch (e) { continue; } if (stat.isFile()) { files.push(entry); } else if (stat.isDirectory()) { directories.push(entry); } } return { files, directories }; } catch (e) { return { files: [], directories: [] }; } } function readDirectory(path: string, extensions?: string[], excludes?: string[], includes?: string[]): string[] { return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), getAccessibleFileSystemEntries); } const enum FileSystemEntryKind { File, Directory } function fileSystemEntryExists(path: string, entryKind: FileSystemEntryKind): boolean { try { const stat = _fs.statSync(path); switch (entryKind) { case FileSystemEntryKind.File: return stat.isFile(); case FileSystemEntryKind.Directory: return stat.isDirectory(); } } catch (e) { return false; } } function fileExists(path: string): boolean { return fileSystemEntryExists(path, FileSystemEntryKind.File); } function directoryExists(path: string): boolean { return fileSystemEntryExists(path, FileSystemEntryKind.Directory); } function getDirectories(path: string): string[] { return filter(_fs.readdirSync(path), dir => fileSystemEntryExists(combinePaths(path, dir), FileSystemEntryKind.Directory)); } const noOpFileWatcher: FileWatcher = { close: noop }; const nodeSystem: System = { args: process.argv.slice(2), newLine: _os.EOL, useCaseSensitiveFileNames: useCaseSensitiveFileNames, write(s: string): void { process.stdout.write(s); }, readFile, writeFile, watchFile: (fileName, callback, pollingInterval) => { if (useNonPollingWatchers) { const watchedFile = watchedFileSet.addFile(fileName, callback); return { close: () => watchedFileSet.removeFile(watchedFile) }; } else { _fs.watchFile(fileName, { persistent: true, interval: pollingInterval || 250 }, fileChanged); return { close: () => _fs.unwatchFile(fileName, fileChanged) }; } function fileChanged(curr: any, prev: any) { if (+curr.mtime <= +prev.mtime) { return; } callback(fileName); } }, watchDirectory: (directoryName, callback, recursive) => { // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643) let options: any; if (!directoryExists(directoryName)) { // do nothing if target folder does not exist return noOpFileWatcher; } if (isNode4OrLater && (process.platform === "win32" || process.platform === "darwin")) { options = { persistent: true, recursive: !!recursive }; } else { options = { persistent: true }; } return _fs.watch( directoryName, options, (eventName: string, relativeFileName: string) => { // In watchDirectory we only care about adding and removing files (when event name is // "rename"); changes made within files are handled by corresponding fileWatchers (when // event name is "change") if (eventName === "rename") { // When deleting a file, the passed baseFileName is null callback(!relativeFileName ? relativeFileName : normalizePath(combinePaths(directoryName, relativeFileName))); }; } ); }, resolvePath: function(path: string): string { return _path.resolve(path); }, fileExists, directoryExists, createDirectory(directoryName: string) { if (!nodeSystem.directoryExists(directoryName)) { _fs.mkdirSync(directoryName); } }, getExecutingFilePath() { return __filename; }, getCurrentDirectory() { return process.cwd(); }, getDirectories, getEnvironmentVariable(name: string) { return process.env[name] || ""; }, 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(); } return process.memoryUsage().heapUsed; }, getFileSize(path) { try { const stat = _fs.statSync(path); if (stat.isFile()) { return stat.size; } } catch (e) { } return 0; }, exit(exitCode?: number): void { process.exit(exitCode); }, realpath(path: string): string { return _fs.realpathSync(path); }, tryEnableSourceMapsForHost() { try { require("source-map-support").install(); } catch (e) { // Could not enable source maps. } }, setTimeout, clearTimeout }; return nodeSystem; } function getChakraSystem(): System { const realpath = ChakraHost.realpath && ((path: string) => ChakraHost.realpath(path)); return { newLine: ChakraHost.newLine || "\r\n", args: ChakraHost.args, useCaseSensitiveFileNames: !!ChakraHost.useCaseSensitiveFileNames, write: ChakraHost.echo, readFile(path: string, _encoding?: string) { // encoding is automatically handled by the implementation in ChakraHost return ChakraHost.readFile(path); }, writeFile(path: string, data: string, writeByteOrderMark?: boolean) { // If a BOM is required, emit one if (writeByteOrderMark) { data = "\uFEFF" + data; } ChakraHost.writeFile(path, data); }, resolvePath: ChakraHost.resolvePath, fileExists: ChakraHost.fileExists, directoryExists: ChakraHost.directoryExists, createDirectory: ChakraHost.createDirectory, getExecutingFilePath: () => ChakraHost.executingFile, getCurrentDirectory: () => ChakraHost.currentDirectory, getDirectories: ChakraHost.getDirectories, getEnvironmentVariable: ChakraHost.getEnvironmentVariable || (() => ""), readDirectory: (path: string, extensions?: string[], excludes?: string[], includes?: string[]) => { const pattern = getFileMatcherPatterns(path, excludes, includes, !!ChakraHost.useCaseSensitiveFileNames, ChakraHost.currentDirectory); return ChakraHost.readDirectory(path, extensions, pattern.basePaths, pattern.excludePattern, pattern.includeFilePattern, pattern.includeDirectoryPattern); }, exit: ChakraHost.quit, realpath }; } function recursiveCreateDirectory(directoryPath: string, sys: System) { const basePath = getDirectoryPath(directoryPath); const shouldCreateParent = directoryPath !== basePath && !sys.directoryExists(basePath); if (shouldCreateParent) { recursiveCreateDirectory(basePath, sys); } if (shouldCreateParent || !sys.directoryExists(directoryPath)) { sys.createDirectory(directoryPath); } } let sys: System; if (typeof ChakraHost !== "undefined") { sys = getChakraSystem(); } else if (typeof process !== "undefined" && process.nextTick && !process.browser && typeof require !== "undefined") { // process and process.nextTick checks if current environment is node-like // process.browser check excludes webpack and browserify sys = getNodeSystem(); } if (sys) { // patch writefile to create folder before writing the file const originalWriteFile = sys.writeFile; sys.writeFile = function(path, data, writeBom) { const directoryPath = getDirectoryPath(normalizeSlashes(path)); if (directoryPath && !sys.directoryExists(directoryPath)) { recursiveCreateDirectory(directoryPath, sys); } originalWriteFile.call(sys, path, data, writeBom); }; } return sys; })(); if (sys && sys.getEnvironmentVariable) { Debug.currentAssertionLevel = /^development$/i.test(sys.getEnvironmentVariable("NODE_ENV")) ? AssertionLevel.Normal : AssertionLevel.None; } }