• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// npm build command
2
3// everything about the installation after the creation of
4// the .npm/{name}/{version}/package folder.
5// linking the modules into the npm.root,
6// resolving dependencies, etc.
7
8// This runs AFTER install or link are completed.
9
10var npm = require('./npm.js')
11var log = require('npmlog')
12var chain = require('slide').chain
13var path = require('path')
14var fs = require('graceful-fs')
15var lifecycle = require('./utils/lifecycle.js')
16var readJson = require('read-package-json')
17var binLinks = require('bin-links')
18var binLinksConfig = require('./config/bin-links.js')
19var ini = require('ini')
20var writeFile = require('write-file-atomic')
21
22module.exports = build
23build.usage = 'npm build [<folder>]'
24
25build._didBuild = {}
26build._noLC = {}
27function build (args, global, didPre, didRB, cb) {
28  if (typeof cb !== 'function') {
29    cb = didRB
30    didRB = false
31  }
32  if (typeof cb !== 'function') {
33    cb = didPre
34    didPre = false
35  }
36  if (typeof cb !== 'function') {
37    cb = global
38    global = npm.config.get('global')
39  }
40
41  if (!args.length) {
42    readJson(path.resolve(npm.localPrefix, 'package.json'), function (er, pkg) {
43      if (!args.length && pkg && pkg.scripts && pkg.scripts.build) {
44        log.warn('build', '`npm build` called with no arguments. Did you mean to `npm run-script build`?')
45      }
46      cb()
47    })
48  } else {
49    // it'd be nice to asyncMap these, but actually, doing them
50    // in parallel generally munges up the output from node-waf
51    var builder = build_(global, didPre, didRB)
52    chain(args.map(function (arg) {
53      return function (cb) {
54        builder(arg, cb)
55      }
56    }), cb)
57  }
58}
59
60function build_ (global, didPre, didRB) {
61  return function (folder, cb) {
62    folder = path.resolve(folder)
63    if (build._didBuild[folder]) log.info('build', 'already built', folder)
64    build._didBuild[folder] = true
65    log.info('build', folder)
66    readJson(path.resolve(folder, 'package.json'), function (er, pkg) {
67      if (er) return cb(er)
68      chain([
69        !didPre && [lifecycle, pkg, 'preinstall', folder],
70        [linkStuff, pkg, folder, global],
71        !didRB && [rebuildBundles, pkg, folder],
72        [writeBuiltinConf, pkg, folder],
73        didPre !== build._noLC && [lifecycle, pkg, 'install', folder],
74        didPre !== build._noLC && [lifecycle, pkg, 'postinstall', folder]
75      ],
76      cb)
77    })
78  }
79}
80
81var writeBuiltinConf = build.writeBuiltinConf = function (pkg, folder, cb) {
82  // the builtin config is "sticky". Any time npm installs
83  // itself globally, it puts its builtin config file there
84  var parent = path.dirname(folder)
85  var dir = npm.globalDir
86
87  // Make this count for canary, too
88  if ((pkg.name !== 'npm' && pkg.name !== 'npmc') ||
89      !npm.config.get('global') ||
90      !npm.config.usingBuiltin ||
91      dir !== parent) {
92    return cb()
93  }
94
95  var data = ini.stringify(npm.config.sources.builtin.data)
96  writeFile(path.resolve(folder, 'npmrc'), data, cb)
97}
98
99var linkStuff = build.linkStuff = function (pkg, folder, global, cb) {
100  // allow to opt out of linking binaries.
101  if (npm.config.get('bin-links') === false) return cb()
102  return binLinks(pkg, folder, global, binLinksConfig(pkg), cb)
103}
104
105function rebuildBundles (pkg, folder, cb) {
106  if (!npm.config.get('rebuild-bundle')) return cb()
107
108  var deps = Object.keys(pkg.dependencies || {})
109    .concat(Object.keys(pkg.devDependencies || {}))
110  var bundles = pkg.bundleDependencies || pkg.bundledDependencies || []
111
112  fs.readdir(path.resolve(folder, 'node_modules'), function (er, files) {
113    // error means no bundles
114    if (er) return cb()
115
116    log.verbose('rebuildBundles', files)
117    // don't asyncMap these, because otherwise build script output
118    // gets interleaved and is impossible to read
119    chain(files.filter(function (file) {
120      // rebuild if:
121      // not a .folder, like .bin or .hooks
122      return !file.match(/^[._-]/) &&
123          // not some old 0.x style bundle
124          file.indexOf('@') === -1 &&
125          // either not a dep, or explicitly bundled
126          (deps.indexOf(file) === -1 || bundles.indexOf(file) !== -1)
127    }).map(function (file) {
128      file = path.resolve(folder, 'node_modules', file)
129      return function (cb) {
130        if (build._didBuild[file]) return cb()
131        log.verbose('rebuild bundle', file)
132        // if file is not a package dir, then don't do it.
133        fs.lstat(path.resolve(file, 'package.json'), function (er) {
134          if (er) return cb()
135          build_(false)(file, cb)
136        })
137      }
138    }), cb)
139  })
140}
141