Latest Update: The issue mentioned has been resolved through a fix included in the pull request available at denoland/deno_std#3103. This fix has been successfully merged and will be part of the upcoming release version of the std
library, starting from 0.173.0
.
The data type of cmd1.stdout
referenced in your query is ReadableStream<Uint8Array>
. Upon passing this through other transform stream classes, the outcome transforms into a ReadableStream<string>
. In scenarios where you utilize a for await...of
loop, each yielded value is essentially a line (string
), hence lacking an inherent reference to determine the end of the stream within the loop for decision-making. (This remains valid even if one were to manually iterate over the stream's
AsyncIterableIterator<string>
.) By the time the loop concludes after the stream ends, it becomes unfeasible to retroactively undo handling of the final (empty) line.
Nevertheless, combatting this predicament involves storing each previous line outside the loop in a variable, subsequently utilizing this preceding line inside the loop instead of the current line. Post-loop completion, assess whether the final previous line is devoid of content — if so, ignore it as required.
Below is a self-contained example that delivers identical results across all platforms without necessitating permissions. It draws upon the information in your inquiry, contrasting the presented input with varied outputs demonstrating diverse endings for comparison purposes. Additionally, it offers line numbers, aiding referencing against the default behavior of TextLineStream
. Noteworthy code resides within the RefinedTextLineStream
class, seamlessly implementable in your own code for achieving the desired output:
const stream = cmd1.stdout
.pipeThrough(new TextDecoderStream())
.pipeThrough(new TextLineStream())
.pipeThrough(new RefinedTextLineStream());
for await (const [line] of stream) {
console.log("result: ", line);
}
Here's the test scenario showcasing reproducible results:
so-74905946.ts
:
import { TextLineStream } from "https://deno.land/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="097a7d6d493927383e392739">[email protected]</a>/streams/text_line_stream.ts";
type LineWithNumber = [line: string, lineNumber: number];
/** Custom implementation tailored for Deno's std library TextLineStream */
class RefinedTextLineStream extends TransformStream<string, LineWithNumber> {
#lineNumber = 0;
#previous = "";
constructor() {
super({
transform: (textLine, controller) => {
if (this.#lineNumber > 0) {
controller.enqueue([this.#previous, this.#lineNumber]);
}
this.#lineNumber += 1;
this.#previous = textLine;
},
flush: (controller) => {
if (this.#previous.length > 0) {
controller.enqueue([this.#previous, this.#lineNumber]);
}
},
});
}
}
const exampleInputs: [name: string, input: string][] = [
["input from question", "foo\n\nbar\n"],
["input with extra final line feed", "foo\n\nbar\n\n"],
["input without final line feed", "foo\n\nbar"],
];
for (const [name, input] of exampleInputs) {
console.log(`\n${name}: ${JSON.stringify(input)}`);
const [textLineStream1, textLineStream2] = new File([input], "untitled")
.stream()
.pipeThrough(new TextDecoderStream())
.pipeThrough(new TextLineStream())
.tee();
console.log("\ndefault:");
let lineNumber = 0;
for await (const line of textLineStream1) {
lineNumber += 1;
console.log(lineNumber, line);
}
console.log("\nskipping empty final line:");
const stream = textLineStream2.pipeThrough(new RefinedTextLineStream());
for await (const [line, lineNumber] of stream) console.log(lineNumber, line);
}
To execute in the terminal:
% deno --version
deno 1.29.1 (release, x86_64-apple-darwin)
v8 10.9.194.5
typescript 4.9.4
% deno run so-74905946.ts
input from question: "foo\n\nbar\n"
default:
1 foo
2
3 bar
4
skipping empty final line:
1 foo
2
3 bar
input with extra final line feed: "foo\n\nbar\n\n"
default:
1 foo
2
3 bar
4
5
skipping empty final line:
1 foo
2
3 bar
4
input without final line feed: "foo\n\nbar"
default:
1 foo
2
3 bar
skipping empty final line:
1 foo
2
3 bar