"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parse = parse;
exports.parseForESLint = parseForESLint;
const scope_manager_1 = require("@typescript-eslint/scope-manager");
const typescript_estree_1 = require("@typescript-eslint/typescript-estree");
const visitor_keys_1 = require("@typescript-eslint/visitor-keys");
const debug_1 = __importDefault(require("debug"));
const typescript_1 = require("typescript");
const log = (0, debug_1.default)('typescript-eslint:parser:parser');
function validateBoolean(value, fallback = false) {
    if (typeof value !== 'boolean') {
        return fallback;
    }
    return value;
}
const LIB_FILENAME_REGEX = /lib\.(.+)\.d\.[cm]?ts$/;
function getLib(compilerOptions) {
    if (compilerOptions.lib) {
        return compilerOptions.lib
            .map(lib => LIB_FILENAME_REGEX.exec(lib.toLowerCase())?.[1])
            .filter((lib) => !!lib);
    }
    const target = compilerOptions.target ?? typescript_1.ScriptTarget.ES5;
    // https://github.com/microsoft/TypeScript/blob/ae582a22ee1bb052e19b7c1bc4cac60509b574e0/src/compiler/utilitiesPublic.ts#L13-L36
    switch (target) {
        case typescript_1.ScriptTarget.ES2015:
            return ['es6'];
        case typescript_1.ScriptTarget.ES2016:
            return ['es2016.full'];
        case typescript_1.ScriptTarget.ES2017:
            return ['es2017.full'];
        case typescript_1.ScriptTarget.ES2018:
            return ['es2018.full'];
        case typescript_1.ScriptTarget.ES2019:
            return ['es2019.full'];
        case typescript_1.ScriptTarget.ES2020:
            return ['es2020.full'];
        case typescript_1.ScriptTarget.ES2021:
            return ['es2021.full'];
        case typescript_1.ScriptTarget.ES2022:
            return ['es2022.full'];
        case typescript_1.ScriptTarget.ES2023:
            return ['es2023.full'];
        case typescript_1.ScriptTarget.ES2024:
            return ['es2024.full'];
        case typescript_1.ScriptTarget.ESNext:
            return ['esnext.full'];
        default:
            return ['lib'];
    }
}
function parse(code, options) {
    return parseForESLint(code, options).ast;
}
function parseForESLint(code, parserOptions) {
    if (!parserOptions || typeof parserOptions !== 'object') {
        parserOptions = {};
    }
    else {
        parserOptions = { ...parserOptions };
    }
    // https://eslint.org/docs/user-guide/configuring#specifying-parser-options
    // if sourceType is not provided by default eslint expect that it will be set to "script"
    if (parserOptions.sourceType !== 'module' &&
        parserOptions.sourceType !== 'script') {
        parserOptions.sourceType = 'script';
    }
    if (typeof parserOptions.ecmaFeatures !== 'object') {
        parserOptions.ecmaFeatures = {};
    }
    /**
     * Allow the user to suppress the warning from typescript-estree if they are using an unsupported
     * version of TypeScript
     */
    const warnOnUnsupportedTypeScriptVersion = validateBoolean(parserOptions.warnOnUnsupportedTypeScriptVersion, true);
    const tsestreeOptions = {
        jsx: validateBoolean(parserOptions.ecmaFeatures.jsx),
        ...(!warnOnUnsupportedTypeScriptVersion && { loggerFn: false }),
        ...parserOptions,
        // Override errorOnTypeScriptSyntacticAndSemanticIssues and set it to false to prevent use from user config
        // https://github.com/typescript-eslint/typescript-eslint/issues/8681#issuecomment-2000411834
        errorOnTypeScriptSyntacticAndSemanticIssues: false,
        // comment, loc, range, and tokens should always be set for ESLint usage
        // https://github.com/typescript-eslint/typescript-eslint/issues/8347
        comment: true,
        loc: true,
        range: true,
        tokens: true,
    };
    const analyzeOptions = {
        globalReturn: parserOptions.ecmaFeatures.globalReturn,
        jsxFragmentName: parserOptions.jsxFragmentName,
        jsxPragma: parserOptions.jsxPragma,
        lib: parserOptions.lib,
        sourceType: parserOptions.sourceType,
    };
    const { ast, services } = (0, typescript_estree_1.parseAndGenerateServices)(code, tsestreeOptions);
    ast.sourceType = parserOptions.sourceType;
    if (services.program) {
        // automatically apply the options configured for the program
        const compilerOptions = services.program.getCompilerOptions();
        if (analyzeOptions.lib == null) {
            analyzeOptions.lib = getLib(compilerOptions);
            log('Resolved libs from program: %o', analyzeOptions.lib);
        }
        if (
        // eslint-disable-next-line @typescript-eslint/internal/eqeq-nullish
        analyzeOptions.jsxPragma === undefined &&
            compilerOptions.jsxFactory != null) {
            // in case the user has specified something like "preact.h"
            const factory = compilerOptions.jsxFactory.split('.')[0].trim();
            analyzeOptions.jsxPragma = factory;
            log('Resolved jsxPragma from program: %s', analyzeOptions.jsxPragma);
        }
        if (
        // eslint-disable-next-line @typescript-eslint/internal/eqeq-nullish
        analyzeOptions.jsxFragmentName === undefined &&
            compilerOptions.jsxFragmentFactory != null) {
            // in case the user has specified something like "preact.Fragment"
            const fragFactory = compilerOptions.jsxFragmentFactory
                .split('.')[0]
                .trim();
            analyzeOptions.jsxFragmentName = fragFactory;
            log('Resolved jsxFragmentName from program: %s', analyzeOptions.jsxFragmentName);
        }
    }
    const scopeManager = (0, scope_manager_1.analyze)(ast, analyzeOptions);
    // if not defined - override from the parserOptions
    services.emitDecoratorMetadata ??=
        parserOptions.emitDecoratorMetadata === true;
    services.experimentalDecorators ??=
        parserOptions.experimentalDecorators === true;
    services.isolatedDeclarations ??= parserOptions.isolatedDeclarations === true;
    return { ast, scopeManager, services, visitorKeys: visitor_keys_1.visitorKeys };
}
