// Planu — Build tool detection engine (SPEC-050)
// Detects Vite, Webpack, Turbopack, esbuild, Rollup, Parcel, Rspack from
// package.json devDependencies and config files.
import { readFile, access } from 'node:fs/promises';
import { join } from 'node:path';
// -- Constants ----------------------------------------------------------------
/** devDependency key → BuildTool */
const DEV_DEP_TO_BUILD_TOOL = new Map([
    ['vite', 'vite'],
    ['@vitejs/core', 'vite'],
    ['webpack', 'webpack'],
    ['webpack-cli', 'webpack'],
    ['webpack-dev-server', 'webpack'],
    ['turbopack', 'turbopack'],
    ['esbuild', 'esbuild'],
    ['rollup', 'rollup'],
    ['parcel', 'parcel'],
    ['parcel-bundler', 'parcel'],
    ['@parcel/core', 'parcel'],
    ['rspack', 'rspack'],
    ['@rspack/core', 'rspack'],
    ['@rspack/cli', 'rspack'],
]);
/** Config file basename → BuildTool */
const CONFIG_FILE_TO_BUILD_TOOL = new Map([
    ['vite.config.ts', 'vite'],
    ['vite.config.js', 'vite'],
    ['vite.config.mts', 'vite'],
    ['vite.config.mjs', 'vite'],
    ['vite.config.cts', 'vite'],
    ['vite.config.cjs', 'vite'],
    ['webpack.config.js', 'webpack'],
    ['webpack.config.ts', 'webpack'],
    ['webpack.config.mjs', 'webpack'],
    ['webpack.config.cjs', 'webpack'],
    ['rspack.config.js', 'rspack'],
    ['rspack.config.ts', 'rspack'],
    ['rspack.config.mjs', 'rspack'],
]);
/** Ordered priority: first match wins when multiple build tools detected */
const BUILD_TOOL_PRIORITY = [
    'turbopack',
    'rspack',
    'vite',
    'webpack',
    'esbuild',
    'rollup',
    'parcel',
];
const LEGACY_PLUGIN_PACKAGES = ['@vitejs/plugin-legacy', 'vite-plugin-legacy'];
const MODERN_TARGETS = new Set(['es2022', 'es2023', 'es2024', 'esnext']);
const LEGACY_TARGETS = new Set(['es5', 'es6', 'es2015', 'es2016', 'es2017', 'es2018']);
// -- Helpers ------------------------------------------------------------------
async function readPkg(projectPath) {
    try {
        const content = await readFile(join(projectPath, 'package.json'), 'utf-8');
        return JSON.parse(content);
    }
    catch {
        return null;
    }
}
async function configFileExists(filePath) {
    try {
        await access(filePath);
        return true;
    }
    catch {
        return false;
    }
}
async function readFileSafe(filePath) {
    try {
        return await readFile(filePath, 'utf-8');
    }
    catch {
        return '';
    }
}
function allDeps(pkg) {
    return { ...pkg.dependencies, ...pkg.devDependencies };
}
// -- Detection helpers --------------------------------------------------------
/**
 * Find the first matching config file and return { buildTool, configFile }.
 */
async function detectFromConfigFiles(projectPath) {
    for (const [filename, tool] of CONFIG_FILE_TO_BUILD_TOOL) {
        if (await configFileExists(join(projectPath, filename))) {
            return { buildTool: tool, configFile: filename };
        }
    }
    return null;
}
/**
 * Detect all build tools present in devDependencies and dependencies.
 */
function detectFromDeps(pkg) {
    const deps = allDeps(pkg);
    const detected = new Set();
    const versions = new Map();
    for (const [dep, version] of Object.entries(deps)) {
        const tool = DEV_DEP_TO_BUILD_TOOL.get(dep);
        if (tool !== undefined) {
            detected.add(tool);
            // Prefer the canonical dep key (e.g. 'vite' over '@vitejs/core')
            if (!versions.has(tool) || dep === tool) {
                versions.set(tool, version);
            }
        }
    }
    return { detected: [...detected], versions };
}
/**
 * Select highest-priority build tool from a set of detected tools.
 */
function selectPrimaryTool(detected) {
    for (const tool of BUILD_TOOL_PRIORITY) {
        if (detected.includes(tool)) {
            return tool;
        }
    }
    return 'unknown';
}
// -- Rolldown detection -------------------------------------------------------
/**
 * Detect if the project uses Rolldown as its bundler.
 * Criteria: vite >= "8.0.0-beta" OR "rolldown" in devDependencies.
 */
function detectRolldown(pkg, viteVersion) {
    const deps = allDeps(pkg);
    if ('rolldown' in deps) {
        return 'rolldown';
    }
    if (viteVersion !== undefined) {
        const cleaned = viteVersion.replace(/^[^0-9]*/, '');
        const majorMatch = /^(\d+)/.exec(cleaned);
        const major = majorMatch !== null ? parseInt(majorMatch[1] ?? '0', 10) : 0;
        if (major >= 8) {
            return 'rolldown';
        }
    }
    return undefined;
}
/**
 * Determine bundler for non-Vite build tools.
 */
function detectBundlerForTool(buildTool) {
    const bundlerMap = {
        webpack: 'webpack',
        esbuild: 'esbuild',
        rollup: 'rollup',
        rspack: 'esbuild',
    };
    return bundlerMap[buildTool];
}
// -- Browser target detection -------------------------------------------------
/**
 * Detect browser target from vite.config content and package.json deps.
 */
function detectBrowserTarget(pkg, viteConfigContent) {
    const deps = allDeps(pkg);
    // Legacy plugin always means legacy target
    for (const legacyPkg of LEGACY_PLUGIN_PACKAGES) {
        if (legacyPkg in deps) {
            return 'legacy';
        }
    }
    if (viteConfigContent.length > 0) {
        const targetMatch = /build\s*[:{][^}]*target\s*[:=]\s*['"`]([^'"`]+)['"`]/.exec(viteConfigContent);
        if (targetMatch !== null) {
            const target = (targetMatch[1] ?? '').toLowerCase();
            if (LEGACY_TARGETS.has(target)) {
                return 'legacy';
            }
            if (MODERN_TARGETS.has(target)) {
                return 'modern';
            }
            if (target === 'baseline-widely-available') {
                return 'baseline-widely-available';
            }
        }
    }
    return 'unknown';
}
// -- Main export --------------------------------------------------------------
/**
 * Detect the build tool used in a project.
 * Reads package.json devDependencies and checks for config files.
 * Returns BuildToolDetectionResult with buildTool: "unknown" if none found.
 */
export async function detectBuildTool(projectPath) {
    const pkg = await readPkg(projectPath);
    if (pkg === null) {
        const fromConfig = await detectFromConfigFiles(projectPath);
        if (fromConfig !== null) {
            return { buildTool: fromConfig.buildTool, configFile: fromConfig.configFile };
        }
        return { buildTool: 'unknown' };
    }
    const { detected, versions } = detectFromDeps(pkg);
    const fromConfig = await detectFromConfigFiles(projectPath);
    const allDetected = new Set(detected);
    if (fromConfig !== null) {
        allDetected.add(fromConfig.buildTool);
    }
    if (allDetected.size === 0) {
        return { buildTool: 'unknown' };
    }
    const buildTool = selectPrimaryTool([...allDetected]);
    const version = versions.get(buildTool);
    const configFile = fromConfig !== null && fromConfig.buildTool === buildTool ? fromConfig.configFile : undefined;
    // Bundler detection
    let bundler;
    if (buildTool === 'vite') {
        bundler = detectRolldown(pkg, version) ?? 'rollup';
    }
    else {
        bundler = detectBundlerForTool(buildTool);
    }
    // Browser target (only relevant for Vite)
    let browserTarget;
    if (buildTool === 'vite') {
        const viteConfigContent = configFile !== undefined ? await readFileSafe(join(projectPath, configFile)) : '';
        browserTarget = detectBrowserTarget(pkg, viteConfigContent);
    }
    return {
        buildTool,
        ...(version !== undefined && { version }),
        ...(bundler !== undefined && { bundler }),
        ...(browserTarget !== undefined && { browserTarget }),
        ...(configFile !== undefined && { configFile }),
    };
}
//# sourceMappingURL=build-tool-detector.js.map