"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());
    });
};
const path = require('path');
const normalizePath = require('normalize-path');
const common_tags_1 = require('common-tags');
const relSymlink_1 = require('../fs/relSymlink');
const fs = require('mz/fs');
const mkdirp_1 = require('../fs/mkdirp');
const requireJson_1 = require('../fs/requireJson');
const getPkgDirs_1 = require('../fs/getPkgDirs');
const binify_1 = require('../binify');
const env_1 = require('../env');
function linkAllBins(modules) {
    return __awaiter(this, void 0, void 0, function* () {
        const pkgDirs = yield getPkgDirs_1.default(modules);
        return Promise.all(pkgDirs.map((pkgDir) => linkPkgBins(modules, pkgDir)));
    });
}
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = linkAllBins;
/**
 * Links executable into `node_modules/.bin`.
 *
 * @param {String} modules - the node_modules path
 * @param {String} target - where the module is now; read package.json from here
 *
 * @example
 *     module = 'project/node_modules'
 *     target = 'project/node_modules/.store/rimraf@2.5.1'
 *     linkPkgBins(module, target)
 *
 *     // node_modules/.bin/rimraf -> ../.store/rimraf@2.5.1/cmd.js
 */
function linkPkgBins(modules, target) {
    return __awaiter(this, void 0, void 0, function* () {
        const pkg = tryRequire(path.join(target, 'package.json'));
        if (!pkg || !pkg.bin)
            return;
        const bins = binify_1.default(pkg);
        const binDir = path.join(modules, '.bin');
        yield mkdirp_1.default(binDir);
        yield Promise.all(Object.keys(bins).map(function (bin) {
            return __awaiter(this, void 0, void 0, function* () {
                const actualBin = bins[bin];
                const externalBinPath = path.join(binDir, bin);
                const targetPath = normalizePath(path.join(pkg.name, actualBin));
                if (env_1.isWindows) {
                    if (!env_1.preserveSymlinks) {
                        return cmdShim(externalBinPath, '../' + targetPath);
                    }
                    const proxyFilePath = path.join(binDir, bin + '.proxy');
                    fs.writeFileSync(proxyFilePath, 'require("../' + targetPath + '")', 'utf8');
                    return cmdShim(externalBinPath, path.relative(binDir, proxyFilePath));
                }
                if (!env_1.preserveSymlinks) {
                    yield makeExecutable(path.join(target, actualBin));
                    return relSymlink_1.default(path.join(target, actualBin), externalBinPath);
                }
                return proxy(externalBinPath, targetPath);
            });
        }));
    });
}
exports.linkPkgBins = linkPkgBins;
function makeExecutable(filePath) {
    return fs.chmod(filePath, 0o755);
}
function proxy(proxyPath, targetPath) {
    const proxyContent = common_tags_1.stripIndent `
    #!/bin/sh
    ":" //# comment; exec /usr/bin/env node --preserve-symlinks "$0" "$@"
    require('../${targetPath}')`;
    fs.writeFileSync(proxyPath, proxyContent, 'utf8');
    return makeExecutable(proxyPath);
}
function cmdShim(proxyPath, targetPath) {
    const nodeOptions = env_1.preserveSymlinks ? '--preserve-symlinks' : '';
    const cmdContent = common_tags_1.stripIndent `
    @IF EXIST "%~dp0\\node.exe" (
      "%~dp0\\node.exe ${nodeOptions}"  "%~dp0/${targetPath}" %*
    ) ELSE (
      @SETLOCAL
      @SET PATHEXT=%PATHEXT:;.JS;=;%
      node ${nodeOptions}  "%~dp0/${targetPath}" %*
    )`;
    fs.writeFileSync(proxyPath + '.cmd', cmdContent, 'utf8');
    const shContent = common_tags_1.stripIndent `
    #!/bin/sh
    basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")

    case \`uname\` in
        *CYGWIN*) basedir=\`cygpath -w "$basedir"\`;;
    esac

    if [ -x "$basedir/node" ]; then
      "$basedir/node ${nodeOptions}"  "$basedir/${targetPath}" "$@"
      ret=$?
    else
      node ${nodeOptions}  "$basedir/${targetPath}" "$@"
      ret=$?
    fi
    exit $ret
    `;
    fs.writeFileSync(proxyPath, shContent, 'utf8');
    return Promise.all([
        makeExecutable(proxyPath + '.cmd'),
        makeExecutable(proxyPath)
    ]);
}
/**
 * Like `require()`, but returns `undefined` when it fails
 */
function tryRequire(path) {
    try {
        return requireJson_1.default(path);
    }
    catch (e) {
        return null;
    }
}
//# sourceMappingURL=linkBins.js.map