'use strict';
const arrify = require('arrify');
const taskkill = require('taskkill');
const execa = require('execa');
const AggregateError = require('aggregate-error');
const pidFromPort = require('pid-from-port');
const processExists = require('process-exists');
const psList = require('ps-list');

const missingBinaryError = async (command, arguments_) => {
	try {
		return await execa(command, arguments_);
	} catch (error) {
		if (error.code === 'ENOENT') {
			const newError = new Error(`\`${command}\` doesn't seem to be installed and is required by fkill`);
			newError.sourceError = error;
			throw newError;
		}

		throw error;
	}
};

const windowsKill = (input, options) => {
	return taskkill(input, {
		force: options.force,
		tree: typeof options.tree === 'undefined' ? true : options.tree
	});
};

const macosKill = (input, options) => {
	const killByName = typeof input === 'string';
	const command = killByName ? 'pkill' : 'kill';
	const arguments_ = [input];

	if (options.force) {
		arguments_.unshift('-9');
	}

	if (killByName && options.ignoreCase) {
		arguments_.unshift('-i');
	}

	return missingBinaryError(command, arguments_);
};

const defaultKill = (input, options) => {
	const killByName = typeof input === 'string';
	const command = killByName ? 'killall' : 'kill';
	const arguments_ = [input];

	if (options.force) {
		arguments_.unshift('-9');
	}

	if (killByName && options.ignoreCase) {
		arguments_.unshift('-I');
	}

	return missingBinaryError(command, arguments_);
};

const kill = (() => {
	if (process.platform === 'darwin') {
		return macosKill;
	}

	if (process.platform === 'win32') {
		return windowsKill;
	}

	return defaultKill;
})();

const parseInput = async input => {
	if (typeof input === 'string' && input[0] === ':') {
		return pidFromPort(parseInt(input.slice(1), 10));
	}

	return input;
};

const fkill = async (inputs, options = {}) => {
	inputs = arrify(inputs);

	const exists = await processExists.all(inputs);

	const errors = [];

	const handleKill = async input => {
		try {
			input = await parseInput(input);

			if (input === process.pid) {
				return;
			}

			if (input === 'node') {
				const processes = await psList();
				await Promise.all(processes.map(async ps => {
					if (ps.name === 'node' && ps.pid !== process.pid) {
						await kill(ps.pid, options);
					}
				}));
				return;
			}

			await kill(input, options);
		} catch (error) {
			if (!exists.get(input)) {
				errors.push(`Killing process ${input} failed: Process doesn't exist`);
				return;
			}

			errors.push(`Killing process ${input} failed: ${error.message.replace(/.*\n/, '').replace(/kill: \d+: /, '').trim()}`);
		}
	};

	await Promise.all(
		inputs.map(input => handleKill(input))
	);

	if (errors.length > 0 && !options.silent) {
		throw new AggregateError(errors);
	}
};

module.exports = fkill;

@@ -983,32 +983,96 @@
 ptions.force) %7B%0A
+%09%09if (killByName) %7B%0A%09%09%09arguments_.unshift('-KILL');%0A%09%09%7D else %7B%0A%09
 %09%09arguments_.uns
@@ -1075,32 +1075,36 @@
 .unshift('-9');%0A
+%09%09%7D%0A
 %09%7D%0A%0A%09if (killByN

Fix `force` on macOS when killing by name (#41)