Skip to content

Commit

Permalink
Add env to allow switch to non-polling if really wants to
Browse files Browse the repository at this point in the history
  • Loading branch information
zhengbli committed Apr 22, 2016
1 parent 576e5f8 commit fbceea2
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 11 deletions.
114 changes: 112 additions & 2 deletions src/compiler/sys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
namespace ts {
export type FileWatcherCallback = (fileName: string, removed?: boolean) => void;
export type DirectoryWatcherCallback = (directoryName: string) => void;
export interface WatchedFile {
fileName: string;
callback: FileWatcherCallback;
mtime?: Date;
}

export interface System {
args: string[];
Expand Down Expand Up @@ -223,6 +228,92 @@ namespace ts {
const _path = require("path");
const _os = require("os");
const _crypto = require("crypto");
const _process = require("process");

const useNonPollingWatchers = _process.env["TSC_NONPOLLING_WATCHER"];

function createWatchedFileSet() {
const dirWatchers: Map<DirectoryWatcher> = {};
// One file can have multiple watchers
const fileWatcherCallbacks: Map<FileWatcherCallback[]> = {};
return { addFile, removeFile };

function reduceDirWatcherRefCountForFile(fileName: string) {
const dirName = getDirectoryPath(fileName);
if (hasProperty(dirWatchers, dirName)) {
const watcher = dirWatchers[dirName];
watcher.referenceCount -= 1;
if (watcher.referenceCount <= 0) {
watcher.close();
delete dirWatchers[dirName];
}
}
}

function addDirWatcher(dirPath: string): void {
if (hasProperty(dirWatchers, dirPath)) {
const watcher = dirWatchers[dirPath];
watcher.referenceCount += 1;
return;
}

const watcher: DirectoryWatcher = _fs.watch(
dirPath,
{ persistent: true },
(eventName: string, relativeFileName: string) => fileEventHandler(eventName, relativeFileName, dirPath)
);
watcher.referenceCount = 1;
dirWatchers[dirPath] = watcher;
return;
}

function addFileWatcherCallback(filePath: string, callback: FileWatcherCallback): void {
if (hasProperty(fileWatcherCallbacks, filePath)) {
fileWatcherCallbacks[filePath].push(callback);
}
else {
fileWatcherCallbacks[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) {
if (hasProperty(fileWatcherCallbacks, filePath)) {
const newCallbacks = copyListRemovingItem(callback, fileWatcherCallbacks[filePath]);
if (newCallbacks.length === 0) {
delete fileWatcherCallbacks[filePath];
}
else {
fileWatcherCallbacks[filePath] = newCallbacks;
}
}
}

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") && hasProperty(fileWatcherCallbacks, fileName)) {
for (const fileCallback of fileWatcherCallbacks[fileName]) {
fileCallback(fileName);
}
}
}
}
const watchedFileSet = createWatchedFileSet();

function isNode4OrLater(): boolean {
return parseInt(process.version.charAt(1)) >= 4;
Expand Down Expand Up @@ -319,7 +410,7 @@ namespace ts {
const files = _fs.readdirSync(path || ".").sort();
const directories: string[] = [];
for (const current of files) {
// This is necessary because on some file system node fails to exclude
// This is necessary because on some file system node fails to exclude
// "." and "..". See https://github.com/nodejs/node/issues/4002
if (current === "." || current === "..") {
continue;
Expand Down Expand Up @@ -353,7 +444,26 @@ namespace ts {
readFile,
writeFile,
watchFile: (fileName, callback) => {
return _fs.watchFile(fileName, callback);
if (useNonPollingWatchers) {
const watchedFile = watchedFileSet.addFile(fileName, callback);
return {
close: () => watchedFileSet.removeFile(watchedFile)
};
}
else {
_fs.watchFile(fileName, { persistent: true, interval: 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
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2104,7 +2104,7 @@ namespace ts {
jsxFlags?: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with
resolvedJsxType?: Type; // resolved element attributes type of a JSX openinglike element
hasSuperCall?: boolean; // recorded result when we try to find super-call. We only try to find one if this flag is undefined, indicating that we haven't made an attempt.
superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing
superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing
}

export const enum TypeFlags {
Expand Down Expand Up @@ -2484,7 +2484,7 @@ namespace ts {
// When options come from a config file, its path is recorded here
configFilePath?: string;
/* @internal */
// Path used to used to compute primary search locations
// Path used to used to compute primary search locations
typesRoot?: string;
types?: string[];

Expand Down Expand Up @@ -2801,7 +2801,7 @@ namespace ts {
*/
resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
/**
* This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files
* This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files
*/
resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
}
Expand Down
6 changes: 0 additions & 6 deletions src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,6 @@ namespace ts.server {
detailLevel?: string;
}

interface WatchedFile {
fileName: string;
callback: FileWatcherCallback;
mtime?: Date;
}

function parseLoggingEnvironmentString(logEnvStr: string): LogOptions {
const logEnv: LogOptions = {};
const args = logEnvStr.split(" ");
Expand Down

0 comments on commit fbceea2

Please sign in to comment.