Add way to exclude files and directories to watch (#39243)

* Parse excludeDirectories and excludeFiles

* Use watch factory in typings installer

* Some refactoring for watchFactory

* Create Noop watcher if file or directory being watched is excluded

* Baselines without using exclude watch options

* Baselines including exclude option

* Handle exclude options in the system watches

* Add test without exclude option for recursive directory watching

* Test baselines with exclude option

* Always set sysLog

* Test for exclude option in server

* Add exclude options in the config file and fix the test

* Fix host configuration for server

* Handle host configuration for watch options

* Fix sysLog time log so baselines can be clean

* Handle reloadProjects to reload the project from scratch

* Ensure that file updates are reflected

* Feedback

* Feedback
This commit is contained in:
Sheetal Nandi 2020-11-04 13:30:06 -08:00 committed by GitHub
parent 6c6315db58
commit 76cf8fd78b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 1912 additions and 275 deletions

View file

@ -133,6 +133,30 @@ namespace ts {
category: Diagnostics.Advanced_Options,
description: Diagnostics.Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively,
},
{
name: "excludeDirectories",
type: "list",
element: {
name: "excludeDirectory",
type: "string",
isFilePath: true,
extraValidation: specToDiagnostic
},
category: Diagnostics.Advanced_Options,
description: Diagnostics.Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively,
},
{
name: "excludeFiles",
type: "list",
element: {
name: "excludeFile",
type: "string",
isFilePath: true,
extraValidation: specToDiagnostic
},
category: Diagnostics.Advanced_Options,
description: Diagnostics.Synchronously_call_callbacks_and_update_the_state_of_directory_watchers_on_platforms_that_don_t_support_recursive_watching_natively,
},
];
/* @internal */
@ -1231,9 +1255,9 @@ namespace ts {
const values = value.split(",");
switch (opt.element.type) {
case "number":
return map(values, parseInt);
return mapDefined(values, v => validateJsonOptionValue(opt.element, parseInt(v), errors));
case "string":
return map(values, v => v || "");
return mapDefined(values, v => validateJsonOptionValue(opt.element, v || "", errors));
default:
return mapDefined(values, v => parseCustomTypeOption(<CommandLineOptionOfCustomType>opt.element, v, errors));
}
@ -1363,7 +1387,7 @@ namespace ts {
}
else if (opt.type === "boolean") {
if (optValue === "false") {
options[opt.name] = false;
options[opt.name] = validateJsonOptionValue(opt, /*value*/ false, errors);
i++;
}
else {
@ -1385,20 +1409,20 @@ namespace ts {
if (args[i] !== "null") {
switch (opt.type) {
case "number":
options[opt.name] = parseInt(args[i]);
options[opt.name] = validateJsonOptionValue(opt, parseInt(args[i]), errors);
i++;
break;
case "boolean":
// boolean flag has optional value true, false, others
const optValue = args[i];
options[opt.name] = optValue !== "false";
options[opt.name] = validateJsonOptionValue(opt, optValue !== "false", errors);
// consume next argument as boolean flag value
if (optValue === "false" || optValue === "true") {
i++;
}
break;
case "string":
options[opt.name] = args[i] || "";
options[opt.name] = validateJsonOptionValue(opt, args[i] || "", errors);
i++;
break;
case "list":
@ -1843,9 +1867,10 @@ namespace ts {
function convertArrayLiteralExpressionToJson(
elements: NodeArray<Expression>,
elementOption: CommandLineOption | undefined
): any[] | void {
) {
if (!returnValue) {
return elements.forEach(element => convertPropertyValueToJson(element, elementOption));
elements.forEach(element => convertPropertyValueToJson(element, elementOption));
return undefined;
}
// Filter out invalid values
@ -1853,18 +1878,19 @@ namespace ts {
}
function convertPropertyValueToJson(valueExpression: Expression, option: CommandLineOption | undefined): any {
let invalidReported: boolean | undefined;
switch (valueExpression.kind) {
case SyntaxKind.TrueKeyword:
reportInvalidOptionValue(option && option.type !== "boolean");
return true;
return validateValue(/*value*/ true);
case SyntaxKind.FalseKeyword:
reportInvalidOptionValue(option && option.type !== "boolean");
return false;
return validateValue(/*value*/ false);
case SyntaxKind.NullKeyword:
reportInvalidOptionValue(option && option.name === "extends"); // "extends" is the only option we don't allow null/undefined for
return null; // eslint-disable-line no-null/no-null
return validateValue(/*value*/ null); // eslint-disable-line no-null/no-null
case SyntaxKind.StringLiteral:
if (!isDoubleQuotedString(valueExpression)) {
@ -1882,20 +1908,21 @@ namespace ts {
(message, arg0, arg1) => createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, message, arg0, arg1)
)
);
invalidReported = true;
}
}
return text;
return validateValue(text);
case SyntaxKind.NumericLiteral:
reportInvalidOptionValue(option && option.type !== "number");
return Number((<NumericLiteral>valueExpression).text);
return validateValue(Number((<NumericLiteral>valueExpression).text));
case SyntaxKind.PrefixUnaryExpression:
if ((<PrefixUnaryExpression>valueExpression).operator !== SyntaxKind.MinusToken || (<PrefixUnaryExpression>valueExpression).operand.kind !== SyntaxKind.NumericLiteral) {
break; // not valid JSON syntax
}
reportInvalidOptionValue(option && option.type !== "number");
return -Number((<NumericLiteral>(<PrefixUnaryExpression>valueExpression).operand).text);
return validateValue(-Number((<NumericLiteral>(<PrefixUnaryExpression>valueExpression).operand).text));
case SyntaxKind.ObjectLiteralExpression:
reportInvalidOptionValue(option && option.type !== "object");
@ -1909,20 +1936,20 @@ namespace ts {
// If need arises, we can modify this interface and callbacks as needed
if (option) {
const { elementOptions, extraKeyDiagnostics, name: optionName } = <TsConfigOnlyOption>option;
return convertObjectLiteralExpressionToJson(objectLiteralExpression,
elementOptions, extraKeyDiagnostics, optionName);
return validateValue(convertObjectLiteralExpressionToJson(objectLiteralExpression,
elementOptions, extraKeyDiagnostics, optionName));
}
else {
return convertObjectLiteralExpressionToJson(
return validateValue(convertObjectLiteralExpressionToJson(
objectLiteralExpression, /* knownOptions*/ undefined,
/*extraKeyDiagnosticMessage */ undefined, /*parentOption*/ undefined);
/*extraKeyDiagnosticMessage */ undefined, /*parentOption*/ undefined));
}
case SyntaxKind.ArrayLiteralExpression:
reportInvalidOptionValue(option && option.type !== "list");
return convertArrayLiteralExpressionToJson(
return validateValue(convertArrayLiteralExpressionToJson(
(<ArrayLiteralExpression>valueExpression).elements,
option && (<CommandLineOptionOfListType>option).element);
option && (<CommandLineOptionOfListType>option).element));
}
// Not in expected format
@ -1935,9 +1962,21 @@ namespace ts {
return undefined;
function validateValue(value: CompilerOptionsValue) {
if (!invalidReported) {
const diagnostic = option?.extraValidation?.(value);
if (diagnostic) {
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, ...diagnostic));
return undefined;
}
}
return value;
}
function reportInvalidOptionValue(isError: boolean | undefined) {
if (isError) {
errors.push(createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, Diagnostics.Compiler_option_0_requires_a_value_of_type_1, option!.name, getCompilerOptionValueTypeString(option!)));
invalidReported = true;
}
}
}
@ -2846,7 +2885,8 @@ namespace ts {
return defaultOptions;
}
function convertJsonOption(opt: CommandLineOption, value: any, basePath: string, errors: Push<Diagnostic>): CompilerOptionsValue {
/*@internal*/
export function convertJsonOption(opt: CommandLineOption, value: any, basePath: string, errors: Push<Diagnostic>): CompilerOptionsValue {
if (isCompilerOptionsValue(opt, value)) {
const optType = opt.type;
if (optType === "list" && isArray(value)) {
@ -2855,7 +2895,8 @@ namespace ts {
else if (!isString(optType)) {
return convertJsonOptionOfCustomType(<CommandLineOptionOfCustomType>opt, <string>value, errors);
}
return normalizeNonListOptionValue(opt, basePath, value);
const validatedValue = validateJsonOptionValue(opt, value, errors);
return isNullOrUndefined(validatedValue) ? validatedValue : normalizeNonListOptionValue(opt, basePath, validatedValue);
}
else {
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, opt.name, getCompilerOptionValueTypeString(opt)));
@ -2887,12 +2928,20 @@ namespace ts {
return value;
}
function validateJsonOptionValue<T extends CompilerOptionsValue>(opt: CommandLineOption, value: T, errors: Push<Diagnostic>): T | undefined {
if (isNullOrUndefined(value)) return undefined;
const d = opt.extraValidation?.(value);
if (!d) return value;
errors.push(createCompilerDiagnostic(...d));
return undefined;
}
function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Push<Diagnostic>) {
if (isNullOrUndefined(value)) return undefined;
const key = value.toLowerCase();
const val = opt.type.get(key);
if (val !== undefined) {
return val;
return validateJsonOptionValue(opt, val, errors);
}
else {
errors.push(createCompilerDiagnosticForInvalidCustomType(opt));
@ -2994,11 +3043,11 @@ namespace ts {
// file system.
if (includeSpecs) {
validatedIncludeSpecs = validateSpecs(includeSpecs, errors, /*allowTrailingRecursion*/ false, jsonSourceFile, "include");
validatedIncludeSpecs = validateSpecs(includeSpecs, errors, /*disallowTrailingRecursion*/ true, jsonSourceFile, "include");
}
if (excludeSpecs) {
validatedExcludeSpecs = validateSpecs(excludeSpecs, errors, /*allowTrailingRecursion*/ true, jsonSourceFile, "exclude");
validatedExcludeSpecs = validateSpecs(excludeSpecs, errors, /*disallowTrailingRecursion*/ false, jsonSourceFile, "exclude");
}
// Wildcard directories (provided as part of a wildcard path) are stored in a
@ -3142,19 +3191,44 @@ namespace ts {
}
}
const excludePattern = getRegularExpressionForWildcard(validatedExcludeSpecs, combinePaths(normalizePath(currentDirectory), basePath), "exclude");
return matchesExcludeWorker(pathToCheck, validatedExcludeSpecs, useCaseSensitiveFileNames, currentDirectory, basePath);
}
/* @internal */
export function matchesExclude(
pathToCheck: string,
excludeSpecs: readonly string[] | undefined,
useCaseSensitiveFileNames: boolean,
currentDirectory: string
) {
return matchesExcludeWorker(
pathToCheck,
filter(excludeSpecs, spec => !invalidDotDotAfterRecursiveWildcardPattern.test(spec)),
useCaseSensitiveFileNames,
currentDirectory
);
}
function matchesExcludeWorker(
pathToCheck: string,
excludeSpecs: readonly string[] | undefined,
useCaseSensitiveFileNames: boolean,
currentDirectory: string,
basePath?: string
) {
const excludePattern = getRegularExpressionForWildcard(excludeSpecs, combinePaths(normalizePath(currentDirectory), basePath), "exclude");
const excludeRegex = excludePattern && getRegexFromPattern(excludePattern, useCaseSensitiveFileNames);
if (!excludeRegex) return false;
if (excludeRegex.test(pathToCheck)) return true;
return !hasExtension(pathToCheck) && excludeRegex.test(ensureTrailingDirectorySeparator(pathToCheck));
}
function validateSpecs(specs: readonly string[], errors: Push<Diagnostic>, allowTrailingRecursion: boolean, jsonSourceFile: TsConfigSourceFile | undefined, specKey: string): readonly string[] {
function validateSpecs(specs: readonly string[], errors: Push<Diagnostic>, disallowTrailingRecursion: boolean, jsonSourceFile: TsConfigSourceFile | undefined, specKey: string): readonly string[] {
return specs.filter(spec => {
if (!isString(spec)) return false;
const diag = specToDiagnostic(spec, allowTrailingRecursion);
const diag = specToDiagnostic(spec, disallowTrailingRecursion);
if (diag !== undefined) {
errors.push(createDiagnostic(diag, spec));
errors.push(createDiagnostic(...diag));
}
return diag === undefined;
});
@ -3167,12 +3241,12 @@ namespace ts {
}
}
function specToDiagnostic(spec: string, allowTrailingRecursion: boolean): DiagnosticMessage | undefined {
if (!allowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) {
return Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0;
function specToDiagnostic(spec: string, disallowTrailingRecursion?: boolean): [DiagnosticMessage, string] | undefined {
if (disallowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) {
return [Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, spec];
}
else if (invalidDotDotAfterRecursiveWildcardPattern.test(spec)) {
return Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0;
return [Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, spec];
}
}

View file

@ -449,6 +449,7 @@ namespace ts {
export interface RecursiveDirectoryWatcherHost {
watchDirectory: HostWatchDirectory;
useCaseSensitiveFileNames: boolean;
getCurrentDirectory: System["getCurrentDirectory"];
getAccessibleSortedChildDirectories(path: string): readonly string[];
directoryExists(dir: string): boolean;
realpath(s: string): string;
@ -462,7 +463,16 @@ namespace ts {
* (eg on OS that dont support recursive watch using fs.watch use fs.watchFile)
*/
/*@internal*/
export function createDirectoryWatcherSupportingRecursive(host: RecursiveDirectoryWatcherHost): HostWatchDirectory {
export function createDirectoryWatcherSupportingRecursive({
watchDirectory,
useCaseSensitiveFileNames,
getCurrentDirectory,
getAccessibleSortedChildDirectories,
directoryExists,
realpath,
setTimeout,
clearTimeout
}: RecursiveDirectoryWatcherHost): HostWatchDirectory {
interface ChildDirectoryWatcher extends FileWatcher {
dirName: string;
}
@ -478,12 +488,12 @@ namespace ts {
const cacheToUpdateChildWatches = new Map<Path, { dirName: string; options: WatchOptions | undefined; fileNames: string[]; }>();
let timerToUpdateChildWatches: any;
const filePathComparer = getStringComparer(!host.useCaseSensitiveFileNames);
const toCanonicalFilePath = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
const filePathComparer = getStringComparer(!useCaseSensitiveFileNames);
const toCanonicalFilePath = createGetCanonicalFileName(useCaseSensitiveFileNames);
return (dirName, callback, recursive, options) => recursive ?
createDirectoryWatcher(dirName, options, callback) :
host.watchDirectory(dirName, callback, recursive, options);
watchDirectory(dirName, callback, recursive, options);
/**
* Create the directory watcher for the dirPath.
@ -496,8 +506,8 @@ namespace ts {
}
else {
directoryWatcher = {
watcher: host.watchDirectory(dirName, fileName => {
if (isIgnoredPath(fileName)) return;
watcher: watchDirectory(dirName, fileName => {
if (isIgnoredPath(fileName, options)) return;
if (options?.synchronousWatchDirectory) {
// Call the actual callback
@ -578,7 +588,7 @@ namespace ts {
function nonSyncUpdateChildWatches(dirName: string, dirPath: Path, fileName: string, options: WatchOptions | undefined) {
// Iterate through existing children and update the watches if needed
const parentWatcher = cache.get(dirPath);
if (parentWatcher && host.directoryExists(dirName)) {
if (parentWatcher && directoryExists(dirName)) {
// Schedule the update and postpone invoke for callbacks
scheduleUpdateChildWatches(dirName, dirPath, fileName, options);
return;
@ -598,10 +608,10 @@ namespace ts {
cacheToUpdateChildWatches.set(dirPath, { dirName, options, fileNames: [fileName] });
}
if (timerToUpdateChildWatches) {
host.clearTimeout(timerToUpdateChildWatches);
clearTimeout(timerToUpdateChildWatches);
timerToUpdateChildWatches = undefined;
}
timerToUpdateChildWatches = host.setTimeout(onTimerToUpdateChildWatches, 1000);
timerToUpdateChildWatches = setTimeout(onTimerToUpdateChildWatches, 1000);
}
function onTimerToUpdateChildWatches() {
@ -620,7 +630,7 @@ namespace ts {
invokeCallbacks(dirPath, invokeMap, hasChanges ? undefined : fileNames);
}
sysLog(`sysLog:: invokingWatchers:: ${timestamp() - start}ms:: ${cacheToUpdateChildWatches.size}`);
sysLog(`sysLog:: invokingWatchers:: Elapsed:: ${timestamp() - start}ms:: ${cacheToUpdateChildWatches.size}`);
callbackCache.forEach((callbacks, rootDirName) => {
const existing = invokeMap.get(rootDirName);
if (existing) {
@ -636,7 +646,7 @@ namespace ts {
});
const elapsed = timestamp() - start;
sysLog(`sysLog:: Elapsed ${elapsed}ms:: onTimerToUpdateChildWatches:: ${cacheToUpdateChildWatches.size} ${timerToUpdateChildWatches}`);
sysLog(`sysLog:: Elapsed:: ${elapsed}ms:: onTimerToUpdateChildWatches:: ${cacheToUpdateChildWatches.size} ${timerToUpdateChildWatches}`);
}
function removeChildWatches(parentWatcher: HostDirectoryWatcher | undefined) {
@ -655,11 +665,11 @@ namespace ts {
if (!parentWatcher) return false;
let newChildWatches: ChildDirectoryWatcher[] | undefined;
const hasChanges = enumerateInsertsAndDeletes<string, ChildDirectoryWatcher>(
host.directoryExists(parentDir) ? mapDefined(host.getAccessibleSortedChildDirectories(parentDir), child => {
directoryExists(parentDir) ? mapDefined(getAccessibleSortedChildDirectories(parentDir), child => {
const childFullName = getNormalizedAbsolutePath(child, parentDir);
// Filter our the symbolic link directories since those arent included in recursive watch
// which is same behaviour when recursive: true is passed to fs.watch
return !isIgnoredPath(childFullName) && filePathComparer(childFullName, normalizePath(host.realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined;
return !isIgnoredPath(childFullName, options) && filePathComparer(childFullName, normalizePath(realpath(childFullName))) === Comparison.EqualTo ? childFullName : undefined;
}) : emptyArray,
parentWatcher.childWatches,
(child, childWatcher) => filePathComparer(child, childWatcher.dirName),
@ -686,13 +696,14 @@ namespace ts {
}
}
function isIgnoredPath(path: string) {
return some(ignoredPaths, searchPath => isInPath(path, searchPath));
function isIgnoredPath(path: string, options: WatchOptions | undefined) {
return some(ignoredPaths, searchPath => isInPath(path, searchPath)) ||
isIgnoredByWatchOptions(path, options, useCaseSensitiveFileNames, getCurrentDirectory);
}
function isInPath(path: string, searchPath: string) {
if (stringContains(path, searchPath)) return true;
if (host.useCaseSensitiveFileNames) return false;
if (useCaseSensitiveFileNames) return false;
return stringContains(toCanonicalFilePath(path), searchPath);
}
}
@ -729,14 +740,35 @@ namespace ts {
};
}
function createFsWatchCallbackForDirectoryWatcherCallback(directoryName: string, callback: DirectoryWatcherCallback): FsWatchCallback {
function isIgnoredByWatchOptions(
pathToCheck: string,
options: WatchOptions | undefined,
useCaseSensitiveFileNames: boolean,
getCurrentDirectory: System["getCurrentDirectory"],
) {
return (options?.excludeDirectories || options?.excludeFiles) && (
matchesExclude(pathToCheck, options?.excludeFiles, useCaseSensitiveFileNames, getCurrentDirectory()) ||
matchesExclude(pathToCheck, options?.excludeDirectories, useCaseSensitiveFileNames, getCurrentDirectory())
);
}
function createFsWatchCallbackForDirectoryWatcherCallback(
directoryName: string,
callback: DirectoryWatcherCallback,
options: WatchOptions | undefined,
useCaseSensitiveFileNames: boolean,
getCurrentDirectory: System["getCurrentDirectory"],
): FsWatchCallback {
return (eventName, relativeFileName) => {
// In watchDirectory we only care about adding and removing files (when event name is
// "rename"); changes made within files are handled by corresponding fileWatchers (when
// event name is "change")
if (eventName === "rename") {
// When deleting a file, the passed baseFileName is null
callback(!relativeFileName ? directoryName : normalizePath(combinePaths(directoryName, relativeFileName)));
const fileName = !relativeFileName ? directoryName : normalizePath(combinePaths(directoryName, relativeFileName));
if (!relativeFileName || !isIgnoredByWatchOptions(fileName, options, useCaseSensitiveFileNames, getCurrentDirectory)) {
callback(fileName);
}
}
};
}
@ -753,6 +785,7 @@ namespace ts {
fsWatch: FsWatch;
fileExists: System["fileExists"];
useCaseSensitiveFileNames: boolean;
getCurrentDirectory: System["getCurrentDirectory"];
fsSupportsRecursiveFsWatch: boolean;
directoryExists: System["directoryExists"];
getAccessibleSortedChildDirectories(path: string): readonly string[];
@ -772,6 +805,7 @@ namespace ts {
fsWatch,
fileExists,
useCaseSensitiveFileNames,
getCurrentDirectory,
fsSupportsRecursiveFsWatch,
directoryExists,
getAccessibleSortedChildDirectories,
@ -868,7 +902,7 @@ namespace ts {
return fsWatch(
directoryName,
FileSystemEntryKind.Directory,
createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback),
createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback, options, useCaseSensitiveFileNames, getCurrentDirectory),
recursive,
PollingInterval.Medium,
getFallbackOptions(options)
@ -878,6 +912,7 @@ namespace ts {
if (!hostRecursiveDirectoryWatcher) {
hostRecursiveDirectoryWatcher = createDirectoryWatcherSupportingRecursive({
useCaseSensitiveFileNames,
getCurrentDirectory,
directoryExists,
getAccessibleSortedChildDirectories,
watchDirectory: nonRecursiveWatchDirectory,
@ -891,8 +926,8 @@ namespace ts {
function nonRecursiveWatchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean, options: WatchOptions | undefined): FileWatcher {
Debug.assert(!recursive);
options = updateOptionsForWatchDirectory(options);
const watchDirectoryKind = Debug.checkDefined(options.watchDirectory);
const watchDirectoryOptions = updateOptionsForWatchDirectory(options);
const watchDirectoryKind = Debug.checkDefined(watchDirectoryOptions.watchDirectory);
switch (watchDirectoryKind) {
case WatchDirectoryKind.FixedPollingInterval:
return pollingWatchFile(
@ -912,10 +947,10 @@ namespace ts {
return fsWatch(
directoryName,
FileSystemEntryKind.Directory,
createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback),
createFsWatchCallbackForDirectoryWatcherCallback(directoryName, callback, options, useCaseSensitiveFileNames, getCurrentDirectory),
recursive,
PollingInterval.Medium,
getFallbackOptions(options)
getFallbackOptions(watchDirectoryOptions)
);
default:
Debug.assertNever(watchDirectoryKind);
@ -1161,6 +1196,7 @@ namespace ts {
const platform: string = _os.platform();
const useCaseSensitiveFileNames = isFileSystemCaseSensitive();
const fsSupportsRecursiveFsWatch = isNode4OrLater && (process.platform === "win32" || process.platform === "darwin");
const getCurrentDirectory = memoize(() => process.cwd());
const { watchFile, watchDirectory } = createSystemWatchFunctions({
pollingWatchFile: createSingleFileWatcherPerName(fsWatchFileWorker, useCaseSensitiveFileNames),
getModifiedTime,
@ -1168,6 +1204,7 @@ namespace ts {
clearTimeout,
fsWatch,
useCaseSensitiveFileNames,
getCurrentDirectory,
fileExists,
// Node 4.0 `fs.watch` function supports the "recursive" option on both OSX and Windows
// (ref: https://github.com/nodejs/node/pull/2649 and https://github.com/Microsoft/TypeScript/issues/4643)
@ -1214,9 +1251,7 @@ namespace ts {
getExecutingFilePath() {
return __filename;
},
getCurrentDirectory() {
return process.cwd();
},
getCurrentDirectory,
getDirectories,
getEnvironmentVariable(name: string) {
return process.env[name] || "";

View file

@ -209,7 +209,7 @@ namespace ts {
originalGetSourceFile: CompilerHost["getSourceFile"];
}
interface SolutionBuilderState<T extends BuilderProgram = BuilderProgram> {
interface SolutionBuilderState<T extends BuilderProgram = BuilderProgram> extends WatchFactory<WatchType, ResolvedConfigFileName> {
readonly host: SolutionBuilderHost<T>;
readonly hostWithWatch: SolutionBuilderWithWatchHost<T>;
readonly currentDirectory: string;
@ -256,9 +256,6 @@ namespace ts {
timerToBuildInvalidatedProject: any;
reportFileChangeDetected: boolean;
watchFile: WatchFile<WatchType, ResolvedConfigFileName>;
watchFilePath: WatchFilePath<WatchType, ResolvedConfigFileName>;
watchDirectory: WatchDirectory<WatchType, ResolvedConfigFileName>;
writeLog: (s: string) => void;
}
@ -282,7 +279,7 @@ namespace ts {
loadWithLocalCache<ResolvedModuleFull>(Debug.checkEachDefined(moduleNames), containingFile, redirectedReference, loader);
}
const { watchFile, watchFilePath, watchDirectory, writeLog } = createWatchFactory<ResolvedConfigFileName>(hostWithWatch, options);
const { watchFile, watchDirectory, writeLog } = createWatchFactory<ResolvedConfigFileName>(hostWithWatch, options);
const state: SolutionBuilderState<T> = {
host,
@ -331,7 +328,6 @@ namespace ts {
timerToBuildInvalidatedProject: undefined,
reportFileChangeDetected: false,
watchFile,
watchFilePath,
watchDirectory,
writeLog,
};
@ -1783,7 +1779,6 @@ namespace ts {
function watchConfigFile(state: SolutionBuilderState, resolved: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath, parsed: ParsedCommandLine | undefined) {
if (!state.watch || state.allWatchedConfigFiles.has(resolvedPath)) return;
state.allWatchedConfigFiles.set(resolvedPath, state.watchFile(
state.hostWithWatch,
resolved,
() => {
invalidateProjectAndScheduleBuilds(state, resolvedPath, ConfigFileProgramReloadLevel.Full);
@ -1801,7 +1796,6 @@ namespace ts {
getOrCreateValueMapFromConfigFileMap(state.allWatchedWildcardDirectories, resolvedPath),
new Map(getEntries(parsed.configFileSpecs!.wildcardDirectories)),
(dir, flags) => state.watchDirectory(
state.hostWithWatch,
dir,
fileOrDirectory => {
if (isIgnoredFileFromWildCardWatching({
@ -1833,13 +1827,11 @@ namespace ts {
getOrCreateValueMapFromConfigFileMap(state.allWatchedInputFiles, resolvedPath),
arrayToMap(parsed.fileNames, fileName => toPath(state, fileName)),
{
createNewValue: (path, input) => state.watchFilePath(
state.hostWithWatch,
createNewValue: (_path, input) => state.watchFile(
input,
() => invalidateProjectAndScheduleBuilds(state, resolvedPath, ConfigFileProgramReloadLevel.None),
PollingInterval.Low,
parsed?.watchOptions,
path as Path,
WatchType.SourceFile,
resolved
),
@ -1914,9 +1906,7 @@ namespace ts {
}
function reportWatchStatus(state: SolutionBuilderState, message: DiagnosticMessage, ...args: (string | number | undefined)[]) {
if (state.hostWithWatch.onWatchStatusChange) {
state.hostWithWatch.onWatchStatusChange(createCompilerDiagnostic(message, ...args), state.host.getNewLine(), state.baseCompilerOptions);
}
state.hostWithWatch.onWatchStatusChange?.(createCompilerDiagnostic(message, ...args), state.host.getNewLine(), state.baseCompilerOptions);
}
function reportErrors({ host }: SolutionBuilderState, errors: readonly Diagnostic[]) {

View file

@ -5846,6 +5846,8 @@ namespace ts {
watchDirectory?: WatchDirectoryKind;
fallbackPolling?: PollingWatchKind;
synchronousWatchDirectory?: boolean;
excludeDirectories?: string[];
excludeFiles?: string[];
[option: string]: CompilerOptionsValue | undefined;
}
@ -6016,6 +6018,7 @@ namespace ts {
affectsSemanticDiagnostics?: true; // true if option affects semantic diagnostics
affectsEmit?: true; // true if the options affects emit
transpileOptionValue?: boolean | undefined; // If set this means that the option should be set to this value when transpiling
extraValidation?: (value: CompilerOptionsValue) => [DiagnosticMessage, ...string[]] | undefined; // Additional validation to be performed for the value to be valid
}
/* @internal */

View file

@ -270,10 +270,10 @@ namespace ts {
writeLog: (s: string) => void;
}
export function createWatchFactory<Y = undefined>(host: { trace?(s: string): void; }, options: { extendedDiagnostics?: boolean; diagnostics?: boolean; }) {
export function createWatchFactory<Y = undefined>(host: WatchFactoryHost & { trace?(s: string): void; }, options: { extendedDiagnostics?: boolean; diagnostics?: boolean; }) {
const watchLogLevel = host.trace ? options.extendedDiagnostics ? WatchLogLevel.Verbose : options.diagnostics ? WatchLogLevel.TriggerOnly : WatchLogLevel.None : WatchLogLevel.None;
const writeLog: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? (s => host.trace!(s)) : noop;
const result = getWatchFactory<WatchType, Y>(watchLogLevel, writeLog) as WatchFactory<WatchType, Y>;
const result = getWatchFactory<WatchType, Y>(host, watchLogLevel, writeLog) as WatchFactory<WatchType, Y>;
result.writeLog = writeLog;
return result;
}

View file

@ -283,13 +283,13 @@ namespace ts {
newLine = updateNewLine();
}
const { watchFile, watchFilePath, watchDirectory, writeLog } = createWatchFactory<string>(host, compilerOptions);
const { watchFile, watchDirectory, writeLog } = createWatchFactory(host, compilerOptions);
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
writeLog(`Current directory: ${currentDirectory} CaseSensitiveFileNames: ${useCaseSensitiveFileNames}`);
let configFileWatcher: FileWatcher | undefined;
if (configFileName) {
configFileWatcher = watchFile(host, configFileName, scheduleProgramReload, PollingInterval.High, watchOptions, WatchType.ConfigFile);
configFileWatcher = watchFile(configFileName, scheduleProgramReload, PollingInterval.High, watchOptions, WatchType.ConfigFile);
}
const compilerHost = createCompilerHostFromProgramHost(host, () => compilerOptions, directoryStructureHost) as CompilerHost & ResolutionCacheHost;
@ -305,8 +305,8 @@ namespace ts {
compilerHost.toPath = toPath;
compilerHost.getCompilationSettings = () => compilerOptions;
compilerHost.useSourceOfProjectReferenceRedirect = maybeBind(host, host.useSourceOfProjectReferenceRedirect);
compilerHost.watchDirectoryOfFailedLookupLocation = (dir, cb, flags) => watchDirectory(host, dir, cb, flags, watchOptions, WatchType.FailedLookupLocations);
compilerHost.watchTypeRootsDirectory = (dir, cb, flags) => watchDirectory(host, dir, cb, flags, watchOptions, WatchType.TypeRoots);
compilerHost.watchDirectoryOfFailedLookupLocation = (dir, cb, flags) => watchDirectory(dir, cb, flags, watchOptions, WatchType.FailedLookupLocations);
compilerHost.watchTypeRootsDirectory = (dir, cb, flags) => watchDirectory(dir, cb, flags, watchOptions, WatchType.TypeRoots);
compilerHost.getCachedDirectoryStructureHost = () => cachedDirectoryStructureHost;
compilerHost.scheduleInvalidateResolutionsOfFailedLookupLocations = scheduleInvalidateResolutionsOfFailedLookupLocations;
compilerHost.onInvalidatedResolution = scheduleProgramUpdate;
@ -488,7 +488,7 @@ namespace ts {
(hostSourceFile as FilePresentOnHost).sourceFile = sourceFile;
hostSourceFile.version = sourceFile.version;
if (!hostSourceFile.fileWatcher) {
hostSourceFile.fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, watchOptions, path, WatchType.SourceFile);
hostSourceFile.fileWatcher = watchFilePath(path, fileName, onSourceFileChange, PollingInterval.Low, watchOptions, WatchType.SourceFile);
}
}
else {
@ -501,7 +501,7 @@ namespace ts {
}
else {
if (sourceFile) {
const fileWatcher = watchFilePath(host, fileName, onSourceFileChange, PollingInterval.Low, watchOptions, path, WatchType.SourceFile);
const fileWatcher = watchFilePath(path, fileName, onSourceFileChange, PollingInterval.Low, watchOptions, WatchType.SourceFile);
sourceFilesCache.set(path, { sourceFile, version: sourceFile.version, fileWatcher });
}
else {
@ -675,6 +675,17 @@ namespace ts {
hasChangedConfigFileParsingErrors = true;
}
function watchFilePath(
path: Path,
file: string,
callback: (fileName: string, eventKind: FileWatcherEventKind, filePath: Path) => void,
pollingInterval: PollingInterval,
options: WatchOptions | undefined,
watchType: WatchType
): FileWatcher {
return watchFile(file, (fileName, eventKind) => callback(fileName, eventKind, path), pollingInterval, options, watchType);
}
function onSourceFileChange(fileName: string, eventKind: FileWatcherEventKind, path: Path) {
updateCachedSystemWithFile(fileName, path, eventKind);
@ -696,7 +707,7 @@ namespace ts {
}
function watchMissingFilePath(missingFilePath: Path) {
return watchFilePath(host, missingFilePath, onMissingFileChange, PollingInterval.Medium, watchOptions, missingFilePath, WatchType.MissingFile);
return watchFilePath(missingFilePath, missingFilePath, onMissingFileChange, PollingInterval.Medium, watchOptions, WatchType.MissingFile);
}
function onMissingFileChange(fileName: string, eventKind: FileWatcherEventKind, missingFilePath: Path) {
@ -729,7 +740,6 @@ namespace ts {
function watchWildcardDirectory(directory: string, flags: WatchDirectoryFlags) {
return watchDirectory(
host,
directory,
fileOrDirectory => {
Debug.assert(!!configFileName);

View file

@ -421,117 +421,143 @@ namespace ts {
Verbose
}
export interface WatchFileHost {
export interface WatchFactoryHost {
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher;
}
export interface WatchDirectoryHost {
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher;
}
export type WatchFile<X, Y> = (host: WatchFileHost, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined, detailInfo1: X, detailInfo2?: Y) => FileWatcher;
export type FilePathWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, filePath: Path) => void;
export type WatchFilePath<X, Y> = (host: WatchFileHost, file: string, callback: FilePathWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined, path: Path, detailInfo1: X, detailInfo2?: Y) => FileWatcher;
export type WatchDirectory<X, Y> = (host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, options: WatchOptions | undefined, detailInfo1: X, detailInfo2?: Y) => FileWatcher;
export interface WatchFactory<X, Y> {
watchFile: WatchFile<X, Y>;
watchFilePath: WatchFilePath<X, Y>;
watchDirectory: WatchDirectory<X, Y>;
getCurrentDirectory?(): string;
useCaseSensitiveFileNames: boolean | (() => boolean);
}
export function getWatchFactory<X, Y = undefined>(watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo<X, Y>): WatchFactory<X, Y> {
return getWatchFactoryWith(watchLogLevel, log, getDetailWatchInfo, watchFile, watchDirectory);
export interface WatchFactory<X, Y = undefined> {
watchFile: (file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined, detailInfo1: X, detailInfo2?: Y) => FileWatcher;
watchDirectory: (directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, options: WatchOptions | undefined, detailInfo1: X, detailInfo2?: Y) => FileWatcher;
}
function getWatchFactoryWith<X, Y = undefined>(
watchLogLevel: WatchLogLevel,
log: (s: string) => void,
getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined,
watchFile: (host: WatchFileHost, file: string, callback: FileWatcherCallback, watchPriority: PollingInterval, options: WatchOptions | undefined) => FileWatcher,
watchDirectory: (host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, options: WatchOptions | undefined) => FileWatcher
): WatchFactory<X, Y> {
const createFileWatcher: CreateFileWatcher<WatchFileHost, PollingInterval, FileWatcherEventKind, never, X, Y> = getCreateFileWatcher(watchLogLevel, watchFile);
const createFilePathWatcher: CreateFileWatcher<WatchFileHost, PollingInterval, FileWatcherEventKind, Path, X, Y> = watchLogLevel === WatchLogLevel.None ? watchFilePath : createFileWatcher;
const createDirectoryWatcher: CreateFileWatcher<WatchDirectoryHost, WatchDirectoryFlags, undefined, never, X, Y> = getCreateFileWatcher(watchLogLevel, watchDirectory);
if (watchLogLevel === WatchLogLevel.Verbose && sysLog === noop) {
setSysLog(s => log(s));
}
return {
watchFile: (host, file, callback, pollingInterval, options, detailInfo1, detailInfo2) =>
createFileWatcher(host, file, callback, pollingInterval, options, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchFile, log, "FileWatcher", getDetailWatchInfo),
watchFilePath: (host, file, callback, pollingInterval, options, path, detailInfo1, detailInfo2) =>
createFilePathWatcher(host, file, callback, pollingInterval, options, path, detailInfo1, detailInfo2, watchFile, log, "FileWatcher", getDetailWatchInfo),
watchDirectory: (host, directory, callback, flags, options, detailInfo1, detailInfo2) =>
createDirectoryWatcher(host, directory, callback, flags, options, /*passThrough*/ undefined, detailInfo1, detailInfo2, watchDirectory, log, "DirectoryWatcher", getDetailWatchInfo)
};
}
function watchFile(host: WatchFileHost, file: string, callback: FileWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined): FileWatcher {
return host.watchFile(file, callback, pollingInterval, options);
}
function watchFilePath(host: WatchFileHost, file: string, callback: FilePathWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined, path: Path): FileWatcher {
return watchFile(host, file, (fileName, eventKind) => callback(fileName, eventKind, path), pollingInterval, options);
}
function watchDirectory(host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, options: WatchOptions | undefined): FileWatcher {
return host.watchDirectory(directory, callback, (flags & WatchDirectoryFlags.Recursive) !== 0, options);
}
type WatchCallback<T, U> = (fileName: string, cbOptional?: T, passThrough?: U) => void;
type AddWatch<H, T, U, V> = (host: H, file: string, cb: WatchCallback<U, V>, flags: T, options: WatchOptions | undefined, passThrough?: V, detailInfo1?: undefined, detailInfo2?: undefined) => FileWatcher;
export type GetDetailWatchInfo<X, Y> = (detailInfo1: X, detailInfo2: Y | undefined) => string;
export function getWatchFactory<X, Y = undefined>(host: WatchFactoryHost, watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo<X, Y>): WatchFactory<X, Y> {
setSysLog(watchLogLevel === WatchLogLevel.Verbose ? log : noop);
const plainInvokeFactory: WatchFactory<X, Y> = {
watchFile: (file, callback, pollingInterval, options) => host.watchFile(file, callback, pollingInterval, options),
watchDirectory: (directory, callback, flags, options) => host.watchDirectory(directory, callback, (flags & WatchDirectoryFlags.Recursive) !== 0, options),
};
const triggerInvokingFactory: WatchFactory<X, Y> | undefined = watchLogLevel !== WatchLogLevel.None ?
{
watchFile: createTriggerLoggingAddWatch("watchFile"),
watchDirectory: createTriggerLoggingAddWatch("watchDirectory")
} :
undefined;
const factory = watchLogLevel === WatchLogLevel.Verbose ?
{
watchFile: createFileWatcherWithLogging,
watchDirectory: createDirectoryWatcherWithLogging
} :
triggerInvokingFactory || plainInvokeFactory;
const excludeWatcherFactory = watchLogLevel === WatchLogLevel.Verbose ?
createExcludeWatcherWithLogging :
returnNoopFileWatcher;
type CreateFileWatcher<H, T, U, V, X, Y> = (host: H, file: string, cb: WatchCallback<U, V>, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch<H, T, U, V>, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined) => FileWatcher;
function getCreateFileWatcher<H, T, U, V, X, Y>(watchLogLevel: WatchLogLevel, addWatch: AddWatch<H, T, U, V>): CreateFileWatcher<H, T, U, V, X, Y> {
switch (watchLogLevel) {
case WatchLogLevel.None:
return addWatch;
case WatchLogLevel.TriggerOnly:
return createFileWatcherWithTriggerLogging;
case WatchLogLevel.Verbose:
return addWatch === <any>watchDirectory ? createDirectoryWatcherWithLogging : createFileWatcherWithLogging;
return {
watchFile: createExcludeHandlingAddWatch("watchFile"),
watchDirectory: createExcludeHandlingAddWatch("watchDirectory")
};
function createExcludeHandlingAddWatch<T extends keyof WatchFactory<X, Y>>(key: T): WatchFactory<X, Y>[T] {
return (
file: string,
cb: FileWatcherCallback | DirectoryWatcherCallback,
flags: PollingInterval | WatchDirectoryFlags,
options: WatchOptions | undefined,
detailInfo1: X,
detailInfo2?: Y
) => !matchesExclude(file, key === "watchFile" ? options?.excludeFiles : options?.excludeDirectories, useCaseSensitiveFileNames(), host.getCurrentDirectory?.() || "") ?
factory[key].call(/*thisArgs*/ undefined, file, cb, flags, options, detailInfo1, detailInfo2) :
excludeWatcherFactory(file, flags, options, detailInfo1, detailInfo2);
}
}
function createFileWatcherWithLogging<H, T, U, V, X, Y>(host: H, file: string, cb: WatchCallback<U, V>, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch<H, T, U, V>, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined): FileWatcher {
log(`${watchCaption}:: Added:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`);
const watcher = createFileWatcherWithTriggerLogging(host, file, cb, flags, options, passThrough, detailInfo1, detailInfo2, addWatch, log, watchCaption, getDetailWatchInfo);
return {
close: () => {
log(`${watchCaption}:: Close:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`);
watcher.close();
}
};
}
function useCaseSensitiveFileNames() {
return typeof host.useCaseSensitiveFileNames === "boolean" ?
host.useCaseSensitiveFileNames :
host.useCaseSensitiveFileNames();
}
function createDirectoryWatcherWithLogging<H, T, U, V, X, Y>(host: H, file: string, cb: WatchCallback<U, V>, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch<H, T, U, V>, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined): FileWatcher {
const watchInfo = `${watchCaption}:: Added:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`;
log(watchInfo);
const start = timestamp();
const watcher = createFileWatcherWithTriggerLogging(host, file, cb, flags, options, passThrough, detailInfo1, detailInfo2, addWatch, log, watchCaption, getDetailWatchInfo);
const elapsed = timestamp() - start;
log(`Elapsed:: ${elapsed}ms ${watchInfo}`);
return {
close: () => {
const watchInfo = `${watchCaption}:: Close:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`;
log(watchInfo);
const start = timestamp();
watcher.close();
const elapsed = timestamp() - start;
log(`Elapsed:: ${elapsed}ms ${watchInfo}`);
}
};
}
function createExcludeWatcherWithLogging(
file: string,
flags: PollingInterval | WatchDirectoryFlags,
options: WatchOptions | undefined,
detailInfo1: X,
detailInfo2?: Y
) {
log(`ExcludeWatcher:: Added:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`);
return {
close: () => log(`ExcludeWatcher:: Close:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`)
};
}
function createFileWatcherWithTriggerLogging<H, T, U, V, X, Y>(host: H, file: string, cb: WatchCallback<U, V>, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch<H, T, U, V>, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined): FileWatcher {
return addWatch(host, file, (fileName, cbOptional) => {
const triggerredInfo = `${watchCaption}:: Triggered with ${fileName} ${cbOptional !== undefined ? cbOptional : ""}:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`;
log(triggerredInfo);
function createFileWatcherWithLogging(
file: string,
cb: FileWatcherCallback,
flags: PollingInterval,
options: WatchOptions | undefined,
detailInfo1: X,
detailInfo2?: Y
): FileWatcher {
log(`FileWatcher:: Added:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`);
const watcher = triggerInvokingFactory!.watchFile(file, cb, flags, options, detailInfo1, detailInfo2);
return {
close: () => {
log(`FileWatcher:: Close:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`);
watcher.close();
}
};
}
function createDirectoryWatcherWithLogging(
file: string,
cb: DirectoryWatcherCallback,
flags: WatchDirectoryFlags,
options: WatchOptions | undefined,
detailInfo1: X,
detailInfo2?: Y
): FileWatcher {
const watchInfo = `DirectoryWatcher:: Added:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`;
log(watchInfo);
const start = timestamp();
cb(fileName, cbOptional, passThrough);
const watcher = triggerInvokingFactory!.watchDirectory(file, cb, flags, options, detailInfo1, detailInfo2);
const elapsed = timestamp() - start;
log(`Elapsed:: ${elapsed}ms ${triggerredInfo}`);
}, flags, options);
log(`Elapsed:: ${elapsed}ms ${watchInfo}`);
return {
close: () => {
const watchInfo = `DirectoryWatcher:: Close:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`;
log(watchInfo);
const start = timestamp();
watcher.close();
const elapsed = timestamp() - start;
log(`Elapsed:: ${elapsed}ms ${watchInfo}`);
}
};
}
function createTriggerLoggingAddWatch<T extends keyof WatchFactory<X, Y>>(key: T): WatchFactory<X, Y>[T] {
return (
file: string,
cb: FileWatcherCallback | DirectoryWatcherCallback,
flags: PollingInterval | WatchDirectoryFlags,
options: WatchOptions | undefined,
detailInfo1: X,
detailInfo2?: Y
) => plainInvokeFactory[key].call(/*thisArgs*/ undefined, file, (...args: any[]) => {
const triggerredInfo = `${key === "watchFile" ? "FileWatcher" : "DirectoryWatcher"}:: Triggered with ${args[0]} ${args[1] !== undefined ? args[1] : ""}:: ${getWatchInfo(file, flags, options, detailInfo1, detailInfo2, getDetailWatchInfo)}`;
log(triggerredInfo);
const start = timestamp();
cb.call(/*thisArg*/ undefined, ...args);
const elapsed = timestamp() - start;
log(`Elapsed:: ${elapsed}ms ${triggerredInfo}`);
}, flags, options, detailInfo1, detailInfo2);
}
function getWatchInfo<T>(file: string, flags: T, options: WatchOptions | undefined, detailInfo1: X, detailInfo2: Y | undefined, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined) {
return `WatchInfo: ${file} ${flags} ${JSON.stringify(options)} ${getDetailWatchInfo ? getDetailWatchInfo(detailInfo1, detailInfo2) : detailInfo2 === undefined ? detailInfo1 : `${detailInfo1} ${detailInfo2}`}`;
}
}
export function getFallbackOptions(options: WatchOptions | undefined): WatchOptions {
@ -543,10 +569,6 @@ namespace ts {
};
}
function getWatchInfo<T, X, Y>(file: string, flags: T, options: WatchOptions | undefined, detailInfo1: X, detailInfo2: Y | undefined, getDetailWatchInfo: GetDetailWatchInfo<X, Y> | undefined) {
return `WatchInfo: ${file} ${flags} ${JSON.stringify(options)} ${getDetailWatchInfo ? getDetailWatchInfo(detailInfo1, detailInfo2) : detailInfo2 === undefined ? detailInfo1 : `${detailInfo1} ${detailInfo2}`}`;
}
export function closeFileWatcherOf<T extends { watcher: FileWatcher; }>(objWithWatcher: T) {
objWithWatcher.watcher.close();
}

View file

@ -251,7 +251,7 @@ interface Array<T> { length: number; [n: number]: T; }`
else {
recursiveOrExpectedDetails = recursiveOrEachDirectoryWatchCount as boolean;
checkMap(
`fsWatches{recursive ? " recursive" : ""}`,
`fsWatches${recursiveOrExpectedDetails ? " recursive" : ""}`,
recursiveOrExpectedDetails ? host.fsWatchesRecursive : host.fsWatches,
expectedDirectories,
[expectedDetails, ({ directoryName, fallbackPollingInterval, fallbackOptions }) => ({ directoryName, fallbackPollingInterval, fallbackOptions })]
@ -433,6 +433,7 @@ interface Array<T> { length: number; [n: number]: T; }`
fsWatch: this.fsWatch.bind(this),
fileExists: this.fileExists.bind(this),
useCaseSensitiveFileNames: this.useCaseSensitiveFileNames,
getCurrentDirectory: this.getCurrentDirectory.bind(this),
fsSupportsRecursiveFsWatch: tscWatchDirectory ? false : !runWithoutRecursiveWatches,
directoryExists: this.directoryExists.bind(this),
getAccessibleSortedChildDirectories: path => this.getDirectories(path),

View file

@ -83,8 +83,9 @@ declare namespace ts.server {
useCaseSensitiveFileNames: boolean;
writeFile(path: string, content: string): void;
createDirectory(path: string): void;
watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: CompilerOptions): FileWatcher;
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: CompilerOptions): FileWatcher;
getCurrentDirectory?(): string;
watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher;
watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher;
}
export interface SetTypings extends ProjectResponse {

View file

@ -11,6 +11,7 @@ namespace ts.server {
export const ProjectLanguageServiceStateEvent = "projectLanguageServiceState";
export const ProjectInfoTelemetryEvent = "projectInfo";
export const OpenFileInfoTelemetryEvent = "openFileInfo";
const ensureProjectForOpenFileSchedule = "*ensureProjectForOpenFiles*";
export interface ProjectsUpdatedInBackgroundEvent {
eventName: typeof ProjectsUpdatedInBackgroundEvent;
@ -251,16 +252,18 @@ namespace ts.server {
return <any>protocolOptions;
}
export function convertWatchOptions(protocolOptions: protocol.ExternalProjectCompilerOptions): WatchOptions | undefined {
let result: WatchOptions | undefined;
watchOptionsConverters.forEach((mappedValues, id) => {
const propertyValue = protocolOptions[id];
export function convertWatchOptions(protocolOptions: protocol.ExternalProjectCompilerOptions, currentDirectory?: string): WatchOptionsAndErrors | undefined {
let watchOptions: WatchOptions | undefined;
let errors: Diagnostic[] | undefined;
optionsForWatch.forEach(option => {
const propertyValue = protocolOptions[option.name];
if (propertyValue === undefined) return;
(result || (result = {}))[id] = isString(propertyValue) ?
mappedValues.get(propertyValue.toLowerCase()) :
propertyValue;
const mappedValues = watchOptionsConverters.get(option.name);
(watchOptions || (watchOptions = {}))[option.name] = mappedValues ?
isString(propertyValue) ? mappedValues.get(propertyValue.toLowerCase()) : propertyValue :
convertJsonOption(option, propertyValue, currentDirectory || "", errors || (errors = []));
});
return result;
return watchOptions && { watchOptions, errors };
}
export function convertTypeAcquisition(protocolOptions: protocol.InferredProjectCompilerOptions): TypeAcquisition | undefined {
@ -633,6 +636,11 @@ namespace ts.server {
changes: Iterator<TextChange>;
}
export interface WatchOptionsAndErrors {
watchOptions: WatchOptions;
errors: Diagnostic[] | undefined;
}
export class ProjectService {
/*@internal*/
@ -691,8 +699,8 @@ namespace ts.server {
private compilerOptionsForInferredProjects: CompilerOptions | undefined;
private compilerOptionsForInferredProjectsPerProjectRoot = new Map<string, CompilerOptions>();
private watchOptionsForInferredProjects: WatchOptions | undefined;
private watchOptionsForInferredProjectsPerProjectRoot = new Map<string, WatchOptions | false>();
private watchOptionsForInferredProjects: WatchOptionsAndErrors | undefined;
private watchOptionsForInferredProjectsPerProjectRoot = new Map<string, WatchOptionsAndErrors | false>();
private typeAcquisitionForInferredProjects: TypeAcquisition | undefined;
private typeAcquisitionForInferredProjectsPerProjectRoot = new Map<string, TypeAcquisition | undefined>();
/**
@ -819,10 +827,9 @@ namespace ts.server {
this.watchFactory = this.serverMode !== LanguageServiceMode.Semantic ?
{
watchFile: returnNoopFileWatcher,
watchFilePath: returnNoopFileWatcher,
watchDirectory: returnNoopFileWatcher,
} :
getWatchFactory(watchLogLevel, log, getDetailWatchInfo);
getWatchFactory(this.host, watchLogLevel, log, getDetailWatchInfo);
}
toPath(fileName: string) {
@ -924,7 +931,7 @@ namespace ts.server {
/*@internal*/
delayEnsureProjectForOpenFiles() {
this.pendingEnsureProjectForOpenFiles = true;
this.throttledOperations.schedule("*ensureProjectForOpenFiles*", /*delay*/ 2500, () => {
this.throttledOperations.schedule(ensureProjectForOpenFileSchedule, /*delay*/ 2500, () => {
if (this.pendingProjectUpdates.size !== 0) {
this.delayEnsureProjectForOpenFiles();
}
@ -1039,7 +1046,7 @@ namespace ts.server {
Debug.assert(projectRootPath === undefined || this.useInferredProjectPerProjectRoot, "Setting compiler options per project root path is only supported when useInferredProjectPerProjectRoot is enabled");
const compilerOptions = convertCompilerOptions(projectCompilerOptions);
const watchOptions = convertWatchOptions(projectCompilerOptions);
const watchOptions = convertWatchOptions(projectCompilerOptions, projectRootPath);
const typeAcquisition = convertTypeAcquisition(projectCompilerOptions);
// always set 'allowNonTsExtensions' for inferred projects since user cannot configure it from the outside
@ -1070,8 +1077,9 @@ namespace ts.server {
project.projectRootPath === canonicalProjectRootPath :
!project.projectRootPath || !this.compilerOptionsForInferredProjectsPerProjectRoot.has(project.projectRootPath)) {
project.setCompilerOptions(compilerOptions);
project.setWatchOptions(watchOptions);
project.setTypeAcquisition(typeAcquisition);
project.setWatchOptions(watchOptions?.watchOptions);
project.setProjectErrors(watchOptions?.errors);
project.compileOnSaveEnabled = compilerOptions.compileOnSave!;
project.markAsDirty();
this.delayUpdateProjectGraph(project);
@ -1173,26 +1181,20 @@ namespace ts.server {
return this.hostConfiguration.preferences;
}
private onSourceFileChanged(fileName: string, eventKind: FileWatcherEventKind, path: Path) {
const info = this.getScriptInfoForPath(path);
if (!info) {
this.logger.msg(`Error: got watch notification for unknown file: ${fileName}`);
private onSourceFileChanged(info: ScriptInfo, eventKind: FileWatcherEventKind) {
if (info.containingProjects) {
info.containingProjects.forEach(project => project.resolutionCache.removeResolutionsFromProjectReferenceRedirects(info.path));
}
else {
if (info.containingProjects) {
info.containingProjects.forEach(project => project.resolutionCache.removeResolutionsFromProjectReferenceRedirects(info.path));
}
if (eventKind === FileWatcherEventKind.Deleted) {
// File was deleted
this.handleDeletedFile(info);
}
else if (!info.isScriptOpen()) {
// file has been changed which might affect the set of referenced files in projects that include
// this file and set of inferred projects
info.delayReloadNonMixedContentFile();
this.delayUpdateProjectGraphs(info.containingProjects, /*clearSourceMapperCache*/ false);
this.handleSourceMapProjects(info);
}
if (eventKind === FileWatcherEventKind.Deleted) {
// File was deleted
this.handleDeletedFile(info);
}
else if (!info.isScriptOpen()) {
// file has been changed which might affect the set of referenced files in projects that include
// this file and set of inferred projects
info.delayReloadNonMixedContentFile();
this.delayUpdateProjectGraphs(info.containingProjects, /*clearSourceMapperCache*/ false);
this.handleSourceMapProjects(info);
}
}
@ -1259,7 +1261,6 @@ namespace ts.server {
watchWildcardDirectory(directory: Path, flags: WatchDirectoryFlags, project: ConfiguredProject) {
const watchOptions = this.getWatchOptions(project);
return this.watchFactory.watchDirectory(
this.host,
directory,
fileOrDirectory => {
const fileOrDirectoryPath = this.toPath(fileOrDirectory);
@ -1683,7 +1684,6 @@ namespace ts.server {
configFileExistenceInfo.configFileWatcherForRootOfInferredProject =
canWatchDirectory(getDirectoryPath(canonicalConfigFilePath) as Path) ?
this.watchFactory.watchFile(
this.host,
configFileName,
(_filename, eventKind) => this.onConfigFileChangeForOpenScriptInfo(configFileName, eventKind),
PollingInterval.High,
@ -1968,7 +1968,7 @@ namespace ts.server {
private createExternalProject(projectFileName: string, files: protocol.ExternalFile[], options: protocol.ExternalProjectCompilerOptions, typeAcquisition: TypeAcquisition, excludedFiles: NormalizedPath[]) {
const compilerOptions = convertCompilerOptions(options);
const watchOptions = convertWatchOptions(options);
const watchOptionsAndErrors = convertWatchOptions(options, getDirectoryPath(normalizeSlashes(projectFileName)));
const project = new ExternalProject(
projectFileName,
this,
@ -1978,8 +1978,9 @@ namespace ts.server {
options.compileOnSave === undefined ? true : options.compileOnSave,
/*projectFilePath*/ undefined,
this.currentPluginConfigOverrides,
watchOptions
watchOptionsAndErrors?.watchOptions
);
project.setProjectErrors(watchOptionsAndErrors?.errors);
project.excludedFiles = excludedFiles;
this.addFilesToNonInferredProject(project, files, externalFilePropertyReader, typeAcquisition);
@ -2252,9 +2253,10 @@ namespace ts.server {
* Read the config file of the project again by clearing the cache and update the project graph
*/
/* @internal */
reloadConfiguredProject(project: ConfiguredProject, reason: string, isInitialLoad: boolean) {
reloadConfiguredProject(project: ConfiguredProject, reason: string, isInitialLoad: boolean, clearSemanticCache: boolean) {
// At this point, there is no reason to not have configFile in the host
const host = project.getCachedDirectoryStructureHost();
if (clearSemanticCache) this.clearSemanticCache(project);
// Clear the cache since we are reloading the project from disk
host.clearCache();
@ -2268,6 +2270,13 @@ namespace ts.server {
this.sendConfigFileDiagEvent(project, configFileName);
}
/* @internal */
private clearSemanticCache(project: Project) {
project.resolutionCache.clear();
project.getLanguageService(/*ensureSynchronized*/ false).cleanupSemanticCache();
project.markAsDirty();
}
private sendConfigFileDiagEvent(project: ConfiguredProject, triggerFile: NormalizedPath) {
if (!this.eventHandler || this.suppressDiagnosticEvents) {
return;
@ -2354,19 +2363,21 @@ namespace ts.server {
private createInferredProject(currentDirectory: string | undefined, isSingleInferredProject?: boolean, projectRootPath?: NormalizedPath): InferredProject {
const compilerOptions = projectRootPath && this.compilerOptionsForInferredProjectsPerProjectRoot.get(projectRootPath) || this.compilerOptionsForInferredProjects!; // TODO: GH#18217
let watchOptions: WatchOptions | false | undefined;
let watchOptionsAndErrors: WatchOptionsAndErrors | false | undefined;
let typeAcquisition: TypeAcquisition | undefined;
if (projectRootPath) {
watchOptions = this.watchOptionsForInferredProjectsPerProjectRoot.get(projectRootPath);
watchOptionsAndErrors = this.watchOptionsForInferredProjectsPerProjectRoot.get(projectRootPath);
typeAcquisition = this.typeAcquisitionForInferredProjectsPerProjectRoot.get(projectRootPath);
}
if (watchOptions === undefined) {
watchOptions = this.watchOptionsForInferredProjects;
if (watchOptionsAndErrors === undefined) {
watchOptionsAndErrors = this.watchOptionsForInferredProjects;
}
if (typeAcquisition === undefined) {
typeAcquisition = this.typeAcquisitionForInferredProjects;
}
const project = new InferredProject(this, this.documentRegistry, compilerOptions, watchOptions || undefined, projectRootPath, currentDirectory, this.currentPluginConfigOverrides, typeAcquisition);
watchOptionsAndErrors = watchOptionsAndErrors || undefined;
const project = new InferredProject(this, this.documentRegistry, compilerOptions, watchOptionsAndErrors?.watchOptions, projectRootPath, currentDirectory, this.currentPluginConfigOverrides, typeAcquisition);
project.setProjectErrors(watchOptionsAndErrors?.errors);
if (isSingleInferredProject) {
this.inferredProjects.unshift(project);
}
@ -2450,13 +2461,11 @@ namespace ts.server {
!startsWith(info.path, this.globalCacheLocationDirectoryPath))) {
const indexOfNodeModules = info.path.indexOf("/node_modules/");
if (!this.host.getModifiedTime || indexOfNodeModules === -1) {
info.fileWatcher = this.watchFactory.watchFilePath(
this.host,
info.fileWatcher = this.watchFactory.watchFile(
info.fileName,
(fileName, eventKind, path) => this.onSourceFileChanged(fileName, eventKind, path),
(_fileName, eventKind) => this.onSourceFileChanged(info, eventKind),
PollingInterval.Medium,
this.hostConfiguration.watchOptions,
info.path,
WatchType.ClosedScriptInfo
);
}
@ -2477,9 +2486,8 @@ namespace ts.server {
const watchDir = dir + "/node_modules" as Path;
const watcher = this.watchFactory.watchDirectory(
this.host,
watchDir,
(fileOrDirectory) => {
fileOrDirectory => {
const fileOrDirectoryPath = removeIgnoredPath(this.toPath(fileOrDirectory));
if (!fileOrDirectoryPath) return;
@ -2530,7 +2538,7 @@ namespace ts.server {
if (mTime !== info.mTime) {
const eventKind = getFileWatcherEventKind(info.mTime!, mTime);
info.mTime = mTime;
this.onSourceFileChanged(info.fileName, eventKind, info.path);
this.onSourceFileChanged(info, eventKind);
}
}
@ -2715,7 +2723,6 @@ namespace ts.server {
private addMissingSourceMapFile(mapFileName: string, declarationInfoPath: Path) {
const fileWatcher = this.watchFactory.watchFile(
this.host,
mapFileName,
() => {
const declarationInfo = this.getScriptInfoForPath(declarationInfoPath);
@ -2822,7 +2829,7 @@ namespace ts.server {
}
if (args.watchOptions) {
this.hostConfiguration.watchOptions = convertWatchOptions(args.watchOptions);
this.hostConfiguration.watchOptions = convertWatchOptions(args.watchOptions)?.watchOptions;
this.logger.info(`Host watch options changed to ${JSON.stringify(this.hostConfiguration.watchOptions)}, it will be take effect for next watches.`);
}
}
@ -2851,8 +2858,28 @@ namespace ts.server {
// (and would separate out below reloading of projects to be called when immediate reload is needed)
// as there is no need to load contents of the files from the disk
// Reload script infos
this.filenameToScriptInfo.forEach(info => {
if (this.openFiles.has(info.path)) return; // Skip open files
if (!info.fileWatcher) return; // not watched file
// Handle as if file is changed or deleted
this.onSourceFileChanged(info, this.host.fileExists(info.fileName) ? FileWatcherEventKind.Changed : FileWatcherEventKind.Deleted);
});
// Cancel all project updates since we will be updating them now
this.pendingProjectUpdates.forEach((_project, projectName) => {
this.throttledOperations.cancel(projectName);
this.pendingProjectUpdates.delete(projectName);
});
this.throttledOperations.cancel(ensureProjectForOpenFileSchedule);
this.pendingEnsureProjectForOpenFiles = false;
// Reload Projects
this.reloadConfiguredProjectForFiles(this.openFiles as ESMap<Path, NormalizedPath | undefined>, /*delayReload*/ false, returnTrue, "User requested reload projects");
this.reloadConfiguredProjectForFiles(this.openFiles as ESMap<Path, NormalizedPath | undefined>, /*clearSemanticCache*/ true, /*delayReload*/ false, returnTrue, "User requested reload projects");
this.externalProjects.forEach(project => {
this.clearSemanticCache(project);
project.updateGraph();
});
this.inferredProjects.forEach(project => this.clearSemanticCache(project));
this.ensureProjectForOpenFiles();
}
@ -2860,6 +2887,7 @@ namespace ts.server {
// Get open files to reload projects for
this.reloadConfiguredProjectForFiles(
configFileExistenceInfo.openFilesImpactedByConfigFile,
/*clearSemanticCache*/ false,
/*delayReload*/ true,
ignoreIfNotRootOfInferredProject ?
isRootOfInferredProject => isRootOfInferredProject : // Reload open files if they are root of inferred project
@ -2876,12 +2904,12 @@ namespace ts.server {
* If the there is no existing project it just opens the configured project for the config file
* reloadForInfo provides a way to filter out files to reload configured project for
*/
private reloadConfiguredProjectForFiles<T>(openFiles: ESMap<Path, T>, delayReload: boolean, shouldReloadProjectFor: (openFileValue: T) => boolean, reason: string) {
private reloadConfiguredProjectForFiles<T>(openFiles: ESMap<Path, T>, clearSemanticCache: boolean, delayReload: boolean, shouldReloadProjectFor: (openFileValue: T) => boolean, reason: string) {
const updatedProjects = new Map<string, true>();
const reloadChildProject = (child: ConfiguredProject) => {
if (!updatedProjects.has(child.canonicalConfigFilePath)) {
updatedProjects.set(child.canonicalConfigFilePath, true);
this.reloadConfiguredProject(child, reason, /*isInitialLoad*/ false);
this.reloadConfiguredProject(child, reason, /*isInitialLoad*/ false, clearSemanticCache);
}
};
// try to reload config file for all open files
@ -2907,11 +2935,12 @@ namespace ts.server {
if (delayReload) {
project.pendingReload = ConfigFileProgramReloadLevel.Full;
project.pendingReloadReason = reason;
if (clearSemanticCache) this.clearSemanticCache(project);
this.delayUpdateProjectGraph(project);
}
else {
// reload from the disk
this.reloadConfiguredProject(project, reason, /*isInitialLoad*/ false);
this.reloadConfiguredProject(project, reason, /*isInitialLoad*/ false, clearSemanticCache);
// If this project does not contain this file directly, reload the project till the reloaded project contains the script info directly
if (!projectContainsInfoDirectly(project, info)) {
const referencedProject = forEachResolvedProjectReferenceProject(
@ -3730,7 +3759,7 @@ namespace ts.server {
externalProject.excludedFiles = excludedFiles;
if (!tsConfigFiles) {
const compilerOptions = convertCompilerOptions(proj.options);
const watchOptions = convertWatchOptions(proj.options);
const watchOptionsAndErrors = convertWatchOptions(proj.options, externalProject.getCurrentDirectory());
const lastFileExceededProgramSize = this.getFilenameForExceededTotalSizeLimitForNonTsFiles(proj.projectFileName, compilerOptions, proj.rootFiles, externalFilePropertyReader);
if (lastFileExceededProgramSize) {
externalProject.disableLanguageService(lastFileExceededProgramSize);
@ -3738,9 +3767,10 @@ namespace ts.server {
else {
externalProject.enableLanguageService();
}
externalProject.setProjectErrors(watchOptionsAndErrors?.errors);
// external project already exists and not config files were added - update the project and return;
// The graph update here isnt postponed since any file open operation needs all updated external projects
this.updateRootAndOptionsOfNonInferredProject(externalProject, proj.rootFiles, externalFilePropertyReader, compilerOptions, proj.typeAcquisition, proj.options.compileOnSave, watchOptions);
this.updateRootAndOptionsOfNonInferredProject(externalProject, proj.rootFiles, externalFilePropertyReader, compilerOptions, proj.typeAcquisition, proj.options.compileOnSave, watchOptionsAndErrors?.watchOptions);
externalProject.updateGraph();
return;
}
@ -3863,7 +3893,6 @@ namespace ts.server {
if (!watchers.has(path)) {
this.invalidateProjectAutoImports(path);
watchers.set(path, this.watchFactory.watchFile(
this.host,
path,
(fileName, eventKind) => {
const path = this.toPath(fileName);

View file

@ -189,6 +189,8 @@ namespace ts.server {
*/
private projectStateVersion = 0;
protected projectErrors: Diagnostic[] | undefined;
protected isInitialLoadPending: () => boolean = returnFalse;
/*@internal*/
@ -489,7 +491,6 @@ namespace ts.server {
/*@internal*/
watchDirectoryOfFailedLookupLocation(directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags) {
return this.projectService.watchFactory.watchDirectory(
this.projectService.host,
directory,
cb,
flags,
@ -530,7 +531,6 @@ namespace ts.server {
/*@internal*/
watchTypeRootsDirectory(directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags) {
return this.projectService.watchFactory.watchDirectory(
this.projectService.host,
directory,
cb,
flags,
@ -586,11 +586,18 @@ namespace ts.server {
* Get the errors that dont have any file name associated
*/
getGlobalProjectErrors(): readonly Diagnostic[] {
return emptyArray;
return filter(this.projectErrors, diagnostic => !diagnostic.file) || emptyArray;
}
/**
* Get all the project errors
*/
getAllProjectErrors(): readonly Diagnostic[] {
return emptyArray;
return this.projectErrors || emptyArray;
}
setProjectErrors(projectErrors: Diagnostic[] | undefined) {
this.projectErrors = projectErrors;
}
getLanguageService(ensureSynchronized = true): LanguageService {
@ -771,6 +778,7 @@ namespace ts.server {
this.resolutionCache = undefined!;
this.cachedUnresolvedImportsPerFile = undefined!;
this.directoryStructureHost = undefined!;
this.projectErrors = undefined;
// Clean up file watchers waiting for missing files
if (this.missingFilesMap) {
@ -1262,7 +1270,6 @@ namespace ts.server {
private addMissingFileWatcher(missingFilePath: Path) {
const fileWatcher = this.projectService.watchFactory.watchFile(
this.projectService.host,
missingFilePath,
(fileName, eventKind) => {
if (isConfiguredProject(this)) {
@ -1318,7 +1325,6 @@ namespace ts.server {
return {
generatedFilePath: this.toPath(generatedFile),
watcher: this.projectService.watchFactory.watchFile(
this.projectService.host,
generatedFile,
() => {
this.clearSourceMapperCache();
@ -2060,8 +2066,6 @@ namespace ts.server {
/** Ref count to the project when opened from external project */
private externalProjectRefCount = 0;
private projectErrors: Diagnostic[] | undefined;
private projectReferences: readonly ProjectReference[] | undefined;
/** Potential project references before the project is actually loaded (read config file) */
@ -2131,7 +2135,6 @@ namespace ts.server {
/* @internal */
createConfigFileWatcher() {
this.configFileWatcher = this.projectService.watchFactory.watchFile(
this.projectService.host,
this.getConfigFilePath(),
(_fileName, eventKind) => this.projectService.onConfigChangedForConfiguredProject(this, eventKind),
PollingInterval.High,
@ -2160,7 +2163,7 @@ namespace ts.server {
this.openFileWatchTriggered.clear();
const reason = Debug.checkDefined(this.pendingReloadReason);
this.pendingReloadReason = undefined;
this.projectService.reloadConfiguredProject(this, reason, isInitialLoad);
this.projectService.reloadConfiguredProject(this, reason, isInitialLoad, /*clearSemanticCache*/ false);
result = true;
break;
default:
@ -2281,7 +2284,6 @@ namespace ts.server {
}
this.stopWatchingWildCards();
this.projectErrors = undefined;
this.configFileSpecs = undefined;
this.openFileWatchTriggered.clear();
this.compilerHost = undefined;

View file

@ -1509,6 +1509,8 @@ namespace ts.server.protocol {
watchDirectory?: WatchDirectoryKind | ts.WatchDirectoryKind;
fallbackPolling?: PollingWatchKind | ts.PollingWatchKind;
synchronousWatchDirectory?: boolean;
excludeDirectories?: string[];
excludeFiles?: string[];
[option: string]: CompilerOptionsValue | undefined;
}

View file

@ -187,6 +187,7 @@
"unittests/tsserver/projects.ts",
"unittests/tsserver/refactors.ts",
"unittests/tsserver/reload.ts",
"unittests/tsserver/reloadProjects.ts",
"unittests/tsserver/rename.ts",
"unittests/tsserver/resolutionCache.ts",
"unittests/tsserver/session.ts",

View file

@ -663,6 +663,64 @@ namespace ts {
watchOptions: { fallbackPolling: undefined }
});
});
it("parse --excludeDirectories", () => {
assertParseResult(["--excludeDirectories", "**/temp", "0.ts"],
{
errors: [],
fileNames: ["0.ts"],
options: {},
watchOptions: { excludeDirectories: ["**/temp"] }
});
});
it("errors on invalid excludeDirectories", () => {
assertParseResult(["--excludeDirectories", "**/../*", "0.ts"],
{
errors: [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: undefined,
start: undefined,
length: undefined
}
],
fileNames: ["0.ts"],
options: {},
watchOptions: { excludeDirectories: [] }
});
});
it("parse --excludeFiles", () => {
assertParseResult(["--excludeFiles", "**/temp/*.ts", "0.ts"],
{
errors: [],
fileNames: ["0.ts"],
options: {},
watchOptions: { excludeFiles: ["**/temp/*.ts"] }
});
});
it("errors on invalid excludeFiles", () => {
assertParseResult(["--excludeFiles", "**/../*", "0.ts"],
{
errors: [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: undefined,
start: undefined,
length: undefined
}
],
fileNames: ["0.ts"],
options: {},
watchOptions: { excludeFiles: [] }
});
});
});
});
@ -912,6 +970,54 @@ namespace ts {
watchOptions: { fallbackPolling: undefined }
});
});
it("errors on invalid excludeDirectories", () => {
assertParseResult(["--excludeDirectories", "**/../*"],
{
errors: [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: undefined,
start: undefined,
length: undefined
}
],
projects: ["."],
buildOptions: {},
watchOptions: { excludeDirectories: [] }
});
});
it("parse --excludeFiles", () => {
assertParseResult(["--excludeFiles", "**/temp/*.ts"],
{
errors: [],
projects: ["."],
buildOptions: {},
watchOptions: { excludeFiles: ["**/temp/*.ts"] }
});
});
it("errors on invalid excludeFiles", () => {
assertParseResult(["--excludeFiles", "**/../*"],
{
errors: [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: undefined,
start: undefined,
length: undefined
}
],
projects: ["."],
buildOptions: {},
watchOptions: { excludeFiles: [] }
});
});
});
});
}

View file

@ -45,20 +45,33 @@ namespace ts {
expectedOptions: WatchOptions | undefined;
additionalFiles?: vfs.FileSet;
existingWatchOptions?: WatchOptions | undefined;
expectedErrors?: (sourceFile?: SourceFile) => Diagnostic[];
}
function verifyWatchOptions(scenario: () => VerifyWatchOptions[]) {
it("with json api", () => {
for (const { json, expectedOptions, additionalFiles, existingWatchOptions } of scenario()) {
for (const { json, expectedOptions, additionalFiles, existingWatchOptions, expectedErrors } of scenario()) {
const parsed = getParsedCommandJson(json, additionalFiles, existingWatchOptions);
assert.deepEqual(parsed.watchOptions, expectedOptions);
assert.deepEqual(parsed.watchOptions, expectedOptions, `With ${JSON.stringify(json)}`);
if (length(parsed.errors)) {
assert.deepEqual(parsed.errors, expectedErrors?.());
}
else {
assert.equal(0, length(expectedErrors?.()), `Expected no errors`);
}
}
});
it("with json source file api", () => {
for (const { json, expectedOptions, additionalFiles, existingWatchOptions } of scenario()) {
for (const { json, expectedOptions, additionalFiles, existingWatchOptions, expectedErrors } of scenario()) {
const parsed = getParsedCommandJsonNode(json, additionalFiles, existingWatchOptions);
assert.deepEqual(parsed.watchOptions, expectedOptions);
if (length(parsed.errors)) {
assert.deepEqual(parsed.errors, expectedErrors?.(parsed.options.configFile));
}
else {
assert.equal(0, length(expectedErrors?.(parsed.options.configFile)), `Expected no errors`);
}
}
});
}
@ -156,7 +169,47 @@ namespace ts {
{
json: { watchOptions: { synchronousWatchDirectory: true } },
expectedOptions: { synchronousWatchDirectory: true }
}
},
{
json: { watchOptions: { excludeDirectories: ["**/temp"] } },
expectedOptions: { excludeDirectories: ["/**/temp"] }
},
{
json: { watchOptions: { excludeFiles: ["**/temp/*.ts"] } },
expectedOptions: { excludeFiles: ["/**/temp/*.ts"] }
},
{
json: { watchOptions: { excludeDirectories: ["**/../*"] } },
expectedOptions: { excludeDirectories: [] },
expectedErrors: sourceFile => [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: sourceFile,
start: sourceFile && sourceFile.text.indexOf(`"**/../*"`),
length: sourceFile && `"**/../*"`.length,
reportsDeprecated: undefined,
reportsUnnecessary: undefined
}
]
},
{
json: { watchOptions: { excludeFiles: ["**/../*"] } },
expectedOptions: { excludeFiles: [] },
expectedErrors: sourceFile => [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: sourceFile,
start: sourceFile && sourceFile.text.indexOf(`"**/../*"`),
length: sourceFile && `"**/../*"`.length,
reportsDeprecated: undefined,
reportsUnnecessary: undefined
}
]
},
]);
});

View file

@ -411,6 +411,91 @@ namespace ts.tscWatch {
},
changes: emptyArray
});
describe("exclude options", () => {
function sys(watchOptions: WatchOptions, runWithoutRecursiveWatches?: boolean): WatchedSystem {
const configFile: File = {
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({ exclude: ["node_modules"], watchOptions })
};
const main: File = {
path: `${projectRoot}/src/main.ts`,
content: `import { foo } from "bar"; foo();`
};
const bar: File = {
path: `${projectRoot}/node_modules/bar/index.d.ts`,
content: `export { foo } from "./foo";`
};
const foo: File = {
path: `${projectRoot}/node_modules/bar/foo.d.ts`,
content: `export function foo(): string;`
};
const fooBar: File = {
path: `${projectRoot}/node_modules/bar/fooBar.d.ts`,
content: `export function fooBar(): string;`
};
const temp: File = {
path: `${projectRoot}/node_modules/bar/temp/index.d.ts`,
content: "export function temp(): string;"
};
const files = [libFile, main, bar, foo, fooBar, temp, configFile];
return createWatchedSystem(files, { currentDirectory: projectRoot, runWithoutRecursiveWatches });
}
function verifyWorker(...additionalFlags: string[]) {
verifyTscWatch({
scenario,
subScenario: `watchOptions/with excludeFiles option${additionalFlags.join("")}`,
commandLineArgs: ["-w", ...additionalFlags],
sys: () => sys({ excludeFiles: ["node_modules/*"] }),
changes: [
{
caption: "Change foo",
change: sys => replaceFileText(sys, `${projectRoot}/node_modules/bar/foo.d.ts`, "foo", "fooBar"),
timeouts: sys => sys.checkTimeoutQueueLength(0),
}
]
});
verifyTscWatch({
scenario,
subScenario: `watchOptions/with excludeDirectories option${additionalFlags.join("")}`,
commandLineArgs: ["-w", ...additionalFlags],
sys: () => sys({ excludeDirectories: ["node_modules"] }),
changes: [
{
caption: "delete fooBar",
change: sys => sys.deleteFile(`${projectRoot}/node_modules/bar/fooBar.d.ts`),
timeouts: sys => sys.checkTimeoutQueueLength(0), }
]
});
verifyTscWatch({
scenario,
subScenario: `watchOptions/with excludeDirectories option with recursive directory watching${additionalFlags.join("")}`,
commandLineArgs: ["-w", ...additionalFlags],
sys: () => sys({ excludeDirectories: ["**/temp"] }, /*runWithoutRecursiveWatches*/ true),
changes: [
{
caption: "Directory watch updates because of main.js creation",
change: noop,
timeouts: sys => {
sys.checkTimeoutQueueLengthAndRun(1); // To update directory callbacks for main.js output
sys.checkTimeoutQueueLength(0);
},
},
{
caption: "add new folder to temp",
change: sys => sys.ensureFileOrFolder({ path: `${projectRoot}/node_modules/bar/temp/fooBar/index.d.ts`, content: "export function temp(): string;" }),
timeouts: sys => sys.checkTimeoutQueueLength(0),
}
]
});
}
verifyWorker();
verifyWorker("-extendedDiagnostics");
});
});
});
}

View file

@ -0,0 +1,139 @@
namespace ts.projectSystem {
describe("unittests:: tsserver:: reloadProjects", () => {
const configFile: File = {
path: `${tscWatch.projectRoot}/tsconfig.json`,
content: JSON.stringify({
watchOptions: { excludeDirectories: ["node_modules"] }
})
};
const file1: File = {
path: `${tscWatch.projectRoot}/file1.ts`,
content: `import { foo } from "module1";
foo();
import { bar } from "./file2";
bar();`
};
const file2: File = {
path: `${tscWatch.projectRoot}/file2.ts`,
content: `export function bar(){}`
};
const moduleFile: File = {
path: `${tscWatch.projectRoot}/node_modules/module1/index.d.ts`,
content: `export function foo(): string;`
};
function verifyFileUpdates(host: TestServerHost, service: TestProjectService, project: server.Project) {
// update file
const updatedText = `${file2.content}
bar();`;
host.writeFile(file2.path, updatedText);
host.checkTimeoutQueueLength(0);
service.reloadProjects();
assert.equal(project.getCurrentProgram()?.getSourceFile(file2.path)?.text, updatedText);
// delete file
host.deleteFile(file2.path);
host.checkTimeoutQueueLength(0);
service.reloadProjects();
assert.isUndefined(project.getCurrentProgram()?.getSourceFile(file2.path)?.text);
assert.isUndefined(service.getScriptInfo(file2.path));
}
it("configured project", () => {
const host = createServerHost([configFile, libFile, file1, file2]);
const service = createProjectService(host);
service.setHostConfiguration({ watchOptions: { excludeFiles: [file2.path] } });
service.openClientFile(file1.path);
checkNumberOfProjects(service, { configuredProjects: 1 });
const project = service.configuredProjects.get(configFile.path)!;
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, configFile.path]);
// Install module1
host.ensureFileOrFolder(moduleFile);
host.checkTimeoutQueueLength(0);
service.reloadProjects();
checkNumberOfProjects(service, { configuredProjects: 1 });
assert.strictEqual(service.configuredProjects.get(configFile.path), project);
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, configFile.path, moduleFile.path]);
verifyFileUpdates(host, service, project);
});
it("inferred project", () => {
const host = createServerHost([libFile, file1, file2]);
const service = createProjectService(host, /*parameters*/ undefined, { useInferredProjectPerProjectRoot: true, });
service.setHostConfiguration({ watchOptions: { excludeFiles: [file2.path] } });
const timeoutId = host.getNextTimeoutId();
service.setCompilerOptionsForInferredProjects({ excludeDirectories: ["node_modules"] }, tscWatch.projectRoot);
host.clearTimeout(timeoutId);
service.openClientFile(file1.path, /*fileContent*/ undefined, /*scriptKind*/ undefined, tscWatch.projectRoot);
checkNumberOfProjects(service, { inferredProjects: 1 });
const project = service.inferredProjects[0];
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path]);
// Install module1
host.ensureFileOrFolder(moduleFile);
host.checkTimeoutQueueLength(0);
service.reloadProjects();
checkNumberOfProjects(service, { inferredProjects: 1 });
assert.strictEqual(service.inferredProjects[0], project);
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, moduleFile.path]);
verifyFileUpdates(host, service, project);
});
it("external project", () => {
const host = createServerHost([libFile, file1, file2]);
const service = createProjectService(host);
service.setHostConfiguration({ watchOptions: { excludeFiles: [file2.path] } });
service.openExternalProject({
projectFileName: `${tscWatch.projectRoot}/project.sln`,
options: { excludeDirectories: ["node_modules"] },
rootFiles: [{ fileName: file1.path }, { fileName: file2.path }]
});
service.openClientFile(file1.path);
checkNumberOfProjects(service, { externalProjects: 1 });
const project = service.externalProjects[0];
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path]);
// Install module1
host.ensureFileOrFolder(moduleFile);
host.checkTimeoutQueueLength(0);
service.reloadProjects();
checkNumberOfProjects(service, { externalProjects: 1 });
assert.strictEqual(service.externalProjects[0], project);
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, moduleFile.path]);
verifyFileUpdates(host, service, project);
});
it("external project with config file", () => {
const host = createServerHost([libFile, file1, file2, configFile]);
const service = createProjectService(host);
service.setHostConfiguration({ watchOptions: { excludeFiles: [file2.path] } });
service.openExternalProject({
projectFileName: `${tscWatch.projectRoot}/project.sln`,
options: { excludeDirectories: ["node_modules"] },
rootFiles: [{ fileName: file1.path }, { fileName: file2.path }, { fileName: configFile.path }]
});
service.openClientFile(file1.path);
checkNumberOfProjects(service, { configuredProjects: 1 });
const project = service.configuredProjects.get(configFile.path)!;
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, configFile.path]);
// Install module1
host.ensureFileOrFolder(moduleFile);
host.checkTimeoutQueueLength(0);
service.reloadProjects();
checkNumberOfProjects(service, { configuredProjects: 1 });
assert.strictEqual(service.configuredProjects.get(configFile.path), project);
checkProjectActualFiles(project, [libFile.path, file1.path, file2.path, configFile.path, moduleFile.path]);
verifyFileUpdates(host, service, project);
});
});
}

View file

@ -565,6 +565,202 @@ namespace ts.projectSystem {
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectories(host, emptyArray, /*recursive*/ true);
});
describe("excludeDirectories", () => {
function setupFiles() {
const main: File = {
path: `${tscWatch.projectRoot}/src/main.ts`,
content: `import { foo } from "bar"; foo();`
};
const bar: File = {
path: `${tscWatch.projectRoot}/node_modules/bar/index.d.ts`,
content: `export { foo } from "./foo";`
};
const foo: File = {
path: `${tscWatch.projectRoot}/node_modules/bar/foo.d.ts`,
content: `export function foo(): string;`
};
return { main, bar, foo };
}
function setupConfigureHost(service: TestProjectService, configureHost: boolean | undefined) {
if (configureHost) {
service.setHostConfiguration({
watchOptions: { excludeDirectories: ["node_modules"] }
});
}
}
function setup(configureHost?: boolean) {
const configFile: File = {
path: `${tscWatch.projectRoot}/tsconfig.json`,
content: JSON.stringify({ include: ["src"], watchOptions: { excludeDirectories: ["node_modules"] } })
};
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo, configFile];
const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot });
const service = createProjectService(host);
setupConfigureHost(service, configureHost);
service.openClientFile(main.path);
return { host, configFile };
}
it("with excludeDirectories option in configFile", () => {
const { host, configFile } = setup();
checkWatchedFilesDetailed(host, [configFile.path, libFile.path], 1);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectoriesDetailed(
host,
arrayToMap(
[`${tscWatch.projectRoot}/src`, `${tscWatch.projectRoot}/node_modules`],
identity,
f => f === `${tscWatch.projectRoot}/node_modules` ? 1 : 2,
),
/*recursive*/ true,
);
});
it("with excludeDirectories option in configuration", () => {
const { host, configFile } = setup(/*configureHost*/ true);
checkWatchedFilesDetailed(host, [configFile.path, libFile.path], 1);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectoriesDetailed(
host,
[`${tscWatch.projectRoot}/src`],
2,
/*recursive*/ true,
);
});
function setupExternalProject(configureHost?: boolean) {
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot });
const service = createProjectService(host);
setupConfigureHost(service, configureHost);
service.openExternalProject(<protocol.ExternalProject>{
projectFileName: `${tscWatch.projectRoot}/project.csproj`,
rootFiles: toExternalFiles([main.path, bar.path, foo.path]),
options: { excludeDirectories: ["node_modules"] }
});
service.openClientFile(main.path);
return host;
}
it("external project watch options", () => {
const host = setupExternalProject();
checkWatchedFilesDetailed(host, [libFile.path], 1);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectoriesDetailed(
host,
[`${tscWatch.projectRoot}/src`, `${tscWatch.projectRoot}/node_modules`],
1,
/*recursive*/ true,
);
});
it("external project watch options in host configuration", () => {
const host = setupExternalProject(/*configureHost*/ true);
checkWatchedFilesDetailed(host, [libFile.path], 1);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectoriesDetailed(
host,
[`${tscWatch.projectRoot}/src`],
1,
/*recursive*/ true,
);
});
it("external project watch options errors", () => {
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot });
const service = createProjectService(host);
service.openExternalProject(<protocol.ExternalProject>{
projectFileName: `${tscWatch.projectRoot}/project.csproj`,
rootFiles: toExternalFiles([main.path, bar.path, foo.path]),
options: { excludeDirectories: ["**/../*"] }
});
service.openClientFile(main.path);
const project = service.externalProjects[0];
assert.deepEqual(project.getAllProjectErrors(), [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: undefined,
start: undefined,
length: undefined,
reportsDeprecated: undefined,
reportsUnnecessary: undefined,
}
]);
});
function setupInferredProject(configureHost?: boolean) {
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot });
const service = createProjectService(host, {}, { useInferredProjectPerProjectRoot: true });
setupConfigureHost(service, configureHost);
service.setCompilerOptionsForInferredProjects({ excludeDirectories: ["node_modules"] }, tscWatch.projectRoot);
service.openClientFile(main.path, main.content, ScriptKind.TS, tscWatch.projectRoot);
return host;
}
it("inferred project watch options", () => {
const host = setupInferredProject();
checkWatchedFilesDetailed(
host,
[libFile.path, `${tscWatch.projectRoot}/tsconfig.json`, `${tscWatch.projectRoot}/jsconfig.json`, `${tscWatch.projectRoot}/src/tsconfig.json`, `${tscWatch.projectRoot}/src/jsconfig.json`],
1
);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectoriesDetailed(
host,
[`${tscWatch.projectRoot}/src`, `${tscWatch.projectRoot}/node_modules`],
1,
/*recursive*/ true,
);
});
it("inferred project watch options in host configuration", () => {
const host = setupInferredProject(/*configureHost*/ true);
checkWatchedFilesDetailed(
host,
[libFile.path, `${tscWatch.projectRoot}/tsconfig.json`, `${tscWatch.projectRoot}/jsconfig.json`, `${tscWatch.projectRoot}/src/tsconfig.json`, `${tscWatch.projectRoot}/src/jsconfig.json`],
1
);
checkWatchedDirectories(host, emptyArray, /*recursive*/ false);
checkWatchedDirectoriesDetailed(
host,
[`${tscWatch.projectRoot}/src`],
1,
/*recursive*/ true,
);
});
it("inferred project watch options errors", () => {
const { main, bar, foo } = setupFiles();
const files = [libFile, main, bar, foo];
const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot });
const service = createProjectService(host, {}, { useInferredProjectPerProjectRoot: true });
service.setCompilerOptionsForInferredProjects({ excludeDirectories: ["**/../*"] }, tscWatch.projectRoot);
service.openClientFile(main.path, main.content, ScriptKind.TS, tscWatch.projectRoot);
const project = service.inferredProjects[0];
assert.deepEqual(project.getAllProjectErrors(), [
{
messageText: `File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '**/../*'.`,
category: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.category,
code: Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0.code,
file: undefined,
start: undefined,
length: undefined,
reportsDeprecated: undefined,
reportsUnnecessary: undefined,
}
]);
});
});
});
describe("unittests:: tsserver:: watchEnvironment:: file names on case insensitive file system", () => {

View file

@ -86,6 +86,10 @@ namespace ts.server.typingsInstaller {
type ProjectWatchers = ESMap<string, FileWatcher> & { isInvoked?: boolean; };
function getDetailWatchInfo(projectName: string, watchers: ProjectWatchers) {
return `Project: ${projectName} watcher already invoked: ${watchers.isInvoked}`;
}
export abstract class TypingsInstaller {
private readonly packageNameToTypingLocation = new Map<string, JsTyping.CachedTyping>();
private readonly missingTypingsSet = new Set<string>();
@ -100,6 +104,8 @@ namespace ts.server.typingsInstaller {
private inFlightRequestCount = 0;
abstract readonly typesRegistry: ESMap<string, MapLike<string>>;
/*@internal*/
private readonly watchFactory: WatchFactory<string, ProjectWatchers>;
constructor(
protected readonly installTypingHost: InstallTypingHost,
@ -110,9 +116,11 @@ namespace ts.server.typingsInstaller {
protected readonly log = nullLog) {
this.toCanonicalFileName = createGetCanonicalFileName(installTypingHost.useCaseSensitiveFileNames);
this.globalCachePackageJsonPath = combinePaths(globalCachePath, "package.json");
if (this.log.isEnabled()) {
const isLoggingEnabled = this.log.isEnabled();
if (isLoggingEnabled) {
this.log.writeLine(`Global cache location '${globalCachePath}', safe file path '${safeListPath}', types map path ${typesMapLocation}`);
}
this.watchFactory = getWatchFactory(this.installTypingHost as WatchFactoryHost, isLoggingEnabled ? WatchLogLevel.Verbose : WatchLogLevel.None, s => this.log.writeLine(s), getDetailWatchInfo);
this.processCacheLocation(this.globalCachePath);
}
@ -431,19 +439,13 @@ namespace ts.server.typingsInstaller {
this.log.writeLine(`${projectWatcherType}:: Added:: WatchInfo: ${path}`);
}
const watcher = projectWatcherType === ProjectWatcherType.FileWatcher ?
this.installTypingHost.watchFile!(path, (f, eventKind) => { // TODO: GH#18217
if (isLoggingEnabled) {
this.log.writeLine(`FileWatcher:: Triggered with ${f} eventKind: ${FileWatcherEventKind[eventKind]}:: WatchInfo: ${path}:: handler is already invoked '${watchers.isInvoked}'`);
}
this.watchFactory.watchFile(path, () => {
if (!watchers.isInvoked) {
watchers.isInvoked = true;
this.sendResponse({ projectName, kind: ActionInvalidate });
}
}, /*pollingInterval*/ 2000, options) :
this.installTypingHost.watchDirectory!(path, f => { // TODO: GH#18217
if (isLoggingEnabled) {
this.log.writeLine(`DirectoryWatcher:: Triggered with ${f} :: WatchInfo: ${path} recursive :: handler is already invoked '${watchers.isInvoked}'`);
}
}, PollingInterval.High, options, projectName, watchers) :
this.watchFactory.watchDirectory(path, f => {
if (watchers.isInvoked || !fileExtensionIs(f, Extension.Json)) {
return;
}
@ -453,7 +455,7 @@ namespace ts.server.typingsInstaller {
watchers.isInvoked = true;
this.sendResponse({ projectName, kind: ActionInvalidate });
}
}, /*recursive*/ true, options);
}, WatchDirectoryFlags.Recursive, options, projectName, watchers);
watchers.set(canonicalPath, isLoggingEnabled ? {
close: () => {

View file

@ -2881,6 +2881,8 @@ declare namespace ts {
watchDirectory?: WatchDirectoryKind;
fallbackPolling?: PollingWatchKind;
synchronousWatchDirectory?: boolean;
excludeDirectories?: string[];
excludeFiles?: string[];
[option: string]: CompilerOptionsValue | undefined;
}
export interface TypeAcquisition {
@ -7583,6 +7585,8 @@ declare namespace ts.server.protocol {
watchDirectory?: WatchDirectoryKind | ts.WatchDirectoryKind;
fallbackPolling?: PollingWatchKind | ts.PollingWatchKind;
synchronousWatchDirectory?: boolean;
excludeDirectories?: string[];
excludeFiles?: string[];
[option: string]: CompilerOptionsValue | undefined;
}
/**
@ -9260,6 +9264,7 @@ declare namespace ts.server {
* This property is different from projectStructureVersion since in most cases edits don't affect set of files in the project
*/
private projectStateVersion;
protected projectErrors: Diagnostic[] | undefined;
protected isInitialLoadPending: () => boolean;
private readonly cancellationToken;
isNonTsProject(): boolean;
@ -9298,7 +9303,11 @@ declare namespace ts.server {
* Get the errors that dont have any file name associated
*/
getGlobalProjectErrors(): readonly Diagnostic[];
/**
* Get all the project errors
*/
getAllProjectErrors(): readonly Diagnostic[];
setProjectErrors(projectErrors: Diagnostic[] | undefined): void;
getLanguageService(ensureSynchronized?: boolean): LanguageService;
getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[];
/**
@ -9397,7 +9406,6 @@ declare namespace ts.server {
readonly canonicalConfigFilePath: NormalizedPath;
/** Ref count to the project when opened from external project */
private externalProjectRefCount;
private projectErrors;
private projectReferences;
/**
* If the project has reload from disk pending, it reloads (and then updates graph as part of that) instead of just updating the graph
@ -9561,7 +9569,7 @@ declare namespace ts.server {
}
export function convertFormatOptions(protocolOptions: protocol.FormatCodeSettings): FormatCodeSettings;
export function convertCompilerOptions(protocolOptions: protocol.ExternalProjectCompilerOptions): CompilerOptions & protocol.CompileOnSaveMixin;
export function convertWatchOptions(protocolOptions: protocol.ExternalProjectCompilerOptions): WatchOptions | undefined;
export function convertWatchOptions(protocolOptions: protocol.ExternalProjectCompilerOptions, currentDirectory?: string): WatchOptionsAndErrors | undefined;
export function convertTypeAcquisition(protocolOptions: protocol.InferredProjectCompilerOptions): TypeAcquisition | undefined;
export function tryConvertScriptKindName(scriptKindName: protocol.ScriptKindName | ScriptKind): ScriptKind;
export function convertScriptKindName(scriptKindName: protocol.ScriptKindName): ScriptKind.Unknown | ScriptKind.JS | ScriptKind.JSX | ScriptKind.TS | ScriptKind.TSX;
@ -9594,6 +9602,10 @@ declare namespace ts.server {
syntaxOnly?: boolean;
serverMode?: LanguageServiceMode;
}
export interface WatchOptionsAndErrors {
watchOptions: WatchOptions;
errors: Diagnostic[] | undefined;
}
export class ProjectService {
private readonly scriptInfoInNodeModulesWatchers;
/**

View file

@ -2881,6 +2881,8 @@ declare namespace ts {
watchDirectory?: WatchDirectoryKind;
fallbackPolling?: PollingWatchKind;
synchronousWatchDirectory?: boolean;
excludeDirectories?: string[];
excludeFiles?: string[];
[option: string]: CompilerOptionsValue | undefined;
}
export interface TypeAcquisition {

View file

@ -0,0 +1,6 @@
{
"compilerOptions": {},
"watchOptions": {
"excludeDirectories": []
}
}

View file

@ -0,0 +1,6 @@
{
"compilerOptions": {},
"watchOptions": {
"excludeFiles": []
}
}

View file

@ -0,0 +1,133 @@
Input::
//// [/a/lib/lib.d.ts]
/// <reference no-default-lib="true"/>
interface Boolean {}
interface Function {}
interface CallableFunction {}
interface NewableFunction {}
interface IArguments {}
interface Number { toExponential: any; }
interface Object {}
interface RegExp {}
interface String { charAt: any; }
interface Array<T> { length: number; [n: number]: T; }
//// [/user/username/projects/myproject/src/main.ts]
import { foo } from "bar"; foo();
//// [/user/username/projects/myproject/node_modules/bar/index.d.ts]
export { foo } from "./foo";
//// [/user/username/projects/myproject/node_modules/bar/foo.d.ts]
export function foo(): string;
//// [/user/username/projects/myproject/node_modules/bar/fooBar.d.ts]
export function fooBar(): string;
//// [/user/username/projects/myproject/node_modules/bar/temp/index.d.ts]
export function temp(): string;
//// [/user/username/projects/myproject/tsconfig.json]
{"exclude":["node_modules"],"watchOptions":{"excludeDirectories":["node_modules"]}}
/a/lib/tsc.js -w -extendedDiagnostics
Output::
[12:00:37 AM] Starting compilation in watch mode...
Current directory: /user/username/projects/myproject CaseSensitiveFileNames: false
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Config file
Synchronizing program
CreatingProgramWith::
roots: ["/user/username/projects/myproject/src/main.ts"]
options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src/main.ts 250 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Source file
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/bar/index.d.ts 250 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Source file
ExcludeWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Failed Lookup Locations
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/bar/foo.d.ts 250 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Source file
FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 250 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Source file
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Failed Lookup Locations
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Failed Lookup Locations
ExcludeWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Type roots
DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js :: WatchInfo: /user/username/projects/myproject/src 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Failed Lookup Locations
Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js :: WatchInfo: /user/username/projects/myproject/src 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Failed Lookup Locations
[12:00:40 AM] Found 0 errors. Watching for file changes.
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Wild card directory
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Wild card directory
Program root files: ["/user/username/projects/myproject/src/main.ts"]
Program options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
Program structureReused: Not
Program files::
/a/lib/lib.d.ts
/user/username/projects/myproject/node_modules/bar/foo.d.ts
/user/username/projects/myproject/node_modules/bar/index.d.ts
/user/username/projects/myproject/src/main.ts
Semantic diagnostics in builder refreshed for::
/a/lib/lib.d.ts
/user/username/projects/myproject/node_modules/bar/foo.d.ts
/user/username/projects/myproject/node_modules/bar/index.d.ts
/user/username/projects/myproject/src/main.ts
WatchedFiles::
/user/username/projects/myproject/tsconfig.json:
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250}
/user/username/projects/myproject/src/main.ts:
{"fileName":"/user/username/projects/myproject/src/main.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/index.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/index.d.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/foo.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/foo.d.ts","pollingInterval":250}
/a/lib/lib.d.ts:
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250}
FsWatches::
FsWatchesRecursive::
/user/username/projects/myproject/src:
{"directoryName":"/user/username/projects/myproject/src","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject:
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
exitCode:: ExitStatus.undefined
//// [/user/username/projects/myproject/src/main.js]
"use strict";
exports.__esModule = true;
var bar_1 = require("bar");
bar_1.foo();
Change:: delete fooBar
Input::
//// [/user/username/projects/myproject/node_modules/bar/fooBar.d.ts] deleted
Output::
WatchedFiles::
/user/username/projects/myproject/tsconfig.json:
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250}
/user/username/projects/myproject/src/main.ts:
{"fileName":"/user/username/projects/myproject/src/main.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/index.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/index.d.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/foo.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/foo.d.ts","pollingInterval":250}
/a/lib/lib.d.ts:
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250}
FsWatches::
FsWatchesRecursive::
/user/username/projects/myproject/src:
{"directoryName":"/user/username/projects/myproject/src","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject:
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
exitCode:: ExitStatus.undefined

View file

@ -0,0 +1,191 @@
Input::
//// [/a/lib/lib.d.ts]
/// <reference no-default-lib="true"/>
interface Boolean {}
interface Function {}
interface CallableFunction {}
interface NewableFunction {}
interface IArguments {}
interface Number { toExponential: any; }
interface Object {}
interface RegExp {}
interface String { charAt: any; }
interface Array<T> { length: number; [n: number]: T; }
//// [/user/username/projects/myproject/src/main.ts]
import { foo } from "bar"; foo();
//// [/user/username/projects/myproject/node_modules/bar/index.d.ts]
export { foo } from "./foo";
//// [/user/username/projects/myproject/node_modules/bar/foo.d.ts]
export function foo(): string;
//// [/user/username/projects/myproject/node_modules/bar/fooBar.d.ts]
export function fooBar(): string;
//// [/user/username/projects/myproject/node_modules/bar/temp/index.d.ts]
export function temp(): string;
//// [/user/username/projects/myproject/tsconfig.json]
{"exclude":["node_modules"],"watchOptions":{"excludeDirectories":["**/temp"]}}
/a/lib/tsc.js -w -extendedDiagnostics
Output::
[12:00:37 AM] Starting compilation in watch mode...
Current directory: /user/username/projects/myproject CaseSensitiveFileNames: false
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Config file
Synchronizing program
CreatingProgramWith::
roots: ["/user/username/projects/myproject/src/main.ts"]
options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src/main.ts 250 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Source file
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/bar/index.d.ts 250 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Source file
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Failed Lookup Locations
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Failed Lookup Locations
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/bar/foo.d.ts 250 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Source file
FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 250 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Source file
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src 1 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Failed Lookup Locations
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src 1 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Failed Lookup Locations
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Type roots
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Type roots
[12:00:40 AM] Found 0 errors. Watching for file changes.
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Wild card directory
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Wild card directory
Program root files: ["/user/username/projects/myproject/src/main.ts"]
Program options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
Program structureReused: Not
Program files::
/a/lib/lib.d.ts
/user/username/projects/myproject/node_modules/bar/foo.d.ts
/user/username/projects/myproject/node_modules/bar/index.d.ts
/user/username/projects/myproject/src/main.ts
Semantic diagnostics in builder refreshed for::
/a/lib/lib.d.ts
/user/username/projects/myproject/node_modules/bar/foo.d.ts
/user/username/projects/myproject/node_modules/bar/index.d.ts
/user/username/projects/myproject/src/main.ts
WatchedFiles::
/user/username/projects/myproject/tsconfig.json:
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250}
/user/username/projects/myproject/src/main.ts:
{"fileName":"/user/username/projects/myproject/src/main.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/index.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/index.d.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/foo.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/foo.d.ts","pollingInterval":250}
/a/lib/lib.d.ts:
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250}
FsWatches::
/user/username/projects/myproject/node_modules:
{"directoryName":"/user/username/projects/myproject/node_modules","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/bar:
{"directoryName":"/user/username/projects/myproject/node_modules/bar","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/src:
{"directoryName":"/user/username/projects/myproject/src","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/@types:
{"directoryName":"/user/username/projects/myproject/node_modules/@types","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject:
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
FsWatchesRecursive::
exitCode:: ExitStatus.undefined
//// [/user/username/projects/myproject/src/main.js]
"use strict";
exports.__esModule = true;
var bar_1 = require("bar");
bar_1.foo();
Change:: Directory watch updates because of main.js creation
Input::
Output::
sysLog:: onTimerToUpdateChildWatches:: 1
sysLog:: invokingWatchers:: Elapsed:: *ms:: 0
DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js 0:: WatchInfo: /user/username/projects/myproject/src 1 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Failed Lookup Locations
Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js 0:: WatchInfo: /user/username/projects/myproject/src 1 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Failed Lookup Locations
DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js 0:: WatchInfo: /user/username/projects/myproject 1 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Wild card directory
Project: /user/username/projects/myproject/tsconfig.json Detected file add/remove of non supported extension: /user/username/projects/myproject/src/main.js
Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js 0:: WatchInfo: /user/username/projects/myproject 1 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Wild card directory
sysLog:: Elapsed:: *ms:: onTimerToUpdateChildWatches:: 0 undefined
WatchedFiles::
/user/username/projects/myproject/tsconfig.json:
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250}
/user/username/projects/myproject/src/main.ts:
{"fileName":"/user/username/projects/myproject/src/main.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/index.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/index.d.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/foo.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/foo.d.ts","pollingInterval":250}
/a/lib/lib.d.ts:
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250}
FsWatches::
/user/username/projects/myproject/node_modules:
{"directoryName":"/user/username/projects/myproject/node_modules","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/bar:
{"directoryName":"/user/username/projects/myproject/node_modules/bar","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/src:
{"directoryName":"/user/username/projects/myproject/src","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/@types:
{"directoryName":"/user/username/projects/myproject/node_modules/@types","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject:
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
FsWatchesRecursive::
exitCode:: ExitStatus.undefined
Change:: add new folder to temp
Input::
//// [/user/username/projects/myproject/node_modules/bar/temp/fooBar/index.d.ts]
export function temp(): string;
Output::
WatchedFiles::
/user/username/projects/myproject/tsconfig.json:
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250}
/user/username/projects/myproject/src/main.ts:
{"fileName":"/user/username/projects/myproject/src/main.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/index.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/index.d.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/foo.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/foo.d.ts","pollingInterval":250}
/a/lib/lib.d.ts:
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250}
FsWatches::
/user/username/projects/myproject/node_modules:
{"directoryName":"/user/username/projects/myproject/node_modules","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/bar:
{"directoryName":"/user/username/projects/myproject/node_modules/bar","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/src:
{"directoryName":"/user/username/projects/myproject/src","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/@types:
{"directoryName":"/user/username/projects/myproject/node_modules/@types","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject:
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
FsWatchesRecursive::
exitCode:: ExitStatus.undefined

View file

@ -0,0 +1,165 @@
Input::
//// [/a/lib/lib.d.ts]
/// <reference no-default-lib="true"/>
interface Boolean {}
interface Function {}
interface CallableFunction {}
interface NewableFunction {}
interface IArguments {}
interface Number { toExponential: any; }
interface Object {}
interface RegExp {}
interface String { charAt: any; }
interface Array<T> { length: number; [n: number]: T; }
//// [/user/username/projects/myproject/src/main.ts]
import { foo } from "bar"; foo();
//// [/user/username/projects/myproject/node_modules/bar/index.d.ts]
export { foo } from "./foo";
//// [/user/username/projects/myproject/node_modules/bar/foo.d.ts]
export function foo(): string;
//// [/user/username/projects/myproject/node_modules/bar/fooBar.d.ts]
export function fooBar(): string;
//// [/user/username/projects/myproject/node_modules/bar/temp/index.d.ts]
export function temp(): string;
//// [/user/username/projects/myproject/tsconfig.json]
{"exclude":["node_modules"],"watchOptions":{"excludeDirectories":["**/temp"]}}
/a/lib/tsc.js -w
Output::
>> Screen clear
[12:00:37 AM] Starting compilation in watch mode...
[12:00:40 AM] Found 0 errors. Watching for file changes.
Program root files: ["/user/username/projects/myproject/src/main.ts"]
Program options: {"watch":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
Program structureReused: Not
Program files::
/a/lib/lib.d.ts
/user/username/projects/myproject/node_modules/bar/foo.d.ts
/user/username/projects/myproject/node_modules/bar/index.d.ts
/user/username/projects/myproject/src/main.ts
Semantic diagnostics in builder refreshed for::
/a/lib/lib.d.ts
/user/username/projects/myproject/node_modules/bar/foo.d.ts
/user/username/projects/myproject/node_modules/bar/index.d.ts
/user/username/projects/myproject/src/main.ts
WatchedFiles::
/user/username/projects/myproject/tsconfig.json:
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250}
/user/username/projects/myproject/src/main.ts:
{"fileName":"/user/username/projects/myproject/src/main.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/index.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/index.d.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/foo.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/foo.d.ts","pollingInterval":250}
/a/lib/lib.d.ts:
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250}
FsWatches::
/user/username/projects/myproject/node_modules:
{"directoryName":"/user/username/projects/myproject/node_modules","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/bar:
{"directoryName":"/user/username/projects/myproject/node_modules/bar","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/src:
{"directoryName":"/user/username/projects/myproject/src","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/@types:
{"directoryName":"/user/username/projects/myproject/node_modules/@types","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject:
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
FsWatchesRecursive::
exitCode:: ExitStatus.undefined
//// [/user/username/projects/myproject/src/main.js]
"use strict";
exports.__esModule = true;
var bar_1 = require("bar");
bar_1.foo();
Change:: Directory watch updates because of main.js creation
Input::
Output::
WatchedFiles::
/user/username/projects/myproject/tsconfig.json:
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250}
/user/username/projects/myproject/src/main.ts:
{"fileName":"/user/username/projects/myproject/src/main.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/index.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/index.d.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/foo.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/foo.d.ts","pollingInterval":250}
/a/lib/lib.d.ts:
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250}
FsWatches::
/user/username/projects/myproject/node_modules:
{"directoryName":"/user/username/projects/myproject/node_modules","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/bar:
{"directoryName":"/user/username/projects/myproject/node_modules/bar","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/src:
{"directoryName":"/user/username/projects/myproject/src","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/@types:
{"directoryName":"/user/username/projects/myproject/node_modules/@types","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject:
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
FsWatchesRecursive::
exitCode:: ExitStatus.undefined
Change:: add new folder to temp
Input::
//// [/user/username/projects/myproject/node_modules/bar/temp/fooBar/index.d.ts]
export function temp(): string;
Output::
WatchedFiles::
/user/username/projects/myproject/tsconfig.json:
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250}
/user/username/projects/myproject/src/main.ts:
{"fileName":"/user/username/projects/myproject/src/main.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/index.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/index.d.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/foo.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/foo.d.ts","pollingInterval":250}
/a/lib/lib.d.ts:
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250}
FsWatches::
/user/username/projects/myproject/node_modules:
{"directoryName":"/user/username/projects/myproject/node_modules","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/bar:
{"directoryName":"/user/username/projects/myproject/node_modules/bar","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/src:
{"directoryName":"/user/username/projects/myproject/src","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/@types:
{"directoryName":"/user/username/projects/myproject/node_modules/@types","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject:
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
FsWatchesRecursive::
exitCode:: ExitStatus.undefined

View file

@ -0,0 +1,116 @@
Input::
//// [/a/lib/lib.d.ts]
/// <reference no-default-lib="true"/>
interface Boolean {}
interface Function {}
interface CallableFunction {}
interface NewableFunction {}
interface IArguments {}
interface Number { toExponential: any; }
interface Object {}
interface RegExp {}
interface String { charAt: any; }
interface Array<T> { length: number; [n: number]: T; }
//// [/user/username/projects/myproject/src/main.ts]
import { foo } from "bar"; foo();
//// [/user/username/projects/myproject/node_modules/bar/index.d.ts]
export { foo } from "./foo";
//// [/user/username/projects/myproject/node_modules/bar/foo.d.ts]
export function foo(): string;
//// [/user/username/projects/myproject/node_modules/bar/fooBar.d.ts]
export function fooBar(): string;
//// [/user/username/projects/myproject/node_modules/bar/temp/index.d.ts]
export function temp(): string;
//// [/user/username/projects/myproject/tsconfig.json]
{"exclude":["node_modules"],"watchOptions":{"excludeDirectories":["node_modules"]}}
/a/lib/tsc.js -w
Output::
>> Screen clear
[12:00:37 AM] Starting compilation in watch mode...
[12:00:40 AM] Found 0 errors. Watching for file changes.
Program root files: ["/user/username/projects/myproject/src/main.ts"]
Program options: {"watch":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
Program structureReused: Not
Program files::
/a/lib/lib.d.ts
/user/username/projects/myproject/node_modules/bar/foo.d.ts
/user/username/projects/myproject/node_modules/bar/index.d.ts
/user/username/projects/myproject/src/main.ts
Semantic diagnostics in builder refreshed for::
/a/lib/lib.d.ts
/user/username/projects/myproject/node_modules/bar/foo.d.ts
/user/username/projects/myproject/node_modules/bar/index.d.ts
/user/username/projects/myproject/src/main.ts
WatchedFiles::
/user/username/projects/myproject/tsconfig.json:
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250}
/user/username/projects/myproject/src/main.ts:
{"fileName":"/user/username/projects/myproject/src/main.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/index.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/index.d.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/foo.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/foo.d.ts","pollingInterval":250}
/a/lib/lib.d.ts:
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250}
FsWatches::
FsWatchesRecursive::
/user/username/projects/myproject/src:
{"directoryName":"/user/username/projects/myproject/src","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject:
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
exitCode:: ExitStatus.undefined
//// [/user/username/projects/myproject/src/main.js]
"use strict";
exports.__esModule = true;
var bar_1 = require("bar");
bar_1.foo();
Change:: delete fooBar
Input::
//// [/user/username/projects/myproject/node_modules/bar/fooBar.d.ts] deleted
Output::
WatchedFiles::
/user/username/projects/myproject/tsconfig.json:
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250}
/user/username/projects/myproject/src/main.ts:
{"fileName":"/user/username/projects/myproject/src/main.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/index.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/index.d.ts","pollingInterval":250}
/user/username/projects/myproject/node_modules/bar/foo.d.ts:
{"fileName":"/user/username/projects/myproject/node_modules/bar/foo.d.ts","pollingInterval":250}
/a/lib/lib.d.ts:
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250}
FsWatches::
FsWatchesRecursive::
/user/username/projects/myproject/src:
{"directoryName":"/user/username/projects/myproject/src","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject:
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
exitCode:: ExitStatus.undefined

View file

@ -0,0 +1,137 @@
Input::
//// [/a/lib/lib.d.ts]
/// <reference no-default-lib="true"/>
interface Boolean {}
interface Function {}
interface CallableFunction {}
interface NewableFunction {}
interface IArguments {}
interface Number { toExponential: any; }
interface Object {}
interface RegExp {}
interface String { charAt: any; }
interface Array<T> { length: number; [n: number]: T; }
//// [/user/username/projects/myproject/src/main.ts]
import { foo } from "bar"; foo();
//// [/user/username/projects/myproject/node_modules/bar/index.d.ts]
export { foo } from "./foo";
//// [/user/username/projects/myproject/node_modules/bar/foo.d.ts]
export function foo(): string;
//// [/user/username/projects/myproject/node_modules/bar/fooBar.d.ts]
export function fooBar(): string;
//// [/user/username/projects/myproject/node_modules/bar/temp/index.d.ts]
export function temp(): string;
//// [/user/username/projects/myproject/tsconfig.json]
{"exclude":["node_modules"],"watchOptions":{"excludeFiles":["node_modules/*"]}}
/a/lib/tsc.js -w -extendedDiagnostics
Output::
[12:00:37 AM] Starting compilation in watch mode...
Current directory: /user/username/projects/myproject CaseSensitiveFileNames: false
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Config file
Synchronizing program
CreatingProgramWith::
roots: ["/user/username/projects/myproject/src/main.ts"]
options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src/main.ts 250 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Source file
ExcludeWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/bar/index.d.ts 250 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Source file
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Failed Lookup Locations
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Failed Lookup Locations
ExcludeWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/bar/foo.d.ts 250 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Source file
FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 250 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Source file
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src 1 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Failed Lookup Locations
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src 1 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Failed Lookup Locations
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Type roots
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Type roots
DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js :: WatchInfo: /user/username/projects/myproject/src 1 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Failed Lookup Locations
Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js :: WatchInfo: /user/username/projects/myproject/src 1 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Failed Lookup Locations
[12:00:40 AM] Found 0 errors. Watching for file changes.
DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Wild card directory
Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Wild card directory
Program root files: ["/user/username/projects/myproject/src/main.ts"]
Program options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
Program structureReused: Not
Program files::
/a/lib/lib.d.ts
/user/username/projects/myproject/node_modules/bar/foo.d.ts
/user/username/projects/myproject/node_modules/bar/index.d.ts
/user/username/projects/myproject/src/main.ts
Semantic diagnostics in builder refreshed for::
/a/lib/lib.d.ts
/user/username/projects/myproject/node_modules/bar/foo.d.ts
/user/username/projects/myproject/node_modules/bar/index.d.ts
/user/username/projects/myproject/src/main.ts
WatchedFiles::
/user/username/projects/myproject/tsconfig.json:
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250}
/user/username/projects/myproject/src/main.ts:
{"fileName":"/user/username/projects/myproject/src/main.ts","pollingInterval":250}
/a/lib/lib.d.ts:
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250}
FsWatches::
FsWatchesRecursive::
/user/username/projects/myproject/node_modules:
{"directoryName":"/user/username/projects/myproject/node_modules","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/src:
{"directoryName":"/user/username/projects/myproject/src","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/@types:
{"directoryName":"/user/username/projects/myproject/node_modules/@types","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject:
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
exitCode:: ExitStatus.undefined
//// [/user/username/projects/myproject/src/main.js]
"use strict";
exports.__esModule = true;
var bar_1 = require("bar");
bar_1.foo();
Change:: Change foo
Input::
//// [/user/username/projects/myproject/node_modules/bar/foo.d.ts]
export function fooBar(): string;
Output::
WatchedFiles::
/user/username/projects/myproject/tsconfig.json:
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250}
/user/username/projects/myproject/src/main.ts:
{"fileName":"/user/username/projects/myproject/src/main.ts","pollingInterval":250}
/a/lib/lib.d.ts:
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250}
FsWatches::
FsWatchesRecursive::
/user/username/projects/myproject/node_modules:
{"directoryName":"/user/username/projects/myproject/node_modules","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/src:
{"directoryName":"/user/username/projects/myproject/src","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/@types:
{"directoryName":"/user/username/projects/myproject/node_modules/@types","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject:
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
exitCode:: ExitStatus.undefined

View file

@ -0,0 +1,118 @@
Input::
//// [/a/lib/lib.d.ts]
/// <reference no-default-lib="true"/>
interface Boolean {}
interface Function {}
interface CallableFunction {}
interface NewableFunction {}
interface IArguments {}
interface Number { toExponential: any; }
interface Object {}
interface RegExp {}
interface String { charAt: any; }
interface Array<T> { length: number; [n: number]: T; }
//// [/user/username/projects/myproject/src/main.ts]
import { foo } from "bar"; foo();
//// [/user/username/projects/myproject/node_modules/bar/index.d.ts]
export { foo } from "./foo";
//// [/user/username/projects/myproject/node_modules/bar/foo.d.ts]
export function foo(): string;
//// [/user/username/projects/myproject/node_modules/bar/fooBar.d.ts]
export function fooBar(): string;
//// [/user/username/projects/myproject/node_modules/bar/temp/index.d.ts]
export function temp(): string;
//// [/user/username/projects/myproject/tsconfig.json]
{"exclude":["node_modules"],"watchOptions":{"excludeFiles":["node_modules/*"]}}
/a/lib/tsc.js -w
Output::
>> Screen clear
[12:00:37 AM] Starting compilation in watch mode...
[12:00:40 AM] Found 0 errors. Watching for file changes.
Program root files: ["/user/username/projects/myproject/src/main.ts"]
Program options: {"watch":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"}
Program structureReused: Not
Program files::
/a/lib/lib.d.ts
/user/username/projects/myproject/node_modules/bar/foo.d.ts
/user/username/projects/myproject/node_modules/bar/index.d.ts
/user/username/projects/myproject/src/main.ts
Semantic diagnostics in builder refreshed for::
/a/lib/lib.d.ts
/user/username/projects/myproject/node_modules/bar/foo.d.ts
/user/username/projects/myproject/node_modules/bar/index.d.ts
/user/username/projects/myproject/src/main.ts
WatchedFiles::
/user/username/projects/myproject/tsconfig.json:
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250}
/user/username/projects/myproject/src/main.ts:
{"fileName":"/user/username/projects/myproject/src/main.ts","pollingInterval":250}
/a/lib/lib.d.ts:
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250}
FsWatches::
FsWatchesRecursive::
/user/username/projects/myproject/node_modules:
{"directoryName":"/user/username/projects/myproject/node_modules","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/src:
{"directoryName":"/user/username/projects/myproject/src","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/@types:
{"directoryName":"/user/username/projects/myproject/node_modules/@types","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject:
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
exitCode:: ExitStatus.undefined
//// [/user/username/projects/myproject/src/main.js]
"use strict";
exports.__esModule = true;
var bar_1 = require("bar");
bar_1.foo();
Change:: Change foo
Input::
//// [/user/username/projects/myproject/node_modules/bar/foo.d.ts]
export function fooBar(): string;
Output::
WatchedFiles::
/user/username/projects/myproject/tsconfig.json:
{"fileName":"/user/username/projects/myproject/tsconfig.json","pollingInterval":250}
/user/username/projects/myproject/src/main.ts:
{"fileName":"/user/username/projects/myproject/src/main.ts","pollingInterval":250}
/a/lib/lib.d.ts:
{"fileName":"/a/lib/lib.d.ts","pollingInterval":250}
FsWatches::
FsWatchesRecursive::
/user/username/projects/myproject/node_modules:
{"directoryName":"/user/username/projects/myproject/node_modules","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/src:
{"directoryName":"/user/username/projects/myproject/src","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject/node_modules/@types:
{"directoryName":"/user/username/projects/myproject/node_modules/@types","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
/user/username/projects/myproject:
{"directoryName":"/user/username/projects/myproject","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}}
exitCode:: ExitStatus.undefined