"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 dp = require("dependency-path");
const vacuumCB = require("fs-vacuum");
const path = require("path");
const R = require("ramda");
const promisify = require("util.promisify");
const getSaveType_1 = require("./getSaveType");
const loggers_1 = require("./loggers");
const removeTopDependency_1 = require("./removeTopDependency");
const vacuum = promisify(vacuumCB);
function removeOrphanPkgs(opts) {
    return __awaiter(this, void 0, void 0, function* () {
        const oldPkgs = R.toPairs(R.mergeAll(R.map((depType) => opts.oldShrinkwrap[depType], getSaveType_1.dependenciesTypes)));
        const newPkgs = R.toPairs(R.mergeAll(R.map((depType) => opts.newShrinkwrap[depType], getSaveType_1.dependenciesTypes)));
        const removedTopDeps = R.difference(oldPkgs, newPkgs);
        const rootModules = path.join(opts.prefix, 'node_modules');
        yield Promise.all(removedTopDeps.map((depName) => {
            return removeTopDependency_1.default({
                dev: Boolean(opts.oldShrinkwrap.devDependencies && opts.oldShrinkwrap.devDependencies[depName[0]]),
                name: depName[0],
                optional: Boolean(opts.oldShrinkwrap.optionalDependencies && opts.oldShrinkwrap.optionalDependencies[depName[0]]),
            }, {
                bin: opts.bin,
                dryRun: opts.dryRun,
                modules: rootModules,
                prefix: opts.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));
        loggers_1.statsLogger.debug({
            prefix: opts.prefix,
            removed: orphanPkgIds.size,
        });
        if (!opts.dryRun) {
            if (orphanDepPaths.length) {
                if (opts.shamefullyFlatten && opts.oldShrinkwrap.packages) {
                    yield Promise.all(orphanDepPaths.map((orphanDepPath) => __awaiter(this, void 0, void 0, function* () {
                        if (opts.hoistedAliases[orphanDepPath]) {
                            yield Promise.all(opts.hoistedAliases[orphanDepPath].map((alias) => __awaiter(this, void 0, void 0, function* () {
                                yield removeTopDependency_1.default({
                                    dev: false,
                                    name: alias,
                                    optional: false,
                                }, {
                                    bin: opts.bin,
                                    modules: rootModules,
                                    muteLogs: true,
                                    prefix: opts.prefix,
                                });
                            })));
                        }
                        delete opts.hoistedAliases[orphanDepPath];
                    })));
                }
                yield Promise.all(orphanDepPaths.map((orphanDepPath) => __awaiter(this, void 0, void 0, function* () {
                    const pathToRemove = path.join(rootModules, `.${orphanDepPath}`, 'node_modules');
                    loggers_1.removalLogger.debug(pathToRemove);
                    yield vacuum(pathToRemove, {
                        base: rootModules,
                        purge: true,
                    });
                })));
            }
            const addedDepPaths = R.difference(newDepPaths, oldDepPaths);
            const addedPkgIds = new Set(R.props(addedDepPaths, newPkgIdsByDepPaths));
            yield opts.storeController.updateConnections(opts.prefix, {
                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=removeOrphanPkgs.js.map