import path from 'node:path';
import debug from 'debug';
import { moduleRequire } from './module-require.js';
function withoutProjectParserOptions(opts) {
    const { EXPERIMENTAL_useProjectService, project, projectService, ...rest } = opts;
    return rest;
}
const log = debug('eslint-plugin-import-x:parse');
function keysFromParser(_parserPath, parserInstance, parsedResult) {
    if (parsedResult && parsedResult.visitorKeys) {
        return parsedResult.visitorKeys;
    }
    if (parserInstance &&
        'VisitorKeys' in parserInstance &&
        parserInstance.VisitorKeys) {
        return parserInstance.VisitorKeys;
    }
    return null;
}
function makeParseReturn(ast, visitorKeys) {
    return {
        ast,
        visitorKeys,
    };
}
function stripUnicodeBOM(text) {
    return text.codePointAt(0) === 0xfe_ff ? text.slice(1) : text;
}
function transformHashbang(text) {
    return text.replace(/^#!([^\r\n]+)/u, (_, captured) => `//${captured}`);
}
export function parse(path, content, context) {
    if (context == null) {
        throw new Error('need context to parse properly');
    }
    let parserOptions = context.languageOptions?.parserOptions || context.parserOptions;
    const parserOrPath = getParser(path, context);
    if (!parserOrPath) {
        throw new Error('parserPath or languageOptions.parser is required!');
    }
    parserOptions = { ...parserOptions };
    parserOptions.ecmaFeatures = { ...parserOptions.ecmaFeatures };
    parserOptions.comment = true;
    parserOptions.attachComment = true;
    parserOptions.tokens = true;
    parserOptions.loc = true;
    parserOptions.range = true;
    parserOptions.filePath = path;
    parserOptions = withoutProjectParserOptions(parserOptions);
    parserOptions.ecmaVersion ??= context.languageOptions?.ecmaVersion;
    parserOptions.sourceType ??= context.languageOptions?.sourceType;
    const parser = typeof parserOrPath === 'string'
        ? moduleRequire(parserOrPath, context.physicalFilename)
        : parserOrPath;
    content = transformHashbang(stripUnicodeBOM(String(content)));
    if ('parseForESLint' in parser &&
        typeof parser.parseForESLint === 'function') {
        let ast;
        try {
            const parserRaw = parser.parseForESLint(content, parserOptions);
            ast = parserRaw.ast;
            return makeParseReturn(ast, keysFromParser(parserOrPath, parser, parserRaw));
        }
        catch (error_) {
            const error = error_;
            console.warn(`Error while parsing ${parserOptions.filePath}`);
            console.warn(`Line ${error.lineNumber}, column ${error.column}: ${error.message}`);
        }
        if (!ast || typeof ast !== 'object') {
            console.warn(`\`parseForESLint\` from parser \`${typeof parserOrPath === 'string' ? parserOrPath : 'context.languageOptions.parser'}\` is invalid and will just be ignored`, { content, parserMeta: parser.meta });
        }
        else {
            return makeParseReturn(ast, keysFromParser(parserOrPath, parser));
        }
    }
    if ('parse' in parser) {
        const ast = parser.parse(content, parserOptions);
        return makeParseReturn(ast, keysFromParser(parserOrPath, parser));
    }
    throw new Error('Parser must expose a `parse` or `parseForESLint` method');
}
function getParser(path, context) {
    const parserPath = getParserPath(path, context);
    if (parserPath) {
        return parserPath;
    }
    const parser = 'languageOptions' in context && context.languageOptions?.parser;
    if (parser &&
        typeof parser !== 'string' &&
        (('parse' in parser && typeof parse === 'function') ||
            ('parseForESLint' in parser &&
                typeof parser.parseForESLint === 'function'))) {
        return parser;
    }
    return null;
}
function getParserPath(filepath, context) {
    const parsers = context.settings['import-x/parsers'];
    if (parsers != null) {
        const extension = path.extname(filepath);
        for (const parserPath in parsers) {
            if (parsers[parserPath].includes(extension)) {
                log('using alt parser:', parserPath);
                return parserPath;
            }
        }
    }
    return context.parserPath;
}
//# sourceMappingURL=parse.js.map