2011-11-21 18:48:45 +01:00
|
|
|
// npm build command
|
|
|
|
|
|
|
|
// everything about the installation after the creation of
|
|
|
|
// the .npm/{name}/{version}/package folder.
|
|
|
|
// linking the modules into the npm.root,
|
|
|
|
// resolving dependencies, etc.
|
|
|
|
|
|
|
|
// This runs AFTER install or link are completed.
|
|
|
|
|
2015-10-10 08:13:57 +02:00
|
|
|
var npm = require('./npm.js')
|
|
|
|
var log = require('npmlog')
|
|
|
|
var chain = require('slide').chain
|
|
|
|
var fs = require('graceful-fs')
|
|
|
|
var path = require('path')
|
|
|
|
var lifecycle = require('./utils/lifecycle.js')
|
|
|
|
var readJson = require('read-package-json')
|
|
|
|
var link = require('./utils/link.js')
|
|
|
|
var linkIfExists = link.ifExists
|
|
|
|
var cmdShim = require('cmd-shim')
|
|
|
|
var cmdShimIfExists = cmdShim.ifExists
|
2017-10-27 04:35:25 +02:00
|
|
|
var isHashbangFile = require('./utils/is-hashbang-file.js')
|
|
|
|
var dos2Unix = require('./utils/convert-line-endings.js').dos2Unix
|
2015-10-10 08:13:57 +02:00
|
|
|
var asyncMap = require('slide').asyncMap
|
|
|
|
var ini = require('ini')
|
|
|
|
var writeFile = require('write-file-atomic')
|
2015-10-30 00:50:12 +01:00
|
|
|
var packageId = require('./utils/package-id.js')
|
2016-06-24 22:43:51 +02:00
|
|
|
var output = require('./utils/output.js')
|
2011-11-21 18:48:45 +01:00
|
|
|
|
|
|
|
module.exports = build
|
2015-10-10 08:13:57 +02:00
|
|
|
build.usage = 'npm build [<folder>]'
|
2011-11-21 18:48:45 +01:00
|
|
|
|
|
|
|
build._didBuild = {}
|
|
|
|
build._noLC = {}
|
|
|
|
function build (args, global, didPre, didRB, cb) {
|
2015-10-10 08:13:57 +02:00
|
|
|
if (typeof cb !== 'function') {
|
|
|
|
cb = didRB
|
|
|
|
didRB = false
|
2011-11-21 18:48:45 +01:00
|
|
|
}
|
2015-10-10 08:13:57 +02:00
|
|
|
if (typeof cb !== 'function') {
|
|
|
|
cb = didPre
|
|
|
|
didPre = false
|
|
|
|
}
|
|
|
|
if (typeof cb !== 'function') {
|
|
|
|
cb = global
|
|
|
|
global = npm.config.get('global')
|
|
|
|
}
|
|
|
|
|
2017-07-14 19:52:48 +02:00
|
|
|
if (!args.length) {
|
|
|
|
readJson(path.resolve(npm.localPrefix, 'package.json'), function (er, pkg) {
|
|
|
|
if (!args.length && pkg && pkg.scripts && pkg.scripts.build) {
|
|
|
|
log.warn('build', '`npm build` called with no arguments. Did you mean to `npm run-script build`?')
|
|
|
|
}
|
|
|
|
cb()
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
// it'd be nice to asyncMap these, but actually, doing them
|
|
|
|
// in parallel generally munges up the output from node-waf
|
|
|
|
var builder = build_(global, didPre, didRB)
|
|
|
|
chain(args.map(function (arg) {
|
|
|
|
return function (cb) {
|
|
|
|
builder(arg, cb)
|
|
|
|
}
|
|
|
|
}), cb)
|
|
|
|
}
|
2011-11-21 18:48:45 +01:00
|
|
|
}
|
|
|
|
|
2015-10-10 08:13:57 +02:00
|
|
|
function build_ (global, didPre, didRB) {
|
|
|
|
return function (folder, cb) {
|
|
|
|
folder = path.resolve(folder)
|
|
|
|
if (build._didBuild[folder]) log.info('build', 'already built', folder)
|
|
|
|
build._didBuild[folder] = true
|
|
|
|
log.info('build', folder)
|
|
|
|
readJson(path.resolve(folder, 'package.json'), function (er, pkg) {
|
|
|
|
if (er) return cb(er)
|
|
|
|
chain([
|
|
|
|
!didPre && [lifecycle, pkg, 'preinstall', folder],
|
|
|
|
[linkStuff, pkg, folder, global, didRB],
|
|
|
|
[writeBuiltinConf, pkg, folder],
|
|
|
|
didPre !== build._noLC && [lifecycle, pkg, 'install', folder],
|
2016-12-19 05:22:09 +01:00
|
|
|
didPre !== build._noLC && [lifecycle, pkg, 'postinstall', folder]
|
2015-10-10 08:13:57 +02:00
|
|
|
],
|
|
|
|
cb)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2011-11-21 18:48:45 +01:00
|
|
|
|
2015-10-10 08:13:57 +02:00
|
|
|
var writeBuiltinConf = build.writeBuiltinConf = function (pkg, folder, cb) {
|
2014-11-05 00:08:12 +01:00
|
|
|
// the builtin config is "sticky". Any time npm installs
|
|
|
|
// itself globally, it puts its builtin config file there
|
|
|
|
var parent = path.dirname(folder)
|
|
|
|
var dir = npm.globalDir
|
|
|
|
|
2017-06-06 01:31:14 +02:00
|
|
|
// Make this count for canary, too
|
|
|
|
if ((pkg.name !== 'npm' && pkg.name !== 'npmc') ||
|
2015-10-10 08:13:57 +02:00
|
|
|
!npm.config.get('global') ||
|
2014-11-05 00:08:12 +01:00
|
|
|
!npm.config.usingBuiltin ||
|
|
|
|
dir !== parent) {
|
2012-08-15 05:27:28 +02:00
|
|
|
return cb()
|
|
|
|
}
|
2014-11-05 00:08:12 +01:00
|
|
|
|
|
|
|
var data = ini.stringify(npm.config.sources.builtin.data)
|
2015-10-10 08:13:57 +02:00
|
|
|
writeFile(path.resolve(folder, 'npmrc'), data, cb)
|
2011-11-21 18:48:45 +01:00
|
|
|
}
|
|
|
|
|
2015-10-10 08:13:57 +02:00
|
|
|
var linkStuff = build.linkStuff = function (pkg, folder, global, didRB, cb) {
|
2013-02-06 17:39:27 +01:00
|
|
|
// allow to opt out of linking binaries.
|
2015-10-10 08:13:57 +02:00
|
|
|
if (npm.config.get('bin-links') === false) return cb()
|
2013-02-06 17:39:27 +01:00
|
|
|
|
2011-11-21 18:48:45 +01:00
|
|
|
// if it's global, and folder is in {prefix}/node_modules,
|
|
|
|
// then bins are in {prefix}/bin
|
|
|
|
// otherwise, then bins are in folder/../.bin
|
2015-10-10 08:13:57 +02:00
|
|
|
var parent = pkg.name && pkg.name[0] === '@' ? path.dirname(path.dirname(folder)) : path.dirname(folder)
|
2015-04-10 14:56:01 +02:00
|
|
|
var gnm = global && npm.globalDir
|
|
|
|
var gtop = parent === gnm
|
|
|
|
|
2015-10-30 00:50:12 +01:00
|
|
|
log.info('linkStuff', packageId(pkg))
|
|
|
|
log.silly('linkStuff', packageId(pkg), 'has', parent, 'as its parent node_modules')
|
|
|
|
if (global) log.silly('linkStuff', packageId(pkg), 'is part of a global install')
|
|
|
|
if (gnm) log.silly('linkStuff', packageId(pkg), 'is installed into a global node_modules')
|
|
|
|
if (gtop) log.silly('linkStuff', packageId(pkg), 'is installed into the top-level global node_modules')
|
2015-04-10 14:56:01 +02:00
|
|
|
|
2017-07-14 19:52:48 +02:00
|
|
|
asyncMap(
|
|
|
|
[linkBins, linkMans, !didRB && rebuildBundles],
|
|
|
|
function (fn, cb) {
|
|
|
|
if (!fn) return cb()
|
|
|
|
log.verbose(fn.name, packageId(pkg))
|
|
|
|
fn(pkg, folder, parent, gtop, cb)
|
|
|
|
},
|
|
|
|
cb
|
|
|
|
)
|
2011-11-21 18:48:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function rebuildBundles (pkg, folder, parent, gtop, cb) {
|
2015-10-10 08:13:57 +02:00
|
|
|
if (!npm.config.get('rebuild-bundle')) return cb()
|
2011-11-21 18:48:45 +01:00
|
|
|
|
|
|
|
var deps = Object.keys(pkg.dependencies || {})
|
|
|
|
.concat(Object.keys(pkg.devDependencies || {}))
|
2015-10-10 08:13:57 +02:00
|
|
|
var bundles = pkg.bundleDependencies || pkg.bundledDependencies || []
|
2011-11-21 18:48:45 +01:00
|
|
|
|
2015-10-10 08:13:57 +02:00
|
|
|
fs.readdir(path.resolve(folder, 'node_modules'), function (er, files) {
|
2011-11-21 18:48:45 +01:00
|
|
|
// error means no bundles
|
|
|
|
if (er) return cb()
|
|
|
|
|
2015-10-10 08:13:57 +02:00
|
|
|
log.verbose('rebuildBundles', files)
|
2011-11-21 18:48:45 +01:00
|
|
|
// don't asyncMap these, because otherwise build script output
|
|
|
|
// gets interleaved and is impossible to read
|
|
|
|
chain(files.filter(function (file) {
|
|
|
|
// rebuild if:
|
|
|
|
// not a .folder, like .bin or .hooks
|
2015-10-10 08:13:57 +02:00
|
|
|
return !file.match(/^[\._-]/) &&
|
2011-11-21 18:48:45 +01:00
|
|
|
// not some old 0.x style bundle
|
2015-10-10 08:13:57 +02:00
|
|
|
file.indexOf('@') === -1 &&
|
2011-11-21 18:48:45 +01:00
|
|
|
// either not a dep, or explicitly bundled
|
2015-10-10 08:13:57 +02:00
|
|
|
(deps.indexOf(file) === -1 || bundles.indexOf(file) !== -1)
|
2011-11-21 18:48:45 +01:00
|
|
|
}).map(function (file) {
|
2015-10-10 08:13:57 +02:00
|
|
|
file = path.resolve(folder, 'node_modules', file)
|
2011-11-21 18:48:45 +01:00
|
|
|
return function (cb) {
|
|
|
|
if (build._didBuild[file]) return cb()
|
2015-10-10 08:13:57 +02:00
|
|
|
log.verbose('rebuild bundle', file)
|
2011-11-21 18:48:45 +01:00
|
|
|
// if file is not a package dir, then don't do it.
|
2015-10-10 08:13:57 +02:00
|
|
|
fs.lstat(path.resolve(file, 'package.json'), function (er) {
|
2011-11-21 18:48:45 +01:00
|
|
|
if (er) return cb()
|
|
|
|
build_(false)(file, cb)
|
|
|
|
})
|
2015-10-10 08:13:57 +02:00
|
|
|
}
|
|
|
|
}), cb)
|
2011-11-21 18:48:45 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function linkBins (pkg, folder, parent, gtop, cb) {
|
2015-10-10 08:13:57 +02:00
|
|
|
if (!pkg.bin || !gtop && path.basename(parent) !== 'node_modules') {
|
2011-11-21 18:48:45 +01:00
|
|
|
return cb()
|
|
|
|
}
|
|
|
|
var binRoot = gtop ? npm.globalBin
|
2015-10-10 08:13:57 +02:00
|
|
|
: path.resolve(parent, '.bin')
|
2016-12-19 05:22:09 +01:00
|
|
|
log.verbose('linkBins', [pkg.bin, binRoot, gtop])
|
2011-11-21 18:48:45 +01:00
|
|
|
|
|
|
|
asyncMap(Object.keys(pkg.bin), function (b, cb) {
|
2015-10-10 08:13:57 +02:00
|
|
|
linkBin(
|
|
|
|
path.resolve(folder, pkg.bin[b]),
|
|
|
|
path.resolve(binRoot, b),
|
|
|
|
gtop && folder,
|
|
|
|
function (er) {
|
|
|
|
if (er) return cb(er)
|
|
|
|
// bins should always be executable.
|
|
|
|
// XXX skip chmod on windows?
|
|
|
|
var src = path.resolve(folder, pkg.bin[b])
|
|
|
|
fs.chmod(src, npm.modes.exec, function (er) {
|
|
|
|
if (er && er.code === 'ENOENT' && npm.config.get('ignore-scripts')) {
|
|
|
|
return cb()
|
|
|
|
}
|
2017-10-27 04:35:25 +02:00
|
|
|
if (er) return cb(er)
|
|
|
|
isHashbangFile(src).then((isHashbang) => {
|
|
|
|
if (isHashbang) return dos2Unix(src)
|
|
|
|
}).then(() => {
|
|
|
|
if (!gtop) return cb()
|
|
|
|
var dest = path.resolve(binRoot, b)
|
|
|
|
var out = npm.config.get('parseable')
|
|
|
|
? dest + '::' + src + ':BINFILE'
|
|
|
|
: dest + ' -> ' + src
|
|
|
|
if (!npm.config.get('json') && !npm.config.get('parseable')) output(out)
|
|
|
|
cb()
|
|
|
|
}).catch(cb)
|
2015-10-10 08:13:57 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
)
|
2011-11-21 18:48:45 +01:00
|
|
|
}, cb)
|
|
|
|
}
|
|
|
|
|
|
|
|
function linkBin (from, to, gently, cb) {
|
2015-10-10 08:13:57 +02:00
|
|
|
if (process.platform !== 'win32') {
|
2011-11-21 18:48:45 +01:00
|
|
|
return linkIfExists(from, to, gently, cb)
|
|
|
|
} else {
|
|
|
|
return cmdShimIfExists(from, to, cb)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function linkMans (pkg, folder, parent, gtop, cb) {
|
2015-10-10 08:13:57 +02:00
|
|
|
if (!pkg.man || !gtop || process.platform === 'win32') return cb()
|
2013-05-14 23:37:59 +02:00
|
|
|
|
2015-10-10 08:13:57 +02:00
|
|
|
var manRoot = path.resolve(npm.config.get('prefix'), 'share', 'man')
|
|
|
|
log.verbose('linkMans', 'man files are', pkg.man, 'in', manRoot)
|
2013-07-12 22:14:50 +02:00
|
|
|
|
|
|
|
// make sure that the mans are unique.
|
|
|
|
// otherwise, if there are dupes, it'll fail with EEXIST
|
|
|
|
var set = pkg.man.reduce(function (acc, man) {
|
|
|
|
acc[path.basename(man)] = man
|
|
|
|
return acc
|
|
|
|
}, {})
|
|
|
|
pkg.man = pkg.man.filter(function (man) {
|
|
|
|
return set[path.basename(man)] === man
|
|
|
|
})
|
|
|
|
|
2011-11-21 18:48:45 +01:00
|
|
|
asyncMap(pkg.man, function (man, cb) {
|
2015-10-10 08:13:57 +02:00
|
|
|
if (typeof man !== 'string') return cb()
|
|
|
|
log.silly('linkMans', 'preparing to link', man)
|
2013-07-12 17:55:57 +02:00
|
|
|
var parseMan = man.match(/(.*\.([0-9]+)(\.gz)?)$/)
|
2015-01-08 23:37:26 +01:00
|
|
|
if (!parseMan) {
|
|
|
|
return cb(new Error(
|
2015-10-10 08:13:57 +02:00
|
|
|
man + ' is not a valid name for a man file. ' +
|
|
|
|
'Man files must end with a number, ' +
|
|
|
|
'and optionally a .gz suffix if they are compressed.'
|
2015-01-08 23:37:26 +01:00
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
var stem = parseMan[1]
|
|
|
|
var sxn = parseMan[2]
|
|
|
|
var bn = path.basename(stem)
|
2015-02-20 18:03:34 +01:00
|
|
|
var manSrc = path.resolve(folder, man)
|
2015-10-10 08:13:57 +02:00
|
|
|
var manDest = path.join(manRoot, 'man' + sxn, bn)
|
2013-05-14 23:37:59 +02:00
|
|
|
|
2015-02-20 18:03:34 +01:00
|
|
|
linkIfExists(manSrc, manDest, gtop && folder, cb)
|
2011-11-21 18:48:45 +01:00
|
|
|
}, cb)
|
|
|
|
}
|