diff --git a/src/testRunner/unittests/helpers/tsserver.ts b/src/testRunner/unittests/helpers/tsserver.ts index a4707cab4dcc3..c5bca88bd72b4 100644 --- a/src/testRunner/unittests/helpers/tsserver.ts +++ b/src/testRunner/unittests/helpers/tsserver.ts @@ -36,6 +36,29 @@ export const customTypesMap = { }` }; +function replaceAll(source: string, searchValue: string, replaceValue: string): string { + let result: string | undefined = + (source as string & { replaceAll: typeof source.replace }).replaceAll?.(searchValue, replaceValue); + + if (result !== undefined) { + return result; + } + + result = ""; + const searchLength = searchValue.length; + while (true) { + const index = source.indexOf(searchValue); + if (index < 0) { + break; + } + result += source.slice(0, index); + result += replaceValue; + source = source.slice(index + searchLength); + } + result += source; + return result; +} + export interface PostExecAction { readonly success: boolean; requestId: number; @@ -115,23 +138,24 @@ export function createLoggerWritingToConsole(host: TestServerHost): Logger { }, host); } -function sanitizeLog(s: string) { - return s.replace(/Elapsed::?\s*\d+(?:\.\d+)?ms/g, "Elapsed:: *ms") - .replace(/\"updateGraphDurationMs\"\:\s*\d+(?:\.\d+)?/g, `"updateGraphDurationMs": *`) - .replace(/\"createAutoImportProviderProgramDurationMs\"\:\s*\d+(?:\.\d+)?/g, `"createAutoImportProviderProgramDurationMs": *`) - .replace(versionRegExp, `FakeVersion`) - .replace(/getCompletionData: Get current token: \d+(?:\.\d+)?/g, `getCompletionData: Get current token: *`) - .replace(/getCompletionData: Is inside comment: \d+(?:\.\d+)?/g, `getCompletionData: Is inside comment: *`) - .replace(/getCompletionData: Get previous token: \d+(?:\.\d+)?/g, `getCompletionData: Get previous token: *`) - .replace(/getCompletionsAtPosition: isCompletionListBlocker: \d+(?:\.\d+)?/g, `getCompletionsAtPosition: isCompletionListBlocker: *`) - .replace(/getCompletionData: Semantic work: \d+(?:\.\d+)?/g, `getCompletionData: Semantic work: *`) - .replace(/getCompletionsAtPosition: getCompletionEntriesFromSymbols: \d+(?:\.\d+)?/g, `getCompletionsAtPosition: getCompletionEntriesFromSymbols: *`) - .replace(/forEachExternalModuleToImportFrom autoImportProvider: \d+(?:\.\d+)?/g, `forEachExternalModuleToImportFrom autoImportProvider: *`) - .replace(/getExportInfoMap: done in \d+(?:\.\d+)?/g, `getExportInfoMap: done in *`) - .replace(/collectAutoImports: \d+(?:\.\d+)?/g, `collectAutoImports: *`) - .replace(/continuePreviousIncompleteResponse: \d+(?:\.\d+)?/g, `continuePreviousIncompleteResponse: *`) - .replace(/dependencies in \d+(?:\.\d+)?/g, `dependencies in *`) - .replace(/\"exportMapKey\"\:\s*\"[_$a-zA-Z][_$_$a-zA-Z0-9]*\|\d+\|/g, match => match.replace(/\|\d+\|/, `|*|`)); +function sanitizeLog(s: string): string { + s = s.replace(/Elapsed::?\s*\d+(?:\.\d+)?ms/g, "Elapsed:: *ms"); + s = s.replace(/\"updateGraphDurationMs\"\:\s*\d+(?:\.\d+)?/g, `"updateGraphDurationMs": *`); + s = s.replace(/\"createAutoImportProviderProgramDurationMs\"\:\s*\d+(?:\.\d+)?/g, `"createAutoImportProviderProgramDurationMs": *`); + s = replaceAll(s, ts.version, "FakeVersion"); + s = s.replace(/getCompletionData: Get current token: \d+(?:\.\d+)?/g, `getCompletionData: Get current token: *`); + s = s.replace(/getCompletionData: Is inside comment: \d+(?:\.\d+)?/g, `getCompletionData: Is inside comment: *`); + s = s.replace(/getCompletionData: Get previous token: \d+(?:\.\d+)?/g, `getCompletionData: Get previous token: *`); + s = s.replace(/getCompletionsAtPosition: isCompletionListBlocker: \d+(?:\.\d+)?/g, `getCompletionsAtPosition: isCompletionListBlocker: *`); + s = s.replace(/getCompletionData: Semantic work: \d+(?:\.\d+)?/g, `getCompletionData: Semantic work: *`); + s = s.replace(/getCompletionsAtPosition: getCompletionEntriesFromSymbols: \d+(?:\.\d+)?/g, `getCompletionsAtPosition: getCompletionEntriesFromSymbols: *`); + s = s.replace(/forEachExternalModuleToImportFrom autoImportProvider: \d+(?:\.\d+)?/g, `forEachExternalModuleToImportFrom autoImportProvider: *`); + s = s.replace(/getExportInfoMap: done in \d+(?:\.\d+)?/g, `getExportInfoMap: done in *`); + s = s.replace(/collectAutoImports: \d+(?:\.\d+)?/g, `collectAutoImports: *`); + s = s.replace(/continuePreviousIncompleteResponse: \d+(?:\.\d+)?/g, `continuePreviousIncompleteResponse: *`); + s = s.replace(/dependencies in \d+(?:\.\d+)?/g, `dependencies in *`); + s = s.replace(/\"exportMapKey\"\:\s*\"[_$a-zA-Z][_$_$a-zA-Z0-9]*\|\d+\|/g, match => match.replace(/\|\d+\|/, `|*|`)); + return s; } export function createLoggerWithInMemoryLogs(host: TestServerHost): Logger { @@ -159,14 +183,22 @@ export function appendAllScriptInfos(session: TestSession) { session.logger.log(""); } -const versionRegExp = new RegExp(ts.version, "g"); -const tsMajorMinorVersion = new RegExp(`@ts${ts.versionMajorMinor}`, "g"); function loggerToTypingsInstallerLog(logger: Logger): ts.server.typingsInstaller.Log | undefined { return logger?.loggingEnabled() ? { isEnabled: ts.returnTrue, - writeLine: s => logger.log(`TI:: [${nowString(logger.host!)}] ${sanitizeLog(s).replace(versionRegExp, "FakeVersion") - .replace(tsMajorMinorVersion, `@tsFakeMajor.Minor`) - }`), + writeLine: s => { + // This is a VERY VERY NAIVE sanitization strategy. + // If a substring containing the exact TypeScript version is found, + // even if it's unrelated to TypeScript itself, then it will be replaced, + // leaving us with two options: + // + // 1. Deal with flip-flopping baselines. + // 2. Change the TypeScript version until no matching substring is found. + // + const initialLog = sanitizeLog(s); + const pseudoSanitizedLog = replaceAll(initialLog, `@ts${ts.versionMajorMinor}`, `@tsFakeMajor.Minor`); + return logger.log(`TI:: [${nowString(logger.host!)}] ${pseudoSanitizedLog}`); + }, } : undefined; }