/* @internal */ namespace ts { export enum LogLevel { Off, Error, Warning, Info, Verbose } export interface LoggingHost { log(level: LogLevel, s: string): void; } export interface DeprecationOptions { message?: string; error?: boolean; since?: Version | string; warnAfter?: Version | string; errorAfter?: Version | string; typeScriptVersion?: Version | string; name?: string; } export namespace Debug { let typeScriptVersion: Version | undefined; /* eslint-disable prefer-const */ let currentAssertionLevel = AssertionLevel.None; export let currentLogLevel = LogLevel.Warning; export let isDebugging = false; export let loggingHost: LoggingHost | undefined; export let enableDeprecationWarnings = true; /* eslint-enable prefer-const */ type AssertionKeys = MatchingKeys; export function getTypeScriptVersion() { return typeScriptVersion ?? (typeScriptVersion = new Version(version)); } export function shouldLog(level: LogLevel): boolean { return currentLogLevel <= level; } function logMessage(level: LogLevel, s: string): void { if (loggingHost && shouldLog(level)) { loggingHost.log(level, s); } } export function log(s: string): void { logMessage(LogLevel.Info, s); } export namespace log { export function error(s: string): void { logMessage(LogLevel.Error, s); } export function warn(s: string): void { logMessage(LogLevel.Warning, s); } export function log(s: string): void { logMessage(LogLevel.Info, s); } export function trace(s: string): void { logMessage(LogLevel.Verbose, s); } } const assertionCache: Partial> = {}; export function getAssertionLevel() { return currentAssertionLevel; } export function setAssertionLevel(level: AssertionLevel) { const prevAssertionLevel = currentAssertionLevel; currentAssertionLevel = level; if (level > prevAssertionLevel) { // restore assertion functions for the current assertion level (see `shouldAssertFunction`). for (const key of getOwnKeys(assertionCache) as AssertionKeys[]) { const cachedFunc = assertionCache[key]; if (cachedFunc !== undefined && Debug[key] !== cachedFunc.assertion && level >= cachedFunc.level) { (Debug as any)[key] = cachedFunc; assertionCache[key] = undefined; } } } } export function shouldAssert(level: AssertionLevel): boolean { return currentAssertionLevel >= level; } /** * Tests whether an assertion function should be executed. If it shouldn't, it is cached and replaced with `ts.noop`. * Replaced assertion functions are restored when `Debug.setAssertionLevel` is set to a high enough level. * @param level The minimum assertion level required. * @param name The name of the current assertion function. */ function shouldAssertFunction(level: AssertionLevel, name: K): boolean { if (!shouldAssert(level)) { assertionCache[name] = { level, assertion: Debug[name] }; (Debug as any)[name] = noop; return false; } return true; } export function fail(message?: string, stackCrawlMark?: AnyFunction): never { debugger; const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure."); if ((Error as any).captureStackTrace) { (Error as any).captureStackTrace(e, stackCrawlMark || fail); } throw e; } export function failBadSyntaxKind(node: Node, message?: string, stackCrawlMark?: AnyFunction): never { return fail( `${message || "Unexpected node."}\r\nNode ${formatSyntaxKind(node.kind)} was unexpected.`, stackCrawlMark || failBadSyntaxKind); } export function assert(expression: unknown, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: AnyFunction): asserts expression { if (!expression) { message = message ? `False expression: ${message}` : "False expression."; if (verboseDebugInfo) { message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo()); } fail(message, stackCrawlMark || assert); } } export function assertEqual(a: T, b: T, msg?: string, msg2?: string, stackCrawlMark?: AnyFunction): void { if (a !== b) { const message = msg ? msg2 ? `${msg} ${msg2}` : msg : ""; fail(`Expected ${a} === ${b}. ${message}`, stackCrawlMark || assertEqual); } } export function assertLessThan(a: number, b: number, msg?: string, stackCrawlMark?: AnyFunction): void { if (a >= b) { fail(`Expected ${a} < ${b}. ${msg || ""}`, stackCrawlMark || assertLessThan); } } export function assertLessThanOrEqual(a: number, b: number, stackCrawlMark?: AnyFunction): void { if (a > b) { fail(`Expected ${a} <= ${b}`, stackCrawlMark || assertLessThanOrEqual); } } export function assertGreaterThanOrEqual(a: number, b: number, stackCrawlMark?: AnyFunction): void { if (a < b) { fail(`Expected ${a} >= ${b}`, stackCrawlMark || assertGreaterThanOrEqual); } } export function assertIsDefined(value: T, message?: string, stackCrawlMark?: AnyFunction): asserts value is NonNullable { // eslint-disable-next-line no-null/no-null if (value === undefined || value === null) { fail(message, stackCrawlMark || assertIsDefined); } } export function checkDefined(value: T | null | undefined, message?: string, stackCrawlMark?: AnyFunction): T { assertIsDefined(value, message, stackCrawlMark || checkDefined); return value; } export function assertEachIsDefined(value: NodeArray, message?: string, stackCrawlMark?: AnyFunction): asserts value is NodeArray; export function assertEachIsDefined(value: readonly T[], message?: string, stackCrawlMark?: AnyFunction): asserts value is readonly NonNullable[]; export function assertEachIsDefined(value: readonly T[], message?: string, stackCrawlMark?: AnyFunction) { for (const v of value) { assertIsDefined(v, message, stackCrawlMark || assertEachIsDefined); } } export function checkEachDefined(value: A, message?: string, stackCrawlMark?: AnyFunction): A { assertEachIsDefined(value, message, stackCrawlMark || checkEachDefined); return value; } export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never { const detail = typeof member === "object" && hasProperty(member, "kind") && hasProperty(member, "pos") ? "SyntaxKind: " + formatSyntaxKind((member as Node).kind) : JSON.stringify(member); return fail(`${message} ${detail}`, stackCrawlMark || assertNever); } export function assertEachNode(nodes: NodeArray, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is NodeArray; export function assertEachNode(nodes: readonly T[], test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is readonly U[]; export function assertEachNode(nodes: NodeArray | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is NodeArray | undefined; export function assertEachNode(nodes: readonly T[] | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts nodes is readonly U[] | undefined; export function assertEachNode(nodes: readonly Node[], test: (node: Node) => boolean, message?: string, stackCrawlMark?: AnyFunction): void; export function assertEachNode(nodes: readonly Node[] | undefined, test: (node: Node) => boolean, message?: string, stackCrawlMark?: AnyFunction) { if (shouldAssertFunction(AssertionLevel.Normal, "assertEachNode")) { assert( test === undefined || every(nodes, test), message || "Unexpected node.", () => `Node array did not pass test '${getFunctionName(test)}'.`, stackCrawlMark || assertEachNode); } } export function assertNode(node: T | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U; export function assertNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void; export function assertNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) { if (shouldAssertFunction(AssertionLevel.Normal, "assertNode")) { assert( node !== undefined && (test === undefined || test(node)), message || "Unexpected node.", () => `Node ${formatSyntaxKind(node?.kind)} did not pass test '${getFunctionName(test!)}'.`, stackCrawlMark || assertNode); } } export function assertNotNode(node: T | undefined, test: (node: Node) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is Exclude; export function assertNotNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void; export function assertNotNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) { if (shouldAssertFunction(AssertionLevel.Normal, "assertNotNode")) { assert( node === undefined || test === undefined || !test(node), message || "Unexpected node.", () => `Node ${formatSyntaxKind(node!.kind)} should not have passed test '${getFunctionName(test!)}'.`, stackCrawlMark || assertNotNode); } } export function assertOptionalNode(node: T, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U; export function assertOptionalNode(node: T | undefined, test: (node: T) => node is U, message?: string, stackCrawlMark?: AnyFunction): asserts node is U | undefined; export function assertOptionalNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction): void; export function assertOptionalNode(node: Node | undefined, test: ((node: Node) => boolean) | undefined, message?: string, stackCrawlMark?: AnyFunction) { if (shouldAssertFunction(AssertionLevel.Normal, "assertOptionalNode")) { assert( test === undefined || node === undefined || test(node), message || "Unexpected node.", () => `Node ${formatSyntaxKind(node?.kind)} did not pass test '${getFunctionName(test!)}'.`, stackCrawlMark || assertOptionalNode); } } export function assertOptionalToken(node: T, kind: K, message?: string, stackCrawlMark?: AnyFunction): asserts node is Extract; export function assertOptionalToken(node: T | undefined, kind: K, message?: string, stackCrawlMark?: AnyFunction): asserts node is Extract | undefined; export function assertOptionalToken(node: Node | undefined, kind: SyntaxKind | undefined, message?: string, stackCrawlMark?: AnyFunction): void; export function assertOptionalToken(node: Node | undefined, kind: SyntaxKind | undefined, message?: string, stackCrawlMark?: AnyFunction) { if (shouldAssertFunction(AssertionLevel.Normal, "assertOptionalToken")) { assert( kind === undefined || node === undefined || node.kind === kind, message || "Unexpected node.", () => `Node ${formatSyntaxKind(node?.kind)} was not a '${formatSyntaxKind(kind)}' token.`, stackCrawlMark || assertOptionalToken); } } export function assertMissingNode(node: Node | undefined, message?: string, stackCrawlMark?: AnyFunction): asserts node is undefined; export function assertMissingNode(node: Node | undefined, message?: string, stackCrawlMark?: AnyFunction) { if (shouldAssertFunction(AssertionLevel.Normal, "assertMissingNode")) { assert( node === undefined, message || "Unexpected node.", () => `Node ${formatSyntaxKind(node!.kind)} was unexpected'.`, stackCrawlMark || assertMissingNode); } } /** * Asserts a value has the specified type in typespace only (does not perform a runtime assertion). * This is useful in cases where we switch on `node.kind` and can be reasonably sure the type is accurate, and * as a result can reduce the number of unnecessary casts. */ export function type(value: unknown): asserts value is T; export function type(_value: unknown) { } export function getFunctionName(func: AnyFunction) { if (typeof func !== "function") { return ""; } else if (hasProperty(func, "name")) { return (func as any).name; } else { const text = Function.prototype.toString.call(func); const match = /^function\s+([\w\$]+)\s*\(/.exec(text); return match ? match[1] : ""; } } export function formatSymbol(symbol: Symbol): string { return `{ name: ${unescapeLeadingUnderscores(symbol.escapedName)}; flags: ${formatSymbolFlags(symbol.flags)}; declarations: ${map(symbol.declarations, node => formatSyntaxKind(node.kind))} }`; } /** * Formats an enum value as a string for debugging and debug assertions. */ export function formatEnum(value = 0, enumObject: any, isFlags?: boolean) { const members = getEnumMembers(enumObject); if (value === 0) { return members.length > 0 && members[0][0] === 0 ? members[0][1] : "0"; } if (isFlags) { const result: string[] = []; let remainingFlags = value; for (const [enumValue, enumName] of members) { if (enumValue > value) { break; } if (enumValue !== 0 && enumValue & value) { result.push(enumName); remainingFlags &= ~enumValue; } } if (remainingFlags === 0) { return result.join("|"); } } else { for (const [enumValue, enumName] of members) { if (enumValue === value) { return enumName; } } } return value.toString(); } const enumMemberCache = new Map>(); function getEnumMembers(enumObject: any) { // Assuming enum objects do not change at runtime, we can cache the enum members list // to reuse later. This saves us from reconstructing this each and every time we call // a formatting function (which can be expensive for large enums like SyntaxKind). const existing = enumMemberCache.get(enumObject); if (existing) { return existing; } const result: [number, string][] = []; for (const name in enumObject) { const value = enumObject[name]; if (typeof value === "number") { result.push([value, name]); } } const sorted = stableSort<[number, string]>(result, (x, y) => compareValues(x[0], y[0])); enumMemberCache.set(enumObject, sorted); return sorted; } export function formatSyntaxKind(kind: SyntaxKind | undefined): string { return formatEnum(kind, (ts as any).SyntaxKind, /*isFlags*/ false); } export function formatSnippetKind(kind: SnippetKind | undefined): string { return formatEnum(kind, (ts as any).SnippetKind, /*isFlags*/ false); } export function formatNodeFlags(flags: NodeFlags | undefined): string { return formatEnum(flags, (ts as any).NodeFlags, /*isFlags*/ true); } export function formatModifierFlags(flags: ModifierFlags | undefined): string { return formatEnum(flags, (ts as any).ModifierFlags, /*isFlags*/ true); } export function formatTransformFlags(flags: TransformFlags | undefined): string { return formatEnum(flags, (ts as any).TransformFlags, /*isFlags*/ true); } export function formatEmitFlags(flags: EmitFlags | undefined): string { return formatEnum(flags, (ts as any).EmitFlags, /*isFlags*/ true); } export function formatSymbolFlags(flags: SymbolFlags | undefined): string { return formatEnum(flags, (ts as any).SymbolFlags, /*isFlags*/ true); } export function formatTypeFlags(flags: TypeFlags | undefined): string { return formatEnum(flags, (ts as any).TypeFlags, /*isFlags*/ true); } export function formatSignatureFlags(flags: SignatureFlags | undefined): string { return formatEnum(flags, (ts as any).SignatureFlags, /*isFlags*/ true); } export function formatObjectFlags(flags: ObjectFlags | undefined): string { return formatEnum(flags, (ts as any).ObjectFlags, /*isFlags*/ true); } export function formatFlowFlags(flags: FlowFlags | undefined): string { return formatEnum(flags, (ts as any).FlowFlags, /*isFlags*/ true); } export function formatRelationComparisonResult(result: RelationComparisonResult | undefined): string { return formatEnum(result, (ts as any).RelationComparisonResult, /*isFlags*/ true); } export function formatCheckMode(mode: CheckMode | undefined): string { return formatEnum(mode, (ts as any).CheckMode, /*isFlags*/ true); } export function formatSignatureCheckMode(mode: SignatureCheckMode | undefined): string { return formatEnum(mode, (ts as any).SignatureCheckMode, /*isFlags*/ true); } export function formatTypeFacts(facts: TypeFacts | undefined): string { return formatEnum(facts, (ts as any).TypeFacts, /*isFlags*/ true); } let isDebugInfoEnabled = false; interface ExtendedDebugModule { init(_ts: typeof ts): void; formatControlFlowGraph(flowNode: FlowNode): string; } let extendedDebugModule: ExtendedDebugModule | undefined; function extendedDebug() { enableDebugInfo(); if (!extendedDebugModule) { throw new Error("Debugging helpers could not be loaded."); } return extendedDebugModule; } export function printControlFlowGraph(flowNode: FlowNode) { return console.log(formatControlFlowGraph(flowNode)); } export function formatControlFlowGraph(flowNode: FlowNode) { return extendedDebug().formatControlFlowGraph(flowNode); } let flowNodeProto: FlowNodeBase | undefined; function attachFlowNodeDebugInfoWorker(flowNode: FlowNodeBase) { if (!("__debugFlowFlags" in flowNode)) { // eslint-disable-line local/no-in-operator Object.defineProperties(flowNode, { // for use with vscode-js-debug's new customDescriptionGenerator in launch.json __tsDebuggerDisplay: { value(this: FlowNodeBase) { const flowHeader = this.flags & FlowFlags.Start ? "FlowStart" : this.flags & FlowFlags.BranchLabel ? "FlowBranchLabel" : this.flags & FlowFlags.LoopLabel ? "FlowLoopLabel" : this.flags & FlowFlags.Assignment ? "FlowAssignment" : this.flags & FlowFlags.TrueCondition ? "FlowTrueCondition" : this.flags & FlowFlags.FalseCondition ? "FlowFalseCondition" : this.flags & FlowFlags.SwitchClause ? "FlowSwitchClause" : this.flags & FlowFlags.ArrayMutation ? "FlowArrayMutation" : this.flags & FlowFlags.Call ? "FlowCall" : this.flags & FlowFlags.ReduceLabel ? "FlowReduceLabel" : this.flags & FlowFlags.Unreachable ? "FlowUnreachable" : "UnknownFlow"; const remainingFlags = this.flags & ~(FlowFlags.Referenced - 1); return `${flowHeader}${remainingFlags ? ` (${formatFlowFlags(remainingFlags)})`: ""}`; } }, __debugFlowFlags: { get(this: FlowNodeBase) { return formatEnum(this.flags, (ts as any).FlowFlags, /*isFlags*/ true); } }, __debugToString: { value(this: FlowNodeBase) { return formatControlFlowGraph(this); } } }); } } export function attachFlowNodeDebugInfo(flowNode: FlowNodeBase) { if (isDebugInfoEnabled) { if (typeof Object.setPrototypeOf === "function") { // if we're in es2015, attach the method to a shared prototype for `FlowNode` // so the method doesn't show up in the watch window. if (!flowNodeProto) { flowNodeProto = Object.create(Object.prototype) as FlowNodeBase; attachFlowNodeDebugInfoWorker(flowNodeProto); } Object.setPrototypeOf(flowNode, flowNodeProto); } else { // not running in an es2015 environment, attach the method directly. attachFlowNodeDebugInfoWorker(flowNode); } } } let nodeArrayProto: NodeArray | undefined; function attachNodeArrayDebugInfoWorker(array: NodeArray) { if (!("__tsDebuggerDisplay" in array)) { // eslint-disable-line local/no-in-operator Object.defineProperties(array, { __tsDebuggerDisplay: { value(this: NodeArray, defaultValue: string) { // An `Array` with extra properties is rendered as `[A, B, prop1: 1, prop2: 2]`. Most of // these aren't immediately useful so we trim off the `prop1: ..., prop2: ...` part from the // formatted string. // This regex can trigger slow backtracking because of overlapping potential captures. // We don't care, this is debug code that's only enabled with a debugger attached - // we're just taking note of it for anyone checking regex performance in the future. defaultValue = String(defaultValue).replace(/(?:,[\s\w\d_]+:[^,]+)+\]$/, "]"); return `NodeArray ${defaultValue}`; } } }); } } export function attachNodeArrayDebugInfo(array: NodeArray) { if (isDebugInfoEnabled) { if (typeof Object.setPrototypeOf === "function") { // if we're in es2015, attach the method to a shared prototype for `NodeArray` // so the method doesn't show up in the watch window. if (!nodeArrayProto) { nodeArrayProto = Object.create(Array.prototype) as NodeArray; attachNodeArrayDebugInfoWorker(nodeArrayProto); } Object.setPrototypeOf(array, nodeArrayProto); } else { // not running in an es2015 environment, attach the method directly. attachNodeArrayDebugInfoWorker(array); } } } /** * Injects debug information into frequently used types. */ export function enableDebugInfo() { if (isDebugInfoEnabled) return; // avoid recomputing let weakTypeTextMap: WeakMap | undefined; let weakNodeTextMap: WeakMap | undefined; function getWeakTypeTextMap() { if (weakTypeTextMap === undefined) { if (typeof WeakMap === "function") weakTypeTextMap = new WeakMap(); } return weakTypeTextMap; } function getWeakNodeTextMap() { if (weakNodeTextMap === undefined) { if (typeof WeakMap === "function") weakNodeTextMap = new WeakMap(); } return weakNodeTextMap; } // Add additional properties in debug mode to assist with debugging. Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, { // for use with vscode-js-debug's new customDescriptionGenerator in launch.json __tsDebuggerDisplay: { value(this: Symbol) { const symbolHeader = this.flags & SymbolFlags.Transient ? "TransientSymbol" : "Symbol"; const remainingSymbolFlags = this.flags & ~SymbolFlags.Transient; return `${symbolHeader} '${symbolName(this)}'${remainingSymbolFlags ? ` (${formatSymbolFlags(remainingSymbolFlags)})` : ""}`; } }, __debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); } } }); Object.defineProperties(objectAllocator.getTypeConstructor().prototype, { // for use with vscode-js-debug's new customDescriptionGenerator in launch.json __tsDebuggerDisplay: { value(this: Type) { const typeHeader = this.flags & TypeFlags.Nullable ? "NullableType" : this.flags & TypeFlags.StringOrNumberLiteral ? `LiteralType ${JSON.stringify((this as LiteralType).value)}` : this.flags & TypeFlags.BigIntLiteral ? `LiteralType ${(this as BigIntLiteralType).value.negative ? "-" : ""}${(this as BigIntLiteralType).value.base10Value}n` : this.flags & TypeFlags.UniqueESSymbol ? "UniqueESSymbolType" : this.flags & TypeFlags.Enum ? "EnumType" : this.flags & TypeFlags.Intrinsic ? `IntrinsicType ${(this as IntrinsicType).intrinsicName}` : this.flags & TypeFlags.Union ? "UnionType" : this.flags & TypeFlags.Intersection ? "IntersectionType" : this.flags & TypeFlags.Index ? "IndexType" : this.flags & TypeFlags.IndexedAccess ? "IndexedAccessType" : this.flags & TypeFlags.Conditional ? "ConditionalType" : this.flags & TypeFlags.Substitution ? "SubstitutionType" : this.flags & TypeFlags.TypeParameter ? "TypeParameter" : this.flags & TypeFlags.Object ? (this as ObjectType).objectFlags & ObjectFlags.ClassOrInterface ? "InterfaceType" : (this as ObjectType).objectFlags & ObjectFlags.Reference ? "TypeReference" : (this as ObjectType).objectFlags & ObjectFlags.Tuple ? "TupleType" : (this as ObjectType).objectFlags & ObjectFlags.Anonymous ? "AnonymousType" : (this as ObjectType).objectFlags & ObjectFlags.Mapped ? "MappedType" : (this as ObjectType).objectFlags & ObjectFlags.ReverseMapped ? "ReverseMappedType" : (this as ObjectType).objectFlags & ObjectFlags.EvolvingArray ? "EvolvingArrayType" : "ObjectType" : "Type"; const remainingObjectFlags = this.flags & TypeFlags.Object ? (this as ObjectType).objectFlags & ~ObjectFlags.ObjectTypeKindMask : 0; return `${typeHeader}${this.symbol ? ` '${symbolName(this.symbol)}'` : ""}${remainingObjectFlags ? ` (${formatObjectFlags(remainingObjectFlags)})` : ""}`; } }, __debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); } }, __debugObjectFlags: { get(this: Type) { return this.flags & TypeFlags.Object ? formatObjectFlags((this as ObjectType).objectFlags) : ""; } }, __debugTypeToString: { value(this: Type) { // avoid recomputing const map = getWeakTypeTextMap(); let text = map?.get(this); if (text === undefined) { text = this.checker.typeToString(this); map?.set(this, text); } return text; } }, }); Object.defineProperties(objectAllocator.getSignatureConstructor().prototype, { __debugFlags: { get(this: Signature) { return formatSignatureFlags(this.flags); } }, __debugSignatureToString: { value(this: Signature) { return this.checker?.signatureToString(this); } } }); const nodeConstructors = [ objectAllocator.getNodeConstructor(), objectAllocator.getIdentifierConstructor(), objectAllocator.getTokenConstructor(), objectAllocator.getSourceFileConstructor() ]; for (const ctor of nodeConstructors) { if (!hasProperty(ctor.prototype, "__debugKind")) { Object.defineProperties(ctor.prototype, { // for use with vscode-js-debug's new customDescriptionGenerator in launch.json __tsDebuggerDisplay: { value(this: Node) { const nodeHeader = isGeneratedIdentifier(this) ? "GeneratedIdentifier" : isIdentifier(this) ? `Identifier '${idText(this)}'` : isPrivateIdentifier(this) ? `PrivateIdentifier '${idText(this)}'` : isStringLiteral(this) ? `StringLiteral ${JSON.stringify(this.text.length < 10 ? this.text : this.text.slice(10) + "...")}` : isNumericLiteral(this) ? `NumericLiteral ${this.text}` : isBigIntLiteral(this) ? `BigIntLiteral ${this.text}n` : isTypeParameterDeclaration(this) ? "TypeParameterDeclaration" : isParameter(this) ? "ParameterDeclaration" : isConstructorDeclaration(this) ? "ConstructorDeclaration" : isGetAccessorDeclaration(this) ? "GetAccessorDeclaration" : isSetAccessorDeclaration(this) ? "SetAccessorDeclaration" : isCallSignatureDeclaration(this) ? "CallSignatureDeclaration" : isConstructSignatureDeclaration(this) ? "ConstructSignatureDeclaration" : isIndexSignatureDeclaration(this) ? "IndexSignatureDeclaration" : isTypePredicateNode(this) ? "TypePredicateNode" : isTypeReferenceNode(this) ? "TypeReferenceNode" : isFunctionTypeNode(this) ? "FunctionTypeNode" : isConstructorTypeNode(this) ? "ConstructorTypeNode" : isTypeQueryNode(this) ? "TypeQueryNode" : isTypeLiteralNode(this) ? "TypeLiteralNode" : isArrayTypeNode(this) ? "ArrayTypeNode" : isTupleTypeNode(this) ? "TupleTypeNode" : isOptionalTypeNode(this) ? "OptionalTypeNode" : isRestTypeNode(this) ? "RestTypeNode" : isUnionTypeNode(this) ? "UnionTypeNode" : isIntersectionTypeNode(this) ? "IntersectionTypeNode" : isConditionalTypeNode(this) ? "ConditionalTypeNode" : isInferTypeNode(this) ? "InferTypeNode" : isParenthesizedTypeNode(this) ? "ParenthesizedTypeNode" : isThisTypeNode(this) ? "ThisTypeNode" : isTypeOperatorNode(this) ? "TypeOperatorNode" : isIndexedAccessTypeNode(this) ? "IndexedAccessTypeNode" : isMappedTypeNode(this) ? "MappedTypeNode" : isLiteralTypeNode(this) ? "LiteralTypeNode" : isNamedTupleMember(this) ? "NamedTupleMember" : isImportTypeNode(this) ? "ImportTypeNode" : formatSyntaxKind(this.kind); return `${nodeHeader}${this.flags ? ` (${formatNodeFlags(this.flags)})` : ""}`; } }, __debugKind: { get(this: Node) { return formatSyntaxKind(this.kind); } }, __debugNodeFlags: { get(this: Node) { return formatNodeFlags(this.flags); } }, __debugModifierFlags: { get(this: Node) { return formatModifierFlags(getEffectiveModifierFlagsNoCache(this)); } }, __debugTransformFlags: { get(this: Node) { return formatTransformFlags(this.transformFlags); } }, __debugIsParseTreeNode: { get(this: Node) { return isParseTreeNode(this); } }, __debugEmitFlags: { get(this: Node) { return formatEmitFlags(getEmitFlags(this)); } }, __debugGetText: { value(this: Node, includeTrivia?: boolean) { if (nodeIsSynthesized(this)) return ""; // avoid recomputing const map = getWeakNodeTextMap(); let text = map?.get(this); if (text === undefined) { const parseNode = getParseTreeNode(this); const sourceFile = parseNode && getSourceFileOfNode(parseNode); text = sourceFile ? getSourceTextOfNodeFromSourceFile(sourceFile, parseNode, includeTrivia) : ""; map?.set(this, text); } return text; } } }); } } // attempt to load extended debugging information try { if (sys && sys.require) { const basePath = getDirectoryPath(resolvePath(sys.getExecutingFilePath())); const result = sys.require(basePath, "./compiler-debug") as RequireResult; if (!result.error) { result.module.init(ts); extendedDebugModule = result.module; } } } catch { // do nothing } isDebugInfoEnabled = true; } function formatDeprecationMessage(name: string, error: boolean | undefined, errorAfter: Version | undefined, since: Version | undefined, message: string | undefined) { let deprecationMessage = error ? "DeprecationError: " : "DeprecationWarning: "; deprecationMessage += `'${name}' `; deprecationMessage += since ? `has been deprecated since v${since}` : "is deprecated"; deprecationMessage += error ? " and can no longer be used." : errorAfter ? ` and will no longer be usable after v${errorAfter}.` : "."; deprecationMessage += message ? ` ${formatStringFromArgs(message, [name], 0)}` : ""; return deprecationMessage; } function createErrorDeprecation(name: string, errorAfter: Version | undefined, since: Version | undefined, message: string | undefined) { const deprecationMessage = formatDeprecationMessage(name, /*error*/ true, errorAfter, since, message); return () => { throw new TypeError(deprecationMessage); }; } function createWarningDeprecation(name: string, errorAfter: Version | undefined, since: Version | undefined, message: string | undefined) { let hasWrittenDeprecation = false; return () => { if (enableDeprecationWarnings && !hasWrittenDeprecation) { log.warn(formatDeprecationMessage(name, /*error*/ false, errorAfter, since, message)); hasWrittenDeprecation = true; } }; } export function createDeprecation(name: string, options: DeprecationOptions & { error: true }): () => never; export function createDeprecation(name: string, options?: DeprecationOptions): () => void; export function createDeprecation(name: string, options: DeprecationOptions = {}) { const version = typeof options.typeScriptVersion === "string" ? new Version(options.typeScriptVersion) : options.typeScriptVersion ?? getTypeScriptVersion(); const errorAfter = typeof options.errorAfter === "string" ? new Version(options.errorAfter) : options.errorAfter; const warnAfter = typeof options.warnAfter === "string" ? new Version(options.warnAfter) : options.warnAfter; const since = typeof options.since === "string" ? new Version(options.since) : options.since ?? warnAfter; const error = options.error || errorAfter && version.compareTo(errorAfter) <= 0; const warn = !warnAfter || version.compareTo(warnAfter) >= 0; return error ? createErrorDeprecation(name, errorAfter, since, options.message) : warn ? createWarningDeprecation(name, errorAfter, since, options.message) : noop; } function wrapFunction any>(deprecation: () => void, func: F): F { return function (this: unknown) { deprecation(); return func.apply(this, arguments); } as F; } export function deprecate any>(func: F, options?: DeprecationOptions): F { const deprecation = createDeprecation(options?.name ?? getFunctionName(func), options); return wrapFunction(deprecation, func); } export function formatVariance(varianceFlags: VarianceFlags) { const variance = varianceFlags & VarianceFlags.VarianceMask; let result = variance === VarianceFlags.Invariant ? "in out" : variance === VarianceFlags.Bivariant ? "[bivariant]" : variance === VarianceFlags.Contravariant ? "in" : variance === VarianceFlags.Covariant ? "out" : variance === VarianceFlags.Independent ? "[independent]" : ""; if (varianceFlags & VarianceFlags.Unmeasurable) { result += " (unmeasurable)"; } else if (varianceFlags & VarianceFlags.Unreliable) { result += " (unreliable)"; } return result; } export type DebugType = Type & { __debugTypeToString(): string }; // eslint-disable-line @typescript-eslint/naming-convention export class DebugTypeMapper { declare kind: TypeMapKind; __debugToString(): string { // eslint-disable-line @typescript-eslint/naming-convention type(this); switch (this.kind) { case TypeMapKind.Function: return this.debugInfo?.() || "(function mapper)"; case TypeMapKind.Simple: return `${(this.source as DebugType).__debugTypeToString()} -> ${(this.target as DebugType).__debugTypeToString()}`; case TypeMapKind.Array: return zipWith( this.sources as readonly DebugType[], this.targets as readonly DebugType[] || map(this.sources, () => "any"), (s, t) => `${s.__debugTypeToString()} -> ${typeof t === "string" ? t : t.__debugTypeToString()}`).join(", "); case TypeMapKind.Deferred: return zipWith( this.sources, this.targets, (s, t) => `${(s as DebugType).__debugTypeToString()} -> ${(t() as DebugType).__debugTypeToString()}`).join(", "); case TypeMapKind.Merged: case TypeMapKind.Composite: return `m1: ${(this.mapper1 as unknown as DebugTypeMapper).__debugToString().split("\n").join("\n ")} m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n").join("\n ")}`; default: return assertNever(this); } } } export function attachDebugPrototypeIfDebug(mapper: TypeMapper): TypeMapper { if (isDebugging) { return Object.setPrototypeOf(mapper, DebugTypeMapper.prototype); } return mapper; } } }