"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const core_loggers_1 = require("@pnpm/core-loggers");
const logger_1 = require("@pnpm/logger");
const types_1 = require("@pnpm/types");
const dp = require("dependency-path");
const vacuumCB = require("fs-vacuum");
const path = require("path");
const R = require("ramda");
const promisify = require("util.promisify");
const removeDirectDependency_1 = require("./removeDirectDependency");
const vacuum = promisify(vacuumCB);
function removeOrphanPkgs(opts) {
    return __awaiter(this, void 0, void 0, function* () {
        yield Promise.all(opts.importers.map((importer) => {
            const oldImporterShr = opts.oldShrinkwrap.importers[importer.id] || {};
            const oldPkgs = R.toPairs(R.mergeAll(R.map((depType) => oldImporterShr[depType], types_1.DEPENDENCIES_FIELDS)));
            const newPkgs = R.toPairs(R.mergeAll(R.map((depType) => opts.newShrinkwrap.importers[importer.id][depType], types_1.DEPENDENCIES_FIELDS)));
            const removedTopDeps = R.difference(oldPkgs, newPkgs);
            const { bin, modulesDir, prefix } = importer;
            return Promise.all(removedTopDeps.map((depName) => {
                return removeDirectDependency_1.default({
                    dev: Boolean(oldImporterShr.devDependencies && oldImporterShr.devDependencies[depName[0]]),
                    name: depName[0],
                    optional: Boolean(oldImporterShr.optionalDependencies && oldImporterShr.optionalDependencies[depName[0]]),
                }, {
                    bin,
                    dryRun: opts.dryRun,
                    modulesDir,
                    prefix,
                });
            }));
        }));
        const oldPkgIdsByDepPaths = getPkgsDepPaths(opts.oldShrinkwrap.registry, opts.oldShrinkwrap.packages || {});
        const newPkgIdsByDepPaths = getPkgsDepPaths(opts.newShrinkwrap.registry, opts.newShrinkwrap.packages || {});
        const oldDepPaths = Object.keys(oldPkgIdsByDepPaths);
        const newDepPaths = Object.keys(newPkgIdsByDepPaths);
        const orphanDepPaths = R.difference(oldDepPaths, newDepPaths);
        const orphanPkgIds = new Set(R.props(orphanDepPaths, oldPkgIdsByDepPaths));
        core_loggers_1.statsLogger.debug({
            prefix: opts.shrinkwrapDirectory,
            removed: orphanPkgIds.size,
        });
        if (!opts.dryRun) {
            if (orphanDepPaths.length) {
                if (opts.oldShrinkwrap.packages) {
                    yield Promise.all(opts.importers.filter((importer) => importer.shamefullyFlatten).map((importer) => {
                        const { bin, hoistedAliases, modulesDir, prefix } = importer;
                        return Promise.all(orphanDepPaths.map((orphanDepPath) => __awaiter(this, void 0, void 0, function* () {
                            if (hoistedAliases[orphanDepPath]) {
                                yield Promise.all(hoistedAliases[orphanDepPath].map((alias) => {
                                    return removeDirectDependency_1.default({
                                        dev: false,
                                        name: alias,
                                        optional: false,
                                    }, {
                                        bin,
                                        modulesDir,
                                        muteLogs: true,
                                        prefix,
                                    });
                                }));
                            }
                            delete hoistedAliases[orphanDepPath];
                        })));
                    }));
                }
                yield Promise.all(orphanDepPaths.map((orphanDepPath) => __awaiter(this, void 0, void 0, function* () {
                    const pathToRemove = path.join(opts.virtualStoreDir, `.${orphanDepPath}`, 'node_modules');
                    core_loggers_1.removalLogger.debug(pathToRemove);
                    try {
                        yield vacuum(pathToRemove, {
                            base: opts.virtualStoreDir,
                            purge: true,
                        });
                    }
                    catch (err) {
                        logger_1.default.warn({
                            error: err,
                            message: `Failed to remove "${pathToRemove}"`,
                            prefix: opts.shrinkwrapDirectory,
                        });
                    }
                })));
            }
            const addedDepPaths = R.difference(newDepPaths, oldDepPaths);
            const addedPkgIds = new Set(R.props(addedDepPaths, newPkgIdsByDepPaths));
            yield opts.storeController.updateConnections(path.dirname(opts.virtualStoreDir), {
                addDependencies: Array.from(addedPkgIds),
                prune: opts.pruneStore || false,
                removeDependencies: Array.from(orphanPkgIds),
            });
            yield opts.storeController.saveState();
        }
        return new Set(orphanDepPaths);
    });
}
exports.default = removeOrphanPkgs;
function getPkgsDepPaths(registry, packages) {
    const pkgIdsByDepPath = {};
    for (const relDepPath of Object.keys(packages)) {
        const depPath = dp.resolve(registry, relDepPath);
        pkgIdsByDepPath[depPath] = packages[relDepPath].id
            ? packages[relDepPath].id
            : depPath;
    }
    return pkgIdsByDepPath;
}
//# sourceMappingURL=prune.js.map