printable-shell-command 0.1.3 → 0.2.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/.github/workflows/CI.yaml +14 -0
- package/Makefile +15 -0
- package/README.md +4 -4
- package/bun.lock +11 -7
- package/package.json +6 -4
- package/src/index.ts +132 -25
- package/test/index.test.ts +152 -0
package/Makefile
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
.PHONY: test
|
|
2
|
+
test: lint test-js
|
|
3
|
+
|
|
1
4
|
.PHONY: lint
|
|
2
5
|
lint: setup
|
|
3
6
|
bun x @biomejs/biome check
|
|
@@ -6,6 +9,10 @@ lint: setup
|
|
|
6
9
|
format: setup
|
|
7
10
|
bun x @biomejs/biome check --write
|
|
8
11
|
|
|
12
|
+
.PHONY: test-js
|
|
13
|
+
test-js: setup
|
|
14
|
+
bun test
|
|
15
|
+
|
|
9
16
|
# https://github.com/lgarron/repo
|
|
10
17
|
REPO_COMMANDS = publish
|
|
11
18
|
|
|
@@ -18,3 +25,11 @@ ${REPO_COMMANDS}:
|
|
|
18
25
|
.PHONY: setup
|
|
19
26
|
setup:
|
|
20
27
|
bun install --frozen-lockfile
|
|
28
|
+
|
|
29
|
+
.PHONY: clean
|
|
30
|
+
clean:
|
|
31
|
+
# No-op
|
|
32
|
+
|
|
33
|
+
.PHONY: reset
|
|
34
|
+
reset: clean
|
|
35
|
+
rm -rf ./node_modules
|
package/README.md
CHANGED
|
@@ -34,10 +34,10 @@ This prints:
|
|
|
34
34
|
|
|
35
35
|
```shell
|
|
36
36
|
ffmpeg \
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
41
|
```
|
|
42
42
|
|
|
43
43
|
### Spawn a process in `node`
|
package/bun.lock
CHANGED
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
"workspaces": {
|
|
4
4
|
"": {
|
|
5
5
|
"name": "printable-shell-command",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@types/bun": "^1.2.11",
|
|
8
|
+
"@types/node": "^22.15.3",
|
|
9
|
+
},
|
|
6
10
|
"devDependencies": {
|
|
7
11
|
"@biomejs/biome": "^1.9.4",
|
|
8
|
-
"@types/bun": "^1.1.14",
|
|
9
|
-
"@types/node": "^22.10.2",
|
|
10
12
|
},
|
|
11
13
|
},
|
|
12
14
|
},
|
|
@@ -29,14 +31,16 @@
|
|
|
29
31
|
|
|
30
32
|
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="],
|
|
31
33
|
|
|
32
|
-
"@types/bun": ["@types/bun@1.2.
|
|
34
|
+
"@types/bun": ["@types/bun@1.2.11", "", { "dependencies": { "bun-types": "1.2.11" } }, "sha512-ZLbbI91EmmGwlWTRWuV6J19IUiUC5YQ3TCEuSHI3usIP75kuoA8/0PVF+LTrbEnVc8JIhpElWOxv1ocI1fJBbw=="],
|
|
35
|
+
|
|
36
|
+
"@types/node": ["@types/node@22.15.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw=="],
|
|
33
37
|
|
|
34
|
-
"
|
|
38
|
+
"bun-types": ["bun-types@1.2.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-dbkp5Lo8HDrXkLrONm6bk+yiiYQSntvFUzQp0v3pzTAsXk6FtgVMjdQ+lzFNVAmQFUkPQZ3WMZqH5tTo+Dp/IA=="],
|
|
35
39
|
|
|
36
|
-
"
|
|
40
|
+
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
|
37
41
|
|
|
38
|
-
"bun-types": ["
|
|
42
|
+
"bun-types/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
|
39
43
|
|
|
40
|
-
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
|
44
|
+
"bun-types/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
|
41
45
|
}
|
|
42
46
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "printable-shell-command",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"main": "./src/index.ts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -9,9 +9,11 @@
|
|
|
9
9
|
"import": "./src/index.ts"
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@types/bun": "^1.2.11",
|
|
14
|
+
"@types/node": "^22.15.3"
|
|
15
|
+
},
|
|
12
16
|
"devDependencies": {
|
|
13
|
-
"@biomejs/biome": "^1.9.4"
|
|
14
|
-
"@types/bun": "^1.1.14",
|
|
15
|
-
"@types/node": "^22.10.2"
|
|
17
|
+
"@biomejs/biome": "^1.9.4"
|
|
16
18
|
}
|
|
17
19
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ChildProcess as NodeChildProcess,
|
|
3
|
+
SpawnOptionsWithStdioTuple as NodeSpawnOptionsWithStdioTuple,
|
|
4
|
+
StdioNull as NodeStdioNull,
|
|
5
|
+
StdioPipe as NodeStdioPipe,
|
|
6
|
+
SpawnOptions as NodeSpawnOptions,
|
|
7
|
+
SpawnOptionsWithoutStdio as NodeSpawnOptionsWithoutStdio,
|
|
8
|
+
} from "node:child_process";
|
|
9
|
+
import type {
|
|
10
|
+
SpawnOptions as BunSpawnOptions,
|
|
11
|
+
Subprocess as BunSubprocess,
|
|
12
|
+
} from "bun";
|
|
13
|
+
|
|
14
|
+
const DEFAULT_MAIN_INDENTATION = "";
|
|
15
|
+
const DEFAULT_ARG_INDENTATION = " ";
|
|
16
|
+
const DEFAULT_ARGUMENT_LINE_WRAPPING = "by-entry";
|
|
17
|
+
|
|
1
18
|
const INLINE_SEPARATOR = " ";
|
|
2
|
-
const
|
|
19
|
+
const LINE_WRAP_LINE_END = " \\\n";
|
|
3
20
|
|
|
4
21
|
// biome-ignore lint/suspicious/noExplicitAny: This is the correct type nere.
|
|
5
22
|
function isString(s: any): s is string {
|
|
@@ -21,7 +38,12 @@ export interface PrintOptions {
|
|
|
21
38
|
// - "extra-safe": Quote all arguments, even ones that don't need it. This is
|
|
22
39
|
// more likely to be safe under all circumstances.
|
|
23
40
|
quoting?: "auto" | "extra-safe";
|
|
24
|
-
|
|
41
|
+
// Line wrapping to use between arguments. Defaults to `"by-entry"`.
|
|
42
|
+
argumentLineWrapping?:
|
|
43
|
+
| "by-entry"
|
|
44
|
+
| "nested-by-entry"
|
|
45
|
+
| "by-argument"
|
|
46
|
+
| "inline";
|
|
25
47
|
}
|
|
26
48
|
|
|
27
49
|
// https://mywiki.wooledge.org/BashGuide/SpecialCharacters
|
|
@@ -150,7 +172,7 @@ export class PrintableShellCommand {
|
|
|
150
172
|
#escapeArg(
|
|
151
173
|
arg: string,
|
|
152
174
|
isMainCommand: boolean,
|
|
153
|
-
options
|
|
175
|
+
options: PrintOptions,
|
|
154
176
|
): string {
|
|
155
177
|
const argCharacters = new Set(arg);
|
|
156
178
|
const specialShellCharacters = isMainCommand
|
|
@@ -169,49 +191,134 @@ export class PrintableShellCommand {
|
|
|
169
191
|
return arg;
|
|
170
192
|
}
|
|
171
193
|
|
|
172
|
-
#
|
|
173
|
-
return
|
|
194
|
+
#mainIndentation(options: PrintOptions): string {
|
|
195
|
+
return options?.mainIndentation ?? DEFAULT_MAIN_INDENTATION;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
#argIndentation(options: PrintOptions): string {
|
|
199
|
+
return (
|
|
200
|
+
this.#mainIndentation(options) +
|
|
201
|
+
(options?.argIndentation ?? DEFAULT_ARG_INDENTATION)
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
#lineWrapSeparator(options: PrintOptions): string {
|
|
206
|
+
return LINE_WRAP_LINE_END + this.#argIndentation(options);
|
|
174
207
|
}
|
|
175
208
|
|
|
176
|
-
#
|
|
177
|
-
|
|
209
|
+
#argPairSeparator(options: PrintOptions): string {
|
|
210
|
+
switch (options?.argumentLineWrapping ?? DEFAULT_ARGUMENT_LINE_WRAPPING) {
|
|
211
|
+
case "by-entry": {
|
|
212
|
+
return INLINE_SEPARATOR;
|
|
213
|
+
}
|
|
214
|
+
case "nested-by-entry": {
|
|
215
|
+
return this.#lineWrapSeparator(options) + this.#argIndentation(options);
|
|
216
|
+
}
|
|
217
|
+
case "by-argument": {
|
|
218
|
+
return this.#lineWrapSeparator(options);
|
|
219
|
+
}
|
|
220
|
+
case "inline": {
|
|
221
|
+
return INLINE_SEPARATOR;
|
|
222
|
+
}
|
|
223
|
+
default:
|
|
224
|
+
throw new Error("Invalid argument line wrapping argument.");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
#entrySeparator(options: PrintOptions): string {
|
|
229
|
+
switch (options?.argumentLineWrapping ?? DEFAULT_ARGUMENT_LINE_WRAPPING) {
|
|
230
|
+
case "by-entry": {
|
|
231
|
+
return LINE_WRAP_LINE_END + this.#argIndentation(options);
|
|
232
|
+
}
|
|
233
|
+
case "nested-by-entry": {
|
|
234
|
+
return LINE_WRAP_LINE_END + this.#argIndentation(options);
|
|
235
|
+
}
|
|
236
|
+
case "by-argument": {
|
|
237
|
+
return LINE_WRAP_LINE_END + this.#argIndentation(options);
|
|
238
|
+
}
|
|
239
|
+
case "inline": {
|
|
240
|
+
return INLINE_SEPARATOR;
|
|
241
|
+
}
|
|
242
|
+
default:
|
|
243
|
+
throw new Error("Invalid argument line wrapping argument.");
|
|
244
|
+
}
|
|
178
245
|
}
|
|
179
246
|
|
|
180
247
|
public getPrintableCommand(options?: PrintOptions): string {
|
|
181
|
-
|
|
248
|
+
// TODO: Why in the world does TypeScript not give the `options` arg the type of `PrintOptions | undefined`???
|
|
249
|
+
// biome-ignore lint/style/noParameterAssign: We want a default assignment without affecting the signature.
|
|
250
|
+
options ??= {};
|
|
251
|
+
const serializedEntries: string[] = [];
|
|
182
252
|
|
|
183
|
-
|
|
184
|
-
this.#
|
|
253
|
+
serializedEntries.push(
|
|
254
|
+
this.#mainIndentation(options) +
|
|
185
255
|
this.#escapeArg(this.commandName, true, options),
|
|
186
|
-
options,
|
|
187
|
-
),
|
|
188
256
|
);
|
|
189
257
|
|
|
190
|
-
// let pendingNewlineAfterPart = options?.separateLines === "dash-heuristic";
|
|
191
258
|
for (let i = 0; i < this.args.length; i++) {
|
|
192
259
|
const argsEntry = this.args[i];
|
|
193
260
|
|
|
194
261
|
if (isString(argsEntry)) {
|
|
195
|
-
|
|
196
|
-
this.#bigIndent(this.#escapeArg(argsEntry, false, options), options),
|
|
197
|
-
);
|
|
262
|
+
serializedEntries.push(this.#escapeArg(argsEntry, false, options));
|
|
198
263
|
} else {
|
|
199
264
|
const [part1, part2] = argsEntry;
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
this.#escapeArg(
|
|
204
|
-
INLINE_SEPARATOR +
|
|
205
|
-
this.#escapeArg(part2, false, options),
|
|
206
|
-
options,
|
|
207
|
-
),
|
|
265
|
+
serializedEntries.push(
|
|
266
|
+
this.#escapeArg(part1, false, options) +
|
|
267
|
+
this.#argPairSeparator(options) +
|
|
268
|
+
this.#escapeArg(part2, false, options),
|
|
208
269
|
);
|
|
209
270
|
}
|
|
210
271
|
}
|
|
211
|
-
|
|
272
|
+
|
|
273
|
+
return serializedEntries.join(this.#entrySeparator(options));
|
|
212
274
|
}
|
|
213
275
|
|
|
214
276
|
public print(options?: PrintOptions): void {
|
|
215
277
|
console.log(this.getPrintableCommand(options));
|
|
216
278
|
}
|
|
279
|
+
|
|
280
|
+
public spawnNode<
|
|
281
|
+
Stdin extends NodeStdioNull | NodeStdioPipe,
|
|
282
|
+
Stdout extends NodeStdioNull | NodeStdioPipe,
|
|
283
|
+
Stderr extends NodeStdioNull | NodeStdioPipe,
|
|
284
|
+
>(
|
|
285
|
+
options: NodeSpawnOptions
|
|
286
|
+
| NodeSpawnOptionsWithoutStdio | NodeSpawnOptionsWithStdioTuple<Stdin, Stdout, Stderr>,
|
|
287
|
+
): // TODO: figure out how to return `ChildProcessByStdio<…>` without duplicating fragile boilerplate.
|
|
288
|
+
NodeChildProcess {
|
|
289
|
+
const { spawn } = process.getBuiltinModule("node:child_process");
|
|
290
|
+
return spawn(...this.forNode(), options);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// The returned subprocess includes a `.success` `Promise` field, per https://github.com/oven-sh/bun/issues/8313
|
|
294
|
+
public spawnBun<
|
|
295
|
+
const In extends BunSpawnOptions.Writable = "ignore",
|
|
296
|
+
const Out extends BunSpawnOptions.Readable = "pipe",
|
|
297
|
+
const Err extends BunSpawnOptions.Readable = "inherit",
|
|
298
|
+
>(
|
|
299
|
+
options?: Omit<BunSpawnOptions.OptionsObject<In, Out, Err>, "cmd">,
|
|
300
|
+
): BunSubprocess<In, Out, Err> & { success: Promise<void> } {
|
|
301
|
+
const { spawn } = process.getBuiltinModule("bun") as typeof import("bun");
|
|
302
|
+
const subprocess = spawn({
|
|
303
|
+
...options,
|
|
304
|
+
cmd: this.forBun(),
|
|
305
|
+
}) as BunSubprocess<In, Out, Err> & { success: Promise<void> };
|
|
306
|
+
Object.defineProperty(subprocess, "success", {
|
|
307
|
+
get() {
|
|
308
|
+
return new Promise<void>((resolve, reject) =>
|
|
309
|
+
this.exited
|
|
310
|
+
.then((exitCode: number) => {
|
|
311
|
+
if (exitCode === 0) {
|
|
312
|
+
resolve();
|
|
313
|
+
} else {
|
|
314
|
+
reject("Command failed.");
|
|
315
|
+
}
|
|
316
|
+
})
|
|
317
|
+
.catch(reject),
|
|
318
|
+
);
|
|
319
|
+
},
|
|
320
|
+
enumerable: false,
|
|
321
|
+
});
|
|
322
|
+
return subprocess;
|
|
323
|
+
}
|
|
217
324
|
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { PrintableShellCommand } from "../src";
|
|
3
|
+
|
|
4
|
+
const rsyncCommand = new PrintableShellCommand("rsync", [
|
|
5
|
+
"-avz",
|
|
6
|
+
["--exclude", ".DS_Store"],
|
|
7
|
+
["--exclude", ".git"],
|
|
8
|
+
"./dist/web/experiments.cubing.net/test/deploy/",
|
|
9
|
+
"experiments.cubing.net:~/experiments.cubing.net/test/deploy/",
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
test("args for commands", () => {
|
|
13
|
+
expect(rsyncCommand.toCommandWithFlatArgs()).toEqual([
|
|
14
|
+
"rsync",
|
|
15
|
+
[
|
|
16
|
+
"-avz",
|
|
17
|
+
"--exclude",
|
|
18
|
+
".DS_Store",
|
|
19
|
+
"--exclude",
|
|
20
|
+
".git",
|
|
21
|
+
"./dist/web/experiments.cubing.net/test/deploy/",
|
|
22
|
+
"experiments.cubing.net:~/experiments.cubing.net/test/deploy/",
|
|
23
|
+
],
|
|
24
|
+
]);
|
|
25
|
+
expect(rsyncCommand.toFlatCommand()).toEqual([
|
|
26
|
+
"rsync",
|
|
27
|
+
"-avz",
|
|
28
|
+
"--exclude",
|
|
29
|
+
".DS_Store",
|
|
30
|
+
"--exclude",
|
|
31
|
+
".git",
|
|
32
|
+
"./dist/web/experiments.cubing.net/test/deploy/",
|
|
33
|
+
"experiments.cubing.net:~/experiments.cubing.net/test/deploy/",
|
|
34
|
+
]);
|
|
35
|
+
expect(rsyncCommand.toCommandWithFlatArgs()).toEqual(rsyncCommand.forNode());
|
|
36
|
+
expect(rsyncCommand.toFlatCommand()).toEqual(rsyncCommand.forBun());
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("default formatting", () => {
|
|
40
|
+
expect(rsyncCommand.getPrintableCommand()).toEqual(
|
|
41
|
+
`rsync \\
|
|
42
|
+
-avz \\
|
|
43
|
+
--exclude .DS_Store \\
|
|
44
|
+
--exclude .git \\
|
|
45
|
+
./dist/web/experiments.cubing.net/test/deploy/ \\
|
|
46
|
+
experiments.cubing.net:~/experiments.cubing.net/test/deploy/`,
|
|
47
|
+
);
|
|
48
|
+
expect(
|
|
49
|
+
rsyncCommand.getPrintableCommand({
|
|
50
|
+
quoting: "auto",
|
|
51
|
+
argumentLineWrapping: "by-entry",
|
|
52
|
+
}),
|
|
53
|
+
).toEqual(rsyncCommand.getPrintableCommand());
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("extra-safe quoting", () => {
|
|
57
|
+
expect(rsyncCommand.getPrintableCommand({ quoting: "extra-safe" })).toEqual(
|
|
58
|
+
`'rsync' \\
|
|
59
|
+
'-avz' \\
|
|
60
|
+
'--exclude' '.DS_Store' \\
|
|
61
|
+
'--exclude' '.git' \\
|
|
62
|
+
'./dist/web/experiments.cubing.net/test/deploy/' \\
|
|
63
|
+
'experiments.cubing.net:~/experiments.cubing.net/test/deploy/'`,
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("indentation", () => {
|
|
68
|
+
expect(
|
|
69
|
+
rsyncCommand.getPrintableCommand({ argIndentation: "\t \t" }),
|
|
70
|
+
).toEqual(
|
|
71
|
+
`rsync \\
|
|
72
|
+
-avz \\
|
|
73
|
+
--exclude .DS_Store \\
|
|
74
|
+
--exclude .git \\
|
|
75
|
+
./dist/web/experiments.cubing.net/test/deploy/ \\
|
|
76
|
+
experiments.cubing.net:~/experiments.cubing.net/test/deploy/`,
|
|
77
|
+
);
|
|
78
|
+
expect(rsyncCommand.getPrintableCommand({ argIndentation: "↪ " })).toEqual(
|
|
79
|
+
`rsync \\
|
|
80
|
+
↪ -avz \\
|
|
81
|
+
↪ --exclude .DS_Store \\
|
|
82
|
+
↪ --exclude .git \\
|
|
83
|
+
↪ ./dist/web/experiments.cubing.net/test/deploy/ \\
|
|
84
|
+
↪ experiments.cubing.net:~/experiments.cubing.net/test/deploy/`,
|
|
85
|
+
);
|
|
86
|
+
expect(rsyncCommand.getPrintableCommand({ mainIndentation: " " })).toEqual(
|
|
87
|
+
` rsync \\
|
|
88
|
+
-avz \\
|
|
89
|
+
--exclude .DS_Store \\
|
|
90
|
+
--exclude .git \\
|
|
91
|
+
./dist/web/experiments.cubing.net/test/deploy/ \\
|
|
92
|
+
experiments.cubing.net:~/experiments.cubing.net/test/deploy/`,
|
|
93
|
+
);
|
|
94
|
+
expect(
|
|
95
|
+
rsyncCommand.getPrintableCommand({
|
|
96
|
+
mainIndentation: "🙈",
|
|
97
|
+
argIndentation: "🙉",
|
|
98
|
+
}),
|
|
99
|
+
).toEqual(
|
|
100
|
+
`🙈rsync \\
|
|
101
|
+
🙈🙉-avz \\
|
|
102
|
+
🙈🙉--exclude .DS_Store \\
|
|
103
|
+
🙈🙉--exclude .git \\
|
|
104
|
+
🙈🙉./dist/web/experiments.cubing.net/test/deploy/ \\
|
|
105
|
+
🙈🙉experiments.cubing.net:~/experiments.cubing.net/test/deploy/`,
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("line wrapping", () => {
|
|
110
|
+
expect(
|
|
111
|
+
rsyncCommand.getPrintableCommand({ argumentLineWrapping: "by-entry" }),
|
|
112
|
+
).toEqual(rsyncCommand.getPrintableCommand());
|
|
113
|
+
expect(
|
|
114
|
+
rsyncCommand.getPrintableCommand({
|
|
115
|
+
argumentLineWrapping: "nested-by-entry",
|
|
116
|
+
}),
|
|
117
|
+
).toEqual(`rsync \\
|
|
118
|
+
-avz \\
|
|
119
|
+
--exclude \\
|
|
120
|
+
.DS_Store \\
|
|
121
|
+
--exclude \\
|
|
122
|
+
.git \\
|
|
123
|
+
./dist/web/experiments.cubing.net/test/deploy/ \\
|
|
124
|
+
experiments.cubing.net:~/experiments.cubing.net/test/deploy/`);
|
|
125
|
+
expect(
|
|
126
|
+
rsyncCommand.getPrintableCommand({ argumentLineWrapping: "by-argument" }),
|
|
127
|
+
).toEqual(`rsync \\
|
|
128
|
+
-avz \\
|
|
129
|
+
--exclude \\
|
|
130
|
+
.DS_Store \\
|
|
131
|
+
--exclude \\
|
|
132
|
+
.git \\
|
|
133
|
+
./dist/web/experiments.cubing.net/test/deploy/ \\
|
|
134
|
+
experiments.cubing.net:~/experiments.cubing.net/test/deploy/`);
|
|
135
|
+
expect(
|
|
136
|
+
rsyncCommand.getPrintableCommand({
|
|
137
|
+
argumentLineWrapping: "inline",
|
|
138
|
+
}),
|
|
139
|
+
).toEqual(
|
|
140
|
+
"rsync -avz --exclude .DS_Store --exclude .git ./dist/web/experiments.cubing.net/test/deploy/ experiments.cubing.net:~/experiments.cubing.net/test/deploy/",
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("command with space is escaped by default", () => {
|
|
145
|
+
const command = new PrintableShellCommand(
|
|
146
|
+
"/Applications/My App.app/Contents/Resources/my-app",
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
expect(command.getPrintableCommand()).toEqual(
|
|
150
|
+
`'/Applications/My App.app/Contents/Resources/my-app'`,
|
|
151
|
+
);
|
|
152
|
+
});
|