• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict'
2
3const BB = require('bluebird')
4
5const figgyPudding = require('figgy-pudding')
6const stat = BB.promisify(require('graceful-fs').stat)
7const gentlyRm = BB.promisify(require('../../utils/gently-rm.js'))
8const mkdirp = BB.promisify(require('gentle-fs').mkdir)
9const moduleName = require('../../utils/module-name.js')
10const moduleStagingPath = require('../module-staging-path.js')
11const move = require('../../utils/move.js')
12const npa = require('npm-package-arg')
13const npm = require('../../npm.js')
14let npmConfig
15const packageId = require('../../utils/package-id.js')
16const path = require('path')
17const localWorker = require('./extract-worker.js')
18const workerFarm = require('worker-farm')
19const isRegistry = require('../../utils/is-registry.js')
20
21const WORKER_PATH = require.resolve('./extract-worker.js')
22let workers
23
24const ExtractOpts = figgyPudding({
25  log: {}
26}, { other () { return true } })
27
28// Disabled for now. Re-enable someday. Just not today.
29const ENABLE_WORKERS = false
30
31extract.init = () => {
32  if (ENABLE_WORKERS) {
33    workers = workerFarm({
34      maxConcurrentCallsPerWorker: npm.limit.fetch,
35      maxRetries: 1
36    }, WORKER_PATH)
37  }
38  return BB.resolve()
39}
40extract.teardown = () => {
41  if (ENABLE_WORKERS) {
42    workerFarm.end(workers)
43    workers = null
44  }
45  return BB.resolve()
46}
47module.exports = extract
48function extract (staging, pkg, log) {
49  log.silly('extract', packageId(pkg))
50  const extractTo = moduleStagingPath(staging, pkg)
51  if (!npmConfig) {
52    npmConfig = require('../../config/figgy-config.js')
53  }
54  let opts = ExtractOpts(npmConfig()).concat({
55    integrity: pkg.package._integrity,
56    resolved: pkg.package._resolved
57  })
58  const args = [
59    pkg.package._requested,
60    extractTo,
61    opts
62  ]
63  return BB.fromNode((cb) => {
64    let launcher = localWorker
65    let msg = args
66    const spec = typeof args[0] === 'string' ? npa(args[0]) : args[0]
67    args[0] = spec.raw
68    if (ENABLE_WORKERS && (isRegistry(spec) || spec.type === 'remote')) {
69      // We can't serialize these options
70      opts = opts.concat({
71        loglevel: opts.log.level,
72        log: null,
73        dirPacker: null,
74        Promise: null,
75        _events: null,
76        _eventsCount: null,
77        list: null,
78        sources: null,
79        _maxListeners: null,
80        root: null
81      })
82      // workers will run things in parallel!
83      launcher = workers
84      try {
85        msg = JSON.stringify(msg)
86      } catch (e) {
87        return cb(e)
88      }
89    }
90    launcher(msg, cb)
91  }).then(() => {
92    if (pkg.package.bundleDependencies || anyBundled(pkg)) {
93      return readBundled(pkg, staging, extractTo)
94    }
95  }).then(() => {
96    return gentlyRm(path.join(extractTo, 'node_modules'))
97  })
98}
99
100function anyBundled (top, pkg) {
101  if (!pkg) pkg = top
102  return pkg.children.some((child) => child.fromBundle === top || anyBundled(top, child))
103}
104
105function readBundled (pkg, staging, extractTo) {
106  return BB.map(pkg.children, (child) => {
107    if (!child.fromBundle) return
108    if (child.error) {
109      throw child.error
110    } else {
111      return stageBundledModule(pkg, child, staging, extractTo)
112    }
113  }, {concurrency: 10})
114}
115
116function stageBundledModule (bundler, child, staging, parentPath) {
117  const stageFrom = path.join(parentPath, 'node_modules', moduleName(child))
118  const stageTo = moduleStagingPath(staging, child)
119
120  return BB.map(child.children, (child) => {
121    if (child.error) {
122      throw child.error
123    } else {
124      return stageBundledModule(bundler, child, staging, stageFrom)
125    }
126  }).then(() => {
127    return finishModule(bundler, child, stageTo, stageFrom)
128  })
129}
130
131function finishModule (bundler, child, stageTo, stageFrom) {
132  // If we were the one's who bundled this module…
133  if (child.fromBundle === bundler) {
134    return mkdirp(path.dirname(stageTo)).then(() => {
135      return move(stageFrom, stageTo)
136    })
137  } else {
138    return stat(stageFrom).then(() => gentlyRm(stageFrom), () => {})
139  }
140}
141