diff --git a/lib/install.js b/lib/install.js index 98a55c54df..358dd1ce5b 100644 --- a/lib/install.js +++ b/lib/install.js @@ -1,15 +1,14 @@ var Promise = require('./promise') var debug = require('debug')('pnpm:install') var npa = require('npm-package-arg') -var config = require('./config') var join = require('path').join var mkdirp = require('./mkdirp') var fetch = require('./fetch') var resolve = require('./resolve') var getUuid = require('node-uuid') var symlink = require('./force_symlink') +var linkBins = require('./install/link_bins') var fs = require('mz/fs') -var basename = require('path').basename /* * Installs a package. @@ -49,8 +48,9 @@ module.exports = function install (ctx, pkg, modules, options) { var log = ctx.log(pkgSpec) // function var tmp = join(ctx.tmp, getUuid()) // 'node_modules/.tmp/000-11...' - return fs.stat(join(modules, pkgSpec.name)) + return fs.stat(join(modules, pkgSpec.name, 'package.json')) .catch((err) => { + if (err.code !== 'ENOENT') throw err return resolve(pkgSpec) .then(set) .then(_ => log('resolved', pkgData)) @@ -63,7 +63,6 @@ module.exports = function install (ctx, pkg, modules, options) { .then(_ => log('downloading')) .then(_ => mkdirp(store)) .then(_ => mkdirp(tmp)) - // TODO: check for existence of target .then(_ => fetch(tmp, dist.tarball, dist.shasum, log)) .then(_ => linkBins(modules, tmp, fullname)) .then(_ => log('dependencies')) @@ -76,7 +75,7 @@ module.exports = function install (ctx, pkg, modules, options) { .then(_ => unlock(ctx, target)) }) .then(_ => mkdirp(modules)) - .then(_ => doSymlink()) + .then(_ => symlinkToModules(fullname, name, modules, depth)) }) .then(_ => log('done')) .catch((err) => { @@ -92,17 +91,6 @@ module.exports = function install (ctx, pkg, modules, options) { name = res.name dist = res.dist } - - // perform the final symlinking of ./.store/x@1.0.0 => ./x. - function doSymlink () { - if (depth === 0) { - return symlink(join('.store', fullname), join(modules, name)) - } else { - // we'll go back to ..../.store here so the same module will act the same - // on node_modules/.tmp - return symlink(join('..', '..', '..', '.store', fullname), join(modules, name)) - } - } } function lock (ctx, path) { @@ -119,7 +107,7 @@ function isLocked (ctx, path) { } /* - * symlink a package into its own node_modules. this way, babel-runtime@5 can + * Symlink a package into its own node_modules. this way, babel-runtime@5 can * require('babel-runtime') within itself. */ @@ -135,45 +123,20 @@ function symlinkSelf (target, name, depth) { } /* - * Links executables into `node_modules/.bin` + * Perform the final symlinking of ./.store/x@1.0.0 -> ./x. * - * module = 'project/node_modules' - * target = 'project/node_modules/.tmp/a1b3c56...' - * linkBins(module, target, 'rimraf@2.5.1') - * - * // node_modules/.bin/rimraf -> ../.store/rimraf@2.5.1/cmd.js + * fullname = 'lodash@4.0.0' + * name = 'lodash' + * modules = './node_modules' + * symlinkToModules(fullname, name, modules, 0) */ -function linkBins (module, target, fullname) { - var pkg = tryRequire(join(target, 'package.json')) - if (!pkg || !pkg.bin) return - - var bins = binify(pkg) - - return Object.keys(bins).map((bin) => { - var actualBin = bins[bin] - - return Promise.resolve() - .then(_ => fs.chmod(join(target, actualBin), 0o755)) - .then(_ => mkdirp(join(module, '.bin'))) - .then(_ => symlink( - join('..', '.store', fullname, actualBin), - join(module, '.bin', bin))) - }) -} - -function binify (pkg) { - if (typeof pkg.bin === 'string') { - var obj = {} - obj[pkg.name] = pkg.bin - return obj +function symlinkToModules (fullname, name, modules, depth) { + if (depth === 0) { + return symlink(join('.store', fullname), join(modules, name)) + } else { + // we'll go back to ..../.store here so the same module will act the same + // on node_modules/.tmp + return symlink(join('..', '..', '..', '.store', fullname), join(modules, name)) } - - return pkg.bin -} - -function tryRequire (path) { - try { - return require(path) - } catch (e) { } } diff --git a/lib/install/link_bins.js b/lib/install/link_bins.js new file mode 100644 index 0000000000..e1fdb895e4 --- /dev/null +++ b/lib/install/link_bins.js @@ -0,0 +1,65 @@ +var join = require('path').join +var symlink = require('../force_symlink') +var fs = require('mz/fs') +var mkdirp = require('../mkdirp') + +/* + * Links executables into `node_modules/.bin`. + * + * - `modules` (String) - the node_modlules path + * - `target` (String) - where the module is now; read package.json from here + * - `fullname` (String) - fullname of the module (`rimraf@2.5.1`) + * + * module = 'project/node_modules' + * target = 'project/node_modules/.tmp/a1b3c56...' + * linkBins(module, target, 'rimraf@2.5.1') + * + * // node_modules/.bin/rimraf -> ../.store/rimraf@2.5.1/cmd.js + */ + +module.exports = function linkBins (modules, target, fullname) { + var pkg = tryRequire(join(target, 'package.json')) + if (!pkg || !pkg.bin) return + + var bins = binify(pkg) + + return Object.keys(bins).map((bin) => { + var actualBin = bins[bin] + + return Promise.resolve() + .then(_ => fs.chmod(join(target, actualBin), 0o755)) + .then(_ => mkdirp(join(modules, '.bin'))) + .then(_ => symlink( + join('..', '.store', fullname, actualBin), + join(modules, '.bin', bin))) + }) +} + +/* + * Like `require()`, but returns `undefined` when it fails + */ + +function tryRequire (path) { + try { return require(path) } catch (e) { } +} + +/* + * Returns bins for a package in a standard object format. This normalizes + * between npm's string and object formats. + * + * binify({ name: 'rimraf', bin: 'cmd.js' }) + * => { rimraf: 'cmd.js' } + * + * binify({ name: 'rmrf', bin: { rmrf: 'cmd.js' } }) + * => { rmrf: 'cmd.js' } + */ + +function binify (pkg) { + if (typeof pkg.bin === 'string') { + var obj = {} + obj[pkg.name] = pkg.bin + return obj + } + + return pkg.bin +}