///
/* @internal */
namespace ts {
/**
* Ternary values are defined such that
* x & y is False if either x or y is False.
* x & y is Maybe if either x or y is Maybe, but neither x or y is False.
* x & y is True if both x and y are True.
* x | y is False if both x and y are False.
* x | y is Maybe if either x or y is Maybe, but neither x or y is True.
* x | y is True if either x or y is True.
*/
export const enum Ternary {
False = 0,
Maybe = 1,
True = -1
}
export function createFileMap(keyMapper?: (key: string) => string): FileMap {
let files: Map = {};
return {
get,
set,
contains,
remove,
forEachValue: forEachValueInMap,
clear
};
function forEachValueInMap(f: (key: Path, value: T) => void) {
for (const key in files) {
f(key, files[key]);
}
}
// path should already be well-formed so it does not need to be normalized
function get(path: Path): T {
return files[toKey(path)];
}
function set(path: Path, value: T) {
files[toKey(path)] = value;
}
function contains(path: Path) {
return hasProperty(files, toKey(path));
}
function remove(path: Path) {
const key = toKey(path);
delete files[key];
}
function clear() {
files = {};
}
function toKey(path: Path): string {
return keyMapper ? keyMapper(path) : path;
}
}
export function toPath(fileName: string, basePath: string, getCanonicalFileName: (path: string) => string): Path {
const nonCanonicalizedPath = isRootedDiskPath(fileName)
? normalizePath(fileName)
: getNormalizedAbsolutePath(fileName, basePath);
return getCanonicalFileName(nonCanonicalizedPath);
}
export const enum Comparison {
LessThan = -1,
EqualTo = 0,
GreaterThan = 1
}
/**
* Iterates through 'array' by index and performs the callback on each element of array until the callback
* returns a truthy value, then returns that value.
* If no such value is found, the callback is applied to each element of array and undefined is returned.
*/
export function forEach(array: T[], callback: (element: T, index: number) => U): U {
if (array) {
for (let i = 0, len = array.length; i < len; i++) {
const result = callback(array[i], i);
if (result) {
return result;
}
}
}
return undefined;
}
export function contains(array: T[], value: T): boolean {
if (array) {
for (const v of array) {
if (v === value) {
return true;
}
}
}
return false;
}
export function indexOf(array: T[], value: T): number {
if (array) {
for (let i = 0, len = array.length; i < len; i++) {
if (array[i] === value) {
return i;
}
}
}
return -1;
}
export function countWhere(array: T[], predicate: (x: T) => boolean): number {
let count = 0;
if (array) {
for (const v of array) {
if (predicate(v)) {
count++;
}
}
}
return count;
}
export function filter(array: T[], f: (x: T) => boolean): T[] {
let result: T[];
if (array) {
result = [];
for (const item of array) {
if (f(item)) {
result.push(item);
}
}
}
return result;
}
export function map(array: T[], f: (x: T) => U): U[] {
let result: U[];
if (array) {
result = [];
for (const v of array) {
result.push(f(v));
}
}
return result;
}
export function concatenate(array1: T[], array2: T[]): T[] {
if (!array2 || !array2.length) return array1;
if (!array1 || !array1.length) return array2;
return array1.concat(array2);
}
export function deduplicate(array: T[]): T[] {
let result: T[];
if (array) {
result = [];
for (const item of array) {
if (!contains(result, item)) {
result.push(item);
}
}
}
return result;
}
export function sum(array: any[], prop: string): number {
let result = 0;
for (const v of array) {
result += v[prop];
}
return result;
}
export function addRange(to: T[], from: T[]): void {
if (to && from) {
for (const v of from) {
to.push(v);
}
}
}
export function rangeEquals(array1: T[], array2: T[], pos: number, end: number) {
while (pos < end) {
if (array1[pos] !== array2[pos]) {
return false;
}
pos++;
}
return true;
}
/**
* Returns the last element of an array if non-empty, undefined otherwise.
*/
export function lastOrUndefined(array: T[]): T {
if (array.length === 0) {
return undefined;
}
return array[array.length - 1];
}
/**
* Performs a binary search, finding the index at which 'value' occurs in 'array'.
* If no such index is found, returns the 2's-complement of first index at which
* number[index] exceeds number.
* @param array A sorted array whose first element must be no larger than number
* @param number The value to be searched for in the array.
*/
export function binarySearch(array: number[], value: number): number {
let low = 0;
let high = array.length - 1;
while (low <= high) {
const middle = low + ((high - low) >> 1);
const midValue = array[middle];
if (midValue === value) {
return middle;
}
else if (midValue > value) {
high = middle - 1;
}
else {
low = middle + 1;
}
}
return ~low;
}
export function reduceLeft(array: T[], f: (a: T, x: T) => T): T;
export function reduceLeft(array: T[], f: (a: U, x: T) => U, initial: U): U;
export function reduceLeft(array: T[], f: (a: U, x: T) => U, initial?: U): U {
if (array) {
const count = array.length;
if (count > 0) {
let pos = 0;
let result = arguments.length <= 2 ? array[pos] : initial;
pos++;
while (pos < count) {
result = f(result, array[pos]);
pos++;
}
return result;
}
}
return initial;
}
export function reduceRight(array: T[], f: (a: T, x: T) => T): T;
export function reduceRight(array: T[], f: (a: U, x: T) => U, initial: U): U;
export function reduceRight(array: T[], f: (a: U, x: T) => U, initial?: U): U {
if (array) {
let pos = array.length - 1;
if (pos >= 0) {
let result = arguments.length <= 2 ? array[pos] : initial;
pos--;
while (pos >= 0) {
result = f(result, array[pos]);
pos--;
}
return result;
}
}
return initial;
}
const hasOwnProperty = Object.prototype.hasOwnProperty;
export function hasProperty(map: Map, key: string): boolean {
return hasOwnProperty.call(map, key);
}
export function getKeys(map: Map): string[] {
const keys: string[] = [];
for (const key in map) {
keys.push(key);
}
return keys;
}
export function getProperty(map: Map, key: string): T {
return hasOwnProperty.call(map, key) ? map[key] : undefined;
}
export function isEmpty(map: Map) {
for (const id in map) {
if (hasProperty(map, id)) {
return false;
}
}
return true;
}
export function clone(object: T): T {
const result: any = {};
for (const id in object) {
result[id] = (object)[id];
}
return result;
}
export function extend, T2 extends Map<{}>>(first: T1 , second: T2): T1 & T2 {
const result: T1 & T2 = {};
for (const id in first) {
(result as any)[id] = first[id];
}
for (const id in second) {
if (!hasProperty(result, id)) {
(result as any)[id] = second[id];
}
}
return result;
}
export function forEachValue(map: Map, callback: (value: T) => U): U {
let result: U;
for (const id in map) {
if (result = callback(map[id])) break;
}
return result;
}
export function forEachKey(map: Map, callback: (key: string) => U): U {
let result: U;
for (const id in map) {
if (result = callback(id)) break;
}
return result;
}
export function lookUp(map: Map, key: string): T {
return hasProperty(map, key) ? map[key] : undefined;
}
export function copyMap(source: Map, target: Map): void {
for (const p in source) {
target[p] = source[p];
}
}
/**
* Creates a map from the elements of an array.
*
* @param array the array of input elements.
* @param makeKey a function that produces a key for a given element.
*
* This function makes no effort to avoid collisions; if any two elements produce
* the same key with the given 'makeKey' function, then the element with the higher
* index in the array will be the one associated with the produced key.
*/
export function arrayToMap(array: T[], makeKey: (value: T) => string): Map {
const result: Map = {};
forEach(array, value => {
result[makeKey(value)] = value;
});
return result;
}
/**
* Reduce the properties of a map.
*
* @param map The map to reduce
* @param callback An aggregation function that is called for each entry in the map
* @param initial The initial value for the reduction.
*/
export function reduceProperties(map: Map, callback: (aggregate: U, value: T, key: string) => U, initial: U): U {
let result = initial;
if (map) {
for (const key in map) {
if (hasProperty(map, key)) {
result = callback(result, map[key], String(key));
}
}
}
return result;
}
/**
* Tests whether a value is an array.
*/
export function isArray(value: any): value is any[] {
return Array.isArray ? Array.isArray(value) : value instanceof Array;
}
export function memoize(callback: () => T): () => T {
let value: T;
return () => {
if (callback) {
value = callback();
callback = undefined;
}
return value;
};
}
function formatStringFromArgs(text: string, args: { [index: number]: any; }, baseIndex?: number): string {
baseIndex = baseIndex || 0;
return text.replace(/{(\d+)}/g, (match, index?) => args[+index + baseIndex]);
}
export let localizedDiagnosticMessages: Map = undefined;
export function getLocaleSpecificMessage(message: DiagnosticMessage) {
return localizedDiagnosticMessages && localizedDiagnosticMessages[message.key]
? localizedDiagnosticMessages[message.key]
: message.message;
}
export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ...args: any[]): Diagnostic;
export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): Diagnostic {
const end = start + length;
Debug.assert(start >= 0, "start must be non-negative, is " + start);
Debug.assert(length >= 0, "length must be non-negative, is " + length);
if (file) {
Debug.assert(start <= file.text.length, `start must be within the bounds of the file. ${ start } > ${ file.text.length }`);
Debug.assert(end <= file.text.length, `end must be the bounds of the file. ${ end } > ${ file.text.length }`);
}
let text = getLocaleSpecificMessage(message);
if (arguments.length > 4) {
text = formatStringFromArgs(text, arguments, 4);
}
return {
file,
start,
length,
messageText: text,
category: message.category,
code: message.code,
};
}
/* internal */
export function formatMessage(dummy: any, message: DiagnosticMessage): string {
let text = getLocaleSpecificMessage(message);
if (arguments.length > 2) {
text = formatStringFromArgs(text, arguments, 2);
}
return text;
}
export function createCompilerDiagnostic(message: DiagnosticMessage, ...args: any[]): Diagnostic;
export function createCompilerDiagnostic(message: DiagnosticMessage): Diagnostic {
let text = getLocaleSpecificMessage(message);
if (arguments.length > 1) {
text = formatStringFromArgs(text, arguments, 1);
}
return {
file: undefined,
start: undefined,
length: undefined,
messageText: text,
category: message.category,
code: message.code
};
}
export function chainDiagnosticMessages(details: DiagnosticMessageChain, message: DiagnosticMessage, ...args: any[]): DiagnosticMessageChain;
export function chainDiagnosticMessages(details: DiagnosticMessageChain, message: DiagnosticMessage): DiagnosticMessageChain {
let text = getLocaleSpecificMessage(message);
if (arguments.length > 2) {
text = formatStringFromArgs(text, arguments, 2);
}
return {
messageText: text,
category: message.category,
code: message.code,
next: details
};
}
export function concatenateDiagnosticMessageChains(headChain: DiagnosticMessageChain, tailChain: DiagnosticMessageChain): DiagnosticMessageChain {
let lastChain = headChain;
while (lastChain.next) {
lastChain = lastChain.next;
}
lastChain.next = tailChain;
return headChain;
}
export function compareValues(a: T, b: T): Comparison {
if (a === b) return Comparison.EqualTo;
if (a === undefined) return Comparison.LessThan;
if (b === undefined) return Comparison.GreaterThan;
return a < b ? Comparison.LessThan : Comparison.GreaterThan;
}
function getDiagnosticFileName(diagnostic: Diagnostic): string {
return diagnostic.file ? diagnostic.file.fileName : undefined;
}
export function compareDiagnostics(d1: Diagnostic, d2: Diagnostic): Comparison {
return compareValues(getDiagnosticFileName(d1), getDiagnosticFileName(d2)) ||
compareValues(d1.start, d2.start) ||
compareValues(d1.length, d2.length) ||
compareValues(d1.code, d2.code) ||
compareMessageText(d1.messageText, d2.messageText) ||
Comparison.EqualTo;
}
function compareMessageText(text1: string | DiagnosticMessageChain, text2: string | DiagnosticMessageChain): Comparison {
while (text1 && text2) {
// We still have both chains.
const string1 = typeof text1 === "string" ? text1 : text1.messageText;
const string2 = typeof text2 === "string" ? text2 : text2.messageText;
const res = compareValues(string1, string2);
if (res) {
return res;
}
text1 = typeof text1 === "string" ? undefined : text1.next;
text2 = typeof text2 === "string" ? undefined : text2.next;
}
if (!text1 && !text2) {
// if the chains are done, then these messages are the same.
return Comparison.EqualTo;
}
// We still have one chain remaining. The shorter chain should come first.
return text1 ? Comparison.GreaterThan : Comparison.LessThan;
}
export function sortAndDeduplicateDiagnostics(diagnostics: Diagnostic[]): Diagnostic[] {
return deduplicateSortedDiagnostics(diagnostics.sort(compareDiagnostics));
}
export function deduplicateSortedDiagnostics(diagnostics: Diagnostic[]): Diagnostic[] {
if (diagnostics.length < 2) {
return diagnostics;
}
const newDiagnostics = [diagnostics[0]];
let previousDiagnostic = diagnostics[0];
for (let i = 1; i < diagnostics.length; i++) {
const currentDiagnostic = diagnostics[i];
const isDupe = compareDiagnostics(currentDiagnostic, previousDiagnostic) === Comparison.EqualTo;
if (!isDupe) {
newDiagnostics.push(currentDiagnostic);
previousDiagnostic = currentDiagnostic;
}
}
return newDiagnostics;
}
export function normalizeSlashes(path: string): string {
return path.replace(/\\/g, "/");
}
// Returns length of path root (i.e. length of "/", "x:/", "//server/share/, file:///user/files")
export function getRootLength(path: string): number {
if (path.charCodeAt(0) === CharacterCodes.slash) {
if (path.charCodeAt(1) !== CharacterCodes.slash) return 1;
const p1 = path.indexOf("/", 2);
if (p1 < 0) return 2;
const p2 = path.indexOf("/", p1 + 1);
if (p2 < 0) return p1 + 1;
return p2 + 1;
}
if (path.charCodeAt(1) === CharacterCodes.colon) {
if (path.charCodeAt(2) === CharacterCodes.slash) return 3;
return 2;
}
// Per RFC 1738 'file' URI schema has the shape file:///
// if is omitted then it is assumed that host value is 'localhost',
// however slash after the omitted is not removed.
// file:///folder1/file1 - this is a correct URI
// file://folder2/file2 - this is an incorrect URI
if (path.lastIndexOf("file:///", 0) === 0) {
return "file:///".length;
}
const idx = path.indexOf("://");
if (idx !== -1) {
return idx + "://".length;
}
return 0;
}
export let directorySeparator = "/";
function getNormalizedParts(normalizedSlashedPath: string, rootLength: number) {
const parts = normalizedSlashedPath.substr(rootLength).split(directorySeparator);
const normalized: string[] = [];
for (const part of parts) {
if (part !== ".") {
if (part === ".." && normalized.length > 0 && lastOrUndefined(normalized) !== "..") {
normalized.pop();
}
else {
// A part may be an empty string (which is 'falsy') if the path had consecutive slashes,
// e.g. "path//file.ts". Drop these before re-joining the parts.
if (part) {
normalized.push(part);
}
}
}
}
return normalized;
}
export function normalizePath(path: string): string {
path = normalizeSlashes(path);
const rootLength = getRootLength(path);
const normalized = getNormalizedParts(path, rootLength);
return path.substr(0, rootLength) + normalized.join(directorySeparator);
}
export function getDirectoryPath(path: Path): Path;
export function getDirectoryPath(path: string): string;
export function getDirectoryPath(path: string): any {
return path.substr(0, Math.max(getRootLength(path), path.lastIndexOf(directorySeparator)));
}
export function isUrl(path: string) {
return path && !isRootedDiskPath(path) && path.indexOf("://") !== -1;
}
export function isRootedDiskPath(path: string) {
return getRootLength(path) !== 0;
}
function normalizedPathComponents(path: string, rootLength: number) {
const normalizedParts = getNormalizedParts(path, rootLength);
return [path.substr(0, rootLength)].concat(normalizedParts);
}
export function getNormalizedPathComponents(path: string, currentDirectory: string) {
path = normalizeSlashes(path);
let rootLength = getRootLength(path);
if (rootLength === 0) {
// If the path is not rooted it is relative to current directory
path = combinePaths(normalizeSlashes(currentDirectory), path);
rootLength = getRootLength(path);
}
return normalizedPathComponents(path, rootLength);
}
export function getNormalizedAbsolutePath(fileName: string, currentDirectory: string) {
return getNormalizedPathFromPathComponents(getNormalizedPathComponents(fileName, currentDirectory));
}
export function getNormalizedPathFromPathComponents(pathComponents: string[]) {
if (pathComponents && pathComponents.length) {
return pathComponents[0] + pathComponents.slice(1).join(directorySeparator);
}
}
function getNormalizedPathComponentsOfUrl(url: string) {
// Get root length of http://www.website.com/folder1/folder2/
// In this example the root is: http://www.website.com/
// normalized path components should be ["http://www.website.com/", "folder1", "folder2"]
const urlLength = url.length;
// Initial root length is http:// part
let rootLength = url.indexOf("://") + "://".length;
while (rootLength < urlLength) {
// Consume all immediate slashes in the protocol
// eg.initial rootlength is just file:// but it needs to consume another "/" in file:///
if (url.charCodeAt(rootLength) === CharacterCodes.slash) {
rootLength++;
}
else {
// non slash character means we continue proceeding to next component of root search
break;
}
}
// there are no parts after http:// just return current string as the pathComponent
if (rootLength === urlLength) {
return [url];
}
// Find the index of "/" after website.com so the root can be http://www.website.com/ (from existing http://)
const indexOfNextSlash = url.indexOf(directorySeparator, rootLength);
if (indexOfNextSlash !== -1) {
// Found the "/" after the website.com so the root is length of http://www.website.com/
// and get components after the root normally like any other folder components
rootLength = indexOfNextSlash + 1;
return normalizedPathComponents(url, rootLength);
}
else {
// Can't find the host assume the rest of the string as component
// but make sure we append "/" to it as root is not joined using "/"
// eg. if url passed in was http://website.com we want to use root as [http://website.com/]
// so that other path manipulations will be correct and it can be merged with relative paths correctly
return [url + directorySeparator];
}
}
function getNormalizedPathOrUrlComponents(pathOrUrl: string, currentDirectory: string) {
if (isUrl(pathOrUrl)) {
return getNormalizedPathComponentsOfUrl(pathOrUrl);
}
else {
return getNormalizedPathComponents(pathOrUrl, currentDirectory);
}
}
export function getRelativePathToDirectoryOrUrl(directoryPathOrUrl: string, relativeOrAbsolutePath: string, currentDirectory: string, getCanonicalFileName: (fileName: string) => string, isAbsolutePathAnUrl: boolean) {
const pathComponents = getNormalizedPathOrUrlComponents(relativeOrAbsolutePath, currentDirectory);
const directoryComponents = getNormalizedPathOrUrlComponents(directoryPathOrUrl, currentDirectory);
if (directoryComponents.length > 1 && lastOrUndefined(directoryComponents) === "") {
// If the directory path given was of type test/cases/ then we really need components of directory to be only till its name
// that is ["test", "cases", ""] needs to be actually ["test", "cases"]
directoryComponents.length--;
}
// Find the component that differs
let joinStartIndex: number;
for (joinStartIndex = 0; joinStartIndex < pathComponents.length && joinStartIndex < directoryComponents.length; joinStartIndex++) {
if (getCanonicalFileName(directoryComponents[joinStartIndex]) !== getCanonicalFileName(pathComponents[joinStartIndex])) {
break;
}
}
// Get the relative path
if (joinStartIndex) {
let relativePath = "";
const relativePathComponents = pathComponents.slice(joinStartIndex, pathComponents.length);
for (; joinStartIndex < directoryComponents.length; joinStartIndex++) {
if (directoryComponents[joinStartIndex] !== "") {
relativePath = relativePath + ".." + directorySeparator;
}
}
return relativePath + relativePathComponents.join(directorySeparator);
}
// Cant find the relative path, get the absolute path
let absolutePath = getNormalizedPathFromPathComponents(pathComponents);
if (isAbsolutePathAnUrl && isRootedDiskPath(absolutePath)) {
absolutePath = "file:///" + absolutePath;
}
return absolutePath;
}
export function getBaseFileName(path: string) {
if (path === undefined) {
return undefined;
}
const i = path.lastIndexOf(directorySeparator);
return i < 0 ? path : path.substring(i + 1);
}
export function combinePaths(path1: string, path2: string) {
if (!(path1 && path1.length)) return path2;
if (!(path2 && path2.length)) return path1;
if (getRootLength(path2) !== 0) return path2;
if (path1.charAt(path1.length - 1) === directorySeparator) return path1 + path2;
return path1 + directorySeparator + path2;
}
export function fileExtensionIs(path: string, extension: string): boolean {
const pathLen = path.length;
const extLen = extension.length;
return pathLen > extLen && path.substr(pathLen - extLen, extLen) === extension;
}
export function ensureScriptKind(fileName: string, scriptKind?: ScriptKind): ScriptKind {
// Using scriptKind as a condition handles both:
// - 'scriptKind' is unspecified and thus it is `undefined`
// - 'scriptKind' is set and it is `Unknown` (0)
// If the 'scriptKind' is 'undefined' or 'Unknown' then we attempt
// to get the ScriptKind from the file name. If it cannot be resolved
// from the file name then the default 'TS' script kind is returned.
return (scriptKind || getScriptKindFromFileName(fileName)) || ScriptKind.TS;
}
export function getScriptKindFromFileName(fileName: string): ScriptKind {
const ext = fileName.substr(fileName.lastIndexOf("."));
switch (ext.toLowerCase()) {
case ".js":
return ScriptKind.JS;
case ".jsx":
return ScriptKind.JSX;
case ".ts":
return ScriptKind.TS;
case ".tsx":
return ScriptKind.TSX;
default:
return ScriptKind.Unknown;
}
}
/**
* List of supported extensions in order of file resolution precedence.
*/
export const supportedTypeScriptExtensions = [".ts", ".tsx", ".d.ts"];
export const supportedJavascriptExtensions = [".js", ".jsx"];
const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions);
export function getSupportedExtensions(options?: CompilerOptions): string[] {
return options && options.allowJs ? allSupportedExtensions : supportedTypeScriptExtensions;
}
export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions) {
if (!fileName) { return false; }
for (const extension of getSupportedExtensions(compilerOptions)) {
if (fileExtensionIs(fileName, extension)) {
return true;
}
}
return false;
}
const extensionsToRemove = [".d.ts", ".ts", ".js", ".tsx", ".jsx"];
export function removeFileExtension(path: string): string {
for (const ext of extensionsToRemove) {
if (fileExtensionIs(path, ext)) {
return path.substr(0, path.length - ext.length);
}
}
return path;
}
export interface ObjectAllocator {
getNodeConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => Node;
getSourceFileConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => SourceFile;
getSymbolConstructor(): new (flags: SymbolFlags, name: string) => Symbol;
getTypeConstructor(): new (checker: TypeChecker, flags: TypeFlags) => Type;
getSignatureConstructor(): new (checker: TypeChecker) => Signature;
}
function Symbol(flags: SymbolFlags, name: string) {
this.flags = flags;
this.name = name;
this.declarations = undefined;
}
function Type(checker: TypeChecker, flags: TypeFlags) {
this.flags = flags;
}
function Signature(checker: TypeChecker) {
}
function Node(kind: SyntaxKind, pos: number, end: number) {
this.kind = kind;
this.pos = pos;
this.end = end;
this.flags = NodeFlags.None;
this.parent = undefined;
}
export let objectAllocator: ObjectAllocator = {
getNodeConstructor: () => Node,
getSourceFileConstructor: () => Node,
getSymbolConstructor: () => Symbol,
getTypeConstructor: () => Type,
getSignatureConstructor: () => Signature
};
export const enum AssertionLevel {
None = 0,
Normal = 1,
Aggressive = 2,
VeryAggressive = 3,
}
export namespace Debug {
const currentAssertionLevel = AssertionLevel.None;
export function shouldAssert(level: AssertionLevel): boolean {
return currentAssertionLevel >= level;
}
export function assert(expression: boolean, message?: string, verboseDebugInfo?: () => string): void {
if (!expression) {
let verboseDebugString = "";
if (verboseDebugInfo) {
verboseDebugString = "\r\nVerbose Debug Information: " + verboseDebugInfo();
}
debugger;
throw new Error("Debug Failure. False expression: " + (message || "") + verboseDebugString);
}
}
export function fail(message?: string): void {
Debug.assert(/*expression*/ false, message);
}
}
export function copyListRemovingItem(item: T, list: T[]) {
const copiedList: T[] = [];
for (const e of list) {
if (e !== item) {
copiedList.push(e);
}
}
return copiedList;
}
export function createGetCanonicalFileName(useCaseSensitivefileNames: boolean): (fileName: string) => string {
return useCaseSensitivefileNames
? ((fileName) => fileName)
: ((fileName) => fileName.toLowerCase());
}
}