shelljs/src/which.js
Nate Fischer a8e543a8d0
chore(lint): upgrade eslint (#947)
This upgrades eslint to a new version to resolve a security warning.
This fixes a couple new lint errors, and disables some lint checks.

The current version of the airbnb lint config triggers
DeprecationWarnings in the new version of eslint. These warnings will be
resolved in a follow-up.

Test: npm run lint
2019-06-20 23:34:41 -07:00

120 lines
3.5 KiB
JavaScript

var common = require('./common');
var fs = require('fs');
var path = require('path');
common.register('which', _which, {
allowGlobbing: false,
cmdOptions: {
'a': 'all',
},
});
// XP's system default value for `PATHEXT` system variable, just in case it's not
// set on Windows.
var XP_DEFAULT_PATHEXT = '.com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh';
// For earlier versions of NodeJS that doesn't have a list of constants (< v6)
var FILE_EXECUTABLE_MODE = 1;
function isWindowsPlatform() {
return process.platform === 'win32';
}
// Cross-platform method for splitting environment `PATH` variables
function splitPath(p) {
return p ? p.split(path.delimiter) : [];
}
// Tests are running all cases for this func but it stays uncovered by codecov due to unknown reason
/* istanbul ignore next */
function isExecutable(pathName) {
try {
// TODO(node-support): replace with fs.constants.X_OK once remove support for node < v6
fs.accessSync(pathName, FILE_EXECUTABLE_MODE);
} catch (err) {
return false;
}
return true;
}
function checkPath(pathName) {
return fs.existsSync(pathName) && !common.statFollowLinks(pathName).isDirectory()
&& (isWindowsPlatform() || isExecutable(pathName));
}
//@
//@ ### which(command)
//@
//@ Examples:
//@
//@ ```javascript
//@ var nodeExec = which('node');
//@ ```
//@
//@ Searches for `command` in the system's `PATH`. On Windows, this uses the
//@ `PATHEXT` variable to append the extension if it's not already executable.
//@ Returns a [ShellString](#shellstringstr) containing the absolute path to
//@ `command`.
function _which(options, cmd) {
if (!cmd) common.error('must specify command');
var isWindows = isWindowsPlatform();
var pathArray = splitPath(process.env.PATH);
var queryMatches = [];
// No relative/absolute paths provided?
if (cmd.indexOf('/') === -1) {
// Assume that there are no extensions to append to queries (this is the
// case for unix)
var pathExtArray = [''];
if (isWindows) {
// In case the PATHEXT variable is somehow not set (e.g.
// child_process.spawn with an empty environment), use the XP default.
var pathExtEnv = process.env.PATHEXT || XP_DEFAULT_PATHEXT;
pathExtArray = splitPath(pathExtEnv.toUpperCase());
}
// Search for command in PATH
for (var k = 0; k < pathArray.length; k++) {
// already found it
if (queryMatches.length > 0 && !options.all) break;
var attempt = path.resolve(pathArray[k], cmd);
if (isWindows) {
attempt = attempt.toUpperCase();
}
var match = attempt.match(/\.[^<>:"/|?*.]+$/);
if (match && pathExtArray.indexOf(match[0]) >= 0) { // this is Windows-only
// The user typed a query with the file extension, like
// `which('node.exe')`
if (checkPath(attempt)) {
queryMatches.push(attempt);
break;
}
} else { // All-platforms
// Cycle through the PATHEXT array, and check each extension
// Note: the array is always [''] on Unix
for (var i = 0; i < pathExtArray.length; i++) {
var ext = pathExtArray[i];
var newAttempt = attempt + ext;
if (checkPath(newAttempt)) {
queryMatches.push(newAttempt);
break;
}
}
}
}
} else if (checkPath(cmd)) { // a valid absolute or relative path
queryMatches.push(path.resolve(cmd));
}
if (queryMatches.length > 0) {
return options.all ? queryMatches : queryMatches[0];
}
return options.all ? [] : null;
}
module.exports = _which;