From 869abb1414a96e9622f5b04166df53b470bc3a88 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 18 Jun 2020 16:21:17 -0700 Subject: [PATCH 01/19] Parse excludeDirectories and excludeFiles --- src/compiler/commandLineParser.ts | 110 +++++++++++++----- src/compiler/types.ts | 3 + src/server/protocol.ts | 2 + .../unittests/config/commandLineParsing.ts | 106 +++++++++++++++++ .../config/tsconfigParsingWatchOptions.ts | 61 +++++++++- .../reference/api/tsserverlibrary.d.ts | 4 + tests/baselines/reference/api/typescript.d.ts | 2 + .../excludeDirectories/tsconfig.json | 6 + .../excludeFiles/tsconfig.json | 6 + 9 files changed, 265 insertions(+), 35 deletions(-) create mode 100644 tests/baselines/reference/showConfig/Shows tsconfig for single option/excludeDirectories/tsconfig.json create mode 100644 tests/baselines/reference/showConfig/Shows tsconfig for single option/excludeFiles/tsconfig.json diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index e76ffc766b73d..7e9822608622a 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -119,6 +119,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: "excludeDirectories", + 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: "excludeFiles", + 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 */ @@ -1165,9 +1189,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(opt.element, v, errors)); } @@ -1297,7 +1321,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 { @@ -1319,20 +1343,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": @@ -1777,9 +1801,10 @@ namespace ts { function convertArrayLiteralExpressionToJson( elements: NodeArray, 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 @@ -1787,18 +1812,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 validateValueOk(/*value*/ true); case SyntaxKind.FalseKeyword: reportInvalidOptionValue(option && option.type !== "boolean"); - return false; + return validateValueOk(/*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 validateValueOk(/*value*/ null); // eslint-disable-line no-null/no-null case SyntaxKind.StringLiteral: if (!isDoubleQuotedString(valueExpression)) { @@ -1816,20 +1842,21 @@ namespace ts { (message, arg0, arg1) => createDiagnosticForNodeInSourceFile(sourceFile, valueExpression, message, arg0, arg1) ) ); + invalidReported = true; } } - return text; + return validateValueOk(text); case SyntaxKind.NumericLiteral: reportInvalidOptionValue(option && option.type !== "number"); - return Number((valueExpression).text); + return validateValueOk(Number((valueExpression).text)); case SyntaxKind.PrefixUnaryExpression: if ((valueExpression).operator !== SyntaxKind.MinusToken || (valueExpression).operand.kind !== SyntaxKind.NumericLiteral) { break; // not valid JSON syntax } reportInvalidOptionValue(option && option.type !== "number"); - return -Number(((valueExpression).operand).text); + return validateValueOk(-Number(((valueExpression).operand).text)); case SyntaxKind.ObjectLiteralExpression: reportInvalidOptionValue(option && option.type !== "object"); @@ -1843,20 +1870,20 @@ namespace ts { // If need arises, we can modify this interface and callbacks as needed if (option) { const { elementOptions, extraKeyDiagnostics, name: optionName } = option; - return convertObjectLiteralExpressionToJson(objectLiteralExpression, - elementOptions, extraKeyDiagnostics, optionName); + return validateValueOk(convertObjectLiteralExpressionToJson(objectLiteralExpression, + elementOptions, extraKeyDiagnostics, optionName)); } else { - return convertObjectLiteralExpressionToJson( + return validateValueOk(convertObjectLiteralExpressionToJson( objectLiteralExpression, /* knownOptions*/ undefined, - /*extraKeyDiagnosticMessage */ undefined, /*parentOption*/ undefined); + /*extraKeyDiagnosticMessage */ undefined, /*parentOption*/ undefined)); } case SyntaxKind.ArrayLiteralExpression: reportInvalidOptionValue(option && option.type !== "list"); - return convertArrayLiteralExpressionToJson( + return validateValueOk(convertArrayLiteralExpressionToJson( (valueExpression).elements, - option && (option).element); + option && (option).element)); } // Not in expected format @@ -1869,9 +1896,21 @@ namespace ts { return undefined; + function validateValueOk(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; } } } @@ -2782,7 +2821,8 @@ namespace ts { else if (!isString(optType)) { return convertJsonOptionOfCustomType(opt, 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))); @@ -2814,12 +2854,20 @@ namespace ts { return value; } + function validateJsonOptionValue(opt: CommandLineOption, value: T, errors: Push): 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) { 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)); @@ -2921,11 +2969,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 @@ -3068,11 +3116,11 @@ namespace ts { return !hasExtension(pathToCheck) && excludeRegex.test(ensureTrailingDirectorySeparator(pathToCheck)); } - function validateSpecs(specs: readonly string[], errors: Push, allowTrailingRecursion: boolean, jsonSourceFile: TsConfigSourceFile | undefined, specKey: string): readonly string[] { + function validateSpecs(specs: readonly string[], errors: Push, disallowTrailingRecursion: boolean, jsonSourceFile: TsConfigSourceFile | undefined, specKey: string): readonly string[] { return specs.filter(spec => { - 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; }); @@ -3085,12 +3133,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]; } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index dc9abbfd13411..5690800e78370 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5749,6 +5749,8 @@ namespace ts { watchDirectory?: WatchDirectoryKind; fallbackPolling?: PollingWatchKind; synchronousWatchDirectory?: boolean; + excludeDirectories?: string[]; + excludeFiles?: string[]; [option: string]: CompilerOptionsValue | undefined; } @@ -5915,6 +5917,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 */ diff --git a/src/server/protocol.ts b/src/server/protocol.ts index de974058fa167..76630a4de91ea 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1492,6 +1492,8 @@ namespace ts.server.protocol { watchDirectory?: WatchDirectoryKind | ts.WatchDirectoryKind; fallbackPolling?: PollingWatchKind | ts.PollingWatchKind; synchronousWatchDirectory?: boolean; + excludeDirectories?: string[]; + excludeFiles?: string[]; [option: string]: CompilerOptionsValue | undefined; } diff --git a/src/testRunner/unittests/config/commandLineParsing.ts b/src/testRunner/unittests/config/commandLineParsing.ts index beff197912bf0..b1e5dc8be35e9 100644 --- a/src/testRunner/unittests/config/commandLineParsing.ts +++ b/src/testRunner/unittests/config/commandLineParsing.ts @@ -656,6 +656,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: [] } + }); + }); }); }); @@ -905,6 +963,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: [] } + }); + }); }); }); } diff --git a/src/testRunner/unittests/config/tsconfigParsingWatchOptions.ts b/src/testRunner/unittests/config/tsconfigParsingWatchOptions.ts index 83e224e492527..120dd42d2c542 100644 --- a/src/testRunner/unittests/config/tsconfigParsingWatchOptions.ts +++ b/src/testRunner/unittests/config/tsconfigParsingWatchOptions.ts @@ -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 + } + ] + }, ]); }); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 4e3003e58c173..f518dd8ee6b19 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2822,6 +2822,8 @@ declare namespace ts { watchDirectory?: WatchDirectoryKind; fallbackPolling?: PollingWatchKind; synchronousWatchDirectory?: boolean; + excludeDirectories?: string[]; + excludeFiles?: string[]; [option: string]: CompilerOptionsValue | undefined; } export interface TypeAcquisition { @@ -7456,6 +7458,8 @@ declare namespace ts.server.protocol { watchDirectory?: WatchDirectoryKind | ts.WatchDirectoryKind; fallbackPolling?: PollingWatchKind | ts.PollingWatchKind; synchronousWatchDirectory?: boolean; + excludeDirectories?: string[]; + excludeFiles?: string[]; [option: string]: CompilerOptionsValue | undefined; } /** diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 2c90be1c80b86..3b719eb263827 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2822,6 +2822,8 @@ declare namespace ts { watchDirectory?: WatchDirectoryKind; fallbackPolling?: PollingWatchKind; synchronousWatchDirectory?: boolean; + excludeDirectories?: string[]; + excludeFiles?: string[]; [option: string]: CompilerOptionsValue | undefined; } export interface TypeAcquisition { diff --git a/tests/baselines/reference/showConfig/Shows tsconfig for single option/excludeDirectories/tsconfig.json b/tests/baselines/reference/showConfig/Shows tsconfig for single option/excludeDirectories/tsconfig.json new file mode 100644 index 0000000000000..743c9639c9f38 --- /dev/null +++ b/tests/baselines/reference/showConfig/Shows tsconfig for single option/excludeDirectories/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": {}, + "watchOptions": { + "excludeDirectories": [] + } +} diff --git a/tests/baselines/reference/showConfig/Shows tsconfig for single option/excludeFiles/tsconfig.json b/tests/baselines/reference/showConfig/Shows tsconfig for single option/excludeFiles/tsconfig.json new file mode 100644 index 0000000000000..3d35f093f6a68 --- /dev/null +++ b/tests/baselines/reference/showConfig/Shows tsconfig for single option/excludeFiles/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": {}, + "watchOptions": { + "excludeFiles": [] + } +} From b8727a99373c66e7ba610bef1e284683d91a88ed Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 22 Jun 2020 15:07:50 -0700 Subject: [PATCH 02/19] Use watch factory in typings installer --- src/jsTyping/types.ts | 4 ++-- src/typingsInstallerCore/typingsInstaller.ts | 24 +++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/jsTyping/types.ts b/src/jsTyping/types.ts index 34f69d0d624df..62e6fb2cf50d9 100644 --- a/src/jsTyping/types.ts +++ b/src/jsTyping/types.ts @@ -83,8 +83,8 @@ 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; + 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 { diff --git a/src/typingsInstallerCore/typingsInstaller.ts b/src/typingsInstallerCore/typingsInstaller.ts index 770d0bc163cd3..0ef1315688740 100644 --- a/src/typingsInstallerCore/typingsInstaller.ts +++ b/src/typingsInstallerCore/typingsInstaller.ts @@ -86,6 +86,10 @@ namespace ts.server.typingsInstaller { type ProjectWatchers = Map & { isInvoked?: boolean; }; + function getDetailWatchInfo(projectName: string, watchers: ProjectWatchers) { + return `Project: ${projectName} watcher already invoked: ${watchers.isInvoked}`; + } + export abstract class TypingsInstaller { private readonly packageNameToTypingLocation: Map = createMap(); private readonly missingTypingsSet: Map = createMap(); @@ -100,6 +104,8 @@ namespace ts.server.typingsInstaller { private inFlightRequestCount = 0; abstract readonly typesRegistry: Map>; + /*@internal*/ + private readonly watchFactory: WatchFactory; 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(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(this.installTypingHost as WatchFileHost, 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(this.installTypingHost as WatchDirectoryHost, 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: () => { From ac325def2619c944bced63d9792fcf92e10a4d05 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 23 Jun 2020 12:15:26 -0700 Subject: [PATCH 03/19] Some refactoring for watchFactory --- src/compiler/tsbuildPublic.ts | 18 +- src/compiler/watch.ts | 4 +- src/compiler/watchPublic.ts | 26 ++- src/compiler/watchUtilities.ts | 180 +++++++++---------- src/server/editorServices.ts | 50 ++---- src/server/project.ts | 5 - src/typingsInstallerCore/typingsInstaller.ts | 6 +- 7 files changed, 126 insertions(+), 163 deletions(-) diff --git a/src/compiler/tsbuildPublic.ts b/src/compiler/tsbuildPublic.ts index 4b15f22e6ef7b..72cc099d30f23 100644 --- a/src/compiler/tsbuildPublic.ts +++ b/src/compiler/tsbuildPublic.ts @@ -224,7 +224,7 @@ namespace ts { originalGetSourceFile: CompilerHost["getSourceFile"]; } - interface SolutionBuilderState { + interface SolutionBuilderState extends WatchFactory { readonly host: SolutionBuilderHost; readonly hostWithWatch: SolutionBuilderWithWatchHost; readonly currentDirectory: string; @@ -271,9 +271,6 @@ namespace ts { timerToBuildInvalidatedProject: any; reportFileChangeDetected: boolean; - watchFile: WatchFile; - watchFilePath: WatchFilePath; - watchDirectory: WatchDirectory; writeLog: (s: string) => void; } @@ -297,7 +294,7 @@ namespace ts { loadWithLocalCache(Debug.checkEachDefined(moduleNames), containingFile, redirectedReference, loader); } - const { watchFile, watchFilePath, watchDirectory, writeLog } = createWatchFactory(hostWithWatch, options); + const { watchFile, watchDirectory, writeLog } = createWatchFactory(hostWithWatch, options); const state: SolutionBuilderState = { host, @@ -346,7 +343,6 @@ namespace ts { timerToBuildInvalidatedProject: undefined, reportFileChangeDetected: false, watchFile, - watchFilePath, watchDirectory, writeLog, }; @@ -1796,7 +1792,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); @@ -1814,7 +1809,6 @@ namespace ts { getOrCreateValueMapFromConfigFileMap(state.allWatchedWildcardDirectories, resolvedPath), createMapFromTemplate(parsed.configFileSpecs!.wildcardDirectories), (dir, flags) => state.watchDirectory( - state.hostWithWatch, dir, fileOrDirectory => { if (isIgnoredFileFromWildCardWatching({ @@ -1846,13 +1840,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 ), @@ -1927,9 +1919,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[]) { diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 724d57f48b29d..bb0a584db8999 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -270,10 +270,10 @@ namespace ts { writeLog: (s: string) => void; } - export function createWatchFactory(host: { trace?(s: string): void; }, options: { extendedDiagnostics?: boolean; diagnostics?: boolean; }) { + export function createWatchFactory(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(watchLogLevel, writeLog) as WatchFactory; + const result = getWatchFactory(host, watchLogLevel, writeLog) as WatchFactory; result.writeLog = writeLog; return result; } diff --git a/src/compiler/watchPublic.ts b/src/compiler/watchPublic.ts index cfa951c0cdaec..e52120bf9a668 100644 --- a/src/compiler/watchPublic.ts +++ b/src/compiler/watchPublic.ts @@ -283,13 +283,13 @@ namespace ts { newLine = updateNewLine(); } - const { watchFile, watchFilePath, watchDirectory, writeLog } = createWatchFactory(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); diff --git a/src/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts index 9f609d5a179ed..0e9f9c6c4666b 100644 --- a/src/compiler/watchUtilities.ts +++ b/src/compiler/watchUtilities.ts @@ -420,117 +420,103 @@ 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 = (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 = (host: WatchFileHost, file: string, callback: FilePathWatcherCallback, pollingInterval: PollingInterval, options: WatchOptions | undefined, path: Path, detailInfo1: X, detailInfo2?: Y) => FileWatcher; - export type WatchDirectory = (host: WatchDirectoryHost, directory: string, callback: DirectoryWatcherCallback, flags: WatchDirectoryFlags, options: WatchOptions | undefined, detailInfo1: X, detailInfo2?: Y) => FileWatcher; - - export interface WatchFactory { - watchFile: WatchFile; - watchFilePath: WatchFilePath; - watchDirectory: WatchDirectory; - } - export function getWatchFactory(watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo): WatchFactory { - return getWatchFactoryWith(watchLogLevel, log, getDetailWatchInfo, watchFile, watchDirectory); + export interface WatchFactory { + 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( - watchLogLevel: WatchLogLevel, - log: (s: string) => void, - getDetailWatchInfo: GetDetailWatchInfo | 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 { - const createFileWatcher: CreateFileWatcher = getCreateFileWatcher(watchLogLevel, watchFile); - const createFilePathWatcher: CreateFileWatcher = watchLogLevel === WatchLogLevel.None ? watchFilePath : createFileWatcher; - const createDirectoryWatcher: CreateFileWatcher = getCreateFileWatcher(watchLogLevel, watchDirectory); + export type GetDetailWatchInfo = (detailInfo1: X, detailInfo2: Y | undefined) => string; + export function getWatchFactory(host: WatchFactoryHost, watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo): WatchFactory { 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) + const plainInvokeFactory: WatchFactory = { + 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), }; - } - - 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 = (fileName: string, cbOptional?: T, passThrough?: U) => void; - type AddWatch = (host: H, file: string, cb: WatchCallback, flags: T, options: WatchOptions | undefined, passThrough?: V, detailInfo1?: undefined, detailInfo2?: undefined) => FileWatcher; - export type GetDetailWatchInfo = (detailInfo1: X, detailInfo2: Y | undefined) => string; - - type CreateFileWatcher = (host: H, file: string, cb: WatchCallback, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo | undefined) => FileWatcher; - function getCreateFileWatcher(watchLogLevel: WatchLogLevel, addWatch: AddWatch): CreateFileWatcher { - switch (watchLogLevel) { - case WatchLogLevel.None: - return addWatch; - case WatchLogLevel.TriggerOnly: - return createFileWatcherWithTriggerLogging; - case WatchLogLevel.Verbose: - return addWatch === watchDirectory ? createDirectoryWatcherWithLogging : createFileWatcherWithLogging; + const triggerInvokingFactory: WatchFactory | undefined = watchLogLevel !== WatchLogLevel.None ? + { + watchFile: createTriggerLoggingAddWatch("watchFile"), + watchDirectory: createTriggerLoggingAddWatch("watchDirectory") + } : + undefined; + return watchLogLevel === WatchLogLevel.Verbose ? + { + watchFile: createFileWatcherWithLogging, + watchDirectory: createDirectoryWatcherWithLogging + } : + triggerInvokingFactory || plainInvokeFactory; + + 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 createFileWatcherWithLogging(host: H, file: string, cb: WatchCallback, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo | 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 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(); + const watcher = triggerInvokingFactory!.watchDirectory(file, cb, flags, options, detailInfo1, detailInfo2); + const elapsed = timestamp() - start; + 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 createDirectoryWatcherWithLogging(host: H, file: string, cb: WatchCallback, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo | 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); + function createTriggerLoggingAddWatch>(key: T,): WatchFactory[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(); - watcher.close(); + cb.call(/*thisArg*/ undefined, ...args); const elapsed = timestamp() - start; - log(`Elapsed:: ${elapsed}ms ${watchInfo}`); - } - }; - } + log(`Elapsed:: ${elapsed}ms ${triggerredInfo}`); + }, flags, options, detailInfo1, detailInfo2); + } - function createFileWatcherWithTriggerLogging(host: H, file: string, cb: WatchCallback, flags: T, options: WatchOptions | undefined, passThrough: V | undefined, detailInfo1: X | undefined, detailInfo2: Y | undefined, addWatch: AddWatch, log: (s: string) => void, watchCaption: string, getDetailWatchInfo: GetDetailWatchInfo | 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); - const start = timestamp(); - cb(fileName, cbOptional, passThrough); - const elapsed = timestamp() - start; - log(`Elapsed:: ${elapsed}ms ${triggerredInfo}`); - }, flags, options); + function getWatchInfo(file: string, flags: T, options: WatchOptions | undefined, detailInfo1: X, detailInfo2: Y | undefined, getDetailWatchInfo: GetDetailWatchInfo | undefined) { + return `WatchInfo: ${file} ${flags} ${JSON.stringify(options)} ${getDetailWatchInfo ? getDetailWatchInfo(detailInfo1, detailInfo2) : detailInfo2 === undefined ? detailInfo1 : `${detailInfo1} ${detailInfo2}`}`; + } } export function getFallbackOptions(options: WatchOptions | undefined): WatchOptions { @@ -542,10 +528,6 @@ namespace ts { }; } - function getWatchInfo(file: string, flags: T, options: WatchOptions | undefined, detailInfo1: X, detailInfo2: Y | undefined, getDetailWatchInfo: GetDetailWatchInfo | undefined) { - return `WatchInfo: ${file} ${flags} ${JSON.stringify(options)} ${getDetailWatchInfo ? getDetailWatchInfo(detailInfo1, detailInfo2) : detailInfo2 === undefined ? detailInfo1 : `${detailInfo1} ${detailInfo2}`}`; - } - export function closeFileWatcherOf(objWithWatcher: T) { objWithWatcher.watcher.close(); } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index decb0d818b945..d190639962ac4 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -729,10 +729,9 @@ namespace ts.server { this.watchFactory = this.syntaxOnly ? { watchFile: returnNoopFileWatcher, - watchFilePath: returnNoopFileWatcher, watchDirectory: returnNoopFileWatcher, } : - getWatchFactory(watchLogLevel, log, getDetailWatchInfo); + getWatchFactory(this.host, watchLogLevel, log, getDetailWatchInfo); } toPath(fileName: string) { @@ -1079,26 +1078,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); } } @@ -1165,7 +1158,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); @@ -1588,7 +1580,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, @@ -2345,13 +2336,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 ); } @@ -2372,9 +2361,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; @@ -2425,7 +2413,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); } } @@ -2610,7 +2598,6 @@ namespace ts.server { private addMissingSourceMapFile(mapFileName: string, declarationInfoPath: Path) { const fileWatcher = this.watchFactory.watchFile( - this.host, mapFileName, () => { const declarationInfo = this.getScriptInfoForPath(declarationInfoPath); @@ -3725,7 +3712,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); diff --git a/src/server/project.ts b/src/server/project.ts index 688d7b2654e6c..9f585a394bb57 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -470,7 +470,6 @@ namespace ts.server { /*@internal*/ watchDirectoryOfFailedLookupLocation(directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags) { return this.projectService.watchFactory.watchDirectory( - this.projectService.host, directory, cb, flags, @@ -511,7 +510,6 @@ namespace ts.server { /*@internal*/ watchTypeRootsDirectory(directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags) { return this.projectService.watchFactory.watchDirectory( - this.projectService.host, directory, cb, flags, @@ -1235,7 +1233,6 @@ namespace ts.server { private addMissingFileWatcher(missingFilePath: Path) { const fileWatcher = this.projectService.watchFactory.watchFile( - this.projectService.host, missingFilePath, (fileName, eventKind) => { if (isConfiguredProject(this)) { @@ -1291,7 +1288,6 @@ namespace ts.server { return { generatedFilePath: this.toPath(generatedFile), watcher: this.projectService.watchFactory.watchFile( - this.projectService.host, generatedFile, () => { this.clearSourceMapperCache(); @@ -2041,7 +2037,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, diff --git a/src/typingsInstallerCore/typingsInstaller.ts b/src/typingsInstallerCore/typingsInstaller.ts index 0ef1315688740..2f627b236d1d6 100644 --- a/src/typingsInstallerCore/typingsInstaller.ts +++ b/src/typingsInstallerCore/typingsInstaller.ts @@ -120,7 +120,7 @@ namespace ts.server.typingsInstaller { if (isLoggingEnabled) { this.log.writeLine(`Global cache location '${globalCachePath}', safe file path '${safeListPath}', types map path ${typesMapLocation}`); } - this.watchFactory = getWatchFactory(isLoggingEnabled ? WatchLogLevel.Verbose : WatchLogLevel.None, s => this.log.writeLine(s), getDetailWatchInfo); + this.watchFactory = getWatchFactory(this.installTypingHost as WatchFactoryHost, isLoggingEnabled ? WatchLogLevel.Verbose : WatchLogLevel.None, s => this.log.writeLine(s), getDetailWatchInfo); this.processCacheLocation(this.globalCachePath); } @@ -439,13 +439,13 @@ namespace ts.server.typingsInstaller { this.log.writeLine(`${projectWatcherType}:: Added:: WatchInfo: ${path}`); } const watcher = projectWatcherType === ProjectWatcherType.FileWatcher ? - this.watchFactory.watchFile(this.installTypingHost as WatchFileHost, path, () => { + this.watchFactory.watchFile(path, () => { if (!watchers.isInvoked) { watchers.isInvoked = true; this.sendResponse({ projectName, kind: ActionInvalidate }); } }, PollingInterval.High, options, projectName, watchers) : - this.watchFactory.watchDirectory(this.installTypingHost as WatchDirectoryHost, path, f => { + this.watchFactory.watchDirectory(path, f => { if (watchers.isInvoked || !fileExtensionIs(f, Extension.Json)) { return; } From 9cd3e937162421fcbc477286e84aa48dcc8ef0dd Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 24 Jun 2020 12:16:56 -0700 Subject: [PATCH 04/19] Create Noop watcher if file or directory being watched is excluded --- src/compiler/commandLineParser.ts | 27 +++++++++++++++++- src/compiler/watchUtilities.ts | 46 +++++++++++++++++++++++++++++-- src/jsTyping/types.ts | 1 + 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 7e9822608622a..5c4362af15291 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -3109,7 +3109,32 @@ 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; diff --git a/src/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts index 0e9f9c6c4666b..bffef24f40cfe 100644 --- a/src/compiler/watchUtilities.ts +++ b/src/compiler/watchUtilities.ts @@ -423,6 +423,8 @@ namespace ts { export interface WatchFactoryHost { watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher; watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher; + getCurrentDirectory?(): string; + useCaseSensitiveFileNames: boolean | (() => boolean); } export interface WatchFactory { @@ -445,12 +447,52 @@ namespace ts { watchDirectory: createTriggerLoggingAddWatch("watchDirectory") } : undefined; - return watchLogLevel === WatchLogLevel.Verbose ? + const factory = watchLogLevel === WatchLogLevel.Verbose ? { watchFile: createFileWatcherWithLogging, watchDirectory: createDirectoryWatcherWithLogging } : triggerInvokingFactory || plainInvokeFactory; + const excludeWatcherFactory = watchLogLevel === WatchLogLevel.Verbose ? + createExcludeWatcherWithLogging : + returnNoopFileWatcher; + + return { + watchFile: createExcludeHandlingAddWatch("watchFile"), + watchDirectory: createExcludeHandlingAddWatch("watchDirectory") + }; + + function createExcludeHandlingAddWatch>(key: T): WatchFactory[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 useCaseSensitiveFileNames() { + return typeof host.useCaseSensitiveFileNames === "boolean" ? + host.useCaseSensitiveFileNames : + host.useCaseSensitiveFileNames(); + } + + 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 createFileWatcherWithLogging( file: string, @@ -496,7 +538,7 @@ namespace ts { }; } - function createTriggerLoggingAddWatch>(key: T,): WatchFactory[T] { + function createTriggerLoggingAddWatch>(key: T): WatchFactory[T] { return ( file: string, cb: FileWatcherCallback | DirectoryWatcherCallback, diff --git a/src/jsTyping/types.ts b/src/jsTyping/types.ts index 62e6fb2cf50d9..26d2c69795d96 100644 --- a/src/jsTyping/types.ts +++ b/src/jsTyping/types.ts @@ -83,6 +83,7 @@ declare namespace ts.server { useCaseSensitiveFileNames: boolean; writeFile(path: string, content: string): void; createDirectory(path: string): void; + getCurrentDirectory?(): string; watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher; watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher; } From 1b752cf7286f964be95b9d8217a2847f6f37f8d4 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 24 Jun 2020 12:39:09 -0700 Subject: [PATCH 05/19] Baselines without using exclude watch options --- .../unittests/tscWatch/watchEnvironment.ts | 63 ++++++ ...eDirectories-option-extendedDiagnostics.js | 172 +++++++++++++++ .../with-excludeDirectories-option.js | 121 +++++++++++ ...excludeFiles-option-extendedDiagnostics.js | 201 ++++++++++++++++++ .../watchOptions/with-excludeFiles-option.js | 149 +++++++++++++ 5 files changed, 706 insertions(+) create mode 100644 tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js create mode 100644 tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option.js create mode 100644 tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option-extendedDiagnostics.js create mode 100644 tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option.js diff --git a/src/testRunner/unittests/tscWatch/watchEnvironment.ts b/src/testRunner/unittests/tscWatch/watchEnvironment.ts index 20dabc4194789..27dd54dbb7526 100644 --- a/src/testRunner/unittests/tscWatch/watchEnvironment.ts +++ b/src/testRunner/unittests/tscWatch/watchEnvironment.ts @@ -411,6 +411,69 @@ namespace ts.tscWatch { }, changes: emptyArray }); + + describe("exclude options", () => { + function sys(_watchOptions: WatchOptions): WatchedSystem { + const configFile: File = { + path: `${projectRoot}/tsconfig.json`, + content: JSON.stringify({ exclude: ["node_modules"] }) + }; + 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 files = [libFile, main, bar, foo, fooBar, configFile]; + return createWatchedSystem(files, { currentDirectory: projectRoot }); + } + + 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: checkSingleTimeoutQueueLengthAndRun, + } + ] + }); + + 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(1); // Failed lookup + sys.checkTimeoutQueueLength(1); // Actual update + }, + } + ] + }); + } + + verifyWorker(); + verifyWorker("-extendedDiagnostics"); + }); }); }); } diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js new file mode 100644 index 0000000000000..745d4ef3b4fdb --- /dev/null +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js @@ -0,0 +1,172 @@ +Input:: +//// [/a/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { 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/tsconfig.json] +{"exclude":["node_modules"]} + + +/a/lib/tsc.js -w -extendedDiagnostics +Output:: +[12:00:33 AM] Starting compilation in watch mode... + + +Current directory: /user/username/projects/myproject CaseSensitiveFileNames: false + +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined 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 undefined Source file + +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/bar/index.d.ts 250 undefined Source file + +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Failed Lookup Locations + +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Failed Lookup Locations + +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/bar/foo.d.ts 250 undefined Source file + +FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 250 undefined Source file + +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations + +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations + +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Type roots + +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Type roots + +DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js :: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations + +Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js :: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations + +[12:00:36 AM] Found 0 errors. Watching for file changes. + + +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory + +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined 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 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/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:: delete fooBar + +Input:: +//// [/user/username/projects/myproject/node_modules/bar/fooBar.d.ts] deleted + +Output:: +DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/fooBar.d.ts :: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Failed Lookup Locations + +Scheduling invalidateFailedLookup + +Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/fooBar.d.ts :: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Failed Lookup Locations + +DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/fooBar.d.ts :: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory + +Project: /user/username/projects/myproject/tsconfig.json Detected excluded file: /user/username/projects/myproject/node_modules/bar/fooBar.d.ts + +Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/fooBar.d.ts :: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory + + +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/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 + diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option.js new file mode 100644 index 0000000000000..300071608126f --- /dev/null +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option.js @@ -0,0 +1,121 @@ +Input:: +//// [/a/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { 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/tsconfig.json] +{"exclude":["node_modules"]} + + +/a/lib/tsc.js -w +Output:: +>> Screen clear +[12:00:33 AM] Starting compilation in watch mode... + + +[12:00:36 AM] Found 0 errors. Watching for file changes. + + + +Program root files: ["/user/username/projects/myproject/src/main.ts"] +Program options: {"watch":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} +Program 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/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:: 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/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 + diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option-extendedDiagnostics.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option-extendedDiagnostics.js new file mode 100644 index 0000000000000..5532ec0e5003c --- /dev/null +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option-extendedDiagnostics.js @@ -0,0 +1,201 @@ +Input:: +//// [/a/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { 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/tsconfig.json] +{"exclude":["node_modules"]} + + +/a/lib/tsc.js -w -extendedDiagnostics +Output:: +[12:00:33 AM] Starting compilation in watch mode... + + +Current directory: /user/username/projects/myproject CaseSensitiveFileNames: false + +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined 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 undefined Source file + +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/bar/index.d.ts 250 undefined Source file + +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Failed Lookup Locations + +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Failed Lookup Locations + +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/bar/foo.d.ts 250 undefined Source file + +FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 250 undefined Source file + +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations + +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations + +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Type roots + +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Type roots + +DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js :: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations + +Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js :: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations + +[12:00:36 AM] Found 0 errors. Watching for file changes. + + +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory + +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined 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 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/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:: +FileWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/foo.d.ts 1:: WatchInfo: /user/username/projects/myproject/node_modules/bar/foo.d.ts 250 undefined Source file + +Scheduling update + +Elapsed:: *ms FileWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/foo.d.ts 1:: WatchInfo: /user/username/projects/myproject/node_modules/bar/foo.d.ts 250 undefined Source file + +[12:00:40 AM] File change detected. Starting incremental compilation... + + +Synchronizing program + +CreatingProgramWith:: + + roots: ["/user/username/projects/myproject/src/main.ts"] + + options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} + +node_modules/bar/index.d.ts:1:10 - error TS2305: Module '"./foo"' has no exported member 'foo'. + +1 export { foo } from "./foo"; +   ~~~ + + +[12:00:41 AM] Found 1 error. Watching for file changes. + + + +Program root files: ["/user/username/projects/myproject/src/main.ts"] +Program options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} +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:: +/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/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 + diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option.js new file mode 100644 index 0000000000000..d8868bb4df07a --- /dev/null +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option.js @@ -0,0 +1,149 @@ +Input:: +//// [/a/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { 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/tsconfig.json] +{"exclude":["node_modules"]} + + +/a/lib/tsc.js -w +Output:: +>> Screen clear +[12:00:33 AM] Starting compilation in watch mode... + + +[12:00:36 AM] Found 0 errors. Watching for file changes. + + + +Program root files: ["/user/username/projects/myproject/src/main.ts"] +Program options: {"watch":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} +Program 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/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:: +>> Screen clear +[12:00:40 AM] File change detected. Starting incremental compilation... + + +node_modules/bar/index.d.ts:1:10 - error TS2305: Module '"./foo"' has no exported member 'foo'. + +1 export { foo } from "./foo"; +   ~~~ + + +[12:00:41 AM] Found 1 error. 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 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:: +/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/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 + From 317a35df6565988d4fe2137569af3adc21a6f1ee Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 24 Jun 2020 13:02:04 -0700 Subject: [PATCH 06/19] Baselines including exclude option --- .../unittests/tscWatch/watchEnvironment.ts | 12 +-- ...eDirectories-option-extendedDiagnostics.js | 50 ++++-------- .../with-excludeDirectories-option.js | 10 +-- ...excludeFiles-option-extendedDiagnostics.js | 79 ++++--------------- .../watchOptions/with-excludeFiles-option.js | 36 +-------- 5 files changed, 38 insertions(+), 149 deletions(-) diff --git a/src/testRunner/unittests/tscWatch/watchEnvironment.ts b/src/testRunner/unittests/tscWatch/watchEnvironment.ts index 27dd54dbb7526..1c295cfc34d5e 100644 --- a/src/testRunner/unittests/tscWatch/watchEnvironment.ts +++ b/src/testRunner/unittests/tscWatch/watchEnvironment.ts @@ -413,10 +413,10 @@ namespace ts.tscWatch { }); describe("exclude options", () => { - function sys(_watchOptions: WatchOptions): WatchedSystem { + function sys(watchOptions: WatchOptions): WatchedSystem { const configFile: File = { path: `${projectRoot}/tsconfig.json`, - content: JSON.stringify({ exclude: ["node_modules"] }) + content: JSON.stringify({ exclude: ["node_modules"], watchOptions }) }; const main: File = { path: `${projectRoot}/src/main.ts`, @@ -448,7 +448,7 @@ namespace ts.tscWatch { { caption: "Change foo", change: sys => replaceFileText(sys, `${projectRoot}/node_modules/bar/foo.d.ts`, "foo", "fooBar"), - timeouts: checkSingleTimeoutQueueLengthAndRun, + timeouts: sys => sys.checkTimeoutQueueLength(0), } ] }); @@ -462,11 +462,7 @@ namespace ts.tscWatch { { caption: "delete fooBar", change: sys => sys.deleteFile(`${projectRoot}/node_modules/bar/fooBar.d.ts`), - timeouts: sys => { - sys.checkTimeoutQueueLength(1); // Failed lookup - sys.checkTimeoutQueueLength(1); // Actual update - }, - } + timeouts: sys => sys.checkTimeoutQueueLength(0), } ] }); } diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js index 745d4ef3b4fdb..78c091d1e9a5f 100644 --- a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js @@ -25,7 +25,7 @@ export function foo(): string; export function fooBar(): string; //// [/user/username/projects/myproject/tsconfig.json] -{"exclude":["node_modules"]} +{"exclude":["node_modules"],"watchOptions":{"excludeDirectories":["node_modules"]}} /a/lib/tsc.js -w -extendedDiagnostics @@ -35,7 +35,7 @@ Output:: Current directory: /user/username/projects/myproject CaseSensitiveFileNames: false -FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined Config file +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Config file Synchronizing program @@ -45,36 +45,32 @@ CreatingProgramWith:: options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} -FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src/main.ts 250 undefined Source file +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 undefined 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 -DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Failed Lookup Locations +ExcludeWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Failed Lookup Locations -Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined 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: /user/username/projects/myproject/node_modules/bar/foo.d.ts 250 undefined Source file +FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 250 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Source file -FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 250 undefined Source file +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Failed Lookup Locations -DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations +Elapsed:: *ms 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 undefined Failed Lookup Locations +ExcludeWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Type roots -DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined 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:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Type roots - -DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js :: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations - -Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js :: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations +Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js :: WatchInfo: /user/username/projects/myproject/src 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Failed Lookup Locations [12:00:36 AM] Found 0 errors. Watching for file changes. -DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory +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 undefined 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"] @@ -106,12 +102,8 @@ WatchedFiles:: 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"}} @@ -131,17 +123,11 @@ Input:: //// [/user/username/projects/myproject/node_modules/bar/fooBar.d.ts] deleted Output:: -DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/fooBar.d.ts :: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Failed Lookup Locations - -Scheduling invalidateFailedLookup - -Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/fooBar.d.ts :: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Failed Lookup Locations - -DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/fooBar.d.ts :: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory +DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/fooBar.d.ts :: WatchInfo: /user/username/projects/myproject 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Wild card directory Project: /user/username/projects/myproject/tsconfig.json Detected excluded file: /user/username/projects/myproject/node_modules/bar/fooBar.d.ts -Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/fooBar.d.ts :: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory +Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/fooBar.d.ts :: WatchInfo: /user/username/projects/myproject 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Wild card directory WatchedFiles:: @@ -159,12 +145,8 @@ WatchedFiles:: 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"}} diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option.js index 300071608126f..7aa88e2fdc2f4 100644 --- a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option.js +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option.js @@ -25,7 +25,7 @@ export function foo(): string; export function fooBar(): string; //// [/user/username/projects/myproject/tsconfig.json] -{"exclude":["node_modules"]} +{"exclude":["node_modules"],"watchOptions":{"excludeDirectories":["node_modules"]}} /a/lib/tsc.js -w @@ -67,12 +67,8 @@ WatchedFiles:: 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"}} @@ -108,12 +104,8 @@ WatchedFiles:: 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"}} diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option-extendedDiagnostics.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option-extendedDiagnostics.js index 5532ec0e5003c..3a782918008c7 100644 --- a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option-extendedDiagnostics.js +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option-extendedDiagnostics.js @@ -25,7 +25,7 @@ export function foo(): string; export function fooBar(): string; //// [/user/username/projects/myproject/tsconfig.json] -{"exclude":["node_modules"]} +{"exclude":["node_modules"],"watchOptions":{"excludeFiles":["node_modules/*"]}} /a/lib/tsc.js -w -extendedDiagnostics @@ -35,7 +35,7 @@ Output:: Current directory: /user/username/projects/myproject CaseSensitiveFileNames: false -FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined Config file +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Config file Synchronizing program @@ -45,36 +45,36 @@ CreatingProgramWith:: options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} -FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src/main.ts 250 undefined Source file +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src/main.ts 250 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Source file -FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/bar/index.d.ts 250 undefined 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 undefined Failed Lookup Locations +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 undefined 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 -FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/bar/foo.d.ts 250 undefined Source file +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 undefined 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 undefined Failed Lookup Locations +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 undefined 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 undefined Type roots +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 undefined 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 undefined Failed Lookup Locations +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 undefined Failed Lookup Locations +Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js :: WatchInfo: /user/username/projects/myproject/src 1 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Failed Lookup Locations [12:00:36 AM] Found 0 errors. Watching for file changes. -DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory +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 undefined 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"] @@ -96,10 +96,6 @@ WatchedFiles:: {"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} @@ -133,55 +129,12 @@ export function fooBar(): string; Output:: -FileWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/foo.d.ts 1:: WatchInfo: /user/username/projects/myproject/node_modules/bar/foo.d.ts 250 undefined Source file - -Scheduling update - -Elapsed:: *ms FileWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/foo.d.ts 1:: WatchInfo: /user/username/projects/myproject/node_modules/bar/foo.d.ts 250 undefined Source file - -[12:00:40 AM] File change detected. Starting incremental compilation... - - -Synchronizing program - -CreatingProgramWith:: - - roots: ["/user/username/projects/myproject/src/main.ts"] - - options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} - -node_modules/bar/index.d.ts:1:10 - error TS2305: Module '"./foo"' has no exported member 'foo'. - -1 export { foo } from "./foo"; -   ~~~ - - -[12:00:41 AM] Found 1 error. Watching for file changes. - - - -Program root files: ["/user/username/projects/myproject/src/main.ts"] -Program options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} -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:: -/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} diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option.js index d8868bb4df07a..0f15f3536e4a5 100644 --- a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option.js +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option.js @@ -25,7 +25,7 @@ export function foo(): string; export function fooBar(): string; //// [/user/username/projects/myproject/tsconfig.json] -{"exclude":["node_modules"]} +{"exclude":["node_modules"],"watchOptions":{"excludeFiles":["node_modules/*"]}} /a/lib/tsc.js -w @@ -57,10 +57,6 @@ WatchedFiles:: {"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} @@ -94,42 +90,12 @@ export function fooBar(): string; Output:: ->> Screen clear -[12:00:40 AM] File change detected. Starting incremental compilation... - - -node_modules/bar/index.d.ts:1:10 - error TS2305: Module '"./foo"' has no exported member 'foo'. - -1 export { foo } from "./foo"; -   ~~~ - - -[12:00:41 AM] Found 1 error. 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 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:: -/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} From dddd906803675147e4a1ee66c42d6d039566df97 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 24 Jun 2020 13:52:29 -0700 Subject: [PATCH 07/19] Handle exclude options in the system watches --- src/compiler/sys.ts | 83 +++++++++++++------ src/harness/virtualFileSystemWithWatch.ts | 1 + ...eDirectories-option-extendedDiagnostics.js | 6 -- 3 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 725d2330d214a..484aca07c473e 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -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 = createMap<{ 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() { @@ -655,11 +665,11 @@ namespace ts { if (!parentWatcher) return false; let newChildWatches: ChildDirectoryWatcher[] | undefined; const hasChanges = enumerateInsertsAndDeletes( - 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] || ""; diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index 28e7dd1cd85b7..a4ab8644ac4ef 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -433,6 +433,7 @@ interface Array { 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), diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js index 78c091d1e9a5f..621c69ccf3931 100644 --- a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js @@ -123,12 +123,6 @@ Input:: //// [/user/username/projects/myproject/node_modules/bar/fooBar.d.ts] deleted Output:: -DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/fooBar.d.ts :: WatchInfo: /user/username/projects/myproject 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Wild card directory - -Project: /user/username/projects/myproject/tsconfig.json Detected excluded file: /user/username/projects/myproject/node_modules/bar/fooBar.d.ts - -Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules/bar/fooBar.d.ts :: WatchInfo: /user/username/projects/myproject 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Wild card directory - WatchedFiles:: /user/username/projects/myproject/tsconfig.json: From 76685351a1cbe5ca86ab2f9c6833f975af714e93 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 24 Jun 2020 14:30:13 -0700 Subject: [PATCH 08/19] Add test without exclude option for recursive directory watching --- .../unittests/tscWatch/watchEnvironment.ts | 37 ++- ...eDirectories-option-extendedDiagnostics.js | 7 +- ...-directory-watching-extendedDiagnostics.js | 258 ++++++++++++++++++ ...ption-with-recursive-directory-watching.js | 190 +++++++++++++ .../with-excludeDirectories-option.js | 7 +- ...excludeFiles-option-extendedDiagnostics.js | 7 +- .../watchOptions/with-excludeFiles-option.js | 7 +- 7 files changed, 502 insertions(+), 11 deletions(-) create mode 100644 tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching-extendedDiagnostics.js create mode 100644 tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching.js diff --git a/src/testRunner/unittests/tscWatch/watchEnvironment.ts b/src/testRunner/unittests/tscWatch/watchEnvironment.ts index 1c295cfc34d5e..b65ea005a6eff 100644 --- a/src/testRunner/unittests/tscWatch/watchEnvironment.ts +++ b/src/testRunner/unittests/tscWatch/watchEnvironment.ts @@ -413,7 +413,7 @@ namespace ts.tscWatch { }); describe("exclude options", () => { - function sys(watchOptions: WatchOptions): WatchedSystem { + function sys(watchOptions: WatchOptions, runWithoutRecursiveWatches?: boolean): WatchedSystem { const configFile: File = { path: `${projectRoot}/tsconfig.json`, content: JSON.stringify({ exclude: ["node_modules"], watchOptions }) @@ -434,8 +434,12 @@ namespace ts.tscWatch { path: `${projectRoot}/node_modules/bar/fooBar.d.ts`, content: `export function fooBar(): string;` }; - const files = [libFile, main, bar, foo, fooBar, configFile]; - return createWatchedSystem(files, { currentDirectory: projectRoot }); + 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[]) { @@ -465,6 +469,33 @@ namespace ts.tscWatch { timeouts: sys => sys.checkTimeoutQueueLength(0), } ] }); + + verifyTscWatch({ + scenario, + subScenario: `watchOptions/with excludeDirectories option with recursive directory watching${additionalFlags.join("")}`, + commandLineArgs: ["-w", ...additionalFlags], + sys: () => sys({}, /*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.checkTimeoutQueueLengthAndRun(1); // To update directory watchers + sys.checkTimeoutQueueLengthAndRun(2); // To Update program and failed lookup update + sys.checkTimeoutQueueLengthAndRun(1); // Actual program update + sys.checkTimeoutQueueLength(0); + }, + } + ] + }); } verifyWorker(); diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js index 621c69ccf3931..789221c52cf10 100644 --- a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-extendedDiagnostics.js @@ -24,13 +24,16 @@ export function foo(): string; //// [/user/username/projects/myproject/node_modules/bar/fooBar.d.ts] export function fooBar(): string; +//// [/user/username/projects/myproject/node_modules/bar/temp/index.d.ts] +export function temp(): string; + //// [/user/username/projects/myproject/tsconfig.json] {"exclude":["node_modules"],"watchOptions":{"excludeDirectories":["node_modules"]}} /a/lib/tsc.js -w -extendedDiagnostics Output:: -[12:00:33 AM] Starting compilation in watch mode... +[12:00:37 AM] Starting compilation in watch mode... Current directory: /user/username/projects/myproject CaseSensitiveFileNames: false @@ -65,7 +68,7 @@ DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js :: WatchInfo: /user/username/projects/myproject/src 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Failed Lookup Locations -[12:00:36 AM] Found 0 errors. Watching for file changes. +[12:00:40 AM] Found 0 errors. Watching for file changes. DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 {"excludeDirectories":["/user/username/projects/myproject/node_modules"]} Wild card directory diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching-extendedDiagnostics.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching-extendedDiagnostics.js new file mode 100644 index 0000000000000..e07742732a16f --- /dev/null +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching-extendedDiagnostics.js @@ -0,0 +1,258 @@ +Input:: +//// [/a/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { 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":{}} + + +/a/lib/tsc.js -w -extendedDiagnostics +Output:: +[12:00:37 AM] Starting compilation in watch mode... + + +Current directory: /user/username/projects/myproject CaseSensitiveFileNames: false + +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined 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 undefined Source file + +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/bar/index.d.ts 250 undefined Source file + +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Failed Lookup Locations + +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Failed Lookup Locations + +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/bar/foo.d.ts 250 undefined Source file + +FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 250 undefined Source file + +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations + +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations + +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Type roots + +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Type roots + +[12:00:40 AM] Found 0 errors. Watching for file changes. + + +DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory + +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined 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 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/node_modules/bar/temp: + {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp","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:: +DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js 0:: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations + +Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js 0:: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations + +DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js 0:: WatchInfo: /user/username/projects/myproject 1 undefined 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 undefined Wild card directory + + +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/node_modules/bar/temp: + {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp","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:: +DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules :: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Failed Lookup Locations + +Scheduling invalidateFailedLookup + +Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules :: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Failed Lookup Locations + +DirectoryWatcher:: Triggered with /user/username/projects/myproject :: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory + +Scheduling update + +Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject :: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory + +Scheduling update + +[12:00:45 AM] File change detected. Starting incremental compilation... + + +Reloading new file names and options + +Synchronizing program + +CreatingProgramWith:: + + roots: ["/user/username/projects/myproject/src/main.ts"] + + options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} + +[12:00:46 AM] Found 0 errors. Watching for file changes. + + + +Program root files: ["/user/username/projects/myproject/src/main.ts"] +Program options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} +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:: + +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/node_modules/bar/temp: + {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp","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"}} +/user/username/projects/myproject/node_modules/bar/temp/foobar: + {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp/fooBar","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}} + +FsWatchesRecursive:: + +exitCode:: ExitStatus.undefined + diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching.js new file mode 100644 index 0000000000000..3f4b645f34df6 --- /dev/null +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching.js @@ -0,0 +1,190 @@ +Input:: +//// [/a/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { 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":{}} + + +/a/lib/tsc.js -w +Output:: +>> Screen clear +[12:00:37 AM] Starting compilation in watch mode... + + +[12:00:40 AM] Found 0 errors. Watching for file changes. + + + +Program root files: ["/user/username/projects/myproject/src/main.ts"] +Program options: {"watch":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} +Program 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/node_modules/bar/temp: + {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp","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/node_modules/bar/temp: + {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp","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:: +>> Screen clear +[12:00:45 AM] File change detected. Starting incremental compilation... + + +[12:00:46 AM] Found 0 errors. Watching for file changes. + + + +Program root files: ["/user/username/projects/myproject/src/main.ts"] +Program options: {"watch":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} +Program 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:: + +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/node_modules/bar/temp: + {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp","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"}} +/user/username/projects/myproject/node_modules/bar/temp/foobar: + {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp/fooBar","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}} + +FsWatchesRecursive:: + +exitCode:: ExitStatus.undefined + diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option.js index 7aa88e2fdc2f4..a7a3c64538dde 100644 --- a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option.js +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option.js @@ -24,6 +24,9 @@ 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"]}} @@ -31,10 +34,10 @@ export function fooBar(): string; /a/lib/tsc.js -w Output:: >> Screen clear -[12:00:33 AM] Starting compilation in watch mode... +[12:00:37 AM] Starting compilation in watch mode... -[12:00:36 AM] Found 0 errors. Watching for file changes. +[12:00:40 AM] Found 0 errors. Watching for file changes. diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option-extendedDiagnostics.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option-extendedDiagnostics.js index 3a782918008c7..3ddb97a27babb 100644 --- a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option-extendedDiagnostics.js +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option-extendedDiagnostics.js @@ -24,13 +24,16 @@ export function foo(): string; //// [/user/username/projects/myproject/node_modules/bar/fooBar.d.ts] export function fooBar(): string; +//// [/user/username/projects/myproject/node_modules/bar/temp/index.d.ts] +export function temp(): string; + //// [/user/username/projects/myproject/tsconfig.json] {"exclude":["node_modules"],"watchOptions":{"excludeFiles":["node_modules/*"]}} /a/lib/tsc.js -w -extendedDiagnostics Output:: -[12:00:33 AM] Starting compilation in watch mode... +[12:00:37 AM] Starting compilation in watch mode... Current directory: /user/username/projects/myproject CaseSensitiveFileNames: false @@ -69,7 +72,7 @@ DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js :: WatchInfo: /user/username/projects/myproject/src 1 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Failed Lookup Locations -[12:00:36 AM] Found 0 errors. Watching for file changes. +[12:00:40 AM] Found 0 errors. Watching for file changes. DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 {"excludeFiles":["/user/username/projects/myproject/node_modules/*"]} Wild card directory diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option.js index 0f15f3536e4a5..3bbe39202bdb0 100644 --- a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option.js +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeFiles-option.js @@ -24,6 +24,9 @@ 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/*"]}} @@ -31,10 +34,10 @@ export function fooBar(): string; /a/lib/tsc.js -w Output:: >> Screen clear -[12:00:33 AM] Starting compilation in watch mode... +[12:00:37 AM] Starting compilation in watch mode... -[12:00:36 AM] Found 0 errors. Watching for file changes. +[12:00:40 AM] Found 0 errors. Watching for file changes. From ae2ba8e285da56663b1ab74eada9a76358a5c495 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 24 Jun 2020 14:32:40 -0700 Subject: [PATCH 09/19] Test baselines with exclude option --- .../unittests/tscWatch/watchEnvironment.ts | 9 +- ...-directory-watching-extendedDiagnostics.js | 90 +++++-------------- ...ption-with-recursive-directory-watching.js | 27 +----- 3 files changed, 27 insertions(+), 99 deletions(-) diff --git a/src/testRunner/unittests/tscWatch/watchEnvironment.ts b/src/testRunner/unittests/tscWatch/watchEnvironment.ts index b65ea005a6eff..93f1db2728df3 100644 --- a/src/testRunner/unittests/tscWatch/watchEnvironment.ts +++ b/src/testRunner/unittests/tscWatch/watchEnvironment.ts @@ -474,7 +474,7 @@ namespace ts.tscWatch { scenario, subScenario: `watchOptions/with excludeDirectories option with recursive directory watching${additionalFlags.join("")}`, commandLineArgs: ["-w", ...additionalFlags], - sys: () => sys({}, /*runWithoutRecursiveWatches*/ true), + sys: () => sys({ excludeDirectories: ["**/temp"] }, /*runWithoutRecursiveWatches*/ true), changes: [ { caption: "Directory watch updates because of main.js creation", @@ -487,12 +487,7 @@ namespace ts.tscWatch { { 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.checkTimeoutQueueLengthAndRun(1); // To update directory watchers - sys.checkTimeoutQueueLengthAndRun(2); // To Update program and failed lookup update - sys.checkTimeoutQueueLengthAndRun(1); // Actual program update - sys.checkTimeoutQueueLength(0); - }, + timeouts: sys => sys.checkTimeoutQueueLength(0), } ] }); diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching-extendedDiagnostics.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching-extendedDiagnostics.js index e07742732a16f..ad461c41ee92e 100644 --- a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching-extendedDiagnostics.js +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching-extendedDiagnostics.js @@ -28,7 +28,7 @@ export function fooBar(): string; export function temp(): string; //// [/user/username/projects/myproject/tsconfig.json] -{"exclude":["node_modules"],"watchOptions":{}} +{"exclude":["node_modules"],"watchOptions":{"excludeDirectories":["**/temp"]}} /a/lib/tsc.js -w -extendedDiagnostics @@ -38,7 +38,7 @@ Output:: Current directory: /user/username/projects/myproject CaseSensitiveFileNames: false -FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined Config file +FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Config file Synchronizing program @@ -48,32 +48,32 @@ CreatingProgramWith:: options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} -FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/src/main.ts 250 undefined Source file +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 undefined 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 undefined Failed Lookup Locations +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 undefined 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 undefined Source file +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 undefined 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 undefined Failed Lookup Locations +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 undefined 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 undefined Type roots +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 undefined Type roots +Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 {"excludeDirectories":["/user/username/projects/myproject/**/temp"]} Type roots [12:00:40 AM] Found 0 errors. Watching for file changes. -DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory +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 undefined 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"] @@ -107,8 +107,6 @@ FsWatches:: {"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/node_modules/bar/temp: - {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp","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: @@ -133,15 +131,21 @@ Change:: Directory watch updates because of main.js creation Input:: Output:: -DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js 0:: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations +sysLog:: onTimerToUpdateChildWatches:: 1 -Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js 0:: WatchInfo: /user/username/projects/myproject/src 1 undefined Failed Lookup Locations +sysLog:: invokingWatchers:: 0ms:: 0 -DirectoryWatcher:: Triggered with /user/username/projects/myproject/src/main.js 0:: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory +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 undefined Wild card directory +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 0ms:: onTimerToUpdateChildWatches:: 0 undefined WatchedFiles:: @@ -161,8 +165,6 @@ FsWatches:: {"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/node_modules/bar/temp: - {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp","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: @@ -183,46 +185,6 @@ export function temp(): string; Output:: -DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules :: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Failed Lookup Locations - -Scheduling invalidateFailedLookup - -Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject/node_modules :: WatchInfo: /user/username/projects/myproject/node_modules 1 undefined Failed Lookup Locations - -DirectoryWatcher:: Triggered with /user/username/projects/myproject :: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory - -Scheduling update - -Elapsed:: *ms DirectoryWatcher:: Triggered with /user/username/projects/myproject :: WatchInfo: /user/username/projects/myproject 1 undefined Wild card directory - -Scheduling update - -[12:00:45 AM] File change detected. Starting incremental compilation... - - -Reloading new file names and options - -Synchronizing program - -CreatingProgramWith:: - - roots: ["/user/username/projects/myproject/src/main.ts"] - - options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} - -[12:00:46 AM] Found 0 errors. Watching for file changes. - - - -Program root files: ["/user/username/projects/myproject/src/main.ts"] -Program options: {"watch":true,"extendedDiagnostics":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} -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:: WatchedFiles:: /user/username/projects/myproject/tsconfig.json: @@ -241,16 +203,12 @@ FsWatches:: {"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/node_modules/bar/temp: - {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp","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"}} -/user/username/projects/myproject/node_modules/bar/temp/foobar: - {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp/fooBar","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}} FsWatchesRecursive:: diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching.js index 3f4b645f34df6..90f44d178c9b0 100644 --- a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching.js +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching.js @@ -28,7 +28,7 @@ export function fooBar(): string; export function temp(): string; //// [/user/username/projects/myproject/tsconfig.json] -{"exclude":["node_modules"],"watchOptions":{}} +{"exclude":["node_modules"],"watchOptions":{"excludeDirectories":["**/temp"]}} /a/lib/tsc.js -w @@ -72,8 +72,6 @@ FsWatches:: {"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/node_modules/bar/temp: - {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp","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: @@ -116,8 +114,6 @@ FsWatches:: {"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/node_modules/bar/temp: - {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp","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: @@ -138,23 +134,6 @@ export function temp(): string; Output:: ->> Screen clear -[12:00:45 AM] File change detected. Starting incremental compilation... - - -[12:00:46 AM] Found 0 errors. Watching for file changes. - - - -Program root files: ["/user/username/projects/myproject/src/main.ts"] -Program options: {"watch":true,"configFilePath":"/user/username/projects/myproject/tsconfig.json"} -Program 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:: WatchedFiles:: /user/username/projects/myproject/tsconfig.json: @@ -173,16 +152,12 @@ FsWatches:: {"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/node_modules/bar/temp: - {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp","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"}} -/user/username/projects/myproject/node_modules/bar/temp/foobar: - {"directoryName":"/user/username/projects/myproject/node_modules/bar/temp/fooBar","fallbackPollingInterval":500,"fallbackOptions":{"watchFile":"PriorityPollingInterval"}} FsWatchesRecursive:: From cd676bf2d96cca1d8b81b30ad5988574703ff5dd Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 24 Jun 2020 14:46:21 -0700 Subject: [PATCH 10/19] Always set sysLog --- src/compiler/watchUtilities.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts index bffef24f40cfe..abfcd524dc3ef 100644 --- a/src/compiler/watchUtilities.ts +++ b/src/compiler/watchUtilities.ts @@ -434,9 +434,7 @@ namespace ts { export type GetDetailWatchInfo = (detailInfo1: X, detailInfo2: Y | undefined) => string; export function getWatchFactory(host: WatchFactoryHost, watchLogLevel: WatchLogLevel, log: (s: string) => void, getDetailWatchInfo?: GetDetailWatchInfo): WatchFactory { - if (watchLogLevel === WatchLogLevel.Verbose && sysLog === noop) { - setSysLog(s => log(s)); - } + setSysLog(watchLogLevel === WatchLogLevel.Verbose ? log : noop); const plainInvokeFactory: WatchFactory = { 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), From 5fa195c2c79c546fd8c8e87eeb21614d4efba481 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 24 Jun 2020 15:31:39 -0700 Subject: [PATCH 11/19] Test for exclude option in server --- src/harness/virtualFileSystemWithWatch.ts | 2 +- .../unittests/tsserver/watchEnvironment.ts | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index a4ab8644ac4ef..cf2bb14349441 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -251,7 +251,7 @@ interface Array { 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 })] diff --git a/src/testRunner/unittests/tsserver/watchEnvironment.ts b/src/testRunner/unittests/tsserver/watchEnvironment.ts index 1bda288495033..f1aeb619feb27 100644 --- a/src/testRunner/unittests/tsserver/watchEnvironment.ts +++ b/src/testRunner/unittests/tsserver/watchEnvironment.ts @@ -565,6 +565,61 @@ namespace ts.projectSystem { checkWatchedDirectories(host, emptyArray, /*recursive*/ false); checkWatchedDirectories(host, emptyArray, /*recursive*/ true); }); + + describe("excludeDirectories", () => { + function setup(_configureHost?: boolean) { + const configFile: File = { + path: `${tscWatch.projectRoot}/tsconfig.json`, + content: JSON.stringify({ include: ["src"], }) + }; + 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;` + }; + const files = [libFile, main, bar, foo, configFile]; + const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot }); + const service = createProjectService(host); + 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`, `${tscWatch.projectRoot}/node_modules/@types`], + identity, + f => f === `${tscWatch.projectRoot}/node_modules/@types` ? 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, + arrayToMap( + [`${tscWatch.projectRoot}/src`, `${tscWatch.projectRoot}/node_modules`, `${tscWatch.projectRoot}/node_modules/@types`], + identity, + f => f === `${tscWatch.projectRoot}/node_modules/@types` ? 1 : 2, + ), + /*recursive*/ true, + ); + }); + }); }); describe("unittests:: tsserver:: watchEnvironment:: file names on case insensitive file system", () => { From 11cd92630bc25cffc9fd077020a5238fd06b42a6 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 24 Jun 2020 16:01:48 -0700 Subject: [PATCH 12/19] Add exclude options in the config file and fix the test --- src/testRunner/unittests/tsserver/watchEnvironment.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/testRunner/unittests/tsserver/watchEnvironment.ts b/src/testRunner/unittests/tsserver/watchEnvironment.ts index f1aeb619feb27..fb18641c01410 100644 --- a/src/testRunner/unittests/tsserver/watchEnvironment.ts +++ b/src/testRunner/unittests/tsserver/watchEnvironment.ts @@ -570,7 +570,7 @@ namespace ts.projectSystem { function setup(_configureHost?: boolean) { const configFile: File = { path: `${tscWatch.projectRoot}/tsconfig.json`, - content: JSON.stringify({ include: ["src"], }) + content: JSON.stringify({ include: ["src"], watchOptions: { excludeDirectories: ["node_modules"] } }) }; const main: File = { path: `${tscWatch.projectRoot}/src/main.ts`, @@ -597,9 +597,9 @@ namespace ts.projectSystem { checkWatchedDirectoriesDetailed( host, arrayToMap( - [`${tscWatch.projectRoot}/src`, `${tscWatch.projectRoot}/node_modules`, `${tscWatch.projectRoot}/node_modules/@types`], + [`${tscWatch.projectRoot}/src`, `${tscWatch.projectRoot}/node_modules`], identity, - f => f === `${tscWatch.projectRoot}/node_modules/@types` ? 1 : 2, + f => f === `${tscWatch.projectRoot}/node_modules` ? 1 : 2, ), /*recursive*/ true, ); @@ -612,9 +612,9 @@ namespace ts.projectSystem { checkWatchedDirectoriesDetailed( host, arrayToMap( - [`${tscWatch.projectRoot}/src`, `${tscWatch.projectRoot}/node_modules`, `${tscWatch.projectRoot}/node_modules/@types`], + [`${tscWatch.projectRoot}/src`, `${tscWatch.projectRoot}/node_modules`], identity, - f => f === `${tscWatch.projectRoot}/node_modules/@types` ? 1 : 2, + f => f === `${tscWatch.projectRoot}/node_modules` ? 1 : 2, ), /*recursive*/ true, ); From 3777897290f0a2402b5b38f64cdbb0fc8a5fb986 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 24 Jun 2020 16:03:14 -0700 Subject: [PATCH 13/19] Fix host configuration for server --- src/server/editorServices.ts | 1 + .../unittests/tsserver/watchEnvironment.ts | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index d190639962ac4..e8b1021eb9ae5 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -253,6 +253,7 @@ namespace ts.server { export function convertWatchOptions(protocolOptions: protocol.ExternalProjectCompilerOptions): WatchOptions | undefined { let result: WatchOptions | undefined; + debugger; watchOptionsConverters.forEach((mappedValues, id) => { const propertyValue = protocolOptions[id]; if (propertyValue === undefined) return; diff --git a/src/testRunner/unittests/tsserver/watchEnvironment.ts b/src/testRunner/unittests/tsserver/watchEnvironment.ts index fb18641c01410..d15e0cdd5c1ba 100644 --- a/src/testRunner/unittests/tsserver/watchEnvironment.ts +++ b/src/testRunner/unittests/tsserver/watchEnvironment.ts @@ -567,7 +567,7 @@ namespace ts.projectSystem { }); describe("excludeDirectories", () => { - function setup(_configureHost?: boolean) { + function setup(configureHost?: boolean) { const configFile: File = { path: `${tscWatch.projectRoot}/tsconfig.json`, content: JSON.stringify({ include: ["src"], watchOptions: { excludeDirectories: ["node_modules"] } }) @@ -587,6 +587,11 @@ namespace ts.projectSystem { const files = [libFile, main, bar, foo, configFile]; const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot }); const service = createProjectService(host); + if (configureHost) { + service.setHostConfiguration({ + watchOptions: { excludeDirectories: ["node_modules"] } + }); + } service.openClientFile(main.path); return { host, configFile }; } @@ -611,11 +616,8 @@ namespace ts.projectSystem { checkWatchedDirectories(host, emptyArray, /*recursive*/ false); checkWatchedDirectoriesDetailed( host, - arrayToMap( - [`${tscWatch.projectRoot}/src`, `${tscWatch.projectRoot}/node_modules`], - identity, - f => f === `${tscWatch.projectRoot}/node_modules` ? 1 : 2, - ), + [`${tscWatch.projectRoot}/src`], + 2, /*recursive*/ true, ); }); From 0a8d84fc8bec42bf3e47bb5b8846437d3c8c0ddf Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 24 Jun 2020 16:55:00 -0700 Subject: [PATCH 14/19] Handle host configuration for watch options --- src/compiler/commandLineParser.ts | 3 +- src/server/editorServices.ts | 57 ++++--- src/server/project.ts | 35 ++-- .../unittests/tsserver/watchEnvironment.ts | 155 +++++++++++++++++- .../reference/api/tsserverlibrary.d.ts | 21 ++- 5 files changed, 205 insertions(+), 66 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 5c4362af15291..452dedfa3568a 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -2812,7 +2812,8 @@ namespace ts { return defaultOptions; } - function convertJsonOption(opt: CommandLineOption, value: any, basePath: string, errors: Push): CompilerOptionsValue { + /*@internal*/ + export function convertJsonOption(opt: CommandLineOption, value: any, basePath: string, errors: Push): CompilerOptionsValue { if (isCompilerOptionsValue(opt, value)) { const optType = opt.type; if (optType === "list" && isArray(value)) { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index e8b1021eb9ae5..aaff8a3b371ce 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -251,17 +251,18 @@ namespace ts.server { return protocolOptions; } - export function convertWatchOptions(protocolOptions: protocol.ExternalProjectCompilerOptions): WatchOptions | undefined { - let result: WatchOptions | undefined; - debugger; - 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 tryConvertScriptKindName(scriptKindName: protocol.ScriptKindName | ScriptKind): ScriptKind { @@ -560,6 +561,11 @@ namespace ts.server { changes: Iterator; } + export interface WatchOptionsAndErrors { + watchOptions: WatchOptions; + errors: Diagnostic[] | undefined; + } + export class ProjectService { /*@internal*/ @@ -616,8 +622,8 @@ namespace ts.server { private compilerOptionsForInferredProjects: CompilerOptions | undefined; private compilerOptionsForInferredProjectsPerProjectRoot = createMap(); - private watchOptionsForInferredProjects: WatchOptions | undefined; - private watchOptionsForInferredProjectsPerProjectRoot = createMap(); + private watchOptionsForInferredProjects: WatchOptionsAndErrors | undefined; + private watchOptionsForInferredProjectsPerProjectRoot = createMap(); /** * Project size for configured or external projects */ @@ -949,7 +955,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); // always set 'allowNonTsExtensions' for inferred projects since user cannot configure it from the outside // previously we did not expose a way for user to change these settings and this option was enabled by default @@ -977,7 +983,8 @@ namespace ts.server { project.projectRootPath === canonicalProjectRootPath : !project.projectRootPath || !this.compilerOptionsForInferredProjectsPerProjectRoot.has(project.projectRootPath)) { project.setCompilerOptions(compilerOptions); - project.setWatchOptions(watchOptions); + project.setWatchOptions(watchOptions?.watchOptions); + project.setProjectErrors(watchOptions?.errors); project.compileOnSaveEnabled = compilerOptions.compileOnSave!; project.markAsDirty(); this.delayUpdateProjectGraph(project); @@ -1860,7 +1867,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, @@ -1870,8 +1877,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); @@ -2246,14 +2254,16 @@ 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; if (projectRootPath) { - watchOptions = this.watchOptionsForInferredProjectsPerProjectRoot.get(projectRootPath); + watchOptionsAndErrors = this.watchOptionsForInferredProjectsPerProjectRoot.get(projectRootPath); } - if (watchOptions === undefined) { - watchOptions = this.watchOptionsForInferredProjects; + if (watchOptionsAndErrors === undefined) { + watchOptionsAndErrors = this.watchOptionsForInferredProjects; } - const project = new InferredProject(this, this.documentRegistry, compilerOptions, watchOptions || undefined, projectRootPath, currentDirectory, this.currentPluginConfigOverrides); + watchOptionsAndErrors = watchOptionsAndErrors || undefined; + const project = new InferredProject(this, this.documentRegistry, compilerOptions, watchOptionsAndErrors?.watchOptions, projectRootPath, currentDirectory, this.currentPluginConfigOverrides); + project.setProjectErrors(watchOptionsAndErrors?.errors); if (isSingleInferredProject) { this.inferredProjects.unshift(project); } @@ -2705,7 +2715,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.`); } } @@ -3579,7 +3589,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); @@ -3587,9 +3597,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; } diff --git a/src/server/project.ts b/src/server/project.ts index 9f585a394bb57..dba9215819d31 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -189,6 +189,8 @@ namespace ts.server { */ private projectStateVersion = 0; + protected projectErrors: Diagnostic[] | undefined; + protected isInitialLoadPending: () => boolean = returnFalse; /*@internal*/ @@ -565,11 +567,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 { @@ -742,6 +751,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) { @@ -1966,8 +1976,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) */ @@ -2135,24 +2143,6 @@ namespace ts.server { this.enableGlobalPlugins(options, pluginConfigOverrides); } - /** - * Get the errors that dont have any file name associated - */ - getGlobalProjectErrors(): readonly Diagnostic[] { - return filter(this.projectErrors, diagnostic => !diagnostic.file) || emptyArray; - } - - /** - * Get all the project errors - */ - getAllProjectErrors(): readonly Diagnostic[] { - return this.projectErrors || emptyArray; - } - - setProjectErrors(projectErrors: Diagnostic[]) { - this.projectErrors = projectErrors; - } - setTypeAcquisition(newTypeAcquisition: TypeAcquisition): void { this.typeAcquisition = this.removeLocalTypingsFromTypeAcquisition(newTypeAcquisition); } @@ -2186,7 +2176,6 @@ namespace ts.server { } this.stopWatchingWildCards(); - this.projectErrors = undefined; this.configFileSpecs = undefined; this.openFileWatchTriggered.clear(); this.compilerHost = undefined; diff --git a/src/testRunner/unittests/tsserver/watchEnvironment.ts b/src/testRunner/unittests/tsserver/watchEnvironment.ts index d15e0cdd5c1ba..f2bd262992717 100644 --- a/src/testRunner/unittests/tsserver/watchEnvironment.ts +++ b/src/testRunner/unittests/tsserver/watchEnvironment.ts @@ -567,11 +567,7 @@ namespace ts.projectSystem { }); describe("excludeDirectories", () => { - function setup(configureHost?: boolean) { - const configFile: File = { - path: `${tscWatch.projectRoot}/tsconfig.json`, - content: JSON.stringify({ include: ["src"], watchOptions: { excludeDirectories: ["node_modules"] } }) - }; + function setupFiles() { const main: File = { path: `${tscWatch.projectRoot}/src/main.ts`, content: `import { foo } from "bar"; foo();` @@ -584,17 +580,30 @@ namespace ts.projectSystem { path: `${tscWatch.projectRoot}/node_modules/bar/foo.d.ts`, content: `export function foo(): string;` }; - const files = [libFile, main, bar, foo, configFile]; - const host = createServerHost(files, { currentDirectory: tscWatch.projectRoot }); - const service = createProjectService(host); + 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); @@ -621,6 +630,136 @@ namespace ts.projectSystem { /*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({ + 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({ + 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, + } + ]); + }); }); }); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index f518dd8ee6b19..4928e4618ed53 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -9109,6 +9109,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; @@ -9147,7 +9148,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[]; /** @@ -9243,7 +9248,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 @@ -9253,15 +9257,6 @@ declare namespace ts.server { getConfigFilePath(): NormalizedPath; getProjectReferences(): readonly ProjectReference[] | undefined; updateReferences(refs: readonly ProjectReference[] | undefined): void; - /** - * Get the errors that dont have any file name associated - */ - getGlobalProjectErrors(): readonly Diagnostic[]; - /** - * Get all the project errors - */ - getAllProjectErrors(): readonly Diagnostic[]; - setProjectErrors(projectErrors: Diagnostic[]): void; setTypeAcquisition(newTypeAcquisition: TypeAcquisition): void; getTypeAcquisition(): TypeAcquisition; close(): void; @@ -9412,7 +9407,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 tryConvertScriptKindName(scriptKindName: protocol.ScriptKindName | ScriptKind): ScriptKind; export function convertScriptKindName(scriptKindName: protocol.ScriptKindName): ScriptKind.Unknown | ScriptKind.JS | ScriptKind.JSX | ScriptKind.TS | ScriptKind.TSX; export interface HostConfiguration { @@ -9442,6 +9437,10 @@ declare namespace ts.server { typesMapLocation?: string; syntaxOnly?: boolean; } + export interface WatchOptionsAndErrors { + watchOptions: WatchOptions; + errors: Diagnostic[] | undefined; + } export class ProjectService { private readonly scriptInfoInNodeModulesWatchers; /** From 81d0abefd0096c96ccd4aa2dadf6c018f84e25e1 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 24 Jun 2020 17:31:08 -0700 Subject: [PATCH 15/19] Fix sysLog time log so baselines can be clean --- src/compiler/sys.ts | 4 ++-- ...n-with-recursive-directory-watching-extendedDiagnostics.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 484aca07c473e..b1a6a41956344 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -630,7 +630,7 @@ namespace ts { invokeCallbacks(dirPath as Path, 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) { @@ -646,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) { diff --git a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching-extendedDiagnostics.js b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching-extendedDiagnostics.js index ad461c41ee92e..0b729b2436959 100644 --- a/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching-extendedDiagnostics.js +++ b/tests/baselines/reference/tscWatch/watchEnvironment/watchOptions/with-excludeDirectories-option-with-recursive-directory-watching-extendedDiagnostics.js @@ -133,7 +133,7 @@ Input:: Output:: sysLog:: onTimerToUpdateChildWatches:: 1 -sysLog:: invokingWatchers:: 0ms:: 0 +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 @@ -145,7 +145,7 @@ Project: /user/username/projects/myproject/tsconfig.json Detected file add/remov 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 0ms:: onTimerToUpdateChildWatches:: 0 undefined +sysLog:: Elapsed:: *ms:: onTimerToUpdateChildWatches:: 0 undefined WatchedFiles:: From 496939d55ba9b44e7c518ab4bb762e90d853cc3a Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 4 Sep 2020 14:55:29 -0700 Subject: [PATCH 16/19] Handle reloadProjects to reload the project from scratch --- src/server/editorServices.ts | 25 +++- src/server/project.ts | 2 +- src/testRunner/tsconfig.json | 1 + .../unittests/tsserver/reloadProjects.ts | 109 ++++++++++++++++++ 4 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 src/testRunner/unittests/tsserver/reloadProjects.ts diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 7afe1f6a534c4..7882d268efe2a 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -2196,9 +2196,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(); @@ -2212,6 +2213,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; @@ -2789,7 +2797,12 @@ namespace ts.server { // as there is no need to load contents of the files from the disk // Reload Projects - this.reloadConfiguredProjectForFiles(this.openFiles as ESMap, /*delayReload*/ false, returnTrue, "User requested reload projects"); + this.reloadConfiguredProjectForFiles(this.openFiles as ESMap, /*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(); } @@ -2797,6 +2810,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 @@ -2813,12 +2827,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(openFiles: ESMap, delayReload: boolean, shouldReloadProjectFor: (openFileValue: T) => boolean, reason: string) { + private reloadConfiguredProjectForFiles(openFiles: ESMap, clearSemanticCache: boolean, delayReload: boolean, shouldReloadProjectFor: (openFileValue: T) => boolean, reason: string) { const updatedProjects = new Map(); 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 @@ -2844,11 +2858,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( diff --git a/src/server/project.ts b/src/server/project.ts index 2de80951cb02d..435d4c1f59be2 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -2131,7 +2131,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: diff --git a/src/testRunner/tsconfig.json b/src/testRunner/tsconfig.json index 7c07d226c077e..41f853fc94126 100644 --- a/src/testRunner/tsconfig.json +++ b/src/testRunner/tsconfig.json @@ -184,6 +184,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", diff --git a/src/testRunner/unittests/tsserver/reloadProjects.ts b/src/testRunner/unittests/tsserver/reloadProjects.ts new file mode 100644 index 0000000000000..d0654c1ec8663 --- /dev/null +++ b/src/testRunner/unittests/tsserver/reloadProjects.ts @@ -0,0 +1,109 @@ +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();` + }; + const file2: File = { + path: `${tscWatch.projectRoot}/file2.ts`, + content: `${file1.content} + export function bar(){}` + }; + const moduleFile: File = { + path: `${tscWatch.projectRoot}/node_modules/module1/index.d.ts`, + content: `export function foo(): string;` + }; + + it("configured project", () => { + const host = createServerHost([configFile, libFile, file1, file2]); + const service = createProjectService(host); + 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]); + }); + + it("inferred project", () => { + const host = createServerHost([libFile, file1, file2]); + const service = createProjectService(host, /*parameters*/ undefined, { useInferredProjectPerProjectRoot: true, }); + 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]); + + // 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, moduleFile.path]); + }); + + it("external project", () => { + const host = createServerHost([libFile, file1, file2]); + const service = createProjectService(host); + 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]); + }); + + it("external project with config file", () => { + const host = createServerHost([libFile, file1, file2, configFile]); + const service = createProjectService(host); + 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]); + }); + }); +} From cd18da8f1712458a49b94f727a0f5316ee7713fc Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 4 Sep 2020 16:04:01 -0700 Subject: [PATCH 17/19] Ensure that file updates are reflected --- src/server/editorServices.ts | 18 ++++++++- .../unittests/tsserver/reloadProjects.ts | 40 ++++++++++++++++--- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 7882d268efe2a..ccd3eea2048e5 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -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; @@ -878,7 +879,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(); } @@ -2796,6 +2797,21 @@ 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, /*clearSemanticCache*/ true, /*delayReload*/ false, returnTrue, "User requested reload projects"); this.externalProjects.forEach(project => { diff --git a/src/testRunner/unittests/tsserver/reloadProjects.ts b/src/testRunner/unittests/tsserver/reloadProjects.ts index d0654c1ec8663..970a9b2995eba 100644 --- a/src/testRunner/unittests/tsserver/reloadProjects.ts +++ b/src/testRunner/unittests/tsserver/reloadProjects.ts @@ -9,21 +9,40 @@ namespace ts.projectSystem { const file1: File = { path: `${tscWatch.projectRoot}/file1.ts`, content: `import { foo } from "module1"; - foo();` + foo(); + import { bar } from "./file2"; + bar();` }; const file2: File = { path: `${tscWatch.projectRoot}/file2.ts`, - content: `${file1.content} - export function bar(){}` + 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)!; @@ -37,18 +56,21 @@ namespace ts.projectSystem { 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]); + checkProjectActualFiles(project, [libFile.path, file1.path, file2.path]); // Install module1 host.ensureFileOrFolder(moduleFile); @@ -57,12 +79,15 @@ namespace ts.projectSystem { service.reloadProjects(); checkNumberOfProjects(service, { inferredProjects: 1 }); assert.strictEqual(service.inferredProjects[0], project); - checkProjectActualFiles(project, [libFile.path, file1.path, moduleFile.path]); + 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"] }, @@ -81,11 +106,14 @@ namespace ts.projectSystem { 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"] }, @@ -104,6 +132,8 @@ namespace ts.projectSystem { 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); }); }); } From 8a69e5c171b19adbc9ff861ed85842b2014a22dc Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 14 Sep 2020 12:44:13 -0700 Subject: [PATCH 18/19] Feedback --- src/compiler/commandLineParser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index f1612a5d76226..26ee292d52c44 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -136,7 +136,7 @@ namespace ts { name: "excludeDirectories", type: "list", element: { - name: "excludeDirectories", + name: "excludeDirectory", type: "string", isFilePath: true, extraValidation: specToDiagnostic @@ -148,7 +148,7 @@ namespace ts { name: "excludeFiles", type: "list", element: { - name: "excludeFiles", + name: "excludeFile", type: "string", isFilePath: true, extraValidation: specToDiagnostic From 90bcea54378021ae39bf197edeb322d54484b174 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 4 Nov 2020 12:00:24 -0800 Subject: [PATCH 19/19] Feedback --- src/compiler/commandLineParser.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index f37f937bd57f6..51cf7923a1009 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1882,15 +1882,15 @@ namespace ts { switch (valueExpression.kind) { case SyntaxKind.TrueKeyword: reportInvalidOptionValue(option && option.type !== "boolean"); - return validateValueOk(/*value*/ true); + return validateValue(/*value*/ true); case SyntaxKind.FalseKeyword: reportInvalidOptionValue(option && option.type !== "boolean"); - return validateValueOk(/*value*/ 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 validateValueOk(/*value*/ 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)) { @@ -1911,18 +1911,18 @@ namespace ts { invalidReported = true; } } - return validateValueOk(text); + return validateValue(text); case SyntaxKind.NumericLiteral: reportInvalidOptionValue(option && option.type !== "number"); - return validateValueOk(Number((valueExpression).text)); + return validateValue(Number((valueExpression).text)); case SyntaxKind.PrefixUnaryExpression: if ((valueExpression).operator !== SyntaxKind.MinusToken || (valueExpression).operand.kind !== SyntaxKind.NumericLiteral) { break; // not valid JSON syntax } reportInvalidOptionValue(option && option.type !== "number"); - return validateValueOk(-Number(((valueExpression).operand).text)); + return validateValue(-Number(((valueExpression).operand).text)); case SyntaxKind.ObjectLiteralExpression: reportInvalidOptionValue(option && option.type !== "object"); @@ -1936,18 +1936,18 @@ namespace ts { // If need arises, we can modify this interface and callbacks as needed if (option) { const { elementOptions, extraKeyDiagnostics, name: optionName } = option; - return validateValueOk(convertObjectLiteralExpressionToJson(objectLiteralExpression, + return validateValue(convertObjectLiteralExpressionToJson(objectLiteralExpression, elementOptions, extraKeyDiagnostics, optionName)); } else { - return validateValueOk(convertObjectLiteralExpressionToJson( + return validateValue(convertObjectLiteralExpressionToJson( objectLiteralExpression, /* knownOptions*/ undefined, /*extraKeyDiagnosticMessage */ undefined, /*parentOption*/ undefined)); } case SyntaxKind.ArrayLiteralExpression: reportInvalidOptionValue(option && option.type !== "list"); - return validateValueOk(convertArrayLiteralExpressionToJson( + return validateValue(convertArrayLiteralExpressionToJson( (valueExpression).elements, option && (option).element)); } @@ -1962,7 +1962,7 @@ namespace ts { return undefined; - function validateValueOk(value: CompilerOptionsValue) { + function validateValue(value: CompilerOptionsValue) { if (!invalidReported) { const diagnostic = option?.extraValidation?.(value); if (diagnostic) {