which.js (3163B)
1 const isWindows = process.platform === 'win32' || 2 process.env.OSTYPE === 'cygwin' || 3 process.env.OSTYPE === 'msys' 4 5 const path = require('path') 6 const COLON = isWindows ? ';' : ':' 7 const isexe = require('isexe') 8 9 const getNotFoundError = (cmd) => 10 Object.assign(new Error(`not found: ${cmd}`), { code: 'ENOENT' }) 11 12 const getPathInfo = (cmd, opt) => { 13 const colon = opt.colon || COLON 14 15 // If it has a slash, then we don't bother searching the pathenv. 16 // just check the file itself, and that's it. 17 const pathEnv = cmd.match(/\//) || isWindows && cmd.match(/\\/) ? [''] 18 : ( 19 [ 20 // windows always checks the cwd first 21 ...(isWindows ? [process.cwd()] : []), 22 ...(opt.path || process.env.PATH || 23 /* istanbul ignore next: very unusual */ '').split(colon), 24 ] 25 ) 26 const pathExtExe = isWindows 27 ? opt.pathExt || process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM' 28 : '' 29 const pathExt = isWindows ? pathExtExe.split(colon) : [''] 30 31 if (isWindows) { 32 if (cmd.indexOf('.') !== -1 && pathExt[0] !== '') 33 pathExt.unshift('') 34 } 35 36 return { 37 pathEnv, 38 pathExt, 39 pathExtExe, 40 } 41 } 42 43 const which = (cmd, opt, cb) => { 44 if (typeof opt === 'function') { 45 cb = opt 46 opt = {} 47 } 48 if (!opt) 49 opt = {} 50 51 const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt) 52 const found = [] 53 54 const step = i => new Promise((resolve, reject) => { 55 if (i === pathEnv.length) 56 return opt.all && found.length ? resolve(found) 57 : reject(getNotFoundError(cmd)) 58 59 const ppRaw = pathEnv[i] 60 const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw 61 62 const pCmd = path.join(pathPart, cmd) 63 const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd 64 : pCmd 65 66 resolve(subStep(p, i, 0)) 67 }) 68 69 const subStep = (p, i, ii) => new Promise((resolve, reject) => { 70 if (ii === pathExt.length) 71 return resolve(step(i + 1)) 72 const ext = pathExt[ii] 73 isexe(p + ext, { pathExt: pathExtExe }, (er, is) => { 74 if (!er && is) { 75 if (opt.all) 76 found.push(p + ext) 77 else 78 return resolve(p + ext) 79 } 80 return resolve(subStep(p, i, ii + 1)) 81 }) 82 }) 83 84 return cb ? step(0).then(res => cb(null, res), cb) : step(0) 85 } 86 87 const whichSync = (cmd, opt) => { 88 opt = opt || {} 89 90 const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt) 91 const found = [] 92 93 for (let i = 0; i < pathEnv.length; i ++) { 94 const ppRaw = pathEnv[i] 95 const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw 96 97 const pCmd = path.join(pathPart, cmd) 98 const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd 99 : pCmd 100 101 for (let j = 0; j < pathExt.length; j ++) { 102 const cur = p + pathExt[j] 103 try { 104 const is = isexe.sync(cur, { pathExt: pathExtExe }) 105 if (is) { 106 if (opt.all) 107 found.push(cur) 108 else 109 return cur 110 } 111 } catch (ex) {} 112 } 113 } 114 115 if (opt.all && found.length) 116 return found 117 118 if (opt.nothrow) 119 return null 120 121 throw getNotFoundError(cmd) 122 } 123 124 module.exports = which 125 which.sync = whichSync