1'use strict' 2 3const deprCheck = require('./utils/depr-check') 4const path = require('path') 5const log = require('npmlog') 6const pacote = require('pacote') 7const readPackageTree = require('read-package-tree') 8const rimraf = require('rimraf') 9const validate = require('aproba') 10const npa = require('npm-package-arg') 11const npm = require('./npm') 12let npmConfig 13const npmlog = require('npmlog') 14const limit = require('call-limit') 15const tempFilename = require('./utils/temp-filename.js') 16const replaceInfo = require('./utils/replace-info.js') 17const isWindows = require('./utils/is-windows.js') 18 19function andLogAndFinish (spec, tracker, done) { 20 validate('SOF|SZF|OOF|OZF', [spec, tracker, done]) 21 return (er, pkg) => { 22 if (er) { 23 er.message = replaceInfo(er.message) 24 var spc = replaceInfo(String(spec)) 25 log.silly('fetchPackageMetaData', 'error for ' + spc, er.message) 26 if (tracker) tracker.finish() 27 } 28 return done(er, pkg) 29 } 30} 31 32const LRUCache = require('lru-cache') 33const CACHE = new LRUCache({ 34 max: 300 * 1024 * 1024, 35 length: (p) => p._contentLength 36}) 37 38module.exports = limit(fetchPackageMetadata, npm.limit.fetch) 39function fetchPackageMetadata (spec, where, opts, done) { 40 validate('SSOF|SSFZ|OSOF|OSFZ', [spec, where, opts, done]) 41 42 if (!done) { 43 done = opts 44 opts = {} 45 } 46 var tracker = opts.tracker 47 const logAndFinish = andLogAndFinish(spec, tracker, done) 48 49 if (typeof spec === 'object') { 50 var dep = spec 51 } else { 52 dep = npa(spec) 53 } 54 if (!isWindows && dep.type === 'directory' && /^[a-zA-Z]:/.test(dep.fetchSpec)) { 55 var err = new Error(`Can't install from windows path on a non-windows system: ${dep.fetchSpec.replace(/[/]/g, '\\')}`) 56 err.code = 'EWINDOWSPATH' 57 return logAndFinish(err) 58 } 59 if (!npmConfig) { 60 npmConfig = require('./config/figgy-config.js') 61 } 62 pacote.manifest(dep, npmConfig({ 63 annotate: true, 64 fullMetadata: opts.fullMetadata, 65 log: tracker || npmlog, 66 memoize: CACHE, 67 where: where 68 })).then( 69 (pkg) => logAndFinish(null, deprCheck(pkg)), 70 (err) => { 71 if (dep.type !== 'directory') return logAndFinish(err) 72 if (err.code === 'ENOTDIR') { 73 var enolocal = new Error(`Could not install "${path.relative(process.cwd(), dep.fetchSpec)}" as it is not a directory and is not a file with a name ending in .tgz, .tar.gz or .tar`) 74 enolocal.code = 'ENOLOCAL' 75 if (err.stack) enolocal.stack = err.stack 76 return logAndFinish(enolocal) 77 } else if (err.code === 'ENOPACKAGEJSON') { 78 var enopackage = new Error(`Could not install from "${path.relative(process.cwd(), dep.fetchSpec)}" as it does not contain a package.json file.`) 79 enopackage.code = 'ENOLOCAL' 80 if (err.stack) enopackage.stack = err.stack 81 return logAndFinish(enopackage) 82 } else { 83 return logAndFinish(err) 84 } 85 } 86 ) 87} 88 89module.exports.addBundled = addBundled 90function addBundled (pkg, next) { 91 validate('OF', arguments) 92 if (pkg._bundled !== undefined) return next(null, pkg) 93 94 if (!pkg.bundleDependencies && pkg._requested.type !== 'directory') return next(null, pkg) 95 const requested = pkg._requested || npa(pkg._from) 96 if (requested.type === 'directory') { 97 pkg._bundled = null 98 return readPackageTree(pkg._requested.fetchSpec, function (er, tree) { 99 if (tree) pkg._bundled = tree.children 100 return next(null, pkg) 101 }) 102 } 103 pkg._bundled = null 104 const target = tempFilename('unpack') 105 if (!npmConfig) { 106 npmConfig = require('./config/figgy-config.js') 107 } 108 const opts = npmConfig({integrity: pkg._integrity}) 109 pacote.extract(pkg._resolved || pkg._requested || npa.resolve(pkg.name, pkg.version), target, opts).then(() => { 110 log.silly('addBundled', 'read tarball') 111 readPackageTree(target, (err, tree) => { 112 if (err) { return next(err) } 113 log.silly('cleanup', 'remove extracted module') 114 rimraf(target, function () { 115 if (tree) { 116 pkg._bundled = tree.children 117 } 118 next(null, pkg) 119 }) 120 }) 121 }, next) 122} 123