command-exists.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. 'use strict';
  2. var exec = require('child_process').exec;
  3. var execSync = require('child_process').execSync;
  4. var fs = require('fs');
  5. var path = require('path');
  6. var access = fs.access;
  7. var accessSync = fs.accessSync;
  8. var constants = fs.constants || fs;
  9. var isUsingWindows = process.platform == 'win32'
  10. var fileNotExists = function(commandName, callback){
  11. access(commandName, constants.F_OK,
  12. function(err){
  13. callback(!err);
  14. });
  15. };
  16. var fileNotExistsSync = function(commandName){
  17. try{
  18. accessSync(commandName, constants.F_OK);
  19. return false;
  20. }catch(e){
  21. return true;
  22. }
  23. };
  24. var localExecutable = function(commandName, callback){
  25. access(commandName, constants.F_OK | constants.X_OK,
  26. function(err){
  27. callback(null, !err);
  28. });
  29. };
  30. var localExecutableSync = function(commandName){
  31. try{
  32. accessSync(commandName, constants.F_OK | constants.X_OK);
  33. return true;
  34. }catch(e){
  35. return false;
  36. }
  37. }
  38. var commandExistsUnix = function(commandName, cleanedCommandName, callback) {
  39. fileNotExists(commandName, function(isFile){
  40. if(!isFile){
  41. var child = exec('command -v ' + cleanedCommandName +
  42. ' 2>/dev/null' +
  43. ' && { echo >&1 ' + cleanedCommandName + '; exit 0; }',
  44. function (error, stdout, stderr) {
  45. callback(null, !!stdout);
  46. });
  47. return;
  48. }
  49. localExecutable(commandName, callback);
  50. });
  51. }
  52. var commandExistsWindows = function(commandName, cleanedCommandName, callback) {
  53. // Regex from Julio from: https://stackoverflow.com/questions/51494579/regex-windows-path-validator
  54. if (!(/^(?!(?:.*\s|.*\.|\W+)$)(?:[a-zA-Z]:)?(?:(?:[^<>:"\|\?\*\n])+(?:\/\/|\/|\\\\|\\)?)+$/m.test(commandName))) {
  55. callback(null, false);
  56. return;
  57. }
  58. var child = exec('where ' + cleanedCommandName,
  59. function (error) {
  60. if (error !== null){
  61. callback(null, false);
  62. } else {
  63. callback(null, true);
  64. }
  65. }
  66. )
  67. }
  68. var commandExistsUnixSync = function(commandName, cleanedCommandName) {
  69. if(fileNotExistsSync(commandName)){
  70. try {
  71. var stdout = execSync('command -v ' + cleanedCommandName +
  72. ' 2>/dev/null' +
  73. ' && { echo >&1 ' + cleanedCommandName + '; exit 0; }'
  74. );
  75. return !!stdout;
  76. } catch (error) {
  77. return false;
  78. }
  79. }
  80. return localExecutableSync(commandName);
  81. }
  82. var commandExistsWindowsSync = function(commandName, cleanedCommandName, callback) {
  83. // Regex from Julio from: https://stackoverflow.com/questions/51494579/regex-windows-path-validator
  84. if (!(/^(?!(?:.*\s|.*\.|\W+)$)(?:[a-zA-Z]:)?(?:(?:[^<>:"\|\?\*\n])+(?:\/\/|\/|\\\\|\\)?)+$/m.test(commandName))) {
  85. return false;
  86. }
  87. try {
  88. var stdout = execSync('where ' + cleanedCommandName, {stdio: []});
  89. return !!stdout;
  90. } catch (error) {
  91. return false;
  92. }
  93. }
  94. var cleanInput = function(s) {
  95. if (/[^A-Za-z0-9_\/:=-]/.test(s)) {
  96. s = "'"+s.replace(/'/g,"'\\''")+"'";
  97. s = s.replace(/^(?:'')+/g, '') // unduplicate single-quote at the beginning
  98. .replace(/\\'''/g, "\\'" ); // remove non-escaped single-quote if there are enclosed between 2 escaped
  99. }
  100. return s;
  101. }
  102. if (isUsingWindows) {
  103. cleanInput = function(s) {
  104. var isPathName = /[\\]/.test(s);
  105. if (isPathName) {
  106. var dirname = '"' + path.dirname(s) + '"';
  107. var basename = '"' + path.basename(s) + '"';
  108. return dirname + ':' + basename;
  109. }
  110. return '"' + s + '"';
  111. }
  112. }
  113. module.exports = function commandExists(commandName, callback) {
  114. var cleanedCommandName = cleanInput(commandName);
  115. if (!callback && typeof Promise !== 'undefined') {
  116. return new Promise(function(resolve, reject){
  117. commandExists(commandName, function(error, output) {
  118. if (output) {
  119. resolve(commandName);
  120. } else {
  121. reject(error);
  122. }
  123. });
  124. });
  125. }
  126. if (isUsingWindows) {
  127. commandExistsWindows(commandName, cleanedCommandName, callback);
  128. } else {
  129. commandExistsUnix(commandName, cleanedCommandName, callback);
  130. }
  131. };
  132. module.exports.sync = function(commandName) {
  133. var cleanedCommandName = cleanInput(commandName);
  134. if (isUsingWindows) {
  135. return commandExistsWindowsSync(commandName, cleanedCommandName);
  136. } else {
  137. return commandExistsUnixSync(commandName, cleanedCommandName);
  138. }
  139. };