Skip to content

Commit

Permalink
Merge pull request #227 from arethetypeswrong/bug/184
Browse files Browse the repository at this point in the history
Fix truncated stdout when piping more than 64kb
  • Loading branch information
andrewbranch authored Dec 6, 2024
2 parents 1f3381f + 55544ac commit 2950f9b
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .changeset/polite-gifts-judge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@arethetypeswrong/cli": patch
---

Fix truncated stdout when piping more than 64kb to another process
17 changes: 12 additions & 5 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import * as render from "./render/index.js";
import { major, minor } from "semver";
import { getExitCode } from "./getExitCode.js";
import { applyProfile, profiles } from "./profiles.js";
import { write } from "./write.js";
import { Writable } from "stream";

const packageJson = createRequire(import.meta.url)("../package.json");
const version = packageJson.version;
Expand Down Expand Up @@ -98,8 +100,13 @@ particularly ESM-related module resolution issues.`,
applyProfile(opts.profile, opts);
}

let out: Writable = process.stdout;
if (opts.quiet) {
console.log = () => {};
out = new (class extends Writable {
_write(_chunk: any, _encoding: BufferEncoding, callback: (error?: Error | null) => void) {
callback();
}
})();
}

if (!opts.color) {
Expand Down Expand Up @@ -223,7 +230,7 @@ particularly ESM-related module resolution issues.`,
result.problems = groupProblemsByKind(analysis.problems);
}

console.log(JSON.stringify(result, undefined, 2));
await write(JSON.stringify(result, undefined, 2), out);

if (deleteTgz) {
await unlink(deleteTgz);
Expand All @@ -237,12 +244,12 @@ particularly ESM-related module resolution issues.`,
return;
}

console.log();
await write("", out);
if (analysis.types) {
console.log(await render.typed(analysis, opts));
await write(await render.typed(analysis, opts), out);
process.exitCode = getExitCode(analysis, opts);
} else {
console.log(render.untyped(analysis as core.UntypedResult));
await write(render.untyped(analysis as core.UntypedResult), out);
}

if (deleteTgz) {
Expand Down
27 changes: 27 additions & 0 deletions packages/cli/src/write.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Readable, Writable } from "node:stream";

// JSON output is often longer than 64 kb, so we need to use streams to write it to stdout
// in order to avoid truncation when piping to other commands.
export async function write(data: string, out: Writable): Promise<void> {
return new Promise((resolve, reject) => {
const stream = new Readable({
read() {
this.push(data);
this.push("\n");
this.push(null);
},
});

stream.on("data", (chunk) => {
out.write(chunk);
});

stream.on("end", () => {
resolve();
});

out.on("error", (err) => {
reject(err);
});
});
}
7 changes: 4 additions & 3 deletions packages/cli/test/snapshots.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { execFileSync, type SpawnSyncReturns } from "child_process";
import { access, readFile, writeFile } from "fs/promises";
import { execSync, type SpawnSyncReturns } from "child_process";
import assert from "node:assert";
import path from "node:path";
import { after, describe, test } from "node:test";
Expand All @@ -10,7 +10,7 @@ function resolveFileRelativePath(relPath: string) {
return path.resolve(directoryPath, relPath);
}

const attw = `node ${resolveFileRelativePath("../../dist/index.js")}`;
const attw = resolveFileRelativePath("../../dist/index.js");
const updateSnapshots = process.env.UPDATE_SNAPSHOTS || process.env.U;
const testFilter = (process.env.TEST_FILTER || process.env.T)?.toLowerCase();

Expand Down Expand Up @@ -91,7 +91,8 @@ describe("snapshots", async () => {
let stderr = "";
let exitCode = 0;
try {
stdout = execSync(`${attw} ${tarballPath} ${options ?? defaultOpts}`, {
stdout = execFileSync(process.execPath, [attw, tarballPath, ...(options ?? defaultOpts).split(" ")], {
maxBuffer: 1024 * 1024 * 1024,
encoding: "utf8",
env: { ...process.env, FORCE_COLOR: "0" },
});
Expand Down

0 comments on commit 2950f9b

Please sign in to comment.