"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 logger_1 = require("@pnpm/logger");
const isInnerLink = require("is-inner-link");
const isSubdir = require("is-subdir");
const fs = require("mz/fs");
const path = require("path");
const rimraf = require("rimraf-then");
const depsFromPackage_1 = require("../depsFromPackage");
const modulesController_1 = require("../fs/modulesController");
const readPkg_1 = require("../fs/readPkg");
const realNodeModulesDir_1 = require("../fs/realNodeModulesDir");
const extendInstallOptions_1 = require("./extendInstallOptions");
const install_1 = require("./install");
function unlinkPkgs(pkgNames, maybeOpts) {
    return __awaiter(this, void 0, void 0, function* () {
        const reporter = maybeOpts && maybeOpts.reporter;
        if (reporter) {
            logger_1.streamParser.on('data', reporter);
        }
        const opts = yield _extendOptions(maybeOpts);
        const modulesYaml = yield modulesController_1.read(opts.prefix);
        opts.store = modulesYaml && modulesYaml.store || opts.store;
        yield _unlinkPkgs(pkgNames, opts);
        if (reporter) {
            logger_1.streamParser.removeListener('data', reporter);
        }
    });
}
exports.unlinkPkgs = unlinkPkgs;
function _unlinkPkgs(pkgNames, opts) {
    return __awaiter(this, void 0, void 0, function* () {
        const modules = yield realNodeModulesDir_1.default(opts.prefix);
        const pkg = yield readPkg_1.fromDir(opts.prefix);
        const allDeps = depsFromPackage_1.default(pkg);
        const packagesToInstall = [];
        for (const pkgName of pkgNames) {
            if (!(yield isExternalLink(opts.store, modules, pkgName))) {
                logger_1.default.warn(`${pkgName} is not an external link`);
                continue;
            }
            yield rimraf(path.join(modules, pkgName));
            if (allDeps[pkgName]) {
                packagesToInstall.push(pkgName);
            }
        }
        if (!packagesToInstall.length)
            return;
        // TODO: install only those that were unlinked
        // but don't update their version specs in package.json
        yield install_1.install(opts);
    });
}
exports._unlinkPkgs = _unlinkPkgs;
function unlink(maybeOpts) {
    return __awaiter(this, void 0, void 0, function* () {
        const reporter = maybeOpts && maybeOpts.reporter;
        if (reporter) {
            logger_1.streamParser.on('data', reporter);
        }
        const opts = yield _extendOptions(maybeOpts);
        const modulesYaml = yield modulesController_1.read(opts.prefix);
        opts.store = modulesYaml && modulesYaml.store || opts.store;
        const modules = yield realNodeModulesDir_1.default(opts.prefix);
        const externalPackages = yield getExternalPackages(modules, opts.store);
        yield _unlinkPkgs(externalPackages, opts);
        if (reporter) {
            logger_1.streamParser.removeListener('data', reporter);
        }
    });
}
exports.unlink = unlink;
function getExternalPackages(modules, store, scope) {
    return __awaiter(this, void 0, void 0, function* () {
        let externalLinks = [];
        const parentDir = scope ? path.join(modules, scope) : modules;
        for (const dir of yield fs.readdir(parentDir)) {
            if (dir[0] === '.')
                continue;
            if (!scope && dir[0] === '@') {
                externalLinks = externalLinks.concat(yield getExternalPackages(modules, store, dir));
                continue;
            }
            const pkgName = scope ? `${scope}/${dir}` : dir;
            if (yield isExternalLink(store, modules, pkgName)) {
                externalLinks.push(pkgName);
            }
        }
        return externalLinks;
    });
}
function isExternalLink(store, modules, pkgName) {
    return __awaiter(this, void 0, void 0, function* () {
        const link = yield isInnerLink(modules, pkgName);
        // checking whether the link is pointing to the store is needed
        // because packages are linked to store when independent-leaves = true
        return !link.isInner && !isSubdir(store, link.target);
    });
}
function _extendOptions(maybeOpts) {
    return __awaiter(this, void 0, void 0, function* () {
        maybeOpts = maybeOpts || {};
        if (maybeOpts.depth === undefined)
            maybeOpts.depth = -1;
        return yield extendInstallOptions_1.default(maybeOpts);
    });
}
//# sourceMappingURL=unlink.js.map