1'use strict' 2 3const BB = require('bluebird') 4 5const fs = require('fs') 6const figgyPudding = require('figgy-pudding') 7const ini = require('ini') 8const path = require('path') 9const spawn = require('child_process').spawn 10 11const readFileAsync = BB.promisify(fs.readFile) 12 13const NpmConfig = figgyPudding({ 14 cache: { default: '' }, 15 then: {}, 16 userconfig: {} 17}) 18 19module.exports = NpmConfig 20 21module.exports.fromNpm = getNpmConfig 22function getNpmConfig (argv) { 23 return new BB((resolve, reject) => { 24 const npmBin = process.platform === 'win32' ? 'npm.cmd' : 'npm' 25 const child = spawn(npmBin, [ 26 'config', 'ls', '--json', '-l' 27 // We add argv here to get npm to parse those options for us :D 28 ].concat(argv || []), { 29 env: process.env, 30 cwd: process.cwd(), 31 stdio: [0, 'pipe', 2] 32 }) 33 34 let stdout = '' 35 if (child.stdout) { 36 child.stdout.on('data', (chunk) => { 37 stdout += chunk 38 }) 39 } 40 41 child.on('error', reject) 42 child.on('close', (code) => { 43 if (code === 127) { 44 reject(new Error('`npm` command not found. Please ensure you have npm@5.4.0 or later installed.')) 45 } else { 46 try { 47 resolve(JSON.parse(stdout)) 48 } catch (e) { 49 reject(new Error('`npm config ls --json` failed to output json. Please ensure you have npm@5.4.0 or later installed.')) 50 } 51 } 52 }) 53 }).then(opts => { 54 return BB.all( 55 process.cwd().split(path.sep).reduce((acc, next) => { 56 acc.path = path.join(acc.path, next) 57 acc.promises.push(maybeReadIni(path.join(acc.path, '.npmrc'))) 58 acc.promises.push(maybeReadIni(path.join(acc.path, 'npmrc'))) 59 return acc 60 }, { 61 path: '', 62 promises: [] 63 }).promises.concat( 64 opts.userconfig ? maybeReadIni(opts.userconfig) : {} 65 ) 66 ).then(configs => NpmConfig(...configs, opts)) 67 }).then(opts => { 68 if (opts.cache) { 69 return opts.concat({ cache: path.join(opts.cache, '_cacache') }) 70 } else { 71 return opts 72 } 73 }) 74} 75 76function maybeReadIni (f) { 77 return readFileAsync(f, 'utf8').catch(err => { 78 if (err.code === 'ENOENT') { 79 return '' 80 } else { 81 throw err 82 } 83 }).then(ini.parse) 84} 85