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