"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.Converter = void 0; exports.convertError = convertError; // There's lots of funny stuff due to the typing of ts.Node /* eslint-disable @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unnecessary-condition, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access */ const ts = __importStar(require("typescript")); const check_syntax_errors_1 = require("./check-syntax-errors"); const getModifiers_1 = require("./getModifiers"); const node_utils_1 = require("./node-utils"); const ts_estree_1 = require("./ts-estree"); const SyntaxKind = ts.SyntaxKind; /** * Extends and formats a given error object * @param error the error object * @returns converted error object */ function convertError(error) { return (0, node_utils_1.createError)(error.start, ('message' in error && error.message) || error.messageText, error.file); } class Converter { allowPattern = false; ast; esTreeNodeToTSNodeMap = new WeakMap(); options; tsNodeToESTreeNodeMap = new WeakMap(); /** * Converts a TypeScript node into an ESTree node * @param ast the full TypeScript AST * @param options additional options for the conversion * @returns the converted ESTreeNode */ constructor(ast, options) { this.ast = ast; this.options = { ...options }; } #checkSyntaxError(node, parent) { if (this.options.allowInvalidAST) { return; } (0, check_syntax_errors_1.checkSyntaxError)(node, parent, this.allowPattern); } #throwError(node, message) { if (this.options.allowInvalidAST) { return; } throw (0, node_utils_1.createError)(node, message, this.ast); } /** * Creates a getter for a property under aliasKey that returns the value under * valueKey. If suppressDeprecatedPropertyWarnings is not enabled, the * getter also console warns about the deprecation. * * @see https://github.com/typescript-eslint/typescript-eslint/issues/6469 */ #withDeprecatedAliasGetter(node, aliasKey, valueKey, suppressWarnings = false) { let warned = suppressWarnings; Object.defineProperty(node, aliasKey, { configurable: true, get: this.options.suppressDeprecatedPropertyWarnings ? () => node[valueKey] : () => { if (!warned) { process.emitWarning(`The '${aliasKey}' property is deprecated on ${node.type} nodes. Use '${valueKey}' instead. See https://tseslint.com/key-property-deprecated.`, 'DeprecationWarning'); warned = true; } return node[valueKey]; }, set(value) { Object.defineProperty(node, aliasKey, { enumerable: true, value, writable: true, }); }, }); return node; } #withDeprecatedGetter(node, deprecatedKey, preferredKey, value) { let warned = false; Object.defineProperty(node, deprecatedKey, { configurable: true, get: this.options.suppressDeprecatedPropertyWarnings ? () => value : () => { if (!warned) { let message = `The '${deprecatedKey}' property is deprecated on ${node.type} nodes.`; if (preferredKey) { message += ` Use ${preferredKey} instead.`; } message += ' See https://tseslint.com/key-property-deprecated.'; process.emitWarning(message, 'DeprecationWarning'); warned = true; } return value; }, set(value) { Object.defineProperty(node, deprecatedKey, { enumerable: true, value, writable: true, }); }, }); return node; } convertBindingNameWithTypeAnnotation(name, tsType, parent) { const id = this.convertPattern(name); if (tsType) { id.typeAnnotation = this.convertTypeAnnotation(tsType, parent); this.fixParentLocation(id, id.typeAnnotation.range); } return id; } /** * Coverts body Nodes and add a directive field to StringLiterals * @param nodes of ts.Node * @param parent parentNode * @returns Array of body statements */ convertBodyExpressions(nodes, parent) { let allowDirectives = (0, node_utils_1.canContainDirective)(parent); return (nodes .map(statement => { const child = this.convertChild(statement); if (allowDirectives) { if (child?.expression && ts.isExpressionStatement(statement) && ts.isStringLiteral(statement.expression)) { const raw = child.expression.raw; child.directive = raw.slice(1, -1); return child; // child can be null, but it's filtered below } allowDirectives = false; } return child; // child can be null, but it's filtered below }) // filter out unknown nodes for now .filter(statement => statement)); } convertChainExpression(node, tsNode) { const { child, isOptional } = (() => { if (node.type === ts_estree_1.AST_NODE_TYPES.MemberExpression) { return { child: node.object, isOptional: node.optional }; } if (node.type === ts_estree_1.AST_NODE_TYPES.CallExpression) { return { child: node.callee, isOptional: node.optional }; } return { child: node.expression, isOptional: false }; })(); const isChildUnwrappable = (0, node_utils_1.isChildUnwrappableOptionalChain)(tsNode, child); if (!isChildUnwrappable && !isOptional) { return node; } if (isChildUnwrappable && (0, node_utils_1.isChainExpression)(child)) { // unwrap the chain expression child const newChild = child.expression; if (node.type === ts_estree_1.AST_NODE_TYPES.MemberExpression) { node.object = newChild; } else if (node.type === ts_estree_1.AST_NODE_TYPES.CallExpression) { node.callee = newChild; } else { node.expression = newChild; } } return this.createNode(tsNode, { type: ts_estree_1.AST_NODE_TYPES.ChainExpression, expression: node, }); } /** * Converts a TypeScript node into an ESTree node. * @param child the child ts.Node * @param parent parentNode * @returns the converted ESTree node */ convertChild(child, parent) { return this.converter(child, parent, false); } /** * Converts TypeScript node array into an ESTree node list. * @param children the child `ts.NodeArray` or `ts.Node[]` * @param parent parentNode * @returns the converted ESTree node list */ convertChildren(children, parent) { return children.map(child => this.converter(child, parent, false)); } /** * Converts a TypeScript node into an ESTree node. * @param child the child ts.Node * @param parent parentNode * @returns the converted ESTree node */ convertPattern(child, parent) { return this.converter(child, parent, true); } /** * Converts a child into a type annotation. This creates an intermediary * TypeAnnotation node to match what Flow does. * @param child The TypeScript AST node to convert. * @param parent parentNode * @returns The type annotation node. */ convertTypeAnnotation(child, parent) { // in FunctionType and ConstructorType typeAnnotation has 2 characters `=>` and in other places is just colon const offset = parent?.kind === SyntaxKind.FunctionType || parent?.kind === SyntaxKind.ConstructorType ? 2 : 1; const annotationStartCol = child.getFullStart() - offset; const range = [annotationStartCol, child.end]; const loc = (0, node_utils_1.getLocFor)(range, this.ast); return { type: ts_estree_1.AST_NODE_TYPES.TSTypeAnnotation, loc, range, typeAnnotation: this.convertChild(child), }; } /** * Converts a ts.Node's typeArguments to TSTypeParameterInstantiation node * @param typeArguments ts.NodeArray typeArguments * @param node parent used to create this node * @returns TypeParameterInstantiation node */ convertTypeArguments(node) { const { typeArguments } = node; if (!typeArguments) { return undefined; } const greaterThanToken = (0, node_utils_1.findNextToken)(typeArguments, this.ast, this.ast); const range = [typeArguments.pos - 1, greaterThanToken.end]; if (typeArguments.length === 0) { this.#throwError(range, 'Type argument list cannot be empty.'); } return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSTypeParameterInstantiation, range, params: this.convertChildren(typeArguments), }); } /** * Converts a ts.Node's typeParameters to TSTypeParameterDeclaration node * @param typeParameters ts.Node typeParameters * @returns TypeParameterDeclaration node */ convertTypeParameters(node) { const { typeParameters } = node; if (!typeParameters) { return undefined; } const greaterThanToken = (0, node_utils_1.findNextToken)(typeParameters, this.ast, this.ast); const range = [ typeParameters.pos - 1, greaterThanToken.end, ]; if (typeParameters.length === 0) { this.#throwError(range, 'Type parameter list cannot be empty.'); } return { type: ts_estree_1.AST_NODE_TYPES.TSTypeParameterDeclaration, loc: (0, node_utils_1.getLocFor)(range, this.ast), range, params: this.convertChildren(typeParameters), }; } /** * Converts an array of ts.Node parameters into an array of ESTreeNode params * @param parameters An array of ts.Node params to be converted * @returns an array of converted ESTreeNode params */ convertParameters(parameters) { if (!parameters?.length) { return []; } return parameters.map(param => { const convertedParam = this.convertChild(param); convertedParam.decorators = this.convertChildren((0, getModifiers_1.getDecorators)(param) ?? []); return convertedParam; }); } /** * Converts a TypeScript node into an ESTree node. * @param node the child ts.Node * @param parent parentNode * @param allowPattern flag to determine if patterns are allowed * @returns the converted ESTree node */ converter(node, parent, allowPattern) { /** * Exit early for null and undefined */ if (!node) { return null; } const pattern = this.allowPattern; if (allowPattern != null) { this.allowPattern = allowPattern; } const parentNode = (parent ?? node.parent); this.#checkSyntaxError(node, parentNode); const result = this.convertNode(node, parentNode); this.registerTSNodeInNodeMap(node, result); this.allowPattern = pattern; return result; } convertImportAttributes(node) { // eslint-disable-next-line @typescript-eslint/no-deprecated const attributes = node.attributes ?? node.assertClause; return this.convertChildren(attributes?.elements ?? []); } convertJSXIdentifier(node) { const result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXIdentifier, name: node.getText(), }); this.registerTSNodeInNodeMap(node, result); return result; } convertJSXNamespaceOrIdentifier(node) { // TypeScript@5.1 added in ts.JsxNamespacedName directly // We prefer using that if it's relevant for this node type if (node.kind === ts.SyntaxKind.JsxNamespacedName) { const result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXNamespacedName, name: this.createNode(node.name, { type: ts_estree_1.AST_NODE_TYPES.JSXIdentifier, name: node.name.text, }), namespace: this.createNode(node.namespace, { type: ts_estree_1.AST_NODE_TYPES.JSXIdentifier, name: node.namespace.text, }), }); this.registerTSNodeInNodeMap(node, result); return result; } // TypeScript@<5.1 has to manually parse the JSX attributes const text = node.getText(); const colonIndex = text.indexOf(':'); // this is intentional we can ignore conversion if `:` is in first character if (colonIndex > 0) { const range = (0, node_utils_1.getRange)(node, this.ast); const result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXNamespacedName, range, name: this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXIdentifier, range: [range[0] + colonIndex + 1, range[1]], name: text.slice(colonIndex + 1), }), namespace: this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXIdentifier, range: [range[0], range[0] + colonIndex], name: text.slice(0, colonIndex), }), }); this.registerTSNodeInNodeMap(node, result); return result; } return this.convertJSXIdentifier(node); } /** * Converts a TypeScript JSX node.tagName into an ESTree node.name * @param node the tagName object from a JSX ts.Node * @returns the converted ESTree name object */ convertJSXTagName(node, parent) { let result; switch (node.kind) { case SyntaxKind.PropertyAccessExpression: if (node.name.kind === SyntaxKind.PrivateIdentifier) { // This is one of the few times where TS explicitly errors, and doesn't even gracefully handle the syntax. // So we shouldn't ever get into this state to begin with. this.#throwError(node.name, 'Non-private identifier expected.'); } result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXMemberExpression, object: this.convertJSXTagName(node.expression, parent), property: this.convertJSXIdentifier(node.name), }); break; case SyntaxKind.ThisKeyword: case SyntaxKind.Identifier: default: return this.convertJSXNamespaceOrIdentifier(node); } this.registerTSNodeInNodeMap(node, result); return result; } convertMethodSignature(node) { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSMethodSignature, accessibility: (0, node_utils_1.getTSNodeAccessibility)(node), computed: (0, node_utils_1.isComputedProperty)(node.name), key: this.convertChild(node.name), kind: (() => { switch (node.kind) { case SyntaxKind.GetAccessor: return 'get'; case SyntaxKind.SetAccessor: return 'set'; case SyntaxKind.MethodSignature: return 'method'; } })(), optional: (0, node_utils_1.isOptional)(node), params: this.convertParameters(node.parameters), readonly: (0, node_utils_1.hasModifier)(SyntaxKind.ReadonlyKeyword, node), returnType: node.type && this.convertTypeAnnotation(node.type, node), static: (0, node_utils_1.hasModifier)(SyntaxKind.StaticKeyword, node), typeParameters: this.convertTypeParameters(node), }); } /** * Uses the provided range location to adjust the location data of the given Node * @param result The node that will have its location data mutated * @param childRange The child node range used to expand location */ fixParentLocation(result, childRange) { if (childRange[0] < result.range[0]) { result.range[0] = childRange[0]; result.loc.start = (0, node_utils_1.getLineAndCharacterFor)(result.range[0], this.ast); } if (childRange[1] > result.range[1]) { result.range[1] = childRange[1]; result.loc.end = (0, node_utils_1.getLineAndCharacterFor)(result.range[1], this.ast); } } /** * Converts a TypeScript node into an ESTree node. * The core of the conversion logic: * Identify and convert each relevant TypeScript SyntaxKind * @returns the converted ESTree node */ convertNode(node, parent) { switch (node.kind) { case SyntaxKind.SourceFile: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Program, range: [node.getStart(this.ast), node.endOfFileToken.end], body: this.convertBodyExpressions(node.statements, node), comments: undefined, sourceType: node.externalModuleIndicator ? 'module' : 'script', tokens: undefined, }); } case SyntaxKind.Block: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.BlockStatement, body: this.convertBodyExpressions(node.statements, node), }); } case SyntaxKind.Identifier: { if ((0, node_utils_1.isThisInTypeQuery)(node)) { // special case for `typeof this.foo` - TS emits an Identifier for `this` // but we want to treat it as a ThisExpression for consistency return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ThisExpression, }); } return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Identifier, decorators: [], name: node.text, optional: false, typeAnnotation: undefined, }); } case SyntaxKind.PrivateIdentifier: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.PrivateIdentifier, // typescript includes the `#` in the text name: node.text.slice(1), }); } case SyntaxKind.WithStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.WithStatement, body: this.convertChild(node.statement), object: this.convertChild(node.expression), }); // Control Flow case SyntaxKind.ReturnStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ReturnStatement, argument: this.convertChild(node.expression), }); case SyntaxKind.LabeledStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.LabeledStatement, body: this.convertChild(node.statement), label: this.convertChild(node.label), }); case SyntaxKind.ContinueStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ContinueStatement, label: this.convertChild(node.label), }); case SyntaxKind.BreakStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.BreakStatement, label: this.convertChild(node.label), }); // Choice case SyntaxKind.IfStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.IfStatement, alternate: this.convertChild(node.elseStatement), consequent: this.convertChild(node.thenStatement), test: this.convertChild(node.expression), }); case SyntaxKind.SwitchStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.SwitchStatement, cases: this.convertChildren(node.caseBlock.clauses), discriminant: this.convertChild(node.expression), }); case SyntaxKind.CaseClause: case SyntaxKind.DefaultClause: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.SwitchCase, // expression is present in case only consequent: this.convertChildren(node.statements), test: node.kind === SyntaxKind.CaseClause ? this.convertChild(node.expression) : null, }); // Exceptions case SyntaxKind.ThrowStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ThrowStatement, argument: this.convertChild(node.expression), }); case SyntaxKind.TryStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TryStatement, block: this.convertChild(node.tryBlock), finalizer: this.convertChild(node.finallyBlock), handler: this.convertChild(node.catchClause), }); case SyntaxKind.CatchClause: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.CatchClause, body: this.convertChild(node.block), param: node.variableDeclaration ? this.convertBindingNameWithTypeAnnotation(node.variableDeclaration.name, node.variableDeclaration.type) : null, }); // Loops case SyntaxKind.WhileStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.WhileStatement, body: this.convertChild(node.statement), test: this.convertChild(node.expression), }); /** * Unlike other parsers, TypeScript calls a "DoWhileStatement" * a "DoStatement" */ case SyntaxKind.DoStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.DoWhileStatement, body: this.convertChild(node.statement), test: this.convertChild(node.expression), }); case SyntaxKind.ForStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ForStatement, body: this.convertChild(node.statement), init: this.convertChild(node.initializer), test: this.convertChild(node.condition), update: this.convertChild(node.incrementor), }); case SyntaxKind.ForInStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ForInStatement, body: this.convertChild(node.statement), left: this.convertPattern(node.initializer), right: this.convertChild(node.expression), }); case SyntaxKind.ForOfStatement: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ForOfStatement, await: node.awaitModifier?.kind === SyntaxKind.AwaitKeyword, body: this.convertChild(node.statement), left: this.convertPattern(node.initializer), right: this.convertChild(node.expression), }); } // Declarations case SyntaxKind.FunctionDeclaration: { const isDeclare = (0, node_utils_1.hasModifier)(SyntaxKind.DeclareKeyword, node); const isAsync = (0, node_utils_1.hasModifier)(SyntaxKind.AsyncKeyword, node); const isGenerator = !!node.asteriskToken; const result = this.createNode(node, { // declare implies no body due to the invariant above type: !node.body ? ts_estree_1.AST_NODE_TYPES.TSDeclareFunction : ts_estree_1.AST_NODE_TYPES.FunctionDeclaration, async: isAsync, body: this.convertChild(node.body) || undefined, declare: isDeclare, expression: false, generator: isGenerator, id: this.convertChild(node.name), params: this.convertParameters(node.parameters), returnType: node.type && this.convertTypeAnnotation(node.type, node), typeParameters: this.convertTypeParameters(node), }); return this.fixExports(node, result); } case SyntaxKind.VariableDeclaration: { const hasExclamationToken = !!node.exclamationToken; const init = this.convertChild(node.initializer); const id = this.convertBindingNameWithTypeAnnotation(node.name, node.type, node); return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.VariableDeclarator, definite: hasExclamationToken, id, init, }); } case SyntaxKind.VariableStatement: { const declarations = node.declarationList.declarations; const result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.VariableDeclaration, declarations: this.convertChildren(declarations), declare: (0, node_utils_1.hasModifier)(SyntaxKind.DeclareKeyword, node), kind: (0, node_utils_1.getDeclarationKind)(node.declarationList), }); /** * Semantically, decorators are not allowed on variable declarations, * Pre 4.8 TS would include them in the AST, so we did as well. * However as of 4.8 TS no longer includes it (as it is, well, invalid). * * So for consistency across versions, we no longer include it either. */ return this.fixExports(node, result); } // mostly for for-of, for-in case SyntaxKind.VariableDeclarationList: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.VariableDeclaration, declarations: this.convertChildren(node.declarations), declare: false, kind: (0, node_utils_1.getDeclarationKind)(node), }); } // Expressions case SyntaxKind.ExpressionStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ExpressionStatement, directive: undefined, expression: this.convertChild(node.expression), }); case SyntaxKind.ThisKeyword: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ThisExpression, }); case SyntaxKind.ArrayLiteralExpression: { // TypeScript uses ArrayLiteralExpression in destructuring assignment, too if (this.allowPattern) { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ArrayPattern, decorators: [], elements: node.elements.map(el => this.convertPattern(el)), optional: false, typeAnnotation: undefined, }); } return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ArrayExpression, elements: this.convertChildren(node.elements), }); } case SyntaxKind.ObjectLiteralExpression: { // TypeScript uses ObjectLiteralExpression in destructuring assignment, too if (this.allowPattern) { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ObjectPattern, decorators: [], optional: false, properties: node.properties.map(el => this.convertPattern(el)), typeAnnotation: undefined, }); } return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ObjectExpression, properties: this.convertChildren(node.properties), }); } case SyntaxKind.PropertyAssignment: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Property, computed: (0, node_utils_1.isComputedProperty)(node.name), key: this.convertChild(node.name), kind: 'init', method: false, optional: false, shorthand: false, value: this.converter(node.initializer, node, this.allowPattern), }); } case SyntaxKind.ShorthandPropertyAssignment: { if (node.objectAssignmentInitializer) { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Property, computed: false, key: this.convertChild(node.name), kind: 'init', method: false, optional: false, shorthand: true, value: this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.AssignmentPattern, decorators: [], left: this.convertPattern(node.name), optional: false, right: this.convertChild(node.objectAssignmentInitializer), typeAnnotation: undefined, }), }); } return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Property, computed: false, key: this.convertChild(node.name), kind: 'init', method: false, optional: false, shorthand: true, value: this.convertChild(node.name), }); } case SyntaxKind.ComputedPropertyName: return this.convertChild(node.expression); case SyntaxKind.PropertyDeclaration: { const isAbstract = (0, node_utils_1.hasModifier)(SyntaxKind.AbstractKeyword, node); const isAccessor = (0, node_utils_1.hasModifier)(SyntaxKind.AccessorKeyword, node); const type = (() => { if (isAccessor) { if (isAbstract) { return ts_estree_1.AST_NODE_TYPES.TSAbstractAccessorProperty; } return ts_estree_1.AST_NODE_TYPES.AccessorProperty; } if (isAbstract) { return ts_estree_1.AST_NODE_TYPES.TSAbstractPropertyDefinition; } return ts_estree_1.AST_NODE_TYPES.PropertyDefinition; })(); const key = this.convertChild(node.name); return this.createNode(node, { type, accessibility: (0, node_utils_1.getTSNodeAccessibility)(node), computed: (0, node_utils_1.isComputedProperty)(node.name), declare: (0, node_utils_1.hasModifier)(SyntaxKind.DeclareKeyword, node), decorators: this.convertChildren((0, getModifiers_1.getDecorators)(node) ?? []), definite: !!node.exclamationToken, key, optional: (key.type === ts_estree_1.AST_NODE_TYPES.Literal || node.name.kind === SyntaxKind.Identifier || node.name.kind === SyntaxKind.ComputedPropertyName || node.name.kind === SyntaxKind.PrivateIdentifier) && !!node.questionToken, override: (0, node_utils_1.hasModifier)(SyntaxKind.OverrideKeyword, node), readonly: (0, node_utils_1.hasModifier)(SyntaxKind.ReadonlyKeyword, node), static: (0, node_utils_1.hasModifier)(SyntaxKind.StaticKeyword, node), typeAnnotation: node.type && this.convertTypeAnnotation(node.type, node), value: isAbstract ? null : this.convertChild(node.initializer), }); } case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: { if (node.parent.kind === SyntaxKind.InterfaceDeclaration || node.parent.kind === SyntaxKind.TypeLiteral) { return this.convertMethodSignature(node); } } // otherwise, it is a non-type accessor - intentional fallthrough case SyntaxKind.MethodDeclaration: { const method = this.createNode(node, { type: !node.body ? ts_estree_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression : ts_estree_1.AST_NODE_TYPES.FunctionExpression, range: [node.parameters.pos - 1, node.end], async: (0, node_utils_1.hasModifier)(SyntaxKind.AsyncKeyword, node), body: this.convertChild(node.body), declare: false, expression: false, // ESTreeNode as ESTreeNode here generator: !!node.asteriskToken, id: null, params: [], returnType: node.type && this.convertTypeAnnotation(node.type, node), typeParameters: this.convertTypeParameters(node), }); if (method.typeParameters) { this.fixParentLocation(method, method.typeParameters.range); } let result; if (parent.kind === SyntaxKind.ObjectLiteralExpression) { method.params = this.convertChildren(node.parameters); result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Property, computed: (0, node_utils_1.isComputedProperty)(node.name), key: this.convertChild(node.name), kind: 'init', method: node.kind === SyntaxKind.MethodDeclaration, optional: !!node.questionToken, shorthand: false, value: method, }); } else { // class const isAbstract = (0, node_utils_1.hasModifier)(SyntaxKind.AbstractKeyword, node); /** * Unlike in object literal methods, class method params can have decorators */ method.params = this.convertParameters(node.parameters); /** * TypeScript class methods can be defined as "abstract" */ const methodDefinitionType = isAbstract ? ts_estree_1.AST_NODE_TYPES.TSAbstractMethodDefinition : ts_estree_1.AST_NODE_TYPES.MethodDefinition; result = this.createNode(node, { type: methodDefinitionType, accessibility: (0, node_utils_1.getTSNodeAccessibility)(node), computed: (0, node_utils_1.isComputedProperty)(node.name), decorators: this.convertChildren((0, getModifiers_1.getDecorators)(node) ?? []), key: this.convertChild(node.name), kind: 'method', optional: !!node.questionToken, override: (0, node_utils_1.hasModifier)(SyntaxKind.OverrideKeyword, node), static: (0, node_utils_1.hasModifier)(SyntaxKind.StaticKeyword, node), value: method, }); } if (node.kind === SyntaxKind.GetAccessor) { result.kind = 'get'; } else if (node.kind === SyntaxKind.SetAccessor) { result.kind = 'set'; } else if (!result.static && node.name.kind === SyntaxKind.StringLiteral && node.name.text === 'constructor' && result.type !== ts_estree_1.AST_NODE_TYPES.Property) { result.kind = 'constructor'; } return result; } // TypeScript uses this even for static methods named "constructor" case SyntaxKind.Constructor: { const lastModifier = (0, node_utils_1.getLastModifier)(node); const constructorToken = (lastModifier && (0, node_utils_1.findNextToken)(lastModifier, node, this.ast)) ?? node.getFirstToken(); const constructor = this.createNode(node, { type: !node.body ? ts_estree_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression : ts_estree_1.AST_NODE_TYPES.FunctionExpression, range: [node.parameters.pos - 1, node.end], async: false, body: this.convertChild(node.body), declare: false, expression: false, // is not present in ESTreeNode generator: false, id: null, params: this.convertParameters(node.parameters), returnType: node.type && this.convertTypeAnnotation(node.type, node), typeParameters: this.convertTypeParameters(node), }); if (constructor.typeParameters) { this.fixParentLocation(constructor, constructor.typeParameters.range); } const constructorKey = constructorToken.kind === SyntaxKind.StringLiteral ? this.createNode(constructorToken, { type: ts_estree_1.AST_NODE_TYPES.Literal, raw: constructorToken.getText(), value: 'constructor', }) : this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Identifier, range: [ constructorToken.getStart(this.ast), constructorToken.end, ], decorators: [], name: 'constructor', optional: false, typeAnnotation: undefined, }); const isStatic = (0, node_utils_1.hasModifier)(SyntaxKind.StaticKeyword, node); return this.createNode(node, { type: (0, node_utils_1.hasModifier)(SyntaxKind.AbstractKeyword, node) ? ts_estree_1.AST_NODE_TYPES.TSAbstractMethodDefinition : ts_estree_1.AST_NODE_TYPES.MethodDefinition, accessibility: (0, node_utils_1.getTSNodeAccessibility)(node), computed: false, decorators: [], key: constructorKey, kind: isStatic ? 'method' : 'constructor', optional: false, override: false, static: isStatic, value: constructor, }); } case SyntaxKind.FunctionExpression: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.FunctionExpression, async: (0, node_utils_1.hasModifier)(SyntaxKind.AsyncKeyword, node), body: this.convertChild(node.body), declare: false, expression: false, generator: !!node.asteriskToken, id: this.convertChild(node.name), params: this.convertParameters(node.parameters), returnType: node.type && this.convertTypeAnnotation(node.type, node), typeParameters: this.convertTypeParameters(node), }); } case SyntaxKind.SuperKeyword: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Super, }); case SyntaxKind.ArrayBindingPattern: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ArrayPattern, decorators: [], elements: node.elements.map(el => this.convertPattern(el)), optional: false, typeAnnotation: undefined, }); // occurs with missing array elements like [,] case SyntaxKind.OmittedExpression: return null; case SyntaxKind.ObjectBindingPattern: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ObjectPattern, decorators: [], optional: false, properties: node.elements.map(el => this.convertPattern(el)), typeAnnotation: undefined, }); case SyntaxKind.BindingElement: { if (parent.kind === SyntaxKind.ArrayBindingPattern) { const arrayItem = this.convertChild(node.name, parent); if (node.initializer) { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.AssignmentPattern, decorators: [], left: arrayItem, optional: false, right: this.convertChild(node.initializer), typeAnnotation: undefined, }); } if (node.dotDotDotToken) { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.RestElement, argument: arrayItem, decorators: [], optional: false, typeAnnotation: undefined, value: undefined, }); } return arrayItem; } let result; if (node.dotDotDotToken) { result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.RestElement, argument: this.convertChild(node.propertyName ?? node.name), decorators: [], optional: false, typeAnnotation: undefined, value: undefined, }); } else { result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Property, computed: node.propertyName?.kind === SyntaxKind.ComputedPropertyName, key: this.convertChild(node.propertyName ?? node.name), kind: 'init', method: false, optional: false, shorthand: !node.propertyName, value: this.convertChild(node.name), }); } if (node.initializer) { result.value = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.AssignmentPattern, range: [node.name.getStart(this.ast), node.initializer.end], decorators: [], left: this.convertChild(node.name), optional: false, right: this.convertChild(node.initializer), typeAnnotation: undefined, }); } return result; } case SyntaxKind.ArrowFunction: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ArrowFunctionExpression, async: (0, node_utils_1.hasModifier)(SyntaxKind.AsyncKeyword, node), body: this.convertChild(node.body), expression: node.body.kind !== SyntaxKind.Block, generator: false, id: null, params: this.convertParameters(node.parameters), returnType: node.type && this.convertTypeAnnotation(node.type, node), typeParameters: this.convertTypeParameters(node), }); } case SyntaxKind.YieldExpression: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.YieldExpression, argument: this.convertChild(node.expression), delegate: !!node.asteriskToken, }); case SyntaxKind.AwaitExpression: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.AwaitExpression, argument: this.convertChild(node.expression), }); // Template Literals case SyntaxKind.NoSubstitutionTemplateLiteral: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TemplateLiteral, expressions: [], quasis: [ this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TemplateElement, tail: true, value: { cooked: node.text, raw: this.ast.text.slice(node.getStart(this.ast) + 1, node.end - 1), }, }), ], }); case SyntaxKind.TemplateExpression: { const result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TemplateLiteral, expressions: [], quasis: [this.convertChild(node.head)], }); node.templateSpans.forEach(templateSpan => { result.expressions.push(this.convertChild(templateSpan.expression)); result.quasis.push(this.convertChild(templateSpan.literal)); }); return result; } case SyntaxKind.TaggedTemplateExpression: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TaggedTemplateExpression, quasi: this.convertChild(node.template), tag: this.convertChild(node.tag), typeArguments: this.convertTypeArguments(node), }); } case SyntaxKind.TemplateHead: case SyntaxKind.TemplateMiddle: case SyntaxKind.TemplateTail: { const tail = node.kind === SyntaxKind.TemplateTail; return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TemplateElement, tail, value: { cooked: node.text, raw: this.ast.text.slice(node.getStart(this.ast) + 1, node.end - (tail ? 1 : 2)), }, }); } // Patterns case SyntaxKind.SpreadAssignment: case SyntaxKind.SpreadElement: { if (this.allowPattern) { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.RestElement, argument: this.convertPattern(node.expression), decorators: [], optional: false, typeAnnotation: undefined, value: undefined, }); } return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.SpreadElement, argument: this.convertChild(node.expression), }); } case SyntaxKind.Parameter: { let parameter; let result; if (node.dotDotDotToken) { parameter = result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.RestElement, argument: this.convertChild(node.name), decorators: [], optional: false, typeAnnotation: undefined, value: undefined, }); } else if (node.initializer) { parameter = this.convertChild(node.name); result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.AssignmentPattern, range: [node.name.getStart(this.ast), node.initializer.end], decorators: [], left: parameter, optional: false, right: this.convertChild(node.initializer), typeAnnotation: undefined, }); const modifiers = (0, getModifiers_1.getModifiers)(node); if (modifiers) { // AssignmentPattern should not contain modifiers in range result.range[0] = parameter.range[0]; result.loc = (0, node_utils_1.getLocFor)(result.range, this.ast); } } else { parameter = result = this.convertChild(node.name, parent); } if (node.type) { parameter.typeAnnotation = this.convertTypeAnnotation(node.type, node); this.fixParentLocation(parameter, parameter.typeAnnotation.range); } if (node.questionToken) { if (node.questionToken.end > parameter.range[1]) { parameter.range[1] = node.questionToken.end; parameter.loc.end = (0, node_utils_1.getLineAndCharacterFor)(parameter.range[1], this.ast); } parameter.optional = true; } const modifiers = (0, getModifiers_1.getModifiers)(node); if (modifiers) { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSParameterProperty, accessibility: (0, node_utils_1.getTSNodeAccessibility)(node), decorators: [], override: (0, node_utils_1.hasModifier)(SyntaxKind.OverrideKeyword, node), parameter: result, readonly: (0, node_utils_1.hasModifier)(SyntaxKind.ReadonlyKeyword, node), static: (0, node_utils_1.hasModifier)(SyntaxKind.StaticKeyword, node), }); } return result; } // Classes case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: { const classNodeType = node.kind === SyntaxKind.ClassDeclaration ? ts_estree_1.AST_NODE_TYPES.ClassDeclaration : ts_estree_1.AST_NODE_TYPES.ClassExpression; const extendsClause = node.heritageClauses?.find(heritageClause => heritageClause.token === SyntaxKind.ExtendsKeyword); const implementsClause = node.heritageClauses?.find(heritageClause => heritageClause.token === SyntaxKind.ImplementsKeyword); const result = this.createNode(node, { type: classNodeType, abstract: (0, node_utils_1.hasModifier)(SyntaxKind.AbstractKeyword, node), body: this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ClassBody, range: [node.members.pos - 1, node.end], body: this.convertChildren(node.members.filter(node_utils_1.isESTreeClassMember)), }), declare: (0, node_utils_1.hasModifier)(SyntaxKind.DeclareKeyword, node), decorators: this.convertChildren((0, getModifiers_1.getDecorators)(node) ?? []), id: this.convertChild(node.name), implements: this.convertChildren(implementsClause?.types ?? []), superClass: extendsClause?.types[0] ? this.convertChild(extendsClause.types[0].expression) : null, superTypeArguments: undefined, typeParameters: this.convertTypeParameters(node), }); if (extendsClause?.types[0]?.typeArguments) { result.superTypeArguments = this.convertTypeArguments(extendsClause.types[0]); } return this.fixExports(node, result); } // Modules case SyntaxKind.ModuleBlock: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSModuleBlock, body: this.convertBodyExpressions(node.statements, node), }); case SyntaxKind.ImportDeclaration: { const result = this.createNode(node, this.#withDeprecatedAliasGetter({ type: ts_estree_1.AST_NODE_TYPES.ImportDeclaration, attributes: this.convertImportAttributes(node), importKind: 'value', source: this.convertChild(node.moduleSpecifier), specifiers: [], }, 'assertions', 'attributes', true)); if (node.importClause) { // TODO(bradzacher) swap to `phaseModifier` once we add support for `import defer` // https://github.com/estree/estree/issues/328 // eslint-disable-next-line @typescript-eslint/no-deprecated if (node.importClause.isTypeOnly) { result.importKind = 'type'; } if (node.importClause.name) { result.specifiers.push(this.convertChild(node.importClause)); } if (node.importClause.namedBindings) { switch (node.importClause.namedBindings.kind) { case SyntaxKind.NamespaceImport: result.specifiers.push(this.convertChild(node.importClause.namedBindings)); break; case SyntaxKind.NamedImports: result.specifiers.push(...this.convertChildren(node.importClause.namedBindings.elements)); break; } } } return result; } case SyntaxKind.NamespaceImport: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ImportNamespaceSpecifier, local: this.convertChild(node.name), }); case SyntaxKind.ImportSpecifier: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ImportSpecifier, imported: this.convertChild(node.propertyName ?? node.name), importKind: node.isTypeOnly ? 'type' : 'value', local: this.convertChild(node.name), }); case SyntaxKind.ImportClause: { const local = this.convertChild(node.name); return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ImportDefaultSpecifier, range: local.range, local, }); } case SyntaxKind.ExportDeclaration: { if (node.exportClause?.kind === SyntaxKind.NamedExports) { return this.createNode(node, this.#withDeprecatedAliasGetter({ type: ts_estree_1.AST_NODE_TYPES.ExportNamedDeclaration, attributes: this.convertImportAttributes(node), declaration: null, exportKind: node.isTypeOnly ? 'type' : 'value', source: this.convertChild(node.moduleSpecifier), specifiers: this.convertChildren(node.exportClause.elements, node), }, 'assertions', 'attributes', true)); } return this.createNode(node, this.#withDeprecatedAliasGetter({ type: ts_estree_1.AST_NODE_TYPES.ExportAllDeclaration, attributes: this.convertImportAttributes(node), exported: node.exportClause?.kind === SyntaxKind.NamespaceExport ? this.convertChild(node.exportClause.name) : null, exportKind: node.isTypeOnly ? 'type' : 'value', source: this.convertChild(node.moduleSpecifier), }, 'assertions', 'attributes', true)); } case SyntaxKind.ExportSpecifier: { const local = node.propertyName ?? node.name; return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ExportSpecifier, exported: this.convertChild(node.name), exportKind: node.isTypeOnly ? 'type' : 'value', local: this.convertChild(local), }); } case SyntaxKind.ExportAssignment: if (node.isExportEquals) { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSExportAssignment, expression: this.convertChild(node.expression), }); } return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ExportDefaultDeclaration, declaration: this.convertChild(node.expression), exportKind: 'value', }); // Unary Operations case SyntaxKind.PrefixUnaryExpression: case SyntaxKind.PostfixUnaryExpression: { const operator = (0, node_utils_1.getTextForTokenKind)(node.operator); /** * ESTree uses UpdateExpression for ++/-- */ if (operator === '++' || operator === '--') { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.UpdateExpression, argument: this.convertChild(node.operand), operator, prefix: node.kind === SyntaxKind.PrefixUnaryExpression, }); } return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.UnaryExpression, argument: this.convertChild(node.operand), operator, prefix: node.kind === SyntaxKind.PrefixUnaryExpression, }); } case SyntaxKind.DeleteExpression: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.UnaryExpression, argument: this.convertChild(node.expression), operator: 'delete', prefix: true, }); case SyntaxKind.VoidExpression: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.UnaryExpression, argument: this.convertChild(node.expression), operator: 'void', prefix: true, }); case SyntaxKind.TypeOfExpression: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.UnaryExpression, argument: this.convertChild(node.expression), operator: 'typeof', prefix: true, }); case SyntaxKind.TypeOperator: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSTypeOperator, operator: (0, node_utils_1.getTextForTokenKind)(node.operator), typeAnnotation: this.convertChild(node.type), }); // Binary Operations case SyntaxKind.BinaryExpression: { // TypeScript uses BinaryExpression for sequences as well if ((0, node_utils_1.isComma)(node.operatorToken)) { const result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.SequenceExpression, expressions: [], }); const left = this.convertChild(node.left); if (left.type === ts_estree_1.AST_NODE_TYPES.SequenceExpression && node.left.kind !== SyntaxKind.ParenthesizedExpression) { result.expressions.push(...left.expressions); } else { result.expressions.push(left); } result.expressions.push(this.convertChild(node.right)); return result; } const expressionType = (0, node_utils_1.getBinaryExpressionType)(node.operatorToken); if (this.allowPattern && expressionType.type === ts_estree_1.AST_NODE_TYPES.AssignmentExpression) { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.AssignmentPattern, decorators: [], left: this.convertPattern(node.left, node), optional: false, right: this.convertChild(node.right), typeAnnotation: undefined, }); } return this.createNode(node, { ...expressionType, left: this.converter(node.left, node, expressionType.type === ts_estree_1.AST_NODE_TYPES.AssignmentExpression), right: this.convertChild(node.right), }); } case SyntaxKind.PropertyAccessExpression: { const object = this.convertChild(node.expression); const property = this.convertChild(node.name); const computed = false; const result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.MemberExpression, computed, object, optional: node.questionDotToken != null, property, }); return this.convertChainExpression(result, node); } case SyntaxKind.ElementAccessExpression: { const object = this.convertChild(node.expression); const property = this.convertChild(node.argumentExpression); const computed = true; const result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.MemberExpression, computed, object, optional: node.questionDotToken != null, property, }); return this.convertChainExpression(result, node); } case SyntaxKind.CallExpression: { if (node.expression.kind === SyntaxKind.ImportKeyword) { return this.createNode(node, this.#withDeprecatedAliasGetter({ type: ts_estree_1.AST_NODE_TYPES.ImportExpression, options: node.arguments[1] ? this.convertChild(node.arguments[1]) : null, source: this.convertChild(node.arguments[0]), }, 'attributes', 'options', true)); } const callee = this.convertChild(node.expression); const args = this.convertChildren(node.arguments); const typeArguments = this.convertTypeArguments(node); const result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.CallExpression, arguments: args, callee, optional: node.questionDotToken != null, typeArguments, }); return this.convertChainExpression(result, node); } case SyntaxKind.NewExpression: { const typeArguments = this.convertTypeArguments(node); // NOTE - NewExpression cannot have an optional chain in it return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.NewExpression, arguments: this.convertChildren(node.arguments ?? []), callee: this.convertChild(node.expression), typeArguments, }); } case SyntaxKind.ConditionalExpression: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ConditionalExpression, alternate: this.convertChild(node.whenFalse), consequent: this.convertChild(node.whenTrue), test: this.convertChild(node.condition), }); case SyntaxKind.MetaProperty: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.MetaProperty, meta: this.createNode( // TODO: do we really want to convert it to Token? node.getFirstToken(), { type: ts_estree_1.AST_NODE_TYPES.Identifier, decorators: [], name: (0, node_utils_1.getTextForTokenKind)(node.keywordToken), optional: false, typeAnnotation: undefined, }), property: this.convertChild(node.name), }); } case SyntaxKind.Decorator: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Decorator, expression: this.convertChild(node.expression), }); } // Literals case SyntaxKind.StringLiteral: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Literal, raw: node.getText(), value: parent.kind === SyntaxKind.JsxAttribute ? (0, node_utils_1.unescapeStringLiteralText)(node.text) : node.text, }); } case SyntaxKind.NumericLiteral: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Literal, raw: node.getText(), value: Number(node.text), }); } case SyntaxKind.BigIntLiteral: { const range = (0, node_utils_1.getRange)(node, this.ast); const rawValue = this.ast.text.slice(range[0], range[1]); const bigint = rawValue // remove suffix `n` .slice(0, -1) // `BigInt` doesn't accept numeric separator // and `bigint` property should not include numeric separator .replaceAll('_', ''); const value = typeof BigInt !== 'undefined' ? BigInt(bigint) : null; return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Literal, range, bigint: value == null ? bigint : String(value), raw: rawValue, value, }); } case SyntaxKind.RegularExpressionLiteral: { const pattern = node.text.slice(1, node.text.lastIndexOf('/')); const flags = node.text.slice(node.text.lastIndexOf('/') + 1); let regex = null; try { regex = new RegExp(pattern, flags); } catch { // Intentionally blank, so regex stays null } return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Literal, raw: node.text, regex: { flags, pattern, }, value: regex, }); } case SyntaxKind.TrueKeyword: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Literal, raw: 'true', value: true, }); case SyntaxKind.FalseKeyword: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Literal, raw: 'false', value: false, }); case SyntaxKind.NullKeyword: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Literal, raw: 'null', value: null, }); } case SyntaxKind.EmptyStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.EmptyStatement, }); case SyntaxKind.DebuggerStatement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.DebuggerStatement, }); // JSX case SyntaxKind.JsxElement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXElement, children: this.convertChildren(node.children), closingElement: this.convertChild(node.closingElement), openingElement: this.convertChild(node.openingElement), }); case SyntaxKind.JsxFragment: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXFragment, children: this.convertChildren(node.children), closingFragment: this.convertChild(node.closingFragment), openingFragment: this.convertChild(node.openingFragment), }); case SyntaxKind.JsxSelfClosingElement: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXElement, /** * Convert SyntaxKind.JsxSelfClosingElement to SyntaxKind.JsxOpeningElement, * TypeScript does not seem to have the idea of openingElement when tag is self-closing */ children: [], closingElement: null, openingElement: this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXOpeningElement, range: (0, node_utils_1.getRange)(node, this.ast), attributes: this.convertChildren(node.attributes.properties), name: this.convertJSXTagName(node.tagName, node), selfClosing: true, typeArguments: this.convertTypeArguments(node), }), }); } case SyntaxKind.JsxOpeningElement: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXOpeningElement, attributes: this.convertChildren(node.attributes.properties), name: this.convertJSXTagName(node.tagName, node), selfClosing: false, typeArguments: this.convertTypeArguments(node), }); } case SyntaxKind.JsxClosingElement: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXClosingElement, name: this.convertJSXTagName(node.tagName, node), }); case SyntaxKind.JsxOpeningFragment: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXOpeningFragment, }); case SyntaxKind.JsxClosingFragment: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXClosingFragment, }); case SyntaxKind.JsxExpression: { const expression = node.expression ? this.convertChild(node.expression) : this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXEmptyExpression, range: [node.getStart(this.ast) + 1, node.getEnd() - 1], }); if (node.dotDotDotToken) { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXSpreadChild, expression, }); } return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXExpressionContainer, expression, }); } case SyntaxKind.JsxAttribute: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXAttribute, name: this.convertJSXNamespaceOrIdentifier(node.name), value: this.convertChild(node.initializer), }); } case SyntaxKind.JsxText: { const start = node.getFullStart(); const end = node.getEnd(); const text = this.ast.text.slice(start, end); return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXText, range: [start, end], raw: text, value: (0, node_utils_1.unescapeStringLiteralText)(text), }); } case SyntaxKind.JsxSpreadAttribute: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.JSXSpreadAttribute, argument: this.convertChild(node.expression), }); case SyntaxKind.QualifiedName: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSQualifiedName, left: this.convertChild(node.left), right: this.convertChild(node.right), }); } // TypeScript specific case SyntaxKind.TypeReference: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSTypeReference, typeArguments: this.convertTypeArguments(node), typeName: this.convertChild(node.typeName), }); case SyntaxKind.TypeParameter: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSTypeParameter, const: (0, node_utils_1.hasModifier)(SyntaxKind.ConstKeyword, node), constraint: node.constraint && this.convertChild(node.constraint), default: node.default ? this.convertChild(node.default) : undefined, in: (0, node_utils_1.hasModifier)(SyntaxKind.InKeyword, node), name: this.convertChild(node.name), out: (0, node_utils_1.hasModifier)(SyntaxKind.OutKeyword, node), }); } case SyntaxKind.ThisType: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSThisType, }); case SyntaxKind.AnyKeyword: case SyntaxKind.BigIntKeyword: case SyntaxKind.BooleanKeyword: case SyntaxKind.NeverKeyword: case SyntaxKind.NumberKeyword: case SyntaxKind.ObjectKeyword: case SyntaxKind.StringKeyword: case SyntaxKind.SymbolKeyword: case SyntaxKind.UnknownKeyword: case SyntaxKind.VoidKeyword: case SyntaxKind.UndefinedKeyword: case SyntaxKind.IntrinsicKeyword: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES[`TS${SyntaxKind[node.kind]}`], }); } case SyntaxKind.NonNullExpression: { const nnExpr = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSNonNullExpression, expression: this.convertChild(node.expression), }); return this.convertChainExpression(nnExpr, node); } case SyntaxKind.TypeLiteral: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSTypeLiteral, members: this.convertChildren(node.members), }); } case SyntaxKind.ArrayType: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSArrayType, elementType: this.convertChild(node.elementType), }); } case SyntaxKind.IndexedAccessType: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSIndexedAccessType, indexType: this.convertChild(node.indexType), objectType: this.convertChild(node.objectType), }); } case SyntaxKind.ConditionalType: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSConditionalType, checkType: this.convertChild(node.checkType), extendsType: this.convertChild(node.extendsType), falseType: this.convertChild(node.falseType), trueType: this.convertChild(node.trueType), }); } case SyntaxKind.TypeQuery: return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSTypeQuery, exprName: this.convertChild(node.exprName), typeArguments: this.convertTypeArguments(node), }); case SyntaxKind.MappedType: { return this.createNode(node, this.#withDeprecatedGetter({ type: ts_estree_1.AST_NODE_TYPES.TSMappedType, constraint: this.convertChild(node.typeParameter.constraint), key: this.convertChild(node.typeParameter.name), nameType: this.convertChild(node.nameType) ?? null, optional: node.questionToken ? node.questionToken.kind === SyntaxKind.QuestionToken || (0, node_utils_1.getTextForTokenKind)(node.questionToken.kind) : false, readonly: node.readonlyToken ? node.readonlyToken.kind === SyntaxKind.ReadonlyKeyword || (0, node_utils_1.getTextForTokenKind)(node.readonlyToken.kind) : undefined, typeAnnotation: node.type && this.convertChild(node.type), }, 'typeParameter', "'constraint' and 'key'", this.convertChild(node.typeParameter))); } case SyntaxKind.ParenthesizedExpression: return this.convertChild(node.expression, parent); case SyntaxKind.TypeAliasDeclaration: { const result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSTypeAliasDeclaration, declare: (0, node_utils_1.hasModifier)(SyntaxKind.DeclareKeyword, node), id: this.convertChild(node.name), typeAnnotation: this.convertChild(node.type), typeParameters: this.convertTypeParameters(node), }); return this.fixExports(node, result); } case SyntaxKind.MethodSignature: { return this.convertMethodSignature(node); } case SyntaxKind.PropertySignature: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSPropertySignature, accessibility: (0, node_utils_1.getTSNodeAccessibility)(node), computed: (0, node_utils_1.isComputedProperty)(node.name), key: this.convertChild(node.name), optional: (0, node_utils_1.isOptional)(node), readonly: (0, node_utils_1.hasModifier)(SyntaxKind.ReadonlyKeyword, node), static: (0, node_utils_1.hasModifier)(SyntaxKind.StaticKeyword, node), typeAnnotation: node.type && this.convertTypeAnnotation(node.type, node), }); } case SyntaxKind.IndexSignature: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSIndexSignature, accessibility: (0, node_utils_1.getTSNodeAccessibility)(node), parameters: this.convertChildren(node.parameters), readonly: (0, node_utils_1.hasModifier)(SyntaxKind.ReadonlyKeyword, node), static: (0, node_utils_1.hasModifier)(SyntaxKind.StaticKeyword, node), typeAnnotation: node.type && this.convertTypeAnnotation(node.type, node), }); } case SyntaxKind.ConstructorType: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSConstructorType, abstract: (0, node_utils_1.hasModifier)(SyntaxKind.AbstractKeyword, node), params: this.convertParameters(node.parameters), returnType: node.type && this.convertTypeAnnotation(node.type, node), typeParameters: this.convertTypeParameters(node), }); } case SyntaxKind.FunctionType: case SyntaxKind.ConstructSignature: case SyntaxKind.CallSignature: { const type = node.kind === SyntaxKind.ConstructSignature ? ts_estree_1.AST_NODE_TYPES.TSConstructSignatureDeclaration : node.kind === SyntaxKind.CallSignature ? ts_estree_1.AST_NODE_TYPES.TSCallSignatureDeclaration : ts_estree_1.AST_NODE_TYPES.TSFunctionType; return this.createNode(node, { type, params: this.convertParameters(node.parameters), returnType: node.type && this.convertTypeAnnotation(node.type, node), typeParameters: this.convertTypeParameters(node), }); } case SyntaxKind.ExpressionWithTypeArguments: { const parentKind = parent.kind; const type = parentKind === SyntaxKind.InterfaceDeclaration ? ts_estree_1.AST_NODE_TYPES.TSInterfaceHeritage : parentKind === SyntaxKind.HeritageClause ? ts_estree_1.AST_NODE_TYPES.TSClassImplements : ts_estree_1.AST_NODE_TYPES.TSInstantiationExpression; return this.createNode(node, { type, expression: this.convertChild(node.expression), typeArguments: this.convertTypeArguments(node), }); } case SyntaxKind.InterfaceDeclaration: { const interfaceExtends = node.heritageClauses?.flatMap(heritageClause => // `InterfaceDeclaration` can only have `extends`, // already checked in `check-syntax-errors.ts`, // However, `allowInvalidAST` allow to bypass the syntax check, // We'll just ignore clauses after other keywords heritageClause.token === SyntaxKind.ExtendsKeyword ? heritageClause.types.map(heritageType => this.convertChild(heritageType, node)) : []) ?? []; const result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSInterfaceDeclaration, body: this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSInterfaceBody, range: [node.members.pos - 1, node.end], body: this.convertChildren(node.members), }), declare: (0, node_utils_1.hasModifier)(SyntaxKind.DeclareKeyword, node), extends: interfaceExtends, id: this.convertChild(node.name), typeParameters: this.convertTypeParameters(node), }); return this.fixExports(node, result); } case SyntaxKind.TypePredicate: { const result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSTypePredicate, asserts: node.assertsModifier != null, parameterName: this.convertChild(node.parameterName), typeAnnotation: null, }); /** * Specific fix for type-guard location data */ if (node.type) { result.typeAnnotation = this.convertTypeAnnotation(node.type, node); result.typeAnnotation.loc = result.typeAnnotation.typeAnnotation.loc; result.typeAnnotation.range = result.typeAnnotation.typeAnnotation.range; } return result; } case SyntaxKind.ImportType: { const range = (0, node_utils_1.getRange)(node, this.ast); if (node.isTypeOf) { const token = (0, node_utils_1.findNextToken)(node.getFirstToken(), node, this.ast); range[0] = token.getStart(this.ast); } let options = null; if (node.attributes) { const value = this.createNode(node.attributes, { type: ts_estree_1.AST_NODE_TYPES.ObjectExpression, properties: node.attributes.elements.map(importAttribute => this.createNode(importAttribute, { type: ts_estree_1.AST_NODE_TYPES.Property, computed: false, key: this.convertChild(importAttribute.name), kind: 'init', method: false, optional: false, shorthand: false, value: this.convertChild(importAttribute.value), })), }); const commaToken = (0, node_utils_1.findNextToken)(node.argument, node, this.ast); const openBraceToken = (0, node_utils_1.findNextToken)(commaToken, node, this.ast); const tokenAfterAttributes = (0, node_utils_1.findNextToken)(node.attributes, node, this.ast); // Since TS 5.9, there could be a trailing comma, i.e. `{ with: { ... }, }` const closeBraceToken = tokenAfterAttributes.kind === ts.SyntaxKind.CommaToken ? (0, node_utils_1.findNextToken)(tokenAfterAttributes, node, this.ast) : tokenAfterAttributes; const withOrAssertToken = (0, node_utils_1.findNextToken)(openBraceToken, node, this.ast); const withOrAssertTokenRange = (0, node_utils_1.getRange)(withOrAssertToken, this.ast); const withOrAssertName = withOrAssertToken.kind === ts.SyntaxKind.AssertKeyword ? 'assert' : 'with'; options = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ObjectExpression, range: [openBraceToken.getStart(this.ast), closeBraceToken.end], properties: [ this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Property, range: [withOrAssertTokenRange[0], node.attributes.end], computed: false, key: this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.Identifier, range: withOrAssertTokenRange, decorators: [], name: withOrAssertName, optional: false, typeAnnotation: undefined, }), kind: 'init', method: false, optional: false, shorthand: false, value, }), ], }); } const argument = this.convertChild(node.argument); const source = argument.literal; const result = this.createNode(node, this.#withDeprecatedGetter({ type: ts_estree_1.AST_NODE_TYPES.TSImportType, range, options, qualifier: this.convertChild(node.qualifier), source, typeArguments: this.convertTypeArguments(node) ?? null, }, 'argument', 'source', argument)); if (node.isTypeOf) { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSTypeQuery, exprName: result, typeArguments: undefined, }); } return result; } case SyntaxKind.EnumDeclaration: { const members = this.convertChildren(node.members); const result = this.createNode(node, this.#withDeprecatedGetter({ type: ts_estree_1.AST_NODE_TYPES.TSEnumDeclaration, body: this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSEnumBody, range: [node.members.pos - 1, node.end], members, }), const: (0, node_utils_1.hasModifier)(SyntaxKind.ConstKeyword, node), declare: (0, node_utils_1.hasModifier)(SyntaxKind.DeclareKeyword, node), id: this.convertChild(node.name), }, 'members', `'body.members'`, this.convertChildren(node.members))); return this.fixExports(node, result); } case SyntaxKind.EnumMember: { const computed = node.name.kind === ts.SyntaxKind.ComputedPropertyName; return this.createNode(node, this.#withDeprecatedGetter({ type: ts_estree_1.AST_NODE_TYPES.TSEnumMember, id: this.convertChild(node.name), initializer: node.initializer && this.convertChild(node.initializer), }, 'computed', undefined, computed)); } case SyntaxKind.ModuleDeclaration: { let isDeclare = (0, node_utils_1.hasModifier)(SyntaxKind.DeclareKeyword, node); const result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSModuleDeclaration, ...(() => { if (node.flags & ts.NodeFlags.GlobalAugmentation) { return { body: this.convertChild(node.body), declare: false, global: false, id: this.convertChild(node.name), kind: 'global', }; } if (ts.isStringLiteral(node.name)) { const body = this.convertChild(node.body); return { kind: 'module', ...(body != null ? { body } : {}), declare: false, global: false, id: this.convertChild(node.name), }; } // Nested module declarations are stored in TypeScript as nested tree nodes. // We "unravel" them here by making our own nested TSQualifiedName, // with the innermost node's body as the actual node body. let name = this.createNode(node.name, { type: ts_estree_1.AST_NODE_TYPES.Identifier, range: [node.name.getStart(this.ast), node.name.getEnd()], decorators: [], name: node.name.text, optional: false, typeAnnotation: undefined, }); while (node.body && ts.isModuleDeclaration(node.body) && node.body.name) { node = node.body; isDeclare ||= (0, node_utils_1.hasModifier)(SyntaxKind.DeclareKeyword, node); const nextName = node.name; const right = this.createNode(nextName, { type: ts_estree_1.AST_NODE_TYPES.Identifier, range: [nextName.getStart(this.ast), nextName.getEnd()], decorators: [], name: nextName.text, optional: false, typeAnnotation: undefined, }); name = this.createNode(nextName, { type: ts_estree_1.AST_NODE_TYPES.TSQualifiedName, range: [name.range[0], right.range[1]], left: name, right, }); } return { body: this.convertChild(node.body), declare: false, global: false, id: name, kind: node.flags & ts.NodeFlags.Namespace ? 'namespace' : 'module', }; })(), }); result.declare = isDeclare; if (node.flags & ts.NodeFlags.GlobalAugmentation) { // eslint-disable-next-line @typescript-eslint/no-deprecated result.global = true; } return this.fixExports(node, result); } // TypeScript specific types case SyntaxKind.ParenthesizedType: { return this.convertChild(node.type); } case SyntaxKind.UnionType: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSUnionType, types: this.convertChildren(node.types), }); } case SyntaxKind.IntersectionType: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSIntersectionType, types: this.convertChildren(node.types), }); } case SyntaxKind.AsExpression: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSAsExpression, expression: this.convertChild(node.expression), typeAnnotation: this.convertChild(node.type), }); } case SyntaxKind.InferType: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSInferType, typeParameter: this.convertChild(node.typeParameter), }); } case SyntaxKind.LiteralType: { if (node.literal.kind === SyntaxKind.NullKeyword) { // 4.0 started nesting null types inside a LiteralType node // but our AST is designed around the old way of null being a keyword return this.createNode(node.literal, { type: ts_estree_1.AST_NODE_TYPES.TSNullKeyword, }); } return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSLiteralType, literal: this.convertChild(node.literal), }); } case SyntaxKind.TypeAssertionExpression: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSTypeAssertion, expression: this.convertChild(node.expression), typeAnnotation: this.convertChild(node.type), }); } case SyntaxKind.ImportEqualsDeclaration: { return this.fixExports(node, this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSImportEqualsDeclaration, id: this.convertChild(node.name), importKind: node.isTypeOnly ? 'type' : 'value', moduleReference: this.convertChild(node.moduleReference), })); } case SyntaxKind.ExternalModuleReference: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSExternalModuleReference, expression: this.convertChild(node.expression), }); } case SyntaxKind.NamespaceExportDeclaration: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSNamespaceExportDeclaration, id: this.convertChild(node.name), }); } case SyntaxKind.AbstractKeyword: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSAbstractKeyword, }); } // Tuple case SyntaxKind.TupleType: { const elementTypes = this.convertChildren(node.elements); return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSTupleType, elementTypes, }); } case SyntaxKind.NamedTupleMember: { const member = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSNamedTupleMember, elementType: this.convertChild(node.type, node), label: this.convertChild(node.name, node), optional: node.questionToken != null, }); if (node.dotDotDotToken) { // adjust the start to account for the "..." member.range[0] = member.label.range[0]; member.loc.start = member.label.loc.start; return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSRestType, typeAnnotation: member, }); } return member; } case SyntaxKind.OptionalType: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSOptionalType, typeAnnotation: this.convertChild(node.type), }); } case SyntaxKind.RestType: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSRestType, typeAnnotation: this.convertChild(node.type), }); } // Template Literal Types case SyntaxKind.TemplateLiteralType: { const result = this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSTemplateLiteralType, quasis: [this.convertChild(node.head)], types: [], }); node.templateSpans.forEach(templateSpan => { result.types.push(this.convertChild(templateSpan.type)); result.quasis.push(this.convertChild(templateSpan.literal)); }); return result; } case SyntaxKind.ClassStaticBlockDeclaration: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.StaticBlock, body: this.convertBodyExpressions(node.body.statements, node), }); } // eslint-disable-next-line @typescript-eslint/no-deprecated case SyntaxKind.AssertEntry: case SyntaxKind.ImportAttribute: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ImportAttribute, key: this.convertChild(node.name), value: this.convertChild(node.value), }); } case SyntaxKind.SatisfiesExpression: { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.TSSatisfiesExpression, expression: this.convertChild(node.expression), typeAnnotation: this.convertChild(node.type), }); } default: return this.deeplyCopy(node); } } createNode(node, data) { const result = data; result.range ??= (0, node_utils_1.getRange)(node, this.ast); result.loc ??= (0, node_utils_1.getLocFor)(result.range, this.ast); if (result && this.options.shouldPreserveNodeMaps) { this.esTreeNodeToTSNodeMap.set(result, node); } return result; } convertProgram() { return this.converter(this.ast); } /** * For nodes that are copied directly from the TypeScript AST into * ESTree mostly as-is. The only difference is the addition of a type * property instead of a kind property. Recursively copies all children. */ deeplyCopy(node) { if (node.kind === ts.SyntaxKind.JSDocFunctionType) { this.#throwError(node, 'JSDoc types can only be used inside documentation comments.'); } const customType = `TS${SyntaxKind[node.kind]}`; /** * If the "errorOnUnknownASTType" option is set to true, throw an error, * otherwise fallback to just including the unknown type as-is. */ if (this.options.errorOnUnknownASTType && !ts_estree_1.AST_NODE_TYPES[customType]) { throw new Error(`Unknown AST_NODE_TYPE: "${customType}"`); } const result = this.createNode(node, { type: customType, }); if ('type' in node) { result.typeAnnotation = node.type && 'kind' in node.type && ts.isTypeNode(node.type) ? this.convertTypeAnnotation(node.type, node) : null; } if ('typeArguments' in node) { result.typeArguments = node.typeArguments && 'pos' in node.typeArguments ? this.convertTypeArguments(node) : null; } if ('typeParameters' in node) { result.typeParameters = node.typeParameters && 'pos' in node.typeParameters ? // @ts-expect-error -- Fix me this.convertTypeParameters(node) : null; } const decorators = (0, getModifiers_1.getDecorators)(node); if (decorators?.length) { result.decorators = this.convertChildren(decorators); } // keys we never want to clone from the base typescript node as they // introduce garbage into our AST const KEYS_TO_NOT_COPY = new Set([ '_children', 'decorators', 'end', 'flags', 'heritageClauses', 'illegalDecorators', 'jsDoc', 'kind', 'locals', 'localSymbol', 'modifierFlagsCache', 'modifiers', 'nextContainer', 'parent', 'pos', 'symbol', 'transformFlags', 'type', 'typeArguments', 'typeParameters', ]); Object.entries(node) .filter(([key]) => !KEYS_TO_NOT_COPY.has(key)) .forEach(([key, value]) => { if (Array.isArray(value)) { result[key] = this.convertChildren(value); } else if (value && typeof value === 'object' && value.kind) { // need to check node[key].kind to ensure we don't try to convert a symbol result[key] = this.convertChild(value); } else { result[key] = value; } }); return result; } /** * Fixes the exports of the given ts.Node * @returns the ESTreeNode with fixed exports */ fixExports(node, result) { const isNamespaceNode = ts.isModuleDeclaration(node) && !ts.isStringLiteral(node.name); const modifiers = isNamespaceNode ? (0, node_utils_1.getNamespaceModifiers)(node) : (0, getModifiers_1.getModifiers)(node); if (modifiers?.[0].kind === SyntaxKind.ExportKeyword) { /** * Make sure that original node is registered instead of export */ this.registerTSNodeInNodeMap(node, result); const exportKeyword = modifiers[0]; const nextModifier = modifiers[1]; const declarationIsDefault = nextModifier?.kind === SyntaxKind.DefaultKeyword; const varToken = declarationIsDefault ? (0, node_utils_1.findNextToken)(nextModifier, this.ast, this.ast) : (0, node_utils_1.findNextToken)(exportKeyword, this.ast, this.ast); result.range[0] = varToken.getStart(this.ast); result.loc = (0, node_utils_1.getLocFor)(result.range, this.ast); if (declarationIsDefault) { return this.createNode(node, { type: ts_estree_1.AST_NODE_TYPES.ExportDefaultDeclaration, range: [exportKeyword.getStart(this.ast), result.range[1]], declaration: result, exportKind: 'value', }); } const isType = result.type === ts_estree_1.AST_NODE_TYPES.TSInterfaceDeclaration || result.type === ts_estree_1.AST_NODE_TYPES.TSTypeAliasDeclaration; const isDeclare = 'declare' in result && result.declare; return this.createNode(node, // @ts-expect-error - TODO, narrow the types here this.#withDeprecatedAliasGetter({ type: ts_estree_1.AST_NODE_TYPES.ExportNamedDeclaration, range: [exportKeyword.getStart(this.ast), result.range[1]], attributes: [], declaration: result, exportKind: isType || isDeclare ? 'type' : 'value', source: null, specifiers: [], }, 'assertions', 'attributes', true)); } return result; } getASTMaps() { return { esTreeNodeToTSNodeMap: this.esTreeNodeToTSNodeMap, tsNodeToESTreeNodeMap: this.tsNodeToESTreeNodeMap, }; } /** * Register specific TypeScript node into map with first ESTree node provided */ registerTSNodeInNodeMap(node, result) { if (result && this.options.shouldPreserveNodeMaps && !this.tsNodeToESTreeNodeMap.has(node)) { this.tsNodeToESTreeNodeMap.set(node, result); } } } exports.Converter = Converter;