printable-shell-command 0.1.0 → 0.1.2
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/.github/workflows/publish-github-release.yaml +13 -0
- package/Makefile +15 -2
- package/README.md +29 -15
- package/bun.lock +42 -0
- package/package.json +2 -2
- package/src/index.ts +55 -1
- package/test/simple-test-bun.ts +1 -1
- package/bun.lockb +0 -0
package/Makefile
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
.PHONY: lint
|
|
2
|
-
lint:
|
|
2
|
+
lint: setup
|
|
3
3
|
bun x @biomejs/biome check
|
|
4
4
|
|
|
5
5
|
.PHONY: format
|
|
6
|
-
format:
|
|
6
|
+
format: setup
|
|
7
7
|
bun x @biomejs/biome check --write
|
|
8
|
+
|
|
9
|
+
# https://github.com/lgarron/repo
|
|
10
|
+
REPO_COMMANDS = publish
|
|
11
|
+
|
|
12
|
+
publish: setup
|
|
13
|
+
|
|
14
|
+
.PHONY: ${REPO_COMMANDS}
|
|
15
|
+
${REPO_COMMANDS}:
|
|
16
|
+
repo $@
|
|
17
|
+
|
|
18
|
+
.PHONY: setup
|
|
19
|
+
setup:
|
|
20
|
+
bun install --frozen-lockfile
|
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# `printable-shell-command`
|
|
2
2
|
|
|
3
|
-
A helper class to construct shell
|
|
3
|
+
A helper class to construct shell commands in a way that allows printing them.
|
|
4
4
|
|
|
5
|
-
The goal is to make it easy to print commands that are
|
|
5
|
+
The goal is to make it easy to print commands that are being run by a program, in a way that makes it easy and safe for a user to copy-and-paste.
|
|
6
6
|
|
|
7
|
-
Goals
|
|
7
|
+
## Goals
|
|
8
8
|
|
|
9
9
|
1. Security — the printed commands should be possible to use in all shells without injection vulnerabilities.
|
|
10
|
-
2. Fidelity —
|
|
11
|
-
3.
|
|
10
|
+
2. Fidelity — the printed command must match the arguments provided.
|
|
11
|
+
3. Aesthetics — the command is pretty-printed to make it easy to read and to avoid escaping/quoting simple arguments where humans usually would not.
|
|
12
12
|
|
|
13
13
|
Point 1 is difficult, and maybe even impossible. This library will do its best, but what you don't know can hurt you.
|
|
14
14
|
|
|
@@ -17,9 +17,10 @@ Point 1 is difficult, and maybe even impossible. This library will do its best,
|
|
|
17
17
|
Construct a command by providing a command string and a list of arguments. Each argument can either be an individual string, or a "pair" list containing two strings (usually a command flag and its argument). Pairs do not affect the semantics of the command, but they affect pretty-printing.
|
|
18
18
|
|
|
19
19
|
```typescript
|
|
20
|
+
// example.ts
|
|
20
21
|
import { PrintableShellCommand } from "printable-shell-command";
|
|
21
22
|
|
|
22
|
-
const command = new PrintableShellCommand("ffmpeg", [
|
|
23
|
+
export const command = new PrintableShellCommand("ffmpeg", [
|
|
23
24
|
["-i", "./test/My video.mp4"],
|
|
24
25
|
["-filter:v", "setpts=2.0*PTS"],
|
|
25
26
|
["-filter:a", "atempo=0.5"],
|
|
@@ -29,31 +30,44 @@ const command = new PrintableShellCommand("ffmpeg", [
|
|
|
29
30
|
command.print();
|
|
30
31
|
```
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
This prints:
|
|
34
|
+
|
|
35
|
+
```shell
|
|
36
|
+
ffmpeg \
|
|
37
|
+
-i './test/My video.mp4' \
|
|
38
|
+
-filter:v 'setpts=2.0*PTS' \
|
|
39
|
+
-filter:a atempo=0.5 \
|
|
40
|
+
'./test/My video (slow-mo).mov'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Spawn a process in `node`
|
|
33
44
|
|
|
34
45
|
```typescript
|
|
46
|
+
import { PrintableShellCommand } from "printable-shell-command";
|
|
35
47
|
import { spawn } from "node:child_process";
|
|
36
48
|
|
|
37
|
-
|
|
38
|
-
const child_process = spawn(...command.toCommandWithFlatArgs());
|
|
49
|
+
const command = new PrintableShellCommand(/* … */);
|
|
50
|
+
const child_process = spawn(...command.toCommandWithFlatArgs()); // Note the `...`
|
|
39
51
|
```
|
|
40
52
|
|
|
41
|
-
###
|
|
53
|
+
### Spawn a process in `bun`
|
|
42
54
|
|
|
43
55
|
```typescript
|
|
56
|
+
import { PrintableShellCommand } from "printable-shell-command";
|
|
44
57
|
import { spawn } from "bun";
|
|
45
58
|
|
|
46
|
-
|
|
59
|
+
const command = new PrintableShellCommand(/* … */);
|
|
60
|
+
await spawn(command.toFlatCommand()).exited;
|
|
47
61
|
```
|
|
48
62
|
|
|
49
63
|
## Protections
|
|
50
64
|
|
|
51
65
|
Any command or argument containing the following characters is quoted and escaped:
|
|
52
66
|
|
|
53
|
-
- space character
|
|
54
|
-
- `"`
|
|
55
|
-
- `'`
|
|
56
|
-
-
|
|
67
|
+
- <code> </code> (space character)
|
|
68
|
+
- `"` (double quote)
|
|
69
|
+
- `'` (single quote)
|
|
70
|
+
- <code>`</code> (backtick)
|
|
57
71
|
- `|`
|
|
58
72
|
- `$`
|
|
59
73
|
- `*`
|
package/bun.lock
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"workspaces": {
|
|
4
|
+
"": {
|
|
5
|
+
"name": "printable-shell-command",
|
|
6
|
+
"devDependencies": {
|
|
7
|
+
"@biomejs/biome": "^1.9.4",
|
|
8
|
+
"@types/bun": "^1.1.14",
|
|
9
|
+
"@types/node": "^22.10.2",
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
"packages": {
|
|
14
|
+
"@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="],
|
|
15
|
+
|
|
16
|
+
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="],
|
|
17
|
+
|
|
18
|
+
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="],
|
|
19
|
+
|
|
20
|
+
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="],
|
|
21
|
+
|
|
22
|
+
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="],
|
|
23
|
+
|
|
24
|
+
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="],
|
|
25
|
+
|
|
26
|
+
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="],
|
|
27
|
+
|
|
28
|
+
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="],
|
|
29
|
+
|
|
30
|
+
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="],
|
|
31
|
+
|
|
32
|
+
"@types/bun": ["@types/bun@1.2.4", "", { "dependencies": { "bun-types": "1.2.4" } }, "sha512-QtuV5OMR8/rdKJs213iwXDpfVvnskPXY/S0ZiFbsTjQZycuqPbMW8Gf/XhLfwE5njW8sxI2WjISURXPlHypMFA=="],
|
|
33
|
+
|
|
34
|
+
"@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
|
35
|
+
|
|
36
|
+
"@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
|
|
37
|
+
|
|
38
|
+
"bun-types": ["bun-types@1.2.4", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-nDPymR207ZZEoWD4AavvEaa/KZe/qlrbMSchqpQwovPZCKc7pwMoENjEtHgMKaAjJhy+x6vfqSBA1QU3bJgs0Q=="],
|
|
39
|
+
|
|
40
|
+
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
|
41
|
+
}
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "printable-shell-command",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"main": "./src/index.ts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"import": "./src/index.ts"
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
|
-
"
|
|
12
|
+
"devDependencies": {
|
|
13
13
|
"@biomejs/biome": "^1.9.4",
|
|
14
14
|
"@types/bun": "^1.1.14",
|
|
15
15
|
"@types/node": "^22.10.2"
|
package/src/index.ts
CHANGED
|
@@ -52,14 +52,16 @@ const SPECIAL_SHELL_CHARACTERS_FOR_MAIN_COMMAND =
|
|
|
52
52
|
SPECIAL_SHELL_CHARACTERS.union(new Set(["="]));
|
|
53
53
|
|
|
54
54
|
export class PrintableShellCommand {
|
|
55
|
+
#commandName: string;
|
|
55
56
|
constructor(
|
|
56
|
-
|
|
57
|
+
commandName: string,
|
|
57
58
|
private args: Args = [],
|
|
58
59
|
) {
|
|
59
60
|
if (!isString(commandName)) {
|
|
60
61
|
// biome-ignore lint/suspicious/noExplicitAny: We want to print this, no matter what it is.
|
|
61
62
|
throw new Error("Command name is not a string:", commandName as any);
|
|
62
63
|
}
|
|
64
|
+
this.#commandName = commandName;
|
|
63
65
|
if (typeof args === "undefined") {
|
|
64
66
|
return;
|
|
65
67
|
}
|
|
@@ -83,15 +85,67 @@ export class PrintableShellCommand {
|
|
|
83
85
|
}
|
|
84
86
|
}
|
|
85
87
|
|
|
88
|
+
get commandName(): string {
|
|
89
|
+
return this.#commandName;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// For use with `bun`.
|
|
93
|
+
//
|
|
94
|
+
// Usage example:
|
|
95
|
+
//
|
|
96
|
+
// import { PrintableShellCommand } from "printable-shell-command";
|
|
97
|
+
// import { spawn } from "bun";
|
|
98
|
+
//
|
|
99
|
+
// const command = new PrintableShellCommand(/* … */);
|
|
100
|
+
// await spawn(command.toFlatCommand()).exited;
|
|
101
|
+
//
|
|
86
102
|
public toFlatCommand(): string[] {
|
|
87
103
|
return [this.commandName, ...this.args.flat()];
|
|
88
104
|
}
|
|
89
105
|
|
|
106
|
+
// Convenient alias for `toFlatCommand()`.
|
|
107
|
+
//
|
|
108
|
+
// Usage example:
|
|
109
|
+
//
|
|
110
|
+
// import { PrintableShellCommand } from "printable-shell-command";
|
|
111
|
+
// import { spawn } from "bun";
|
|
112
|
+
//
|
|
113
|
+
// const command = new PrintableShellCommand(/* … */);
|
|
114
|
+
// await spawn(command.forBun()).exited;
|
|
115
|
+
//
|
|
116
|
+
public forBun(): string[] {
|
|
117
|
+
return this.toFlatCommand();
|
|
118
|
+
}
|
|
119
|
+
|
|
90
120
|
// For use with `node:child_process`
|
|
121
|
+
//
|
|
122
|
+
// Usage example:
|
|
123
|
+
//
|
|
124
|
+
// import { PrintableShellCommand } from "printable-shell-command";
|
|
125
|
+
// import { spawn } from "node:child_process";
|
|
126
|
+
//
|
|
127
|
+
// const command = new PrintableShellCommand(/* … */);
|
|
128
|
+
// const child_process = spawn(...command.toCommandWithFlatArgs()); // Note the `...`
|
|
129
|
+
//
|
|
91
130
|
public toCommandWithFlatArgs(): [string, string[]] {
|
|
92
131
|
return [this.commandName, this.args.flat()];
|
|
93
132
|
}
|
|
94
133
|
|
|
134
|
+
// For use with `node:child_process`
|
|
135
|
+
//
|
|
136
|
+
// Usage example:
|
|
137
|
+
//
|
|
138
|
+
// import { PrintableShellCommand } from "printable-shell-command";
|
|
139
|
+
// import { spawn } from "node:child_process";
|
|
140
|
+
//
|
|
141
|
+
// const command = new PrintableShellCommand(/* … */);
|
|
142
|
+
// const child_process = spawn(...command.forNode()); // Note the `...`
|
|
143
|
+
//
|
|
144
|
+
// Convenient alias for `toCommandWithFlatArgs()`.
|
|
145
|
+
public forNode(): [string, string[]] {
|
|
146
|
+
return this.toCommandWithFlatArgs();
|
|
147
|
+
}
|
|
148
|
+
|
|
95
149
|
#escapeArg(
|
|
96
150
|
arg: string,
|
|
97
151
|
isMainCommand: boolean,
|
package/test/simple-test-bun.ts
CHANGED
package/bun.lockb
DELETED
|
Binary file
|