@typescript-eslint/typescript-estree
Version: 
A parser that converts TypeScript source code into an ESTree compatible form
1,010 lines • 138 kB
JavaScript
"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 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)(('message' in error && error.message) || error.messageText, error.file, error.start);
}
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 };
    }
    #checkForStatementDeclaration(initializer, kind) {
        const loop = kind === ts.SyntaxKind.ForInStatement ? 'for...in' : 'for...of';
        if (ts.isVariableDeclarationList(initializer)) {
            if (initializer.declarations.length !== 1) {
                this.#throwError(initializer, `Only a single variable declaration is allowed in a '${loop}' statement.`);
            }
            const declaration = initializer.declarations[0];
            if (declaration.initializer) {
                this.#throwError(declaration, `The variable declaration of a '${loop}' statement cannot have an initializer.`);
            }
            else if (declaration.type) {
                this.#throwError(declaration, `The variable declaration of a '${loop}' statement cannot have a type annotation.`);
            }
            if (kind === ts.SyntaxKind.ForInStatement &&
                initializer.flags & ts.NodeFlags.Using) {
                this.#throwError(initializer, "The left-hand side of a 'for...in' statement cannot be a 'using' declaration.");
            }
        }
        else if (!(0, node_utils_1.isValidAssignmentTarget)(initializer) &&
            initializer.kind !== ts.SyntaxKind.ObjectLiteralExpression &&
            initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) {
            this.#throwError(initializer, `The left-hand side of a '${loop}' statement must be a variable or a property access.`);
        }
    }
    #checkModifiers(node) {
        if (this.options.allowInvalidAST) {
            return;
        }
        // typescript<5.0.0
        if ((0, node_utils_1.nodeHasIllegalDecorators)(node)) {
            this.#throwError(node.illegalDecorators[0], 'Decorators are not valid here.');
        }
        for (const decorator of (0, getModifiers_1.getDecorators)(node, 
        /* includeIllegalDecorators */ true) ?? []) {
            // `checkGrammarModifiers` function in typescript
            if (!(0, node_utils_1.nodeCanBeDecorated)(node)) {
                if (ts.isMethodDeclaration(node) && !(0, node_utils_1.nodeIsPresent)(node.body)) {
                    this.#throwError(decorator, 'A decorator can only decorate a method implementation, not an overload.');
                }
                else {
                    this.#throwError(decorator, 'Decorators are not valid here.');
                }
            }
        }
        for (const modifier of (0, getModifiers_1.getModifiers)(node, 
        /* includeIllegalModifiers */ true) ?? []) {
            if (modifier.kind !== SyntaxKind.ReadonlyKeyword) {
                if (node.kind === SyntaxKind.PropertySignature ||
                    node.kind === SyntaxKind.MethodSignature) {
                    this.#throwError(modifier, `'${ts.tokenToString(modifier.kind)}' modifier cannot appear on a type member`);
                }
                if (node.kind === SyntaxKind.IndexSignature &&
                    (modifier.kind !== SyntaxKind.StaticKeyword ||
                        !ts.isClassLike(node.parent))) {
                    this.#throwError(modifier, `'${ts.tokenToString(modifier.kind)}' modifier cannot appear on an index signature`);
                }
            }
            if (modifier.kind !== SyntaxKind.InKeyword &&
                modifier.kind !== SyntaxKind.OutKeyword &&
                modifier.kind !== SyntaxKind.ConstKeyword &&
                node.kind === SyntaxKind.TypeParameter) {
                this.#throwError(modifier, `'${ts.tokenToString(modifier.kind)}' modifier cannot appear on a type parameter`);
            }
            if ((modifier.kind === SyntaxKind.InKeyword ||
                modifier.kind === SyntaxKind.OutKeyword) &&
                (node.kind !== SyntaxKind.TypeParameter ||
                    !(ts.isInterfaceDeclaration(node.parent) ||
                        ts.isClassLike(node.parent) ||
                        ts.isTypeAliasDeclaration(node.parent)))) {
                this.#throwError(modifier, `'${ts.tokenToString(modifier.kind)}' modifier can only appear on a type parameter of a class, interface or type alias`);
            }
            if (modifier.kind === SyntaxKind.ReadonlyKeyword &&
                node.kind !== SyntaxKind.PropertyDeclaration &&
                node.kind !== SyntaxKind.PropertySignature &&
                node.kind !== SyntaxKind.IndexSignature &&
                node.kind !== SyntaxKind.Parameter) {
                this.#throwError(modifier, "'readonly' modifier can only appear on a property declaration or index signature.");
            }
            if (modifier.kind === SyntaxKind.DeclareKeyword &&
                ts.isClassLike(node.parent) &&
                !ts.isPropertyDeclaration(node)) {
                this.#throwError(modifier, `'${ts.tokenToString(modifier.kind)}' modifier cannot appear on class elements of this kind.`);
            }
            if (modifier.kind === SyntaxKind.DeclareKeyword &&
                ts.isVariableStatement(node)) {
                const declarationKind = (0, node_utils_1.getDeclarationKind)(node.declarationList);
                if (declarationKind === 'using' || declarationKind === 'await using') {
                    this.#throwError(modifier, `'declare' modifier cannot appear on a '${declarationKind}' declaration.`);
                }
            }
            if (modifier.kind === SyntaxKind.AbstractKeyword &&
                node.kind !== SyntaxKind.ClassDeclaration &&
                node.kind !== SyntaxKind.ConstructorType &&
                node.kind !== SyntaxKind.MethodDeclaration &&
                node.kind !== SyntaxKind.PropertyDeclaration &&
                node.kind !== SyntaxKind.GetAccessor &&
                node.kind !== SyntaxKind.SetAccessor) {
                this.#throwError(modifier, `'${ts.tokenToString(modifier.kind)}' modifier can only appear on a class, method, or property declaration.`);
            }
            if ((modifier.kind === SyntaxKind.StaticKeyword ||
                modifier.kind === SyntaxKind.PublicKeyword ||
                modifier.kind === SyntaxKind.ProtectedKeyword ||
                modifier.kind === SyntaxKind.PrivateKeyword) &&
                (node.parent.kind === SyntaxKind.ModuleBlock ||
                    node.parent.kind === SyntaxKind.SourceFile)) {
                this.#throwError(modifier, `'${ts.tokenToString(modifier.kind)}' modifier cannot appear on a module or namespace element.`);
            }
            if (modifier.kind === SyntaxKind.AccessorKeyword &&
                node.kind !== SyntaxKind.PropertyDeclaration) {
                this.#throwError(modifier, "'accessor' modifier can only appear on a property declaration.");
            }
            // `checkGrammarAsyncModifier` function in `typescript`
            if (modifier.kind === SyntaxKind.AsyncKeyword &&
                node.kind !== SyntaxKind.MethodDeclaration &&
                node.kind !== SyntaxKind.FunctionDeclaration &&
                node.kind !== SyntaxKind.FunctionExpression &&
                node.kind !== SyntaxKind.ArrowFunction) {
                this.#throwError(modifier, "'async' modifier cannot be used here.");
            }
            // `checkGrammarModifiers` function in `typescript`
            if (node.kind === SyntaxKind.Parameter &&
                (modifier.kind === SyntaxKind.StaticKeyword ||
                    modifier.kind === SyntaxKind.ExportKeyword ||
                    modifier.kind === SyntaxKind.DeclareKeyword ||
                    modifier.kind === SyntaxKind.AsyncKeyword)) {
                this.#throwError(modifier, `'${ts.tokenToString(modifier.kind)}' modifier cannot appear on a parameter.`);
            }
            // `checkGrammarModifiers` function in `typescript`
            if (modifier.kind === SyntaxKind.PublicKeyword ||
                modifier.kind === SyntaxKind.ProtectedKeyword ||
                modifier.kind === SyntaxKind.PrivateKeyword) {
                for (const anotherModifier of (0, getModifiers_1.getModifiers)(node) ?? []) {
                    if (anotherModifier !== modifier &&
                        (anotherModifier.kind === SyntaxKind.PublicKeyword ||
                            anotherModifier.kind === SyntaxKind.ProtectedKeyword ||
                            anotherModifier.kind === SyntaxKind.PrivateKeyword)) {
                        this.#throwError(anotherModifier, `Accessibility modifier already seen.`);
                    }
                }
            }
            // `checkParameter` function in `typescript`
            if (node.kind === SyntaxKind.Parameter &&
                // In `typescript` package, it's `ts.hasSyntacticModifier(node, ts.ModifierFlags.ParameterPropertyModifier)`
                // https://github.com/typescript-eslint/typescript-eslint/pull/6615#discussion_r1136489935
                (modifier.kind === SyntaxKind.PublicKeyword ||
                    modifier.kind === SyntaxKind.PrivateKeyword ||
                    modifier.kind === SyntaxKind.ProtectedKeyword ||
                    modifier.kind === SyntaxKind.ReadonlyKeyword ||
                    modifier.kind === SyntaxKind.OverrideKeyword)) {
                const func = (0, node_utils_1.getContainingFunction)(node);
                if (!(func.kind === SyntaxKind.Constructor && (0, node_utils_1.nodeIsPresent)(func.body))) {
                    this.#throwError(modifier, 'A parameter property is only allowed in a constructor implementation.');
                }
            }
        }
    }
    #throwError(node, message) {
        let start;
        let end;
        if (typeof node === 'number') {
            start = end = node;
        }
        else {
            start = node.getStart(this.ast);
            end = node.getEnd();
        }
        throw (0, node_utils_1.createError)(message, this.ast, start, end);
    }
    #throwUnlessAllowInvalidAST(node, message) {
        if (!this.options.allowInvalidAST) {
            this.#throwError(node, message);
        }
    }
    /**
     * 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://typescript-eslint.io/troubleshooting/faqs/general#the-key-property-is-deprecated-on-type-nodes-use-key-instead-warnings.`, '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) {
                        process.emitWarning(`The '${deprecatedKey}' property is deprecated on ${node.type} nodes. Use ${preferredKey} instead. See https://typescript-eslint.io/troubleshooting/faqs/general#the-key-property-is-deprecated-on-type-nodes-use-key-instead-warnings.`, 'DeprecationWarning');
                        warned = true;
                    }
                    return value;
                },
            set(value) {
                Object.defineProperty(node, deprecatedKey, {
                    enumerable: true,
                    value,
                    writable: true,
                });
            },
        });
        return node;
    }
    assertModuleSpecifier(node, allowNull) {
        if (!allowNull && node.moduleSpecifier == null) {
            this.#throwUnlessAllowInvalidAST(node, 'Module specifier must be a string literal.');
        }
        if (node.moduleSpecifier &&
            node.moduleSpecifier?.kind !== SyntaxKind.StringLiteral) {
            this.#throwUnlessAllowInvalidAST(node.moduleSpecifier, 'Module specifier must be a string literal.');
        }
    }
    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 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
     */
    convertTypeArgumentsToTypeParameterInstantiation(typeArguments, node) {
        const greaterThanToken = (0, node_utils_1.findNextToken)(typeArguments, this.ast, this.ast);
        return this.createNode(node, {
            type: ts_estree_1.AST_NODE_TYPES.TSTypeParameterInstantiation,
            range: [typeArguments.pos - 1, greaterThanToken.end],
            params: typeArguments.map(typeArgument => this.convertChild(typeArgument)),
        });
    }
    /**
     * Converts a ts.Node's typeParameters to TSTypeParameterDeclaration node
     * @param typeParameters ts.Node typeParameters
     * @returns TypeParameterDeclaration node
     */
    convertTSTypeParametersToTypeParametersDeclaration(typeParameters) {
        const greaterThanToken = (0, node_utils_1.findNextToken)(typeParameters, this.ast, this.ast);
        const range = [
            typeParameters.pos - 1,
            greaterThanToken.end,
        ];
        return {
            type: ts_estree_1.AST_NODE_TYPES.TSTypeParameterDeclaration,
            loc: (0, node_utils_1.getLocFor)(range, this.ast),
            range,
            params: typeParameters.map(typeParameter => this.convertChild(typeParameter)),
        };
    }
    /**
     * 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 =
                (0, getModifiers_1.getDecorators)(param)?.map(el => this.convertChild(el)) ?? [];
            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;
        }
        this.#checkModifiers(node);
        const pattern = this.allowPattern;
        if (allowPattern != null) {
            this.allowPattern = allowPattern;
        }
        const result = this.convertNode(node, (parent ?? node.parent));
        this.registerTSNodeInNodeMap(node, result);
        this.allowPattern = pattern;
        return result;
    }
    convertImportAttributes(node) {
        return node == null
            ? []
            : node.elements.map(element => this.convertChild(element));
    }
    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: node.typeParameters &&
                this.convertTSTypeParametersToTypeParametersDeclaration(node.typeParameters),
        });
    }
    /**
     * 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:
                if (node.caseBlock.clauses.filter(switchCase => switchCase.kind === SyntaxKind.DefaultClause).length > 1) {
                    this.#throwError(node, "A 'default' clause cannot appear more than once in a 'switch' statement.");
                }
                return this.createNode(node, {
                    type: ts_estree_1.AST_NODE_TYPES.SwitchStatement,
                    cases: node.caseBlock.clauses.map(el => this.convertChild(el)),
                    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: node.statements.map(el => this.convertChild(el)),
                    test: node.kind === SyntaxKind.CaseClause
                        ? this.convertChild(node.expression)
                        : null,
                });
            // Exceptions
            case SyntaxKind.ThrowStatement:
                if (node.expression.end === node.expression.pos) {
                    this.#throwUnlessAllowInvalidAST(node, 'A throw statement must throw an expression.');
                }
                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:
                if (node.variableDeclaration?.initializer) {
                    this.#throwError(node.variableDeclaration.initializer, 'Catch clause variable cannot have an initializer.');
                }
                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:
                this.#checkForStatementDeclaration(node.initializer, node.kind);
                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: {
                this.#checkForStatementDeclaration(node.initializer, node.kind);
                return this.createNode(node, {
                    type: ts_estree_1.AST_NODE_TYPES.ForOfStatement,
                    await: Boolean(node.awaitModifier &&
                        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;
                if (isDeclare) {
                    if (node.body) {
                        this.#throwError(node, 'An implementation cannot be declared in ambient contexts.');
                    }
                    else if (isAsync) {
                        this.#throwError(node, "'async' modifier cannot be used in an ambient context.");
                    }
                    else if (isGenerator) {
                        this.#throwError(node, 'Generators are not allowed in an ambient context.');
                    }
                }
                else if (!node.body && isGenerator) {
                    this.#throwError(node, 'A function signature cannot be declared as a generator.');
                }
                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: node.typeParameters &&
                        this.convertTSTypeParametersToTypeParametersDeclaration(node.typeParameters),
                });
                return this.fixExports(node, result);
            }
            case SyntaxKind.VariableDeclaration: {
                const definite = !!node.exclamationToken;
                const init = this.convertChild(node.initializer);
                const id = this.convertBindingNameWithTypeAnnotation(node.name, node.type, node);
                if (definite) {
                    if (init) {
                        this.#throwError(node, 'Declarations with initializers cannot also have definite assignment assertions.');
                    }
                    else if (id.type !== ts_estree_1.AST_NODE_TYPES.Identifier ||
                        !id.typeAnnotation) {
                        this.#throwError(node, 'Declarations with definite assignment assertions must also have type annotations.');
                    }
                }
                return this.createNode(node, {
                    type: ts_estree_1.AST_NODE_TYPES.VariableDeclarator,
                    definite,
                    id,
                    init,
                });
            }
            case SyntaxKind.VariableStatement: {
                const result = this.createNode(node, {
                    type: ts_estree_1.AST_NODE_TYPES.VariableDeclaration,
                    declarations: node.declarationList.declarations.map(el => this.convertChild(el)),
                    declare: (0, node_utils_1.hasModifier)(SyntaxKind.DeclareKeyword, node),
                    kind: (0, node_utils_1.getDeclarationKind)(node.declarationList),
                });
                if (!result.declarations.length) {
                    this.#throwUnlessAllowInvalidAST(node, 'A variable declaration list must have at least one variable declarator.');
                }
                if (result.kind === 'using' || result.kind === 'await using') {
                    node.declarationList.declarations.forEach((declaration, i) => {
                        if (result.declarations[i].init == null) {
                            this.#throwError(declaration, `'${result.kind}' declarations must be initialized.`);
                        }
                        if (result.declarations[i].id.type !== ts_estree_1.AST_NODE_TYPES.Identifier) {
                            this.#throwError(declaration.name, `'${result.kind}' declarations may not have binding patterns.`);
                        }
                    });
                }
                // Definite assignment only allowed for non-declare let and var
                if (result.declare ||
                    ['await using', 'const', 'using'].includes(result.kind)) {
                    node.declarationList.declarations.forEach((declaration, i) => {
                        if (result.declarations[i].definite) {
                            this.#throwError(declaration, `A definite assignment assertion '!' is not permitted in this context.`);
                        }
                    });
                }
                if (result.declare) {
                    node.declarationList.declarations.forEach((declaration, i) => {
                        if (result.declarations[i].init &&
                            (['let', 'var'].includes(result.kind) ||
                                result.declarations[i].id.typeAnnotation)) {
                            this.#throwError(declaration, `Initializers are not permitted in ambient contexts.`);
                        }
                    });
                    // Theoretically, only certain initializers are allowed for declare const,
                    // (TS1254: A 'const' initializer in an ambient context must be a string
                    // or numeric literal or literal enum reference.) but we just allow
                    // all expressions
                }
                // Note! No-declare does not mean the variable is not ambient, because
                // it can be further nested in other declare contexts. Therefore we cannot
                // check for const initializers.
                /**
                 * 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: {
                const result = this.createNode(node, {
                    type: ts_estree_1.AST_NODE_TYPES.VariableDeclaration,
                    declarations: node.declarations.map(el => this.convertChild(el)),
                    declare: false,
                    kind: (0, node_utils_1.getDeclarationKind)(node),
                });
                if (result.kind === 'using' || result.kind === 'await using') {
                    node.declarations.forEach((declaration, i) => {
                        if (result.declarations[i].init != null) {
                            this.#throwError(declaration, `'${result.kind}' declarations may not be initialized in for statement.`);
                        }
                        if (result.declarations[i].id.type !== ts_estree_1.AST_NODE_TYPES.Identifier) {
                            this.#throwError(declaration.name, `'${result.kind}' declarations may not have binding patterns.`);
                        }
                    });
                }
                return result;
            }
            // 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: node.elements.map(el => this.convertChild(el)),
                });
            }
            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,
                    });
                }
                const properties = [];
                for (const property of node.properties) {
                    if ((property.kind === SyntaxKind.GetAccessor ||
                        property.kind === SyntaxKind.SetAccessor ||
                        property.kind === SyntaxKind.MethodDeclaration) &&
                        !property.body) {
                        this.#throwUnlessAllowInvalidAST(property.end - 1, "'{' expected.");
                    }
                    properties.push(this.convertChild(property));
                }
                return this.createNode(node, {
                    type: ts_estree_1.AST_NODE_TYPES.ObjectExpression,
                    properties,
                });
            }
            case SyntaxKind.PropertyAssignment: {
                // eslint-disable-next-line @typescript-eslint/no-deprecated
                const { exclamationToken, questionToken } = node;
                if (questionToken) {
                    this.#throwError(questionToken, 'A property assignment cannot have a question token.');
                }
                if (exclamationToken) {
                    this.#throwError(exclamationToken, 'A property assignment cannot have an exclamation token.');
                }
                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: {
                // eslint-disable-next-line @typescript-eslint/no-deprecated
                const { exclamationToken, modifiers, questionToken } = node;
                if (modifiers) {
                    this.#throwError(modifiers[0], 'A shorthand property assignment cannot have modifiers.');
                }
                if (questionToken) {
                    this.#throwError(questionToken, 'A shorthand property assignment cannot have a question token.');
                }
                if (exclamationToken) {
                    this.#throwError(exclamationToken, 'A shorthand property assignment cannot have an exclamation token.');
                }
                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.objectAssignmentInitiali