"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const logger_1 = require("@pnpm/logger");
const dependency_path_1 = require("dependency-path");
const R = require("ramda");
const constants_1 = require("./constants");
function prune(shr, pkg) {
    const packages = {};
    let allDeps;
    if (pkg) {
        const optionalDependencies = R.keys(pkg.optionalDependencies);
        const dependencies = R.difference(R.keys(pkg.dependencies), optionalDependencies);
        const devDependencies = R.difference(R.difference(R.keys(pkg.devDependencies), optionalDependencies), dependencies);
        allDeps = R.reduce(R.union, [], [optionalDependencies, devDependencies, dependencies]);
    }
    else {
        allDeps = R.keys(shr.specifiers);
    }
    const specifiers = {};
    const shrDependencies = {};
    const shrOptionalDependencies = {};
    const shrDevDependencies = {};
    const nonOptional = new Set();
    const notProdOnly = new Set();
    R.keys(shr.specifiers).forEach((depName) => {
        if (allDeps.indexOf(depName) === -1)
            return;
        specifiers[depName] = shr.specifiers[depName];
        if (shr.dependencies && shr.dependencies[depName]) {
            shrDependencies[depName] = shr.dependencies[depName];
        }
        else if (shr.optionalDependencies && shr.optionalDependencies[depName]) {
            shrOptionalDependencies[depName] = shr.optionalDependencies[depName];
        }
        else if (shr.devDependencies && shr.devDependencies[depName]) {
            shrDevDependencies[depName] = shr.devDependencies[depName];
        }
    });
    if (shr.dependencies) {
        for (const dep of R.keys(shr.dependencies)) {
            if (!shrDependencies[dep] && shr.dependencies[dep].startsWith('link:')) {
                shrDependencies[dep] = shr.dependencies[dep];
            }
        }
    }
    const devDepRelativePaths = R.keys(shrDevDependencies)
        .filter((pkgName) => !shrDevDependencies[pkgName].startsWith('link:'))
        .map((pkgName) => dependency_path_1.refToRelative(shrDevDependencies[pkgName], pkgName));
    copyDependencySubTree(packages, devDepRelativePaths, shr, new Set(), { registry: shr.registry, nonOptional, notProdOnly, dev: true });
    const depRelativePaths = R.keys(shrDependencies)
        .filter((pkgName) => !shrDependencies[pkgName].startsWith('link:'))
        .map((pkgName) => dependency_path_1.refToRelative(shrDependencies[pkgName], pkgName));
    copyDependencySubTree(packages, depRelativePaths, shr, new Set(), {
        nonOptional,
        notProdOnly,
        registry: shr.registry,
    });
    if (shrOptionalDependencies) {
        const optionalDepRelativePaths = R.keys(shrOptionalDependencies)
            .filter((pkgName) => !shrOptionalDependencies[pkgName].startsWith('link:'))
            .map((pkgName) => dependency_path_1.refToRelative(shrOptionalDependencies[pkgName], pkgName));
        copyDependencySubTree(packages, optionalDepRelativePaths, shr, new Set(), { registry: shr.registry, nonOptional, notProdOnly, optional: true });
    }
    copyDependencySubTree(packages, devDepRelativePaths, shr, new Set(), {
        dev: true,
        nonOptional,
        notProdOnly,
        registry: shr.registry,
        walkOptionals: true,
    });
    copyDependencySubTree(packages, depRelativePaths, shr, new Set(), {
        nonOptional,
        notProdOnly,
        registry: shr.registry,
        walkOptionals: true,
    });
    const result = {
        registry: shr.registry,
        shrinkwrapVersion: constants_1.SHRINKWRAP_VERSION,
        specifiers,
    };
    if (typeof shr.shrinkwrapMinorVersion === 'number') {
        result.shrinkwrapMinorVersion = shr.shrinkwrapMinorVersion;
    }
    if (!R.isEmpty(packages)) {
        result.packages = packages;
    }
    if (!R.isEmpty(shrDependencies)) {
        result.dependencies = shrDependencies;
    }
    if (!R.isEmpty(shrOptionalDependencies)) {
        result.optionalDependencies = shrOptionalDependencies;
    }
    if (!R.isEmpty(shrDevDependencies)) {
        result.devDependencies = shrDevDependencies;
    }
    return result;
}
exports.default = prune;
function copyDependencySubTree(resolvedPackages, depRelativePaths, shr, walked, opts) {
    for (const depRalativePath of depRelativePaths) {
        if (walked.has(depRalativePath))
            continue;
        walked.add(depRalativePath);
        if (!shr.packages || !shr.packages[depRalativePath]) {
            // local dependencies don't need to be resolved in shrinkwrap.yaml
            // except local tarball dependencies
            if (depRalativePath.startsWith('link:') || depRalativePath.startsWith('file:') && !depRalativePath.endsWith('.tar.gz'))
                continue;
            logger_1.default.warn(`Cannot find resolution of ${depRalativePath} in shrinkwrap file`);
            continue;
        }
        const depShr = shr.packages[depRalativePath];
        resolvedPackages[depRalativePath] = depShr;
        if (opts.optional && !opts.nonOptional.has(depRalativePath)) {
            depShr.optional = true;
        }
        else {
            opts.nonOptional.add(depRalativePath);
            delete depShr.optional;
        }
        if (opts.dev) {
            opts.notProdOnly.add(depRalativePath);
            depShr.dev = true;
        }
        else if (depShr.dev === true) { // keeping if dev is explicitly false
            delete depShr.dev;
        }
        else if (depShr.dev === undefined && !opts.notProdOnly.has(depRalativePath)) {
            depShr.dev = false;
        }
        const newDependencies = R.keys(depShr.dependencies)
            .map((pkgName) => dependency_path_1.refToRelative((depShr.dependencies && depShr.dependencies[pkgName]), pkgName));
        copyDependencySubTree(resolvedPackages, newDependencies, shr, walked, opts);
        if (!opts.walkOptionals)
            continue;
        const newOptionalDependencies = R.keys(depShr.optionalDependencies)
            .map((pkgName) => dependency_path_1.refToRelative((depShr.optionalDependencies && depShr.optionalDependencies[pkgName]), pkgName));
        copyDependencySubTree(resolvedPackages, newOptionalDependencies, shr, walked, Object.assign({}, opts, { optional: true }));
    }
}
//# sourceMappingURL=prune.js.map