• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any;
2declare function clearTimeout(handle: any): void;
3
4namespace ts {
5    /**
6     * djb2 hashing algorithm
7     * http://www.cse.yorku.ca/~oz/hash.html
8     */
9    /* @internal */
10    export function generateDjb2Hash(data: string): string {
11        let acc = 5381;
12        for (let i = 0; i < data.length; i++) {
13            acc = ((acc << 5) + acc) + data.charCodeAt(i);
14        }
15        return acc.toString();
16    }
17
18    /**
19     * Set a high stack trace limit to provide more information in case of an error.
20     * Called for command-line and server use cases.
21     * Not called if TypeScript is used as a library.
22     */
23    /* @internal */
24    export function setStackTraceLimit() {
25        if ((Error as any).stackTraceLimit < 100) { // Also tests that we won't set the property if it doesn't exist.
26            (Error as any).stackTraceLimit = 100;
27        }
28    }
29
30    export enum FileWatcherEventKind {
31        Created,
32        Changed,
33        Deleted
34    }
35
36    export type FileWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind) => void;
37    export type DirectoryWatcherCallback = (fileName: string) => void;
38    /*@internal*/
39    export interface WatchedFile {
40        readonly fileName: string;
41        readonly callback: FileWatcherCallback;
42        mtime: Date;
43    }
44
45    /* @internal */
46    export enum PollingInterval {
47        High = 2000,
48        Medium = 500,
49        Low = 250
50    }
51
52    /* @internal */
53    export type HostWatchFile = (fileName: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined) => FileWatcher;
54    /* @internal */
55    export type HostWatchDirectory = (fileName: string, callback: DirectoryWatcherCallback, recursive: boolean, options: WatchOptions | undefined) => FileWatcher;
56
57    /* @internal */
58    export const missingFileModifiedTime = new Date(0); // Any subsequent modification will occur after this time
59
60    interface Levels {
61        Low: number;
62        Medium: number;
63        High: number;
64    }
65
66    function createPollingIntervalBasedLevels(levels: Levels) {
67        return {
68            [PollingInterval.Low]: levels.Low,
69            [PollingInterval.Medium]: levels.Medium,
70            [PollingInterval.High]: levels.High
71        };
72    }
73
74    const defaultChunkLevels: Levels = { Low: 32, Medium: 64, High: 256 };
75    let pollingChunkSize = createPollingIntervalBasedLevels(defaultChunkLevels);
76    /* @internal */
77    export let unchangedPollThresholds = createPollingIntervalBasedLevels(defaultChunkLevels);
78
79    /* @internal */
80    export function setCustomPollingValues(system: System) {
81        if (!system.getEnvironmentVariable) {
82            return;
83        }
84        const pollingIntervalChanged = setCustomLevels("TSC_WATCH_POLLINGINTERVAL", PollingInterval);
85        pollingChunkSize = getCustomPollingBasedLevels("TSC_WATCH_POLLINGCHUNKSIZE", defaultChunkLevels) || pollingChunkSize;
86        unchangedPollThresholds = getCustomPollingBasedLevels("TSC_WATCH_UNCHANGEDPOLLTHRESHOLDS", defaultChunkLevels) || unchangedPollThresholds;
87
88        function getLevel(envVar: string, level: keyof Levels) {
89            return system.getEnvironmentVariable(`${envVar}_${level.toUpperCase()}`);
90        }
91
92        function getCustomLevels(baseVariable: string) {
93            let customLevels: Partial<Levels> | undefined;
94            setCustomLevel("Low");
95            setCustomLevel("Medium");
96            setCustomLevel("High");
97            return customLevels;
98
99            function setCustomLevel(level: keyof Levels) {
100                const customLevel = getLevel(baseVariable, level);
101                if (customLevel) {
102                    (customLevels || (customLevels = {}))[level] = Number(customLevel);
103                }
104            }
105        }
106
107        function setCustomLevels(baseVariable: string, levels: Levels) {
108            const customLevels = getCustomLevels(baseVariable);
109            if (customLevels) {
110                setLevel("Low");
111                setLevel("Medium");
112                setLevel("High");
113                return true;
114            }
115            return false;
116
117            function setLevel(level: keyof Levels) {
118                levels[level] = customLevels![level] || levels[level];
119            }
120        }
121
122        function getCustomPollingBasedLevels(baseVariable: string, defaultLevels: Levels) {
123            const customLevels = getCustomLevels(baseVariable);
124            return (pollingIntervalChanged || customLevels) &&
125                createPollingIntervalBasedLevels(customLevels ? { ...defaultLevels, ...customLevels } : defaultLevels);
126        }
127    }
128
129    /* @internal */
130    export function createDynamicPriorityPollingWatchFile(host: {
131        getModifiedTime: NonNullable<System["getModifiedTime"]>;
132        setTimeout: NonNullable<System["setTimeout"]>;
133    }): HostWatchFile {
134        interface WatchedFile extends ts.WatchedFile {
135            isClosed?: boolean;
136            unchangedPolls: number;
137        }
138
139        interface PollingIntervalQueue extends Array<WatchedFile> {
140            pollingInterval: PollingInterval;
141            pollIndex: number;
142            pollScheduled: boolean;
143        }
144
145        const watchedFiles: WatchedFile[] = [];
146        const changedFilesInLastPoll: WatchedFile[] = [];
147        const lowPollingIntervalQueue = createPollingIntervalQueue(PollingInterval.Low);
148        const mediumPollingIntervalQueue = createPollingIntervalQueue(PollingInterval.Medium);
149        const highPollingIntervalQueue = createPollingIntervalQueue(PollingInterval.High);
150        return watchFile;
151
152        function watchFile(fileName: string, callback: FileWatcherCallback, defaultPollingInterval: PollingInterval): FileWatcher {
153            const file: WatchedFile = {
154                fileName,
155                callback,
156                unchangedPolls: 0,
157                mtime: getModifiedTime(fileName)
158            };
159            watchedFiles.push(file);
160
161            addToPollingIntervalQueue(file, defaultPollingInterval);
162            return {
163                close: () => {
164                    file.isClosed = true;
165                    // Remove from watchedFiles
166                    unorderedRemoveItem(watchedFiles, file);
167                    // Do not update polling interval queue since that will happen as part of polling
168                }
169            };
170        }
171
172        function createPollingIntervalQueue(pollingInterval: PollingInterval): PollingIntervalQueue {
173            const queue = [] as WatchedFile[] as PollingIntervalQueue;
174            queue.pollingInterval = pollingInterval;
175            queue.pollIndex = 0;
176            queue.pollScheduled = false;
177            return queue;
178        }
179
180        function pollPollingIntervalQueue(queue: PollingIntervalQueue) {
181            queue.pollIndex = pollQueue(queue, queue.pollingInterval, queue.pollIndex, pollingChunkSize[queue.pollingInterval]);
182            // Set the next polling index and timeout
183            if (queue.length) {
184                scheduleNextPoll(queue.pollingInterval);
185            }
186            else {
187                Debug.assert(queue.pollIndex === 0);
188                queue.pollScheduled = false;
189            }
190        }
191
192        function pollLowPollingIntervalQueue(queue: PollingIntervalQueue) {
193            // Always poll complete list of changedFilesInLastPoll
194            pollQueue(changedFilesInLastPoll, PollingInterval.Low, /*pollIndex*/ 0, changedFilesInLastPoll.length);
195
196            // Finally do the actual polling of the queue
197            pollPollingIntervalQueue(queue);
198            // Schedule poll if there are files in changedFilesInLastPoll but no files in the actual queue
199            // as pollPollingIntervalQueue wont schedule for next poll
200            if (!queue.pollScheduled && changedFilesInLastPoll.length) {
201                scheduleNextPoll(PollingInterval.Low);
202            }
203        }
204
205        function pollQueue(queue: (WatchedFile | undefined)[], pollingInterval: PollingInterval, pollIndex: number, chunkSize: number) {
206            // Max visit would be all elements of the queue
207            let needsVisit = queue.length;
208            let definedValueCopyToIndex = pollIndex;
209            for (let polled = 0; polled < chunkSize && needsVisit > 0; nextPollIndex(), needsVisit--) {
210                const watchedFile = queue[pollIndex];
211                if (!watchedFile) {
212                    continue;
213                }
214                else if (watchedFile.isClosed) {
215                    queue[pollIndex] = undefined;
216                    continue;
217                }
218
219                polled++;
220                const fileChanged = onWatchedFileStat(watchedFile, getModifiedTime(watchedFile.fileName));
221                if (watchedFile.isClosed) {
222                    // Closed watcher as part of callback
223                    queue[pollIndex] = undefined;
224                }
225                else if (fileChanged) {
226                    watchedFile.unchangedPolls = 0;
227                    // Changed files go to changedFilesInLastPoll queue
228                    if (queue !== changedFilesInLastPoll) {
229                        queue[pollIndex] = undefined;
230                        addChangedFileToLowPollingIntervalQueue(watchedFile);
231                    }
232                }
233                else if (watchedFile.unchangedPolls !== unchangedPollThresholds[pollingInterval]) {
234                    watchedFile.unchangedPolls++;
235                }
236                else if (queue === changedFilesInLastPoll) {
237                    // Restart unchangedPollCount for unchanged file and move to low polling interval queue
238                    watchedFile.unchangedPolls = 1;
239                    queue[pollIndex] = undefined;
240                    addToPollingIntervalQueue(watchedFile, PollingInterval.Low);
241                }
242                else if (pollingInterval !== PollingInterval.High) {
243                    watchedFile.unchangedPolls++;
244                    queue[pollIndex] = undefined;
245                    addToPollingIntervalQueue(watchedFile, pollingInterval === PollingInterval.Low ? PollingInterval.Medium : PollingInterval.High);
246                }
247
248                if (queue[pollIndex]) {
249                    // Copy this file to the non hole location
250                    if (definedValueCopyToIndex < pollIndex) {
251                        queue[definedValueCopyToIndex] = watchedFile;
252                        queue[pollIndex] = undefined;
253                    }
254                    definedValueCopyToIndex++;
255                }
256            }
257
258            // Return next poll index
259            return pollIndex;
260
261            function nextPollIndex() {
262                pollIndex++;
263                if (pollIndex === queue.length) {
264                    if (definedValueCopyToIndex < pollIndex) {
265                        // There are holes from nextDefinedValueIndex to end of queue, change queue size
266                        queue.length = definedValueCopyToIndex;
267                    }
268                    pollIndex = 0;
269                    definedValueCopyToIndex = 0;
270                }
271            }
272        }
273
274        function pollingIntervalQueue(pollingInterval: PollingInterval) {
275            switch (pollingInterval) {
276                case PollingInterval.Low:
277                    return lowPollingIntervalQueue;
278                case PollingInterval.Medium:
279                    return mediumPollingIntervalQueue;
280                case PollingInterval.High:
281                    return highPollingIntervalQueue;
282            }
283        }
284
285        function addToPollingIntervalQueue(file: WatchedFile, pollingInterval: PollingInterval) {
286            pollingIntervalQueue(pollingInterval).push(file);
287            scheduleNextPollIfNotAlreadyScheduled(pollingInterval);
288        }
289
290        function addChangedFileToLowPollingIntervalQueue(file: WatchedFile) {
291            changedFilesInLastPoll.push(file);
292            scheduleNextPollIfNotAlreadyScheduled(PollingInterval.Low);
293        }
294
295        function scheduleNextPollIfNotAlreadyScheduled(pollingInterval: PollingInterval) {
296            if (!pollingIntervalQueue(pollingInterval).pollScheduled) {
297                scheduleNextPoll(pollingInterval);
298            }
299        }
300
301        function scheduleNextPoll(pollingInterval: PollingInterval) {
302            pollingIntervalQueue(pollingInterval).pollScheduled = host.setTimeout(pollingInterval === PollingInterval.Low ? pollLowPollingIntervalQueue : pollPollingIntervalQueue, pollingInterval, pollingIntervalQueue(pollingInterval));
303        }
304
305        function getModifiedTime(fileName: string) {
306            return host.getModifiedTime(fileName) || missingFileModifiedTime;
307        }
308    }
309
310    function createUseFsEventsOnParentDirectoryWatchFile(fsWatch: FsWatch, useCaseSensitiveFileNames: boolean): HostWatchFile {
311        // One file can have multiple watchers
312        const fileWatcherCallbacks = createMultiMap<FileWatcherCallback>();
313        const dirWatchers = new Map<string, DirectoryWatcher>();
314        const toCanonicalName = createGetCanonicalFileName(useCaseSensitiveFileNames);
315        return nonPollingWatchFile;
316
317        function nonPollingWatchFile(fileName: string, callback: FileWatcherCallback, _pollingInterval: PollingInterval, fallbackOptions: WatchOptions | undefined): FileWatcher {
318            const filePath = toCanonicalName(fileName);
319            fileWatcherCallbacks.add(filePath, callback);
320            const dirPath = getDirectoryPath(filePath) || ".";
321            const watcher = dirWatchers.get(dirPath) ||
322                createDirectoryWatcher(getDirectoryPath(fileName) || ".", dirPath, fallbackOptions);
323            watcher.referenceCount++;
324            return {
325                close: () => {
326                    if (watcher.referenceCount === 1) {
327                        watcher.close();
328                        dirWatchers.delete(dirPath);
329                    }
330                    else {
331                        watcher.referenceCount--;
332                    }
333                    fileWatcherCallbacks.remove(filePath, callback);
334                }
335            };
336        }
337
338        function createDirectoryWatcher(dirName: string, dirPath: string, fallbackOptions: WatchOptions | undefined) {
339            const watcher = fsWatch(
340                dirName,
341                FileSystemEntryKind.Directory,
342                (_eventName: string, relativeFileName) => {
343                    // When files are deleted from disk, the triggered "rename" event would have a relativefileName of "undefined"
344                    if (!isString(relativeFileName)) { return; }
345                    const fileName = getNormalizedAbsolutePath(relativeFileName, dirName);
346                    // Some applications save a working file via rename operations
347                    const callbacks = fileName && fileWatcherCallbacks.get(toCanonicalName(fileName));
348                    if (callbacks) {
349                        for (const fileCallback of callbacks) {
350                            fileCallback(fileName, FileWatcherEventKind.Changed);
351                        }
352                    }
353                },
354                /*recursive*/ false,
355                PollingInterval.Medium,
356                fallbackOptions
357            ) as DirectoryWatcher;
358            watcher.referenceCount = 0;
359            dirWatchers.set(dirPath, watcher);
360            return watcher;
361        }
362    }
363
364    /* @internal */
365    export function createSingleFileWatcherPerName(
366        watchFile: HostWatchFile,
367        useCaseSensitiveFileNames: boolean
368    ): HostWatchFile {
369        interface SingleFileWatcher {
370            watcher: FileWatcher;
371            refCount: number;
372        }
373        const cache = new Map<string, SingleFileWatcher>();
374        const callbacksCache = createMultiMap<FileWatcherCallback>();
375        const toCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
376
377        return (fileName, callback, pollingInterval, options) => {
378            const path = toCanonicalFileName(fileName);
379            const existing = cache.get(path);
380            if (existing) {
381                existing.refCount++;
382            }
383            else {
384                cache.set(path, {
385                    watcher: watchFile(
386                        fileName,
387                        (fileName, eventKind) => forEach(
388                            callbacksCache.get(path),
389                            cb => cb(fileName, eventKind)
390                        ),
391                        pollingInterval,
392                        options
393                    ),
394                    refCount: 1
395                });
396            }
397            callbacksCache.add(path, callback);
398
399            return {
400                close: () => {
401                    const watcher = Debug.checkDefined(cache.get(path));
402                    callbacksCache.remove(path, callback);
403                    watcher.refCount--;
404                    if (watcher.refCount) return;
405                    cache.delete(path);
406                    closeFileWatcherOf(watcher);
407                }
408            };
409        };
410    }
411
412    /**
413     * Returns true if file status changed
414     */
415    /*@internal*/
416    export function onWatchedFileStat(watchedFile: WatchedFile, modifiedTime: Date): boolean {
417        const oldTime = watchedFile.mtime.getTime();
418        const newTime = modifiedTime.getTime();
419        if (oldTime !== newTime) {
420            watchedFile.mtime = modifiedTime;
421            watchedFile.callback(watchedFile.fileName, getFileWatcherEventKind(oldTime, newTime));
422            return true;
423        }
424
425        return false;
426    }
427
428    /*@internal*/
429    export function getFileWatcherEventKind(oldTime: number, newTime: number) {
430        return oldTime === 0
431            ? FileWatcherEventKind.Created
432            : newTime === 0
433                ? FileWatcherEventKind.Deleted
434                : FileWatcherEventKind.Changed;
435    }
436
437    /*@internal*/
438    export const ignoredPaths = ["/node_modules/.", "/.git", "/.#"];
439
440    /*@internal*/
441    export let sysLog: (s: string) => void = noop; // eslint-disable-line prefer-const
442
443    /*@internal*/
444    export function setSysLog(logger: typeof sysLog) {
445        sysLog = logger;
446    }
447
448    /*@internal*/
449    export interface RecursiveDirectoryWatcherHost {
450        watchDirectory: HostWatchDirectory;
451        useCaseSensitiveFileNames: boolean;
452        getCurrentDirectory: System["getCurrentDirectory"];
453        getAccessibleSortedChildDirectories(path: string): readonly string[];
454        directoryExists(dir: string): boolean;
455        realpath(s: string): string;
456        setTimeout: NonNullable<System["setTimeout"]>;
457        clearTimeout: NonNullable<System["clearTimeout"]>;
458    }
459
460    /**
461     * Watch the directory recursively using host provided method to watch child directories
462     * that means if this is recursive watcher, watch the children directories as well
463     * (eg on OS that dont support recursive watch using fs.watch use fs.watchFile)
464     */
465    /*@internal*/
466    export function createDirectoryWatcherSupportingRecursive({
467        watchDirectory,
468        useCaseSensitiveFileNames,
469        getCurrentDirectory,
470        getAccessibleSortedChildDirectories,
471        directoryExists,
472        realpath,
473        setTimeout,
474        clearTimeout
475    }: RecursiveDirectoryWatcherHost): HostWatchDirectory {
476        interface ChildDirectoryWatcher extends FileWatcher {
477            dirName: string;
478        }
479        type ChildWatches = readonly ChildDirectoryWatcher[];
480        interface HostDirectoryWatcher {
481            watcher: FileWatcher;
482            childWatches: ChildWatches;
483            refCount: number;
484        }
485
486        const cache = new Map<string, HostDirectoryWatcher>();
487        const callbackCache = createMultiMap<Path, { dirName: string; callback: DirectoryWatcherCallback; }>();
488        const cacheToUpdateChildWatches = new Map<Path, { dirName: string; options: WatchOptions | undefined; fileNames: string[]; }>();
489        let timerToUpdateChildWatches: any;
490
491        const filePathComparer = getStringComparer(!useCaseSensitiveFileNames);
492        const toCanonicalFilePath = createGetCanonicalFileName(useCaseSensitiveFileNames);
493
494        return (dirName, callback, recursive, options) => recursive ?
495            createDirectoryWatcher(dirName, options, callback) :
496            watchDirectory(dirName, callback, recursive, options);
497
498        /**
499         * Create the directory watcher for the dirPath.
500         */
501        function createDirectoryWatcher(dirName: string, options: WatchOptions | undefined, callback?: DirectoryWatcherCallback): ChildDirectoryWatcher {
502            const dirPath = toCanonicalFilePath(dirName) as Path;
503            let directoryWatcher = cache.get(dirPath);
504            if (directoryWatcher) {
505                directoryWatcher.refCount++;
506            }
507            else {
508                directoryWatcher = {
509                    watcher: watchDirectory(dirName, fileName => {
510                        if (isIgnoredPath(fileName, options)) return;
511
512                        if (options?.synchronousWatchDirectory) {
513                            // Call the actual callback
514                            invokeCallbacks(dirPath, fileName);
515
516                            // Iterate through existing children and update the watches if needed
517                            updateChildWatches(dirName, dirPath, options);
518                        }
519                        else {
520                            nonSyncUpdateChildWatches(dirName, dirPath, fileName, options);
521                        }
522                    }, /*recursive*/ false, options),
523                    refCount: 1,
524                    childWatches: emptyArray
525                };
526                cache.set(dirPath, directoryWatcher);
527                updateChildWatches(dirName, dirPath, options);
528            }
529
530            const callbackToAdd = callback && { dirName, callback };
531            if (callbackToAdd) {
532                callbackCache.add(dirPath, callbackToAdd);
533            }
534
535            return {
536                dirName,
537                close: () => {
538                    const directoryWatcher = Debug.checkDefined(cache.get(dirPath));
539                    if (callbackToAdd) callbackCache.remove(dirPath, callbackToAdd);
540                    directoryWatcher.refCount--;
541
542                    if (directoryWatcher.refCount) return;
543
544                    cache.delete(dirPath);
545                    closeFileWatcherOf(directoryWatcher);
546                    directoryWatcher.childWatches.forEach(closeFileWatcher);
547                }
548            };
549        }
550
551        type InvokeMap = ESMap<Path, string[] | true>;
552        function invokeCallbacks(dirPath: Path, fileName: string): void;
553        function invokeCallbacks(dirPath: Path, invokeMap: InvokeMap, fileNames: string[] | undefined): void;
554        function invokeCallbacks(dirPath: Path, fileNameOrInvokeMap: string | InvokeMap, fileNames?: string[]) {
555            let fileName: string | undefined;
556            let invokeMap: InvokeMap | undefined;
557            if (isString(fileNameOrInvokeMap)) {
558                fileName = fileNameOrInvokeMap;
559            }
560            else {
561                invokeMap = fileNameOrInvokeMap;
562            }
563            // Call the actual callback
564            callbackCache.forEach((callbacks, rootDirName) => {
565                if (invokeMap && invokeMap.get(rootDirName) === true) return;
566                if (rootDirName === dirPath || (startsWith(dirPath, rootDirName) && dirPath[rootDirName.length] === directorySeparator)) {
567                    if (invokeMap) {
568                        if (fileNames) {
569                            const existing = invokeMap.get(rootDirName);
570                            if (existing) {
571                                (existing as string[]).push(...fileNames);
572                            }
573                            else {
574                                invokeMap.set(rootDirName, fileNames.slice());
575                            }
576                        }
577                        else {
578                            invokeMap.set(rootDirName, true);
579                        }
580                    }
581                    else {
582                        callbacks.forEach(({ callback }) => callback(fileName!));
583                    }
584                }
585            });
586        }
587
588        function nonSyncUpdateChildWatches(dirName: string, dirPath: Path, fileName: string, options: WatchOptions | undefined) {
589            // Iterate through existing children and update the watches if needed
590            const parentWatcher = cache.get(dirPath);
591            if (parentWatcher && directoryExists(dirName)) {
592                // Schedule the update and postpone invoke for callbacks
593                scheduleUpdateChildWatches(dirName, dirPath, fileName, options);
594                return;
595            }
596
597            // Call the actual callbacks and remove child watches
598            invokeCallbacks(dirPath, fileName);
599            removeChildWatches(parentWatcher);
600        }
601
602        function scheduleUpdateChildWatches(dirName: string, dirPath: Path, fileName: string, options: WatchOptions | undefined) {
603            const existing = cacheToUpdateChildWatches.get(dirPath);
604            if (existing) {
605                existing.fileNames.push(fileName);
606            }
607            else {
608                cacheToUpdateChildWatches.set(dirPath, { dirName, options, fileNames: [fileName] });
609            }
610            if (timerToUpdateChildWatches) {
611                clearTimeout(timerToUpdateChildWatches);
612                timerToUpdateChildWatches = undefined;
613            }
614            timerToUpdateChildWatches = setTimeout(onTimerToUpdateChildWatches, 1000);
615        }
616
617        function onTimerToUpdateChildWatches() {
618            timerToUpdateChildWatches = undefined;
619            sysLog(`sysLog:: onTimerToUpdateChildWatches:: ${cacheToUpdateChildWatches.size}`);
620            const start = timestamp();
621            const invokeMap = new Map<Path, string[]>();
622
623            while (!timerToUpdateChildWatches && cacheToUpdateChildWatches.size) {
624                const result = cacheToUpdateChildWatches.entries().next();
625                Debug.assert(!result.done);
626                const { value: [dirPath, { dirName, options, fileNames }] } = result;
627                cacheToUpdateChildWatches.delete(dirPath);
628                // Because the child refresh is fresh, we would need to invalidate whole root directory being watched
629                // to ensure that all the changes are reflected at this time
630                const hasChanges = updateChildWatches(dirName, dirPath, options);
631                invokeCallbacks(dirPath, invokeMap, hasChanges ? undefined : fileNames);
632            }
633
634            sysLog(`sysLog:: invokingWatchers:: Elapsed:: ${timestamp() - start}ms:: ${cacheToUpdateChildWatches.size}`);
635            callbackCache.forEach((callbacks, rootDirName) => {
636                const existing = invokeMap.get(rootDirName);
637                if (existing) {
638                    callbacks.forEach(({ callback, dirName }) => {
639                        if (isArray(existing)) {
640                            existing.forEach(callback);
641                        }
642                        else {
643                            callback(dirName);
644                        }
645                    });
646                }
647            });
648
649            const elapsed = timestamp() - start;
650            sysLog(`sysLog:: Elapsed:: ${elapsed}ms:: onTimerToUpdateChildWatches:: ${cacheToUpdateChildWatches.size} ${timerToUpdateChildWatches}`);
651        }
652
653        function removeChildWatches(parentWatcher: HostDirectoryWatcher | undefined) {
654            if (!parentWatcher) return;
655            const existingChildWatches = parentWatcher.childWatches;
656            parentWatcher.childWatches = emptyArray;
657            for (const childWatcher of existingChildWatches) {
658                childWatcher.close();
659                removeChildWatches(cache.get(toCanonicalFilePath(childWatcher.dirName)));
660            }
661        }
662
663        function updateChildWatches(parentDir: string, parentDirPath: Path, options: WatchOptions | undefined) {
664            // Iterate through existing children and update the watches if needed
665            const parentWatcher = cache.get(parentDirPath);
666            if (!parentWatcher) return false;
667            let newChildWatches: ChildDirectoryWatcher[] | undefined;
668            const hasChanges = enumerateInsertsAndDeletes<string, ChildDirectoryWatcher>(
669                directoryExists(parentDir) ? mapDefined(getAccessibleSortedChildDirectories(parentDir), child => {
670                    const childFullName = getNormalizedAbsolutePath(child, parentDir);
671                    // Filter our the symbolic link directories since those arent included in recursive watch
672                    // which is same behaviour when recursive: true is passed to fs.watch
673                    return !isIgnoredPath(childFullName, options) && filePathComparer(childFullName, normalizePath(realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined;
674                }) : emptyArray,
675                parentWatcher.childWatches,
676                (child, childWatcher) => filePathComparer(child, childWatcher.dirName),
677                createAndAddChildDirectoryWatcher,
678                closeFileWatcher,
679                addChildDirectoryWatcher
680            );
681            parentWatcher.childWatches = newChildWatches || emptyArray;
682            return hasChanges;
683
684            /**
685             * Create new childDirectoryWatcher and add it to the new ChildDirectoryWatcher list
686             */
687            function createAndAddChildDirectoryWatcher(childName: string) {
688                const result = createDirectoryWatcher(childName, options);
689                addChildDirectoryWatcher(result);
690            }
691
692            /**
693             * Add child directory watcher to the new ChildDirectoryWatcher list
694             */
695            function addChildDirectoryWatcher(childWatcher: ChildDirectoryWatcher) {
696                (newChildWatches || (newChildWatches = [])).push(childWatcher);
697            }
698        }
699
700        function isIgnoredPath(path: string, options: WatchOptions | undefined) {
701            return some(ignoredPaths, searchPath => isInPath(path, searchPath)) ||
702                isIgnoredByWatchOptions(path, options, useCaseSensitiveFileNames, getCurrentDirectory);
703        }
704
705        function isInPath(path: string, searchPath: string) {
706            if (stringContains(path, searchPath)) return true;
707            if (useCaseSensitiveFileNames) return false;
708            return stringContains(toCanonicalFilePath(path), searchPath);
709        }
710    }
711
712    /*@internal*/
713    export type FsWatchCallback = (eventName: "rename" | "change", relativeFileName: string | undefined) => void;
714    /*@internal*/
715    export type FsWatch = (fileOrDirectory: string, entryKind: FileSystemEntryKind, callback: FsWatchCallback, recursive: boolean, fallbackPollingInterval: PollingInterval, fallbackOptions: WatchOptions | undefined) => FileWatcher;
716
717    /*@internal*/
718    export const enum FileSystemEntryKind {
719        File,
720        Directory,
721    }
722
723    /*@internal*/
724    export function createFileWatcherCallback(callback: FsWatchCallback): FileWatcherCallback {
725        return (_fileName, eventKind) => callback(eventKind === FileWatcherEventKind.Changed ? "change" : "rename", "");
726    }
727
728    function createFsWatchCallbackForFileWatcherCallback(
729        fileName: string,
730        callback: FileWatcherCallback,
731        fileExists: System["fileExists"]
732    ): FsWatchCallback {
733        return eventName => {
734            if (eventName === "rename") {
735                callback(fileName, fileExists(fileName) ? FileWatcherEventKind.Created : FileWatcherEventKind.Deleted);
736            }
737            else {
738                // Change
739                callback(fileName, FileWatcherEventKind.Changed);
740            }
741        };
742    }
743
744    function isIgnoredByWatchOptions(
745        pathToCheck: string,
746        options: WatchOptions | undefined,
747        useCaseSensitiveFileNames: boolean,
748        getCurrentDirectory: System["getCurrentDirectory"],
749    ) {
750        return (options?.excludeDirectories || options?.excludeFiles) && (
751            matchesExclude(pathToCheck, options?.excludeFiles, useCaseSensitiveFileNames, getCurrentDirectory()) ||
752            matchesExclude(pathToCheck, options?.excludeDirectories, useCaseSensitiveFileNames, getCurrentDirectory())
753        );
754    }
755
756    function createFsWatchCallbackForDirectoryWatcherCallback(
757        directoryName: string,
758        callback: DirectoryWatcherCallback,
759        options: WatchOptions | undefined,
760        useCaseSensitiveFileNames: boolean,
761        getCurrentDirectory: System["getCurrentDirectory"],
762    ): FsWatchCallback {
763        return (eventName, relativeFileName) => {
764            // In watchDirectory we only care about adding and removing files (when event name is
765            // "rename"); changes made within files are handled by corresponding fileWatchers (when
766            // event name is "change")
767            if (eventName === "rename") {
768                // When deleting a file, the passed baseFileName is null
769                const fileName = !relativeFileName ? directoryName : normalizePath(combinePaths(directoryName, relativeFileName));
770                if (!relativeFileName || !isIgnoredByWatchOptions(fileName, options, useCaseSensitiveFileNames, getCurrentDirectory)) {
771                    callback(fileName);
772                }
773            }
774        };
775    }
776
777    /*@internal*/
778    export interface CreateSystemWatchFunctions {
779        // Polling watch file
780        pollingWatchFile: HostWatchFile;
781        // For dynamic polling watch file
782        getModifiedTime: NonNullable<System["getModifiedTime"]>;
783        setTimeout: NonNullable<System["setTimeout"]>;
784        clearTimeout: NonNullable<System["clearTimeout"]>;
785        // For fs events :
786        fsWatch: FsWatch;
787        fileExists: System["fileExists"];
788        useCaseSensitiveFileNames: boolean;
789        getCurrentDirectory: System["getCurrentDirectory"];
790        fsSupportsRecursiveFsWatch: boolean;
791        directoryExists: System["directoryExists"];
792        getAccessibleSortedChildDirectories(path: string): readonly string[];
793        realpath(s: string): string;
794        // For backward compatibility environment variables
795        tscWatchFile: string | undefined;
796        useNonPollingWatchers?: boolean;
797        tscWatchDirectory: string | undefined;
798    }
799
800    /*@internal*/
801    export function createSystemWatchFunctions({
802        pollingWatchFile,
803        getModifiedTime,
804        setTimeout,
805        clearTimeout,
806        fsWatch,
807        fileExists,
808        useCaseSensitiveFileNames,
809        getCurrentDirectory,
810        fsSupportsRecursiveFsWatch,
811        directoryExists,
812        getAccessibleSortedChildDirectories,
813        realpath,
814        tscWatchFile,
815        useNonPollingWatchers,
816        tscWatchDirectory,
817    }: CreateSystemWatchFunctions): { watchFile: HostWatchFile; watchDirectory: HostWatchDirectory; } {
818        let dynamicPollingWatchFile: HostWatchFile | undefined;
819        let nonPollingWatchFile: HostWatchFile | undefined;
820        let hostRecursiveDirectoryWatcher: HostWatchDirectory | undefined;
821        return {
822            watchFile,
823            watchDirectory
824        };
825
826        function watchFile(fileName: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined): FileWatcher {
827            options = updateOptionsForWatchFile(options, useNonPollingWatchers);
828            const watchFileKind = Debug.checkDefined(options.watchFile);
829            switch (watchFileKind) {
830                case WatchFileKind.FixedPollingInterval:
831                    return pollingWatchFile(fileName, callback, PollingInterval.Low, /*options*/ undefined);
832                case WatchFileKind.PriorityPollingInterval:
833                    return pollingWatchFile(fileName, callback, pollingInterval, /*options*/ undefined);
834                case WatchFileKind.DynamicPriorityPolling:
835                    return ensureDynamicPollingWatchFile()(fileName, callback, pollingInterval, /*options*/ undefined);
836                case WatchFileKind.UseFsEvents:
837                    return fsWatch(
838                        fileName,
839                        FileSystemEntryKind.File,
840                        createFsWatchCallbackForFileWatcherCallback(fileName, callback, fileExists),
841                        /*recursive*/ false,
842                        pollingInterval,
843                        getFallbackOptions(options)
844                    );
845                case WatchFileKind.UseFsEventsOnParentDirectory:
846                    if (!nonPollingWatchFile) {
847                        nonPollingWatchFile = createUseFsEventsOnParentDirectoryWatchFile(fsWatch, useCaseSensitiveFileNames);
848                    }
849                    return nonPollingWatchFile(fileName, callback, pollingInterval, getFallbackOptions(options));
850                default:
851                    Debug.assertNever(watchFileKind);
852            }
853        }
854
855        function ensureDynamicPollingWatchFile() {
856            return dynamicPollingWatchFile ||
857                (dynamicPollingWatchFile = createDynamicPriorityPollingWatchFile({ getModifiedTime, setTimeout }));
858        }
859
860        function updateOptionsForWatchFile(options: WatchOptions | undefined, useNonPollingWatchers?: boolean): WatchOptions {
861            if (options && options.watchFile !== undefined) return options;
862            switch (tscWatchFile) {
863                case "PriorityPollingInterval":
864                    // Use polling interval based on priority when create watch using host.watchFile
865                    return { watchFile: WatchFileKind.PriorityPollingInterval };
866                case "DynamicPriorityPolling":
867                    // Use polling interval but change the interval depending on file changes and their default polling interval
868                    return { watchFile: WatchFileKind.DynamicPriorityPolling };
869                case "UseFsEvents":
870                    // Use notifications from FS to watch with falling back to fs.watchFile
871                    return generateWatchFileOptions(WatchFileKind.UseFsEvents, PollingWatchKind.PriorityInterval, options);
872                case "UseFsEventsWithFallbackDynamicPolling":
873                    // Use notifications from FS to watch with falling back to dynamic watch file
874                    return generateWatchFileOptions(WatchFileKind.UseFsEvents, PollingWatchKind.DynamicPriority, options);
875                case "UseFsEventsOnParentDirectory":
876                    useNonPollingWatchers = true;
877                // fall through
878                default:
879                    return useNonPollingWatchers ?
880                        // Use notifications from FS to watch with falling back to fs.watchFile
881                        generateWatchFileOptions(WatchFileKind.UseFsEventsOnParentDirectory, PollingWatchKind.PriorityInterval, options) :
882                        // Default to do not use fixed polling interval
883                        { watchFile: WatchFileKind.FixedPollingInterval };
884            }
885        }
886
887        function generateWatchFileOptions(
888            watchFile: WatchFileKind,
889            fallbackPolling: PollingWatchKind,
890            options: WatchOptions | undefined
891        ): WatchOptions {
892            const defaultFallbackPolling = options?.fallbackPolling;
893            return {
894                watchFile,
895                fallbackPolling: defaultFallbackPolling === undefined ?
896                    fallbackPolling :
897                    defaultFallbackPolling
898            };
899        }
900
901        function watchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean, options: WatchOptions | undefined): FileWatcher {
902            if (fsSupportsRecursiveFsWatch) {
903                return fsWatch(
904                    directoryName,
905                    FileSystemEntryKind.Directory,
906                    createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback, options, useCaseSensitiveFileNames, getCurrentDirectory),
907                    recursive,
908                    PollingInterval.Medium,
909                    getFallbackOptions(options)
910                );
911            }
912
913            if (!hostRecursiveDirectoryWatcher) {
914                hostRecursiveDirectoryWatcher = createDirectoryWatcherSupportingRecursive({
915                    useCaseSensitiveFileNames,
916                    getCurrentDirectory,
917                    directoryExists,
918                    getAccessibleSortedChildDirectories,
919                    watchDirectory: nonRecursiveWatchDirectory,
920                    realpath,
921                    setTimeout,
922                    clearTimeout
923                });
924            }
925            return hostRecursiveDirectoryWatcher(directoryName, callback, recursive, options);
926        }
927
928        function nonRecursiveWatchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean, options: WatchOptions | undefined): FileWatcher {
929            Debug.assert(!recursive);
930            const watchDirectoryOptions = updateOptionsForWatchDirectory(options);
931            const watchDirectoryKind = Debug.checkDefined(watchDirectoryOptions.watchDirectory);
932            switch (watchDirectoryKind) {
933                case WatchDirectoryKind.FixedPollingInterval:
934                    return pollingWatchFile(
935                        directoryName,
936                        () => callback(directoryName),
937                        PollingInterval.Medium,
938                        /*options*/ undefined
939                    );
940                case WatchDirectoryKind.DynamicPriorityPolling:
941                    return ensureDynamicPollingWatchFile()(
942                        directoryName,
943                        () => callback(directoryName),
944                        PollingInterval.Medium,
945                        /*options*/ undefined
946                    );
947                case WatchDirectoryKind.UseFsEvents:
948                    return fsWatch(
949                        directoryName,
950                        FileSystemEntryKind.Directory,
951                        createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback, options, useCaseSensitiveFileNames, getCurrentDirectory),
952                        recursive,
953                        PollingInterval.Medium,
954                        getFallbackOptions(watchDirectoryOptions)
955                    );
956                default:
957                    Debug.assertNever(watchDirectoryKind);
958            }
959        }
960
961        function updateOptionsForWatchDirectory(options: WatchOptions | undefined): WatchOptions {
962            if (options && options.watchDirectory !== undefined) return options;
963            switch (tscWatchDirectory) {
964                case "RecursiveDirectoryUsingFsWatchFile":
965                    // Use polling interval based on priority when create watch using host.watchFile
966                    return { watchDirectory: WatchDirectoryKind.FixedPollingInterval };
967                case "RecursiveDirectoryUsingDynamicPriorityPolling":
968                    // Use polling interval but change the interval depending on file changes and their default polling interval
969                    return { watchDirectory: WatchDirectoryKind.DynamicPriorityPolling };
970                default:
971                    const defaultFallbackPolling = options?.fallbackPolling;
972                    return {
973                        watchDirectory: WatchDirectoryKind.UseFsEvents,
974                        fallbackPolling: defaultFallbackPolling !== undefined ?
975                            defaultFallbackPolling :
976                            undefined
977                    };
978            }
979        }
980    }
981
982    /**
983     * patch writefile to create folder before writing the file
984     */
985    /*@internal*/
986    export function patchWriteFileEnsuringDirectory(sys: System) {
987        // patch writefile to create folder before writing the file
988        const originalWriteFile = sys.writeFile;
989        sys.writeFile = (path, data, writeBom) =>
990            writeFileEnsuringDirectories(
991                path,
992                data,
993                !!writeBom,
994                (path, data, writeByteOrderMark) => originalWriteFile.call(sys, path, data, writeByteOrderMark),
995                path => sys.createDirectory(path),
996                path => sys.directoryExists(path));
997    }
998
999    /*@internal*/
1000    export type BufferEncoding = "ascii" | "utf8" | "utf-8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex";
1001
1002    /*@internal*/
1003    interface NodeBuffer extends Uint8Array {
1004        constructor: any;
1005        write(str: string, encoding?: BufferEncoding): number;
1006        write(str: string, offset: number, encoding?: BufferEncoding): number;
1007        write(str: string, offset: number, length: number, encoding?: BufferEncoding): number;
1008        toString(encoding?: string, start?: number, end?: number): string;
1009        toJSON(): { type: "Buffer"; data: number[] };
1010        equals(otherBuffer: Uint8Array): boolean;
1011        compare(
1012            otherBuffer: Uint8Array,
1013            targetStart?: number,
1014            targetEnd?: number,
1015            sourceStart?: number,
1016            sourceEnd?: number
1017        ): number;
1018        copy(targetBuffer: Uint8Array, targetStart?: number, sourceStart?: number, sourceEnd?: number): number;
1019        slice(begin?: number, end?: number): Buffer;
1020        subarray(begin?: number, end?: number): Buffer;
1021        writeUIntLE(value: number, offset: number, byteLength: number): number;
1022        writeUIntBE(value: number, offset: number, byteLength: number): number;
1023        writeIntLE(value: number, offset: number, byteLength: number): number;
1024        writeIntBE(value: number, offset: number, byteLength: number): number;
1025        readUIntLE(offset: number, byteLength: number): number;
1026        readUIntBE(offset: number, byteLength: number): number;
1027        readIntLE(offset: number, byteLength: number): number;
1028        readIntBE(offset: number, byteLength: number): number;
1029        readUInt8(offset: number): number;
1030        readUInt16LE(offset: number): number;
1031        readUInt16BE(offset: number): number;
1032        readUInt32LE(offset: number): number;
1033        readUInt32BE(offset: number): number;
1034        readInt8(offset: number): number;
1035        readInt16LE(offset: number): number;
1036        readInt16BE(offset: number): number;
1037        readInt32LE(offset: number): number;
1038        readInt32BE(offset: number): number;
1039        readFloatLE(offset: number): number;
1040        readFloatBE(offset: number): number;
1041        readDoubleLE(offset: number): number;
1042        readDoubleBE(offset: number): number;
1043        reverse(): this;
1044        swap16(): Buffer;
1045        swap32(): Buffer;
1046        swap64(): Buffer;
1047        writeUInt8(value: number, offset: number): number;
1048        writeUInt16LE(value: number, offset: number): number;
1049        writeUInt16BE(value: number, offset: number): number;
1050        writeUInt32LE(value: number, offset: number): number;
1051        writeUInt32BE(value: number, offset: number): number;
1052        writeInt8(value: number, offset: number): number;
1053        writeInt16LE(value: number, offset: number): number;
1054        writeInt16BE(value: number, offset: number): number;
1055        writeInt32LE(value: number, offset: number): number;
1056        writeInt32BE(value: number, offset: number): number;
1057        writeFloatLE(value: number, offset: number): number;
1058        writeFloatBE(value: number, offset: number): number;
1059        writeDoubleLE(value: number, offset: number): number;
1060        writeDoubleBE(value: number, offset: number): number;
1061        readBigUInt64BE?(offset?: number): bigint;
1062        readBigUInt64LE?(offset?: number): bigint;
1063        readBigInt64BE?(offset?: number): bigint;
1064        readBigInt64LE?(offset?: number): bigint;
1065        writeBigInt64BE?(value: bigint, offset?: number): number;
1066        writeBigInt64LE?(value: bigint, offset?: number): number;
1067        writeBigUInt64BE?(value: bigint, offset?: number): number;
1068        writeBigUInt64LE?(value: bigint, offset?: number): number;
1069        fill(value: string | Uint8Array | number, offset?: number, end?: number, encoding?: BufferEncoding): this;
1070        indexOf(value: string | number | Uint8Array, byteOffset?: number, encoding?: BufferEncoding): number;
1071        lastIndexOf(value: string | number | Uint8Array, byteOffset?: number, encoding?: BufferEncoding): number;
1072        entries(): IterableIterator<[number, number]>;
1073        includes(value: string | number | Buffer, byteOffset?: number, encoding?: BufferEncoding): boolean;
1074        keys(): IterableIterator<number>;
1075        values(): IterableIterator<number>;
1076    }
1077
1078    /*@internal*/
1079    interface Buffer extends NodeBuffer { }
1080
1081    // TODO: GH#18217 Methods on System are often used as if they are certainly defined
1082    export interface System {
1083        args: string[];
1084        newLine: string;
1085        useCaseSensitiveFileNames: boolean;
1086        write(s: string): void;
1087        writeOutputIsTTY?(): boolean;
1088        readFile(path: string, encoding?: string): string | undefined;
1089        getFileSize?(path: string): number;
1090        writeFile(path: string, data: string, writeByteOrderMark?: boolean): void;
1091
1092        /**
1093         * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that
1094         * use native OS file watching
1095         */
1096        watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher;
1097        watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher;
1098        resolvePath(path: string): string;
1099        fileExists(path: string): boolean;
1100        directoryExists(path: string): boolean;
1101        createDirectory(path: string): void;
1102        getExecutingFilePath(): string;
1103        getCurrentDirectory(): string;
1104        getDirectories(path: string): string[];
1105        readDirectory(path: string, extensions?: readonly string[], exclude?: readonly string[], include?: readonly string[], depth?: number): string[];
1106        getModifiedTime?(path: string): Date | undefined;
1107        setModifiedTime?(path: string, time: Date): void;
1108        deleteFile?(path: string): void;
1109        /**
1110         * A good implementation is node.js' `crypto.createHash`. (https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm)
1111         */
1112        createHash?(data: string): string;
1113        /** This must be cryptographically secure. Only implement this method using `crypto.createHash("sha256")`. */
1114        createSHA256Hash?(data: string): string;
1115        getMemoryUsage?(): number;
1116        exit(exitCode?: number): void;
1117        /*@internal*/ enableCPUProfiler?(path: string, continuation: () => void): boolean;
1118        /*@internal*/ disableCPUProfiler?(continuation: () => void): boolean;
1119        /*@internal*/ cpuProfilingEnabled?(): boolean;
1120        realpath?(path: string): string;
1121        /*@internal*/ getEnvironmentVariable(name: string): string;
1122        /*@internal*/ tryEnableSourceMapsForHost?(): void;
1123        /*@internal*/ debugMode?: boolean;
1124        setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
1125        clearTimeout?(timeoutId: any): void;
1126        clearScreen?(): void;
1127        /*@internal*/ setBlocking?(): void;
1128        base64decode?(input: string): string;
1129        base64encode?(input: string): string;
1130        /*@internal*/ bufferFrom?(input: string, encoding?: string): Buffer;
1131        // For testing
1132        /*@internal*/ now?(): Date;
1133        /*@internal*/ require?(baseDir: string, moduleName: string): RequireResult;
1134    }
1135
1136    export interface FileWatcher {
1137        close(): void;
1138    }
1139
1140    interface DirectoryWatcher extends FileWatcher {
1141        referenceCount: number;
1142    }
1143
1144    declare const require: any;
1145    declare const process: any;
1146    declare const global: any;
1147    declare const __filename: string;
1148    declare const __dirname: string;
1149
1150    export function getNodeMajorVersion(): number | undefined {
1151        if (typeof process === "undefined") {
1152            return undefined;
1153        }
1154        const version: string = process.version;
1155        if (!version) {
1156            return undefined;
1157        }
1158        const dot = version.indexOf(".");
1159        if (dot === -1) {
1160            return undefined;
1161        }
1162        return parseInt(version.substring(1, dot));
1163    }
1164
1165    // TODO: GH#18217 this is used as if it's certainly defined in many places.
1166    // eslint-disable-next-line prefer-const
1167    export let sys: System = (() => {
1168        // NodeJS detects "\uFEFF" at the start of the string and *replaces* it with the actual
1169        // byte order mark from the specified encoding. Using any other byte order mark does
1170        // not actually work.
1171        const byteOrderMarkIndicator = "\uFEFF";
1172
1173        function getNodeSystem(): System {
1174            const nativePattern = /^native |^\([^)]+\)$|^(internal[\\/]|[a-zA-Z0-9_\s]+(\.js)?$)/;
1175            const _fs: typeof import("fs") = require("fs");
1176            const _path: typeof import("path") = require("path");
1177            const _os = require("os");
1178            // crypto can be absent on reduced node installations
1179            let _crypto: typeof import("crypto") | undefined;
1180            try {
1181                _crypto = require("crypto");
1182            }
1183            catch {
1184                _crypto = undefined;
1185            }
1186            let activeSession: import("inspector").Session | "stopping" | undefined;
1187            let profilePath = "./profile.cpuprofile";
1188
1189            const realpathSync = _fs.realpathSync.native ?? _fs.realpathSync;
1190
1191            const Buffer: {
1192                new (input: string, encoding?: string): any;
1193                from?(input: string, encoding?: string): any;
1194            } = require("buffer").Buffer;
1195
1196            const nodeVersion = getNodeMajorVersion();
1197            const isNode4OrLater = nodeVersion! >= 4;
1198            const isLinuxOrMacOs = process.platform === "linux" || process.platform === "darwin";
1199
1200            const platform: string = _os.platform();
1201            const useCaseSensitiveFileNames = isFileSystemCaseSensitive();
1202            const fsSupportsRecursiveFsWatch = isNode4OrLater && (process.platform === "win32" || process.platform === "darwin");
1203            const getCurrentDirectory = memoize(() => process.cwd());
1204            const { watchFile, watchDirectory } = createSystemWatchFunctions({
1205                pollingWatchFile: createSingleFileWatcherPerName(fsWatchFileWorker, useCaseSensitiveFileNames),
1206                getModifiedTime,
1207                setTimeout,
1208                clearTimeout,
1209                fsWatch,
1210                useCaseSensitiveFileNames,
1211                getCurrentDirectory,
1212                fileExists,
1213                // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
1214                // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
1215                fsSupportsRecursiveFsWatch,
1216                directoryExists,
1217                getAccessibleSortedChildDirectories: path => getAccessibleFileSystemEntries(path).directories,
1218                realpath,
1219                tscWatchFile: process.env.TSC_WATCHFILE,
1220                useNonPollingWatchers: process.env.TSC_NONPOLLING_WATCHER,
1221                tscWatchDirectory: process.env.TSC_WATCHDIRECTORY,
1222            });
1223            const nodeSystem: System = {
1224                args: process.argv.slice(2),
1225                newLine: _os.EOL,
1226                useCaseSensitiveFileNames,
1227                write(s: string): void {
1228                    process.stdout.write(s);
1229                },
1230                writeOutputIsTTY() {
1231                    return process.stdout.isTTY;
1232                },
1233                readFile,
1234                writeFile,
1235                watchFile,
1236                watchDirectory,
1237                resolvePath: path => _path.resolve(path),
1238                fileExists,
1239                directoryExists,
1240                createDirectory(directoryName: string) {
1241                    if (!nodeSystem.directoryExists(directoryName)) {
1242                        // Wrapped in a try-catch to prevent crashing if we are in a race
1243                        // with another copy of ourselves to create the same directory
1244                        try {
1245                            _fs.mkdirSync(directoryName);
1246                        }
1247                        catch (e) {
1248                            if (e.code !== "EEXIST") {
1249                                // Failed for some other reason (access denied?); still throw
1250                                throw e;
1251                            }
1252                        }
1253                    }
1254                },
1255                getExecutingFilePath() {
1256                    return __filename;
1257                },
1258                getCurrentDirectory,
1259                getDirectories,
1260                getEnvironmentVariable(name: string) {
1261                    return process.env[name] || "";
1262                },
1263                readDirectory,
1264                getModifiedTime,
1265                setModifiedTime,
1266                deleteFile,
1267                createHash: _crypto ? createSHA256Hash : generateDjb2Hash,
1268                createSHA256Hash: _crypto ? createSHA256Hash : undefined,
1269                getMemoryUsage() {
1270                    if (global.gc) {
1271                        global.gc();
1272                    }
1273                    return process.memoryUsage().heapUsed;
1274                },
1275                getFileSize(path) {
1276                    try {
1277                        const stat = statSync(path);
1278                        if (stat?.isFile()) {
1279                            return stat.size;
1280                        }
1281                    }
1282                    catch { /*ignore*/ }
1283                    return 0;
1284                },
1285                exit(exitCode?: number): void {
1286                    disableCPUProfiler(() => process.exit(exitCode));
1287                },
1288                enableCPUProfiler,
1289                disableCPUProfiler,
1290                cpuProfilingEnabled: () => !!activeSession || contains(process.execArgv, "--cpu-prof") || contains(process.execArgv, "--prof"),
1291                realpath,
1292                debugMode: !!process.env.NODE_INSPECTOR_IPC || !!process.env.VSCODE_INSPECTOR_OPTIONS || some(<string[]>process.execArgv, arg => /^--(inspect|debug)(-brk)?(=\d+)?$/i.test(arg)),
1293                tryEnableSourceMapsForHost() {
1294                    try {
1295                        require("source-map-support").install();
1296                    }
1297                    catch {
1298                        // Could not enable source maps.
1299                    }
1300                },
1301                setTimeout,
1302                clearTimeout,
1303                clearScreen: () => {
1304                    process.stdout.write("\x1Bc");
1305                },
1306                setBlocking: () => {
1307                    if (process.stdout && process.stdout._handle && process.stdout._handle.setBlocking) {
1308                        process.stdout._handle.setBlocking(true);
1309                    }
1310                },
1311                bufferFrom,
1312                base64decode: input => bufferFrom(input, "base64").toString("utf8"),
1313                base64encode: input => bufferFrom(input).toString("base64"),
1314                require: (baseDir, moduleName) => {
1315                    try {
1316                        const modulePath = resolveJSModule(moduleName, baseDir, nodeSystem);
1317                        return { module: require(modulePath), modulePath, error: undefined };
1318                    }
1319                    catch (error) {
1320                        return { module: undefined, modulePath: undefined, error };
1321                    }
1322                }
1323            };
1324            return nodeSystem;
1325
1326            /**
1327             * `throwIfNoEntry` was added so recently that it's not in the node types.
1328             * This helper encapsulates the mitigating usage of `any`.
1329             * See https://github.com/nodejs/node/pull/33716
1330             */
1331            function statSync(path: string): import("fs").Stats | undefined {
1332                // throwIfNoEntry will be ignored by older versions of node
1333                return (_fs as any).statSync(path, { throwIfNoEntry: false });
1334            }
1335
1336            /**
1337             * Uses the builtin inspector APIs to capture a CPU profile
1338             * See https://nodejs.org/api/inspector.html#inspector_example_usage for details
1339             */
1340            function enableCPUProfiler(path: string, cb: () => void) {
1341                if (activeSession) {
1342                    cb();
1343                    return false;
1344                }
1345                const inspector: typeof import("inspector") = require("inspector");
1346                if (!inspector || !inspector.Session) {
1347                    cb();
1348                    return false;
1349                }
1350                const session = new inspector.Session();
1351                session.connect();
1352
1353                session.post("Profiler.enable", () => {
1354                    session.post("Profiler.start", () => {
1355                        activeSession = session;
1356                        profilePath = path;
1357                        cb();
1358                    });
1359                });
1360                return true;
1361            }
1362
1363            /**
1364             * Strips non-TS paths from the profile, so users with private projects shouldn't
1365             * need to worry about leaking paths by submitting a cpu profile to us
1366             */
1367            function cleanupPaths(profile: import("inspector").Profiler.Profile) {
1368                let externalFileCounter = 0;
1369                const remappedPaths = new Map<string, string>();
1370                const normalizedDir = normalizeSlashes(__dirname);
1371                // Windows rooted dir names need an extra `/` prepended to be valid file:/// urls
1372                const fileUrlRoot = `file://${getRootLength(normalizedDir) === 1 ? "" : "/"}${normalizedDir}`;
1373                for (const node of profile.nodes) {
1374                    if (node.callFrame.url) {
1375                        const url = normalizeSlashes(node.callFrame.url);
1376                        if (containsPath(fileUrlRoot, url, useCaseSensitiveFileNames)) {
1377                            node.callFrame.url = getRelativePathToDirectoryOrUrl(fileUrlRoot, url, fileUrlRoot, createGetCanonicalFileName(useCaseSensitiveFileNames), /*isAbsolutePathAnUrl*/ true);
1378                        }
1379                        else if (!nativePattern.test(url)) {
1380                            node.callFrame.url = (remappedPaths.has(url) ? remappedPaths : remappedPaths.set(url, `external${externalFileCounter}.js`)).get(url)!;
1381                            externalFileCounter++;
1382                        }
1383                    }
1384                }
1385                return profile;
1386            }
1387
1388            function disableCPUProfiler(cb: () => void) {
1389                if (activeSession && activeSession !== "stopping") {
1390                    const s = activeSession;
1391                    activeSession.post("Profiler.stop", (err, { profile }) => {
1392                        if (!err) {
1393                            try {
1394                                if (statSync(profilePath)?.isDirectory()) {
1395                                    profilePath = _path.join(profilePath, `${(new Date()).toISOString().replace(/:/g, "-")}+P${process.pid}.cpuprofile`);
1396                                }
1397                            }
1398                            catch {
1399                                // do nothing and ignore fallible fs operation
1400                            }
1401                            try {
1402                                _fs.mkdirSync(_path.dirname(profilePath), { recursive: true });
1403                            }
1404                            catch {
1405                                // do nothing and ignore fallible fs operation
1406                            }
1407                            _fs.writeFileSync(profilePath, JSON.stringify(cleanupPaths(profile)));
1408                        }
1409                        activeSession = undefined;
1410                        s.disconnect();
1411                        cb();
1412                    });
1413                    activeSession = "stopping";
1414                    return true;
1415                }
1416                else {
1417                    cb();
1418                    return false;
1419                }
1420            }
1421
1422            function bufferFrom(input: string, encoding?: string): Buffer {
1423                // See https://github.com/Microsoft/TypeScript/issues/25652
1424                return Buffer.from && (Buffer.from as Function) !== Int8Array.from
1425                    ? Buffer.from(input, encoding)
1426                    : new Buffer(input, encoding);
1427            }
1428
1429            function isFileSystemCaseSensitive(): boolean {
1430                // win32\win64 are case insensitive platforms
1431                if (platform === "win32" || platform === "win64") {
1432                    return false;
1433                }
1434                // If this file exists under a different case, we must be case-insensitve.
1435                return !fileExists(swapCase(__filename));
1436            }
1437
1438            /** Convert all lowercase chars to uppercase, and vice-versa */
1439            function swapCase(s: string): string {
1440                return s.replace(/\w/g, (ch) => {
1441                    const up = ch.toUpperCase();
1442                    return ch === up ? ch.toLowerCase() : up;
1443                });
1444            }
1445
1446            function fsWatchFileWorker(fileName: string, callback: FileWatcherCallback, pollingInterval: number): FileWatcher {
1447                _fs.watchFile(fileName, { persistent: true, interval: pollingInterval }, fileChanged);
1448                let eventKind: FileWatcherEventKind;
1449                return {
1450                    close: () => _fs.unwatchFile(fileName, fileChanged)
1451                };
1452
1453                function fileChanged(curr: any, prev: any) {
1454                    // previous event kind check is to ensure we recongnize the file as previously also missing when it is restored or renamed twice (that is it disappears and reappears)
1455                    // In such case, prevTime returned is same as prev time of event when file was deleted as per node documentation
1456                    const isPreviouslyDeleted = +prev.mtime === 0 || eventKind === FileWatcherEventKind.Deleted;
1457                    if (+curr.mtime === 0) {
1458                        if (isPreviouslyDeleted) {
1459                            // Already deleted file, no need to callback again
1460                            return;
1461                        }
1462                        eventKind = FileWatcherEventKind.Deleted;
1463                    }
1464                    else if (isPreviouslyDeleted) {
1465                        eventKind = FileWatcherEventKind.Created;
1466                    }
1467                    // If there is no change in modified time, ignore the event
1468                    else if (+curr.mtime === +prev.mtime) {
1469                        return;
1470                    }
1471                    else {
1472                        // File changed
1473                        eventKind = FileWatcherEventKind.Changed;
1474                    }
1475                    callback(fileName, eventKind);
1476                }
1477            }
1478
1479            function fsWatch(
1480                fileOrDirectory: string,
1481                entryKind: FileSystemEntryKind,
1482                callback: FsWatchCallback,
1483                recursive: boolean,
1484                fallbackPollingInterval: PollingInterval,
1485                fallbackOptions: WatchOptions | undefined
1486            ): FileWatcher {
1487                let options: any;
1488                let lastDirectoryPartWithDirectorySeparator: string | undefined;
1489                let lastDirectoryPart: string | undefined;
1490                if (isLinuxOrMacOs) {
1491                    lastDirectoryPartWithDirectorySeparator = fileOrDirectory.substr(fileOrDirectory.lastIndexOf(directorySeparator));
1492                    lastDirectoryPart = lastDirectoryPartWithDirectorySeparator.slice(directorySeparator.length);
1493                }
1494                /** Watcher for the file system entry depending on whether it is missing or present */
1495                let watcher = !fileSystemEntryExists(fileOrDirectory, entryKind) ?
1496                    watchMissingFileSystemEntry() :
1497                    watchPresentFileSystemEntry();
1498                return {
1499                    close: () => {
1500                        // Close the watcher (either existing file system entry watcher or missing file system entry watcher)
1501                        watcher.close();
1502                        watcher = undefined!;
1503                    }
1504                };
1505
1506                /**
1507                 * Invoke the callback with rename and update the watcher if not closed
1508                 * @param createWatcher
1509                 */
1510                function invokeCallbackAndUpdateWatcher(createWatcher: () => FileWatcher) {
1511                    sysLog(`sysLog:: ${fileOrDirectory}:: Changing watcher to ${createWatcher === watchPresentFileSystemEntry ? "Present" : "Missing"}FileSystemEntryWatcher`);
1512                    // Call the callback for current directory
1513                    callback("rename", "");
1514
1515                    // If watcher is not closed, update it
1516                    if (watcher) {
1517                        watcher.close();
1518                        watcher = createWatcher();
1519                    }
1520                }
1521
1522                /**
1523                 * Watch the file or directory that is currently present
1524                 * and when the watched file or directory is deleted, switch to missing file system entry watcher
1525                 */
1526                function watchPresentFileSystemEntry(): FileWatcher {
1527                    // Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
1528                    // (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
1529                    if (options === undefined) {
1530                        if (fsSupportsRecursiveFsWatch) {
1531                            options = { persistent: true, recursive: !!recursive };
1532                        }
1533                        else {
1534                            options = { persistent: true };
1535                        }
1536                    }
1537                    try {
1538                        const presentWatcher = _fs.watch(
1539                            fileOrDirectory,
1540                            options,
1541                            isLinuxOrMacOs ?
1542                                callbackChangingToMissingFileSystemEntry :
1543                                callback
1544                        );
1545                        // Watch the missing file or directory or error
1546                        presentWatcher.on("error", () => invokeCallbackAndUpdateWatcher(watchMissingFileSystemEntry));
1547                        return presentWatcher;
1548                    }
1549                    catch (e) {
1550                        // Catch the exception and use polling instead
1551                        // Eg. on linux the number of watches are limited and one could easily exhaust watches and the exception ENOSPC is thrown when creating watcher at that point
1552                        // so instead of throwing error, use fs.watchFile
1553                        return watchPresentFileSystemEntryWithFsWatchFile();
1554                    }
1555                }
1556
1557                function callbackChangingToMissingFileSystemEntry(event: "rename" | "change", relativeName: string | undefined) {
1558                    // because relativeName is not guaranteed to be correct we need to check on each rename with few combinations
1559                    // Eg on ubuntu while watching app/node_modules the relativeName is "node_modules" which is neither relative nor full path
1560                    return event === "rename" &&
1561                        (!relativeName ||
1562                            relativeName === lastDirectoryPart ||
1563                            (relativeName.lastIndexOf(lastDirectoryPartWithDirectorySeparator!) !== -1 && relativeName.lastIndexOf(lastDirectoryPartWithDirectorySeparator!) === relativeName.length - lastDirectoryPartWithDirectorySeparator!.length)) &&
1564                        !fileSystemEntryExists(fileOrDirectory, entryKind) ?
1565                        invokeCallbackAndUpdateWatcher(watchMissingFileSystemEntry) :
1566                        callback(event, relativeName);
1567                }
1568
1569                /**
1570                 * Watch the file or directory using fs.watchFile since fs.watch threw exception
1571                 * Eg. on linux the number of watches are limited and one could easily exhaust watches and the exception ENOSPC is thrown when creating watcher at that point
1572                 */
1573                function watchPresentFileSystemEntryWithFsWatchFile(): FileWatcher {
1574                    sysLog(`sysLog:: ${fileOrDirectory}:: Changing to fsWatchFile`);
1575                    return watchFile(
1576                        fileOrDirectory,
1577                        createFileWatcherCallback(callback),
1578                        fallbackPollingInterval,
1579                        fallbackOptions
1580                    );
1581                }
1582
1583                /**
1584                 * Watch the file or directory that is missing
1585                 * and switch to existing file or directory when the missing filesystem entry is created
1586                 */
1587                function watchMissingFileSystemEntry(): FileWatcher {
1588                    return watchFile(
1589                        fileOrDirectory,
1590                        (_fileName, eventKind) => {
1591                            if (eventKind === FileWatcherEventKind.Created && fileSystemEntryExists(fileOrDirectory, entryKind)) {
1592                                // Call the callback for current file or directory
1593                                // For now it could be callback for the inner directory creation,
1594                                // but just return current directory, better than current no-op
1595                                invokeCallbackAndUpdateWatcher(watchPresentFileSystemEntry);
1596                            }
1597                        },
1598                        fallbackPollingInterval,
1599                        fallbackOptions
1600                    );
1601                }
1602            }
1603
1604            function readFileWorker(fileName: string, _encoding?: string): string | undefined {
1605                let buffer: Buffer;
1606                try {
1607                    buffer = _fs.readFileSync(fileName);
1608                }
1609                catch (e) {
1610                    return undefined;
1611                }
1612                let len = buffer.length;
1613                if (len >= 2 && buffer[0] === 0xFE && buffer[1] === 0xFF) {
1614                    // Big endian UTF-16 byte order mark detected. Since big endian is not supported by node.js,
1615                    // flip all byte pairs and treat as little endian.
1616                    len &= ~1; // Round down to a multiple of 2
1617                    for (let i = 0; i < len; i += 2) {
1618                        const temp = buffer[i];
1619                        buffer[i] = buffer[i + 1];
1620                        buffer[i + 1] = temp;
1621                    }
1622                    return buffer.toString("utf16le", 2);
1623                }
1624                if (len >= 2 && buffer[0] === 0xFF && buffer[1] === 0xFE) {
1625                    // Little endian UTF-16 byte order mark detected
1626                    return buffer.toString("utf16le", 2);
1627                }
1628                if (len >= 3 && buffer[0] === 0xEF && buffer[1] === 0xBB && buffer[2] === 0xBF) {
1629                    // UTF-8 byte order mark detected
1630                    return buffer.toString("utf8", 3);
1631                }
1632                // Default is UTF-8 with no byte order mark
1633                return buffer.toString("utf8");
1634            }
1635
1636            function readFile(fileName: string, _encoding?: string): string | undefined {
1637                perfLogger.logStartReadFile(fileName);
1638                const file = readFileWorker(fileName, _encoding);
1639                perfLogger.logStopReadFile();
1640                return file;
1641            }
1642
1643            function writeFile(fileName: string, data: string, writeByteOrderMark?: boolean): void {
1644                perfLogger.logEvent("WriteFile: " + fileName);
1645                // If a BOM is required, emit one
1646                if (writeByteOrderMark) {
1647                    data = byteOrderMarkIndicator + data;
1648                }
1649
1650                let fd: number | undefined;
1651
1652                try {
1653                    fd = _fs.openSync(fileName, "w");
1654                    _fs.writeSync(fd, data, /*position*/ undefined, "utf8");
1655                }
1656                finally {
1657                    if (fd !== undefined) {
1658                        _fs.closeSync(fd);
1659                    }
1660                }
1661            }
1662
1663            function getAccessibleFileSystemEntries(path: string): FileSystemEntries {
1664                perfLogger.logEvent("ReadDir: " + (path || "."));
1665                try {
1666                    const entries = _fs.readdirSync(path || ".", { withFileTypes: true });
1667                    const files: string[] = [];
1668                    const directories: string[] = [];
1669                    for (const dirent of entries) {
1670                        // withFileTypes is not supported before Node 10.10.
1671                        const entry = typeof dirent === "string" ? dirent : dirent.name;
1672
1673                        // This is necessary because on some file system node fails to exclude
1674                        // "." and "..". See https://github.com/nodejs/node/issues/4002
1675                        if (entry === "." || entry === "..") {
1676                            continue;
1677                        }
1678
1679                        let stat: any;
1680                        if (typeof dirent === "string" || dirent.isSymbolicLink()) {
1681                            const name = combinePaths(path, entry);
1682
1683                            try {
1684                                stat = statSync(name);
1685                                if (!stat) {
1686                                    continue;
1687                                }
1688                            }
1689                            catch (e) {
1690                                continue;
1691                            }
1692                        }
1693                        else {
1694                            stat = dirent;
1695                        }
1696
1697                        if (stat.isFile()) {
1698                            files.push(entry);
1699                        }
1700                        else if (stat.isDirectory()) {
1701                            directories.push(entry);
1702                        }
1703                    }
1704                    files.sort();
1705                    directories.sort();
1706                    return { files, directories };
1707                }
1708                catch (e) {
1709                    return emptyFileSystemEntries;
1710                }
1711            }
1712
1713            function readDirectory(path: string, extensions?: readonly string[], excludes?: readonly string[], includes?: readonly string[], depth?: number): string[] {
1714                return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), depth, getAccessibleFileSystemEntries, realpath);
1715            }
1716
1717            function fileSystemEntryExists(path: string, entryKind: FileSystemEntryKind): boolean {
1718                // Since the error thrown by fs.statSync isn't used, we can avoid collecting a stack trace to improve
1719                // the CPU time performance.
1720                const originalStackTraceLimit = Error.stackTraceLimit;
1721                Error.stackTraceLimit = 0;
1722
1723                try {
1724                    const stat = statSync(path);
1725                    if (!stat) {
1726                        return false;
1727                    }
1728                    switch (entryKind) {
1729                        case FileSystemEntryKind.File: return stat.isFile();
1730                        case FileSystemEntryKind.Directory: return stat.isDirectory();
1731                        default: return false;
1732                    }
1733                }
1734                catch (e) {
1735                    return false;
1736                }
1737                finally {
1738                    Error.stackTraceLimit = originalStackTraceLimit;
1739                }
1740            }
1741
1742            function fileExists(path: string): boolean {
1743                return fileSystemEntryExists(path, FileSystemEntryKind.File);
1744            }
1745
1746            function directoryExists(path: string): boolean {
1747                return fileSystemEntryExists(path, FileSystemEntryKind.Directory);
1748            }
1749
1750            function getDirectories(path: string): string[] {
1751                return getAccessibleFileSystemEntries(path).directories.slice();
1752            }
1753
1754            function realpath(path: string): string {
1755                try {
1756                    return realpathSync(path);
1757                }
1758                catch {
1759                    return path;
1760                }
1761            }
1762
1763            function getModifiedTime(path: string) {
1764                try {
1765                    return statSync(path)?.mtime;
1766                }
1767                catch (e) {
1768                    return undefined;
1769                }
1770            }
1771
1772            function setModifiedTime(path: string, time: Date) {
1773                try {
1774                    _fs.utimesSync(path, time, time);
1775                }
1776                catch (e) {
1777                    return;
1778                }
1779            }
1780
1781            function deleteFile(path: string) {
1782                try {
1783                    return _fs.unlinkSync(path);
1784                }
1785                catch (e) {
1786                    return;
1787                }
1788            }
1789
1790            function createSHA256Hash(data: string): string {
1791                const hash = _crypto!.createHash("sha256");
1792                hash.update(data);
1793                return hash.digest("hex");
1794            }
1795        }
1796
1797        let sys: System | undefined;
1798        if (typeof process !== "undefined" && process.nextTick && !process.browser && typeof require !== "undefined") {
1799            // process and process.nextTick checks if current environment is node-like
1800            // process.browser check excludes webpack and browserify
1801            sys = getNodeSystem();
1802        }
1803        if (sys) {
1804            // patch writefile to create folder before writing the file
1805            patchWriteFileEnsuringDirectory(sys);
1806        }
1807        return sys!;
1808    })();
1809
1810    if (sys && sys.getEnvironmentVariable) {
1811        setCustomPollingValues(sys);
1812        Debug.setAssertionLevel(/^development$/i.test(sys.getEnvironmentVariable("NODE_ENV"))
1813            ? AssertionLevel.Normal
1814            : AssertionLevel.None);
1815    }
1816    if (sys && sys.debugMode) {
1817        Debug.isDebugging = true;
1818    }
1819}
1820