"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PnpmNodeModulesCollector = void 0;
const builder_util_1 = require("builder-util");
const path = require("path");
const nodeModulesCollector_1 = require("./nodeModulesCollector");
const packageManager_1 = require("./packageManager");
class PnpmNodeModulesCollector extends nodeModulesCollector_1.NodeModulesCollector {
    constructor() {
        super(...arguments);
        this.installOptions = {
            manager: packageManager_1.PM.PNPM,
            lockfile: "pnpm-lock.yaml",
        };
    }
    getArgs() {
        return ["list", "--prod", "--json", "--depth", "Infinity", "--long"];
    }
    async resolveActualPath(depTree) {
        // If using hoisted mode, try to find the package at the hoisted location first
        if (await this.isHoisted.value) {
            const packageName = depTree.name || depTree.from;
            if (packageName) {
                const hoistedPath = path.join(this.rootDir, "node_modules", packageName);
                if (await this.existsMemoized(hoistedPath)) {
                    return hoistedPath;
                }
            }
        }
        // Fall back to the reported path (which might be the .pnpm store path)
        return depTree.path;
    }
    async getProductionDependencies(depTree) {
        const packageName = depTree.name || depTree.from;
        if ((0, builder_util_1.isEmptyOrSpaces)(packageName)) {
            builder_util_1.log.error(depTree, `Cannot determine production dependencies for package with empty name`);
            throw new Error(`Cannot compute production dependencies for package with empty name: ${packageName}`);
        }
        const pkgJsonPath = await this.resolvePackage(packageName, depTree.path);
        if (pkgJsonPath == null) {
            builder_util_1.log.warn({ packageName, path: depTree.path, version: depTree.version }, `Cannot find package.json for dependency`);
            return { path: depTree.path, prodDeps: {}, optionalDependencies: {} };
        }
        let packageJson;
        try {
            packageJson = await this.readJsonMemoized(pkgJsonPath.entry);
        }
        catch (error) {
            builder_util_1.log.warn(pkgJsonPath, `Failed to read package.json: ${error.message}`);
            return { path: pkgJsonPath.packageDir, prodDeps: {}, optionalDependencies: {} };
        }
        return {
            path: pkgJsonPath.packageDir,
            prodDeps: { ...packageJson.dependencies, ...packageJson.optionalDependencies },
            optionalDependencies: { ...packageJson.optionalDependencies },
        };
    }
    async extractProductionDependencyGraph(tree, dependencyId) {
        var _a;
        if (this.productionGraph[dependencyId]) {
            return;
        }
        const packageName = tree.name || tree.from;
        const json = packageName === dependencyId ? null : await this.getProductionDependencies(tree);
        const prodDependencies = (_a = json === null || json === void 0 ? void 0 : json.prodDeps) !== null && _a !== void 0 ? _a : { ...(tree.dependencies || {}), ...(tree.optionalDependencies || {}) };
        if (prodDependencies == null) {
            this.productionGraph[dependencyId] = { dependencies: [] };
            return;
        }
        const deps = { ...(tree.dependencies || {}), ...(tree.optionalDependencies || {}) };
        this.productionGraph[dependencyId] = { dependencies: [] };
        const depPromises = Object.entries(deps).map(async ([packageName, dependency]) => {
            var _a;
            // First check if it's in production dependencies
            if (!prodDependencies[packageName]) {
                return undefined;
            }
            // Then check if optional dependency path exists (using actual resolved path)
            if ((_a = json === null || json === void 0 ? void 0 : json.optionalDependencies) === null || _a === void 0 ? void 0 : _a[packageName]) {
                const actualPath = await this.resolveActualPath(dependency);
                if (!(await this.existsMemoized(actualPath))) {
                    builder_util_1.log.debug(null, `Optional dependency ${packageName}@${dependency.version} path doesn't exist: ${actualPath}`);
                    return undefined;
                }
            }
            const childDependencyId = this.packageVersionString(dependency);
            await this.extractProductionDependencyGraph(dependency, childDependencyId);
            return childDependencyId;
        });
        const dependencies = (await Promise.all(depPromises)).filter((id) => id !== undefined);
        this.productionGraph[dependencyId] = { dependencies };
    }
    async collectAllDependencies(tree) {
        // Collect regular dependencies
        for (const [key, value] of Object.entries(tree.dependencies || {})) {
            const json = await this.getProductionDependencies(value);
            this.allDependencies.set(`${key}@${value.version}`, { ...value, path: json.path });
            await this.collectAllDependencies(value);
        }
        // Collect optional dependencies if they exist
        for (const [key, value] of Object.entries(tree.optionalDependencies || {})) {
            const json = await this.getProductionDependencies(value);
            this.allDependencies.set(`${key}@${value.version}`, { ...value, path: json.path });
            await this.collectAllDependencies(value);
        }
    }
    packageVersionString(pkg) {
        // we use 'from' field because 'name' may be different in case of aliases
        return `${pkg.from}@${pkg.version}`;
    }
    async parseDependenciesTree(jsonBlob) {
        const dependencyTree = JSON.parse(jsonBlob);
        // pnpm returns an array of dependency trees
        return Promise.resolve(dependencyTree[0]);
    }
}
exports.PnpmNodeModulesCollector = PnpmNodeModulesCollector;
//# sourceMappingURL=pnpmNodeModulesCollector.js.map