"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 link_dir_1 = require('link-dir');
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');
const cmdShim = require('@zkochan/cmd-shim');
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 = yield safeRequireJson(path.join(target, 'package.json'));
        if (!pkg) {
            console.warn(`There's a directory in node_modules without package.json: ${target}`);
            return;
        }
        if (!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 = path.join(target, actualBin);
                const relTargetPath = normalizePath(path.join('..', pkg.name, actualBin));
                if (env_1.isWindows) {
                    if (!env_1.preserveSymlinks) {
                        return cmdShim(targetPath, externalBinPath, { preserveSymlinks: env_1.preserveSymlinks });
                    }
                    const proxyFilePath = path.join(binDir, `${bin}.proxy`);
                    yield fs.writeFile(proxyFilePath, `#!/usr/bin/env node\r\nrequire("${relTargetPath}")`, 'utf8');
                    return cmdShim(proxyFilePath, externalBinPath, { preserveSymlinks: env_1.preserveSymlinks });
                }
                if (!env_1.preserveSymlinks) {
                    yield makeExecutable(targetPath);
                    return link_dir_1.default(targetPath, externalBinPath);
                }
                return proxy(externalBinPath, relTargetPath);
            });
        }));
    });
}
exports.linkPkgBins = linkPkgBins;
function makeExecutable(filePath) {
    return fs.chmod(filePath, 0o755);
}
function proxy(proxyPath, relTargetPath) {
    return __awaiter(this, void 0, void 0, function* () {
        // NOTE: this will be used only on non-windows
        // Hence, the \n line endings should be used
        const proxyContent = '#!/bin/sh\n' +
            '":" //# comment; exec /usr/bin/env node --preserve-symlinks "$0" "$@"\n' +
            `require("${relTargetPath}")`;
        yield fs.writeFile(proxyPath, proxyContent, 'utf8');
        return makeExecutable(proxyPath);
    });
}
/**
 * Like `require()`, but returns `null` when it is not found
 */
function safeRequireJson(pkgJsonPath) {
    try {
        return requireJson_1.default(pkgJsonPath);
    }
    catch (err) {
        if (err.code !== 'ENOENT')
            throw err;
        return null;
    }
}
//# sourceMappingURL=linkBins.js.map