mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
Call untyped write completion callbacks with the error argument (#2585)
This commit is contained in:
@@ -6966,11 +6966,15 @@ export function lowerStreamMethodCall(
|
||||
const lowerWriteCb = (cbArg: ts.Expression): IrExpr => {
|
||||
// The stream ignores what the callback returns (`() => count++`, a
|
||||
// stored function returning a value): the value is discarded.
|
||||
// An untyped callback (a function a JavaScript module returned) is
|
||||
// called unbound with the error argument, as Node calls it.
|
||||
const raw = lowerer.lowerExpr(cbArg);
|
||||
const cb =
|
||||
raw.type.kind === "func" && raw.type.ret.kind !== "void" && raw.type.params.length <= 1
|
||||
? lowerer.coerceToExpected(raw, funcOf(raw.type.params, VOID))
|
||||
: raw;
|
||||
raw.type.kind === "dyn"
|
||||
? lowerer.coerceToExpected(raw, funcOf([DYN], VOID))
|
||||
: raw.type.kind === "func" && raw.type.ret.kind !== "void" && raw.type.params.length <= 1
|
||||
? lowerer.coerceToExpected(raw, funcOf(raw.type.params, VOID))
|
||||
: raw;
|
||||
if (cb.type.kind !== "func" || cb.type.ret.kind !== "void" || cb.type.params.length > 1) {
|
||||
lowerer.noLowering(
|
||||
"write completion callbacks of this shape",
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/stream-dynamic-write-callback/report.js",
|
||||
"<repo>/tests/corpus/stream-dynamic-write-callback/main.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Write completion callbacks from a JavaScript module run unbound with
|
||||
// Node's arguments: null after a successful write, the _write error after a
|
||||
// failed one, and the write-after-end error. Their return value is ignored.
|
||||
import { Writable } from "node:stream";
|
||||
import { makeReporter } from "./report.js";
|
||||
|
||||
const lines: string[] = [];
|
||||
const sink = new Writable({
|
||||
write(chunk: Buffer, _encoding: string, callback: (error?: Error | null) => void): void {
|
||||
callback(chunk.toString() === "bad" ? new Error("rejected") : null);
|
||||
},
|
||||
});
|
||||
sink.on("error", (error) => lines.push(`error event ${error.message}`));
|
||||
sink.write("good", makeReporter(lines, "good"));
|
||||
sink.write("bad", "utf8", makeReporter(lines, "bad"));
|
||||
|
||||
const ended = new Writable({
|
||||
write(_chunk: Buffer, _encoding: string, callback: (error?: Error | null) => void): void {
|
||||
callback();
|
||||
},
|
||||
});
|
||||
ended.on("error", () => lines.push("ended error event"));
|
||||
ended.end();
|
||||
ended.write("late", makeReporter(lines, "late"));
|
||||
|
||||
setTimeout(() => {
|
||||
for (const line of lines) console.log(line);
|
||||
}, 5);
|
||||
@@ -0,0 +1,11 @@
|
||||
// A write completion callback built in JavaScript: it records how it was
|
||||
// called. Module code is strict, so an unbound call sees `this` undefined.
|
||||
export function makeReporter(lines, label) {
|
||||
return function (...args) {
|
||||
const error = args[0];
|
||||
lines.push(
|
||||
`${label} args=${args.length} error=${error === null ? "null" : error === undefined ? "undefined" : error.message} this=${typeof this}`,
|
||||
);
|
||||
return "ignored";
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{ "plain": 1833, "san": 2753 }
|
||||
Reference in New Issue
Block a user