Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 57 additions & 2 deletions interactive.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,61 @@ const renderProcessForDisplay = (process_, flags, memoryThreshold, cpuThreshold)
};
};

const renderProcessGroupForDisplay = (processes, flags, memoryThreshold, cpuThreshold) => {
const representativeProcess = processes[0];
const processIds = processes.map(process_ => process_.pid);
const allPorts = processes.flatMap(process_ => process_.ports);
const ports = [...new Set(allPorts)].slice(0, 4);
const totalCpu = processes.reduce((total, process_) => total + (process_.cpu ?? 0), 0);
const totalMemory = processes.reduce((total, process_) => total + (process_.memory ?? 0), 0);

return renderProcessForDisplay({
...representativeProcess,
pid: processIds.length,
name: `Kill all ${processes.length} ${representativeProcess.name} processes`,
cmd: `Kill all ${processes.length} ${representativeProcess.name} processes`,
ports,
cpu: totalCpu,
memory: totalMemory,
}, flags, memoryThreshold, cpuThreshold);
};

const createProcessChoices = (processes, flags, memoryThreshold, cpuThreshold) => {
const processGroups = new Map();

for (const process_ of processes) {
const groupedProcesses = processGroups.get(process_.name) ?? [];
groupedProcesses.push(process_);
processGroups.set(process_.name, groupedProcesses);
}

const groupedProcessNames = new Set([...processGroups]
.filter(([, groupedProcesses]) => groupedProcesses.length > 1)
.map(([processName]) => processName));

const renderedGroups = new Set();
const choices = [];

for (const process_ of processes) {
if (!groupedProcessNames.has(process_.name)) {
choices.push(renderProcessForDisplay(process_, flags, memoryThreshold, cpuThreshold));
continue;
}

if (renderedGroups.has(process_.name)) {
continue;
}

const groupedProcesses = processGroups.get(process_.name);
const choice = renderProcessGroupForDisplay(groupedProcesses, flags, memoryThreshold, cpuThreshold);
choice.value = groupedProcesses.map(process_ => process_.pid);
choices.push(choice);
renderedGroups.add(process_.name);
}

return choices;
};

const searchProcessesByPort = (processes, port) => processes.filter(process_ => process_.ports.includes(port));

const searchProcessByPid = (processes, pid) => processes.find(process_ => String(process_.pid) === pid);
Expand Down Expand Up @@ -261,7 +316,7 @@ const listProcesses = async (processes, flags) => {
pageSize: 10,
async source(term = '') {
const matchingProcesses = filterAndSortProcesses(processes, term, searcher, flags);
return matchingProcesses.map(process_ => renderProcessForDisplay(process_, flags, memoryThreshold, cpuThreshold));
return createProcessChoices(matchingProcesses, flags, memoryThreshold, cpuThreshold);
},
});

Expand All @@ -284,4 +339,4 @@ const init = async flags => {
listProcesses(processesWithPorts, flags);
};

export {init, handleFkillError};
export {init, handleFkillError, createProcessChoices};
35 changes: 35 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import delay from 'delay';
import noopProcess from 'noop-process';
import {processExists} from 'process-exists';
import getPort from 'get-port';
import {createProcessChoices} from './interactive.js';

const noopProcessKilled = async (t, pid) => {
// Ensure the noop process has time to exit
Expand Down Expand Up @@ -97,3 +98,37 @@ test('silent flag with -s shortflag works', async t => {
const {exitCode} = await execa('./cli.js', ['-s', '--force', ':1337']);
t.is(exitCode, 0);
});

test('groups same-name processes for interactive kill all', t => {
const choices = createProcessChoices([
{
name: 'node',
cmd: 'node server.js',
pid: 101,
ports: [],
cpu: 0,
memory: 0,
},
{
name: 'node',
cmd: 'node worker.js',
pid: 102,
ports: [],
cpu: 0,
memory: 0,
},
{
name: 'redis',
cmd: 'redis-server',
pid: 201,
ports: [],
cpu: 0,
memory: 0,
},
], {}, 1, 3);

t.is(choices.length, 2);
t.deepEqual(choices[0].value, [101, 102]);
t.regex(choices[0].name, /Kill all 2 node processes/);
t.is(choices[1].value, 201);
});
Loading