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