index.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. 'use strict';
  2. const arrify = require('arrify');
  3. const taskkill = require('taskkill');
  4. const execa = require('execa');
  5. const AggregateError = require('aggregate-error');
  6. const pidFromPort = require('pid-from-port');
  7. const processExists = require('process-exists');
  8. const missingBinaryError = async (command, arguments_) => {
  9. try {
  10. return await execa(command, arguments_);
  11. } catch (error) {
  12. if (error.code === 'ENOENT') {
  13. const newError = new Error(`\`${command}\` doesn't seem to be installed and is required by fkill`);
  14. newError.sourceError = error;
  15. throw newError;
  16. }
  17. throw error;
  18. }
  19. };
  20. const windowsKill = (input, options) => {
  21. return taskkill(input, {
  22. force: options.force,
  23. tree: typeof options.tree === 'undefined' ? true : options.tree
  24. });
  25. };
  26. const macosKill = (input, options) => {
  27. const killByName = typeof input === 'string';
  28. const command = killByName ? 'pkill' : 'kill';
  29. const arguments_ = [input];
  30. if (options.force) {
  31. arguments_.unshift('-9');
  32. }
  33. if (killByName && options.ignoreCase) {
  34. arguments_.unshift('-i');
  35. }
  36. return missingBinaryError(command, arguments_);
  37. };
  38. const defaultKill = (input, options) => {
  39. const killByName = typeof input === 'string';
  40. const command = killByName ? 'killall' : 'kill';
  41. const arguments_ = [input];
  42. if (options.force) {
  43. arguments_.unshift('-9');
  44. }
  45. if (killByName && options.ignoreCase) {
  46. arguments_.unshift('-I');
  47. }
  48. return missingBinaryError(command, arguments_);
  49. };
  50. const kill = (() => {
  51. if (process.platform === 'darwin') {
  52. return macosKill;
  53. }
  54. if (process.platform === 'win32') {
  55. return windowsKill;
  56. }
  57. return defaultKill;
  58. })();
  59. const parseInput = async input => {
  60. if (typeof input === 'string' && input[0] === ':') {
  61. return pidFromPort(parseInt(input.slice(1), 10));
  62. }
  63. return input;
  64. };
  65. const fkill = async (inputs, options = {}) => {
  66. inputs = arrify(inputs);
  67. const exists = await processExists.all(inputs);
  68. const errors = [];
  69. const handleKill = async input => {
  70. try {
  71. input = await parseInput(input);
  72. if (input === process.pid) {
  73. return;
  74. }
  75. await kill(input, options);
  76. } catch (error) {
  77. if (!exists.get(input)) {
  78. errors.push(`Killing process ${input} failed: Process doesn't exist`);
  79. return;
  80. }
  81. errors.push(`Killing process ${input} failed: ${error.message.replace(/.*\n/, '').replace(/kill: \d+: /, '').trim()}`);
  82. }
  83. };
  84. await Promise.all(
  85. inputs.map(input => handleKill(input))
  86. );
  87. if (errors.length > 0 && !options.silent) {
  88. throw new AggregateError(errors);
  89. }
  90. };
  91. module.exports = fkill;
  92. // TODO: remove this in the next major version
  93. module.exports.default = fkill;