"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.__tsfTypeAliases = void 0;
exports.readTypeScriptOutputConfig = readTypeScriptOutputConfig;
exports.resolveTypeScriptOutputPath = resolveTypeScriptOutputPath;
exports.resolveTypeScriptOutDir = resolveTypeScriptOutDir;
const tslib_1 = require("tslib");
const _a = tslib_1.__importStar(require("@zyno-io/ts-server-foundation/type-metadata-runtime"));
const node_fs_1 = require("node:fs");
const node_module_1 = require("node:module");
const node_path_1 = require("node:path");
function readTypeScriptOutputConfig(options = {}) {
    const cwd = (0, node_path_1.resolve)(options.cwd ?? process.cwd());
    const configuredPath = options.tsconfigPath ?? process.env.TSF_TSCONFIG;
    const configPath = configuredPath ? (0, node_path_1.resolve)(cwd, configuredPath) : findTsconfig(cwd);
    if (!configPath)
        return undefined;
    return readEffectiveTypeScriptOutputConfig(configPath, new Set());
}
function resolveTypeScriptOutputPath(sourcePath, options = {}) {
    const cwd = (0, node_path_1.resolve)(options.cwd ?? process.cwd());
    const absoluteSource = (0, node_path_1.resolve)(cwd, sourcePath);
    const configuredPath = options.tsconfigPath ?? process.env.TSF_TSCONFIG;
    const config = configuredPath
        ? readTypeScriptOutputConfig({ cwd, tsconfigPath: configuredPath })
        : readTypeScriptOutputConfig({ cwd: absoluteSource });
    if (!config?.outDir || !config.rootDir)
        return undefined;
    const relativeSource = (0, node_path_1.relative)(config.rootDir, absoluteSource);
    if (isOutsideDirectory(relativeSource))
        return undefined;
    return replaceTypeScriptExtension((0, node_path_1.resolve)(config.outDir, relativeSource));
}
function resolveTypeScriptOutDir(options = {}) {
    return readTypeScriptOutputConfig(options)?.outDir;
}
function readEffectiveTypeScriptOutputConfig(configPath, seen) {
    const absoluteConfigPath = (0, node_path_1.resolve)(configPath);
    if (seen.has(absoluteConfigPath) || !(0, node_fs_1.existsSync)(absoluteConfigPath))
        return undefined;
    seen.add(absoluteConfigPath);
    let config;
    try {
        config = parseJsonC((0, node_fs_1.readFileSync)(absoluteConfigPath, 'utf8'));
    }
    catch {
        return undefined;
    }
    let inherited;
    for (const extended of normalizeExtends(config.extends)) {
        const extendedPath = resolveTsconfigExtends(absoluteConfigPath, extended);
        if (!extendedPath)
            continue;
        const resolved = readEffectiveTypeScriptOutputConfig(extendedPath, new Set(seen));
        if (resolved) {
            inherited = {
                configPath: absoluteConfigPath,
                rootDir: resolved.rootDir ?? inherited?.rootDir,
                outDir: resolved.outDir ?? inherited?.outDir
            };
        }
    }
    const configDir = (0, node_path_1.dirname)(absoluteConfigPath);
    return {
        configPath: absoluteConfigPath,
        rootDir: typeof config.compilerOptions?.rootDir === 'string' ? (0, node_path_1.resolve)(configDir, config.compilerOptions.rootDir) : inherited?.rootDir,
        outDir: typeof config.compilerOptions?.outDir === 'string' ? (0, node_path_1.resolve)(configDir, config.compilerOptions.outDir) : inherited?.outDir
    };
}
function findTsconfig(start) {
    let directory = start;
    try {
        if (!(0, node_fs_1.statSync)(directory).isDirectory())
            directory = (0, node_path_1.dirname)(directory);
    }
    catch {
        directory = (0, node_path_1.extname)(directory) ? (0, node_path_1.dirname)(directory) : directory;
    }
    while (true) {
        const candidate = (0, node_path_1.join)(directory, 'tsconfig.json');
        if ((0, node_fs_1.existsSync)(candidate))
            return candidate;
        const parent = (0, node_path_1.dirname)(directory);
        if (parent === directory)
            return undefined;
        directory = parent;
    }
}
function normalizeExtends(value) {
    if (typeof value === 'string')
        return [value];
    return Array.isArray(value) ? value.filter((entry) => typeof entry === 'string') : [];
}
function resolveTsconfigExtends(configPath, specifier) {
    if ((0, node_path_1.isAbsolute)(specifier) || specifier.startsWith('.')) {
        return firstExistingConfigPath((0, node_path_1.resolve)((0, node_path_1.dirname)(configPath), specifier));
    }
    const requireFromConfig = (0, node_module_1.createRequire)(configPath);
    try {
        return requireFromConfig.resolve(specifier);
    }
    catch {
        try {
            return requireFromConfig.resolve(`${specifier}/tsconfig.json`);
        }
        catch {
            return undefined;
        }
    }
}
function firstExistingConfigPath(path) {
    for (const candidate of [path, `${path}.json`, (0, node_path_1.join)(path, 'tsconfig.json')]) {
        if ((0, node_fs_1.existsSync)(candidate))
            return candidate;
    }
}
function isOutsideDirectory(path) {
    return path === '..' || path.startsWith(`..${node_path_1.sep}`) || (0, node_path_1.isAbsolute)(path);
}
function replaceTypeScriptExtension(path) {
    return path.replace(/(?:\.d)?\.(?:cts|mts|tsx?)$/i, match => {
        const normalized = match.toLowerCase();
        if (normalized === '.mts')
            return '.mjs';
        if (normalized === '.cts')
            return '.cjs';
        return '.js';
    });
}
function parseJsonC(contents) {
    let output = '';
    let inString = false;
    let escaped = false;
    let lineComment = false;
    let blockComment = false;
    for (let index = 0; index < contents.length; index++) {
        const char = contents[index];
        const next = contents[index + 1];
        if (lineComment) {
            if (char === '\n' || char === '\r') {
                lineComment = false;
                output += char;
            }
            continue;
        }
        if (blockComment) {
            if (char === '*' && next === '/') {
                blockComment = false;
                index++;
            }
            continue;
        }
        if (inString) {
            output += char;
            if (escaped)
                escaped = false;
            else if (char === '\\')
                escaped = true;
            else if (char === '"')
                inString = false;
            continue;
        }
        if (char === '"') {
            inString = true;
            output += char;
            continue;
        }
        if (char === '/' && next === '/') {
            lineComment = true;
            index++;
            continue;
        }
        if (char === '/' && next === '*') {
            blockComment = true;
            index++;
            continue;
        }
        output += char;
    }
    return JSON.parse(stripTrailingJsonCommas(output));
}
function stripTrailingJsonCommas(contents) {
    let output = '';
    let inString = false;
    let escaped = false;
    for (let index = 0; index < contents.length; index++) {
        const char = contents[index];
        if (inString) {
            output += char;
            if (escaped)
                escaped = false;
            else if (char === '\\')
                escaped = true;
            else if (char === '"')
                inString = false;
            continue;
        }
        if (char === '"') {
            inString = true;
            output += char;
            continue;
        }
        if (char === ',') {
            let nextIndex = index + 1;
            while (/\s/.test(contents[nextIndex] ?? ''))
                nextIndex++;
            if (contents[nextIndex] === '}' || contents[nextIndex] === ']')
                continue;
        }
        output += char;
    }
    return output;
}
exports.__tsfTypeAliases = _a.decodeCompactMetadataV1("[1,{\"TypeScriptOutputConfig\":{\"kind\":18,\"typeName\":\"TypeScriptOutputConfig\",\"types\":[{\"kind\":20,\"name\":\"configPath\",\"type\":{\"kind\":6},\"optional\":false},{\"kind\":20,\"name\":\"outDir\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"rootDir\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true}]},\"TypeScriptOutputOptions\":{\"kind\":18,\"typeName\":\"TypeScriptOutputOptions\",\"types\":[{\"kind\":20,\"name\":\"cwd\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true},{\"kind\":20,\"name\":\"tsconfigPath\",\"type\":{\"kind\":12,\"types\":[{\"kind\":6},{\"kind\":4}]},\"optional\":true}]}}]", []);
//# sourceMappingURL=typescript-output.js.map