1import * as ts from "./_namespaces/ts"; 2 3/** 4 * Common utilities 5 */ 6 7const testPathPrefixRegExp = /(?:(file:\/{3})|\/)\.(ts|lib|src)\//g; 8export function removeTestPathPrefixes(text: string, retainTrailingDirectorySeparator?: boolean): string { 9 return text !== undefined ? text.replace(testPathPrefixRegExp, (_, scheme) => scheme || (retainTrailingDirectorySeparator ? "/" : "")) : undefined!; // TODO: GH#18217 10} 11 12function createDiagnosticMessageReplacer<R extends (messageArgs: string[], ...args: string[]) => string[]>(diagnosticMessage: ts.DiagnosticMessage, replacer: R) { 13 const messageParts = diagnosticMessage.message.split(/{\d+}/g); 14 const regExp = new RegExp(`^(?:${messageParts.map(ts.regExpEscape).join("(.*?)")})$`); 15 type Args<R> = R extends (messageArgs: string[], ...args: infer A) => string[] ? A : []; 16 return (text: string, ...args: Args<R>) => text.replace(regExp, (_, ...fixedArgs) => ts.formatStringFromArgs(diagnosticMessage.message, replacer(fixedArgs, ...args))); 17} 18 19const replaceTypesVersionsMessage = createDiagnosticMessageReplacer( 20 ts.Diagnostics.package_json_has_a_typesVersions_entry_0_that_matches_compiler_version_1_looking_for_a_pattern_to_match_module_name_2, 21 ([entry, , moduleName], compilerVersion) => [entry, compilerVersion, moduleName]); 22 23export function sanitizeTraceResolutionLogEntry(text: string) { 24 return text && removeTestPathPrefixes(replaceTypesVersionsMessage(text, "3.1.0-dev")); 25} 26 27/** 28 * Removes leading indentation from a template literal string. 29 */ 30export function dedent(array: TemplateStringsArray, ...args: any[]) { 31 let text = array[0]; 32 for (let i = 0; i < args.length; i++) { 33 text += args[i]; 34 text += array[i + 1]; 35 } 36 37 const lineTerminatorRegExp = /\r\n?|\n/g; 38 const lines: string[] = []; 39 const lineTerminators: string[] = []; 40 let match: RegExpExecArray | null; 41 let lineStart = 0; 42 while (match = lineTerminatorRegExp.exec(text)) { 43 if (lineStart !== match.index || lines.length > 0) { 44 lines.push(text.slice(lineStart, match.index)); 45 lineTerminators.push(match[0]); 46 } 47 lineStart = match.index + match[0].length; 48 } 49 50 if (lineStart < text.length) { 51 lines.push(text.slice(lineStart)); 52 } 53 54 const indentation = guessIndentation(lines); 55 56 let result = ""; 57 for (let i = 0; i < lines.length; i++) { 58 const lineText = lines[i]; 59 const line = indentation ? lineText.slice(indentation) : lineText; 60 result += line; 61 if (i < lineTerminators.length) { 62 result += lineTerminators[i]; 63 } 64 } 65 return result; 66} 67 68function guessIndentation(lines: string[]) { 69 let indentation: number | undefined; 70 for (const line of lines) { 71 for (let i = 0; i < line.length && (indentation === undefined || i < indentation); i++) { 72 if (!ts.isWhiteSpaceLike(line.charCodeAt(i))) { 73 if (indentation === undefined || i < indentation) { 74 indentation = i; 75 break; 76 } 77 } 78 } 79 } 80 return indentation; 81} 82 83export function getByteOrderMarkLength(text: string): number { 84 if (text.length >= 1) { 85 const ch0 = text.charCodeAt(0); 86 if (ch0 === 0xfeff) return 1; 87 if (ch0 === 0xfe) return text.length >= 2 && text.charCodeAt(1) === 0xff ? 2 : 0; 88 if (ch0 === 0xff) return text.length >= 2 && text.charCodeAt(1) === 0xfe ? 2 : 0; 89 if (ch0 === 0xef) return text.length >= 3 && text.charCodeAt(1) === 0xbb && text.charCodeAt(2) === 0xbf ? 3 : 0; 90 } 91 return 0; 92} 93 94export function removeByteOrderMark(text: string): string { 95 const length = getByteOrderMarkLength(text); 96 return length ? text.slice(length) : text; 97} 98 99export function addUTF8ByteOrderMark(text: string) { 100 return getByteOrderMarkLength(text) === 0 ? "\u00EF\u00BB\u00BF" + text : text; 101} 102 103export function theory<T extends any[]>(name: string, cb: (...args: T) => void, data: T[]) { 104 for (const entry of data) { 105 it(`${name}(${entry.map(formatTheoryDatum).join(", ")})`, () => cb(...entry)); 106 } 107} 108 109function formatTheoryDatum(value: any) { 110 return typeof value === "function" ? value.name || "<anonymous function>" : 111 value === undefined ? "undefined" : 112 JSON.stringify(value); 113} 114 115export interface Deferred<T> { 116 resolve: (value: T | PromiseLike<T>) => void; 117 reject: (reason: unknown) => void; 118 promise: Promise<T>; 119} 120 121export function defer<T = void>(): Deferred<T> { 122 let resolve!: (value: T | PromiseLike<T>) => void; 123 let reject!: (reason: unknown) => void; 124 const promise = new Promise<T>((_resolve, _reject) => { 125 resolve = _resolve; 126 reject = _reject; 127 }); 128 return { resolve, reject, promise }; 129}