slash-port 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +209 -0
- package/dist/cli.js +238 -0
- package/dist/describe.js +264 -0
- package/dist/format.js +87 -0
- package/dist/kill.js +117 -0
- package/dist/scan/darwin.js +135 -0
- package/dist/scan/index.js +73 -0
- package/dist/scan/linux.js +239 -0
- package/dist/scan/shared.js +56 -0
- package/dist/scan/win32.js +148 -0
- package/dist/types.js +9 -0
- package/dist/ui/App.js +223 -0
- package/dist/ui/theme.js +86 -0
- package/package.json +57 -0
package/dist/ui/theme.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Only the sixteen named terminal colours are used, never hex or 256-colour
|
|
3
|
+
* codes, so the UI inherits whatever palette the user has chosen instead of
|
|
4
|
+
* fighting it. Colour is always redundant: a protected row is also refused and
|
|
5
|
+
* also marked in text.
|
|
6
|
+
*/
|
|
7
|
+
export const roles = {
|
|
8
|
+
heading: 'cyan',
|
|
9
|
+
muted: 'gray',
|
|
10
|
+
danger: 'red',
|
|
11
|
+
warn: 'yellow',
|
|
12
|
+
ok: 'green',
|
|
13
|
+
accent: 'magenta',
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Honours NO_COLOR. Read on every call rather than at import time, because
|
|
17
|
+
* `--no-color` is parsed after this module is loaded.
|
|
18
|
+
*/
|
|
19
|
+
export function colorEnabled() {
|
|
20
|
+
return !process.env['NO_COLOR'];
|
|
21
|
+
}
|
|
22
|
+
export function color(role) {
|
|
23
|
+
return colorEnabled() ? roles[role] : undefined;
|
|
24
|
+
}
|
|
25
|
+
const ELLIPSIS = '…';
|
|
26
|
+
/** Truncate with an ellipsis, so a cut value is visibly cut rather than a lie. */
|
|
27
|
+
export function truncate(value, width) {
|
|
28
|
+
if (width <= 0)
|
|
29
|
+
return '';
|
|
30
|
+
if (value.length <= width)
|
|
31
|
+
return value;
|
|
32
|
+
if (width === 1)
|
|
33
|
+
return ELLIPSIS;
|
|
34
|
+
return value.slice(0, width - 1) + ELLIPSIS;
|
|
35
|
+
}
|
|
36
|
+
export function cell(value, width) {
|
|
37
|
+
return truncate(value, width).padEnd(width);
|
|
38
|
+
}
|
|
39
|
+
const PORT_WIDTH = 11; // "65535/tcp" plus a little slack
|
|
40
|
+
const PID_WIDTH = 7;
|
|
41
|
+
const USER_WIDTH = 10;
|
|
42
|
+
const PROCESS_WIDTH = 18;
|
|
43
|
+
const ADDRESS_WIDTH = 17;
|
|
44
|
+
const MIN_DESCRIPTION = 12;
|
|
45
|
+
/** What the description is worth before an optional column gets anything. */
|
|
46
|
+
const TARGET_DESCRIPTION = 28;
|
|
47
|
+
/**
|
|
48
|
+
* Column widths that always add up to exactly the terminal width, including
|
|
49
|
+
* the single-space gaps. Nothing may overflow: a row one character too wide
|
|
50
|
+
* wraps, and a wrapped row destroys the alignment of every row below it.
|
|
51
|
+
*
|
|
52
|
+
* Columns are allocated by how much they carry. The port is never dropped,
|
|
53
|
+
* the description is reserved next because it is the reason the tool exists,
|
|
54
|
+
* and the rest compete for what is left — so an 80-column window keeps the
|
|
55
|
+
* columns that matter and a 40-column one still reads.
|
|
56
|
+
*
|
|
57
|
+
* A zero width means the column is not shown at all.
|
|
58
|
+
*/
|
|
59
|
+
export function layout(totalWidth) {
|
|
60
|
+
const total = Math.max(24, Math.floor(totalWidth) || 80);
|
|
61
|
+
const port = PORT_WIDTH;
|
|
62
|
+
let budget = total - port;
|
|
63
|
+
let description = Math.min(MIN_DESCRIPTION, budget - 1);
|
|
64
|
+
budget -= description + 1;
|
|
65
|
+
/** Take a column and its leading gap, or nothing if it does not fit. */
|
|
66
|
+
const take = (want) => {
|
|
67
|
+
if (budget < want + 1)
|
|
68
|
+
return 0;
|
|
69
|
+
budget -= want + 1;
|
|
70
|
+
return want;
|
|
71
|
+
};
|
|
72
|
+
const pid = take(PID_WIDTH);
|
|
73
|
+
const process = take(PROCESS_WIDTH);
|
|
74
|
+
const user = take(USER_WIDTH);
|
|
75
|
+
// Bring the description up to a width that can hold "Vite dev server
|
|
76
|
+
// (admin)" before spending anything on the address, which is the column
|
|
77
|
+
// that carries the least. At 80 columns that is the difference between a
|
|
78
|
+
// readable description and a truncated one.
|
|
79
|
+
const bump = Math.min(TARGET_DESCRIPTION - description, budget);
|
|
80
|
+
description += bump;
|
|
81
|
+
budget -= bump;
|
|
82
|
+
const address = take(ADDRESS_WIDTH);
|
|
83
|
+
// Whatever is left widens the description rather than being left as a gap.
|
|
84
|
+
description += budget;
|
|
85
|
+
return { port, pid, user, process, address, description, total };
|
|
86
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "slash-port",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "See what is listening on your ports, understand what it is, and kill it safely.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"port",
|
|
7
|
+
"ports",
|
|
8
|
+
"kill-port",
|
|
9
|
+
"eaddrinuse",
|
|
10
|
+
"lsof",
|
|
11
|
+
"netstat",
|
|
12
|
+
"tui",
|
|
13
|
+
"cli",
|
|
14
|
+
"developer-tools"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Slash UI Pte Ltd",
|
|
18
|
+
"homepage": "https://github.com/Slash-ui/slash-port#readme",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/Slash-ui/slash-port.git"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/Slash-ui/slash-port/issues"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"bin": {
|
|
28
|
+
"slash-port": "dist/cli.js"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=22"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -p tsconfig.build.json && chmod +x dist/cli.js",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"test": "vitest run",
|
|
42
|
+
"test:watch": "vitest",
|
|
43
|
+
"start": "node dist/cli.js",
|
|
44
|
+
"prepublishOnly": "npm run build"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"ink": "^7.1.1",
|
|
48
|
+
"react": "^19.2.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^24.0.0",
|
|
52
|
+
"@types/react": "^19.2.0",
|
|
53
|
+
"ink-testing-library": "^4.0.0",
|
|
54
|
+
"typescript": "^5.9.0",
|
|
55
|
+
"vitest": "^4.1.0"
|
|
56
|
+
}
|
|
57
|
+
}
|