"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const package_bins_1 = require("@pnpm/package-bins");
const read_importer_manifest_1 = require("@pnpm/read-importer-manifest");
const read_package_json_1 = require("@pnpm/read-package-json");
const cmdShim = require("@zkochan/cmd-shim");
const isSubdir = require("is-subdir");
const isWindows = require("is-windows");
const makeDir = require("make-dir");
const Module = require("module");
const fs = require("mz/fs");
const normalizePath = require("normalize-path");
const path = require("path");
const R = require("ramda");
const getPkgDirs_1 = require("./getPkgDirs");
const IS_WINDOWS = isWindows();
const EXECUTABLE_SHEBANG_SUPPORTED = !IS_WINDOWS;
const POWER_SHELL_IS_SUPPORTED = IS_WINDOWS;
exports.default = async (modules, binPath, opts) => {
    const pkgDirs = await getPkgDirs_1.default(modules);
    const pkgBinOpts = Object.assign({ allowExoticManifests: false }, opts);
    const allCmds = R.unnest((await Promise.all(pkgDirs
        .filter((dir) => !isSubdir(dir, binPath)) // Don't link own bins
        .map(normalizePath)
        .map((target) => getPackageBins(target, pkgBinOpts))))
        .filter((cmds) => cmds.length));
    return linkBins(allCmds, binPath, opts);
};
async function linkBinsOfPackages(pkgs, binsTarget, opts) {
    if (!pkgs.length)
        return;
    const allCmds = R.unnest((await Promise.all(pkgs
        .map((pkg) => getPackageBinsFromPackageJson(pkg.manifest, pkg.location))))
        .filter((cmds) => cmds.length));
    return linkBins(allCmds, binsTarget, opts);
}
exports.linkBinsOfPackages = linkBinsOfPackages;
async function linkBins(allCmds, binPath, opts) {
    if (!allCmds.length)
        return;
    await makeDir(binPath);
    const [cmdsWithOwnName, cmdsWithOtherNames] = R.partition((cmd) => cmd.ownName, allCmds);
    await Promise.all(cmdsWithOwnName.map((cmd) => linkBin(cmd, binPath)));
    const usedNames = R.fromPairs(cmdsWithOwnName.map((cmd) => [cmd.name, cmd.name]));
    await Promise.all(cmdsWithOtherNames.map((cmd) => {
        if (usedNames[cmd.name]) {
            opts.warn(`Cannot link bin "${cmd.name}" of "${cmd.pkgName}" to "${binPath}". A package called "${usedNames[cmd.name]}" already has its bin linked.`);
            return;
        }
        usedNames[cmd.name] = cmd.pkgName;
        return linkBin(cmd, binPath);
    }));
}
async function getPackageBins(target, opts) {
    const pkg = opts.allowExoticManifests ? await safeReadImporterManifestOnly(target) : await safeReadPkg(target);
    if (!pkg) {
        // There's a directory in node_modules without package.json: ${target}.
        // This used to be a warning but it didn't really cause any issues.
        return [];
    }
    return getPackageBinsFromPackageJson(pkg, target);
}
async function getPackageBinsFromPackageJson(pkgJson, pkgPath) {
    const cmds = await package_bins_1.default(pkgJson, pkgPath);
    return cmds.map((cmd) => (Object.assign({}, cmd, { ownName: cmd.name === pkgJson.name, pkgName: pkgJson.name })));
}
async function linkBin(cmd, binPath) {
    const externalBinPath = path.join(binPath, cmd.name);
    if (EXECUTABLE_SHEBANG_SUPPORTED) {
        await fs.chmod(cmd.path, 0o755);
    }
    const nodePath = await getBinNodePaths(cmd.path);
    return cmdShim(cmd.path, externalBinPath, {
        createPwshFile: POWER_SHELL_IS_SUPPORTED,
        nodePath,
    });
}
async function getBinNodePaths(target) {
    const targetRealPath = await fs.realpath(target);
    return R.union(Module._nodeModulePaths(targetRealPath), Module._nodeModulePaths(target));
}
async function safeReadPkg(pkgPath) {
    try {
        return await read_package_json_1.fromDir(pkgPath);
    }
    catch (err) {
        if (err.code === 'ENOENT') {
            return null;
        }
        throw err;
    }
}
async function safeReadImporterManifestOnly(importerDir) {
    try {
        return await read_importer_manifest_1.readImporterManifestOnly(importerDir);
    }
    catch (err) {
        if (err.code === 'ERR_PNPM_NO_IMPORTER_MANIFEST_FOUND') {
            return null;
        }
        throw err;
    }
}
//# sourceMappingURL=index.js.map