Skip to content

Commit 314d6ec

Browse files
authored
Let completion output drain before exit (#178)
* fix: let completion output drain before exit * test: document the portable pipe control
1 parent 86dcc5e commit 314d6ec

3 files changed

Lines changed: 33 additions & 2 deletions

File tree

src/cli.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1672,7 +1672,8 @@ async function main(): Promise<void> {
16721672
case "completions": {
16731673
// `pty completions <shell>` — print a fish/bash/zsh completion script.
16741674
const { cmdCompletions } = await import("./completions.ts");
1675-
process.exit(cmdCompletions(args.slice(1)));
1675+
process.exitCode = cmdCompletions(args.slice(1));
1676+
return;
16761677
}
16771678

16781679
case "version":

tests/completions.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { describe, it, expect } from "vitest";
1313
import * as fs from "node:fs";
1414
import * as os from "node:os";
1515
import * as path from "node:path";
16-
import { fileURLToPath } from "node:url";
16+
import { fileURLToPath, pathToFileURL } from "node:url";
1717
import { spawnSync } from "node:child_process";
1818
import { execSync } from "node:child_process";
1919
import { COMMANDS } from "../src/completions.ts";
@@ -186,6 +186,26 @@ printf '%s\n' "\${COMPREPLY[@]}"`;
186186
}
187187
});
188188

189+
it("lets piped stdout drain before exiting", () => {
190+
const delayedStdout = pathToFileURL(
191+
path.join(__dirname, "fixtures", "delayed-stdout.mjs"),
192+
).href;
193+
for (const shell of ["fish", "bash", "zsh"]) {
194+
const r = spawnSync(
195+
nodeBin,
196+
["--import", delayedStdout, cliPath, "completions", shell],
197+
{ encoding: "utf8" },
198+
);
199+
const checkedIn = fs.readFileSync(
200+
path.join(__dirname, "..", "completions", `pty.${shell}`),
201+
"utf8",
202+
);
203+
204+
expect(r.status, r.stderr).toBe(0);
205+
expect(r.stdout, `${shell} output was truncated`).toBe(checkedIn);
206+
}
207+
});
208+
189209
it("prints usage and exits non-zero for an unknown shell", () => {
190210
const r = spawnSync(nodeBin, [cliPath, "completions", "tcsh"], {
191211
encoding: "utf8",

tests/fixtures/delayed-stdout.mjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// The natural race depends on the Node version, operating system, and pipe
2+
// scheduling, so it does not reproduce on every supported setup. Delay the
3+
// write so a forced exit loses the text on every platform, independent of
4+
// pipe size or scheduling luck.
5+
const write = process.stdout.write.bind(process.stdout);
6+
7+
process.stdout.write = (chunk, encoding, callback) => {
8+
setTimeout(() => write(chunk, encoding, callback), 25);
9+
return false;
10+
};

0 commit comments

Comments
 (0)