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:
parent
6c6315db58
commit
76cf8fd78b
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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] || "";
|
||||
|
|
|
@ -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[]) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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: [] }
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
]
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
139
src/testRunner/unittests/tsserver/reloadProjects.ts
Normal file
139
src/testRunner/unittests/tsserver/reloadProjects.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -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", () => {
|
||||
|
|
|
@ -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: () => {
|
||||
|
|
|
@ -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;
|
||||
/**
|
||||
|
|
|
@ -2881,6 +2881,8 @@ declare namespace ts {
|
|||
watchDirectory?: WatchDirectoryKind;
|
||||
fallbackPolling?: PollingWatchKind;
|
||||
synchronousWatchDirectory?: boolean;
|
||||
excludeDirectories?: string[];
|
||||
excludeFiles?: string[];
|
||||
[option: string]: CompilerOptionsValue | undefined;
|
||||
}
|
||||
export interface TypeAcquisition {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {},
|
||||
"watchOptions": {
|
||||
"excludeDirectories": []
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {},
|
||||
"watchOptions": {
|
||||
"excludeFiles": []
|
||||
}
|
||||
}
|
|
@ -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::
|
||||
[[90m12:00:37 AM[0m] 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
|
||||
[[90m12:00:40 AM[0m] 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
|
||||
|
|
@ -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::
|
||||
[[90m12:00:37 AM[0m] 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
|
||||
[[90m12:00:40 AM[0m] 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
|
||||
|
|
@ -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
|
||||
[[90m12:00:37 AM[0m] Starting compilation in watch mode...
|
||||
|
||||
[[90m12:00:40 AM[0m] 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
|
||||
|
|
@ -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
|
||||
[[90m12:00:37 AM[0m] Starting compilation in watch mode...
|
||||
|
||||
[[90m12:00:40 AM[0m] 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
|
||||
|
|
@ -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::
|
||||
[[90m12:00:37 AM[0m] 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
|
||||
[[90m12:00:40 AM[0m] 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
|
||||
|
|
@ -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
|
||||
[[90m12:00:37 AM[0m] Starting compilation in watch mode...
|
||||
|
||||
[[90m12:00:40 AM[0m] 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
|
||||
|
Loading…
Reference in a new issue