2024-01-01 21:11:32 +00:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2024-04-10 02:43:44 +00:00
|
|
|
// This module is browser compatible.
|
2023-03-27 02:48:12 +00:00
|
|
|
import { stringify } from "./stringify.ts";
|
|
|
|
|
2024-01-11 06:02:30 +00:00
|
|
|
/** Options for {@linkcode CsvStringifyStream}. */
|
2023-03-27 02:48:12 +00:00
|
|
|
export interface CsvStringifyStreamOptions {
|
|
|
|
/**
|
|
|
|
* Delimiter used to separate values.
|
|
|
|
*
|
|
|
|
* @default {","}
|
|
|
|
*/
|
|
|
|
readonly separator?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A list of columns to be included in the output.
|
|
|
|
*
|
|
|
|
* If you want to stream objects, this option is required.
|
2024-07-11 09:21:37 +00:00
|
|
|
*
|
|
|
|
* @default {[]}
|
2023-03-27 02:48:12 +00:00
|
|
|
*/
|
|
|
|
readonly columns?: Array<string>;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert each chunk to a CSV record.
|
|
|
|
*
|
2024-08-02 07:07:53 +00:00
|
|
|
* @example Write CSV to a file
|
|
|
|
* ```ts
|
2024-06-28 06:07:36 +00:00
|
|
|
* import { CsvStringifyStream } from "@std/csv/stringify-stream";
|
2024-08-02 07:07:53 +00:00
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
2023-03-27 02:48:12 +00:00
|
|
|
*
|
2024-08-02 07:07:53 +00:00
|
|
|
* async function writeCsvToTempFile(): Promise<string> {
|
|
|
|
* const path = await Deno.makeTempFile();
|
|
|
|
* using file = await Deno.open(path, { write: true });
|
2024-06-03 03:32:09 +00:00
|
|
|
*
|
2024-08-02 07:07:53 +00:00
|
|
|
* const readable = ReadableStream.from([
|
|
|
|
* { id: 1, name: "one" },
|
|
|
|
* { id: 2, name: "two" },
|
|
|
|
* { id: 3, name: "three" },
|
|
|
|
* ]);
|
2023-03-27 02:48:12 +00:00
|
|
|
*
|
2024-08-02 07:07:53 +00:00
|
|
|
* await readable
|
|
|
|
* .pipeThrough(new CsvStringifyStream({ columns: ["id", "name"] }))
|
|
|
|
* .pipeThrough(new TextEncoderStream())
|
|
|
|
* .pipeTo(file.writable);
|
|
|
|
*
|
|
|
|
* return path;
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* const path = await writeCsvToTempFile();
|
|
|
|
* const content = await Deno.readTextFile(path);
|
|
|
|
* assertEquals(content, "id,name\r\n1,one\r\n2,two\r\n3,three\r\n");
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @example Write TSV to a file
|
|
|
|
* ```ts
|
|
|
|
* import { CsvStringifyStream } from "@std/csv/stringify-stream";
|
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
|
|
|
*
|
|
|
|
* async function writeTsvToTempFile(): Promise<string> {
|
|
|
|
* const path = await Deno.makeTempFile();
|
|
|
|
* using file = await Deno.open(path, { write: true });
|
|
|
|
*
|
|
|
|
* const readable = ReadableStream.from([
|
|
|
|
* { id: 1, name: "one" },
|
|
|
|
* { id: 2, name: "two" },
|
|
|
|
* { id: 3, name: "three" },
|
|
|
|
* ]);
|
|
|
|
*
|
|
|
|
* await readable
|
|
|
|
* .pipeThrough(
|
|
|
|
* new CsvStringifyStream({
|
|
|
|
* columns: ["id", "name"],
|
|
|
|
* separator: "\t",
|
|
|
|
* }),
|
|
|
|
* )
|
|
|
|
* .pipeThrough(new TextEncoderStream())
|
|
|
|
* .pipeTo(file.writable);
|
|
|
|
*
|
|
|
|
* return path;
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* const path = await writeTsvToTempFile();
|
|
|
|
* const content = await Deno.readTextFile(path);
|
|
|
|
* assertEquals(content, "id\tname\r\n1\tone\r\n2\ttwo\r\n3\tthree\r\n");
|
2024-06-03 03:32:09 +00:00
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @typeParam TOptions The type of options for the stream.
|
2023-03-27 02:48:12 +00:00
|
|
|
*/
|
|
|
|
export class CsvStringifyStream<TOptions extends CsvStringifyStreamOptions>
|
|
|
|
extends TransformStream<
|
|
|
|
TOptions["columns"] extends Array<string> ? Record<string, unknown>
|
|
|
|
: Array<unknown>,
|
|
|
|
string
|
|
|
|
> {
|
2024-06-03 03:32:09 +00:00
|
|
|
/**
|
|
|
|
* Construct a new instance.
|
|
|
|
*
|
|
|
|
* @param options Options for the stream.
|
|
|
|
*/
|
2023-03-27 02:48:12 +00:00
|
|
|
constructor(options?: TOptions) {
|
2024-07-02 03:17:58 +00:00
|
|
|
const { separator, columns = [] } = options ?? {};
|
2023-03-27 02:48:12 +00:00
|
|
|
|
|
|
|
super(
|
|
|
|
{
|
2023-03-30 06:36:17 +00:00
|
|
|
start(controller) {
|
|
|
|
if (columns && columns.length > 0) {
|
2024-06-27 04:46:09 +00:00
|
|
|
try {
|
|
|
|
controller.enqueue(
|
refactor(archive,async,cli,csv,dotenv,encoding,expect,fmt,front-matter,fs,http,internal,log,net,path,semver,testing,text,webgpu,yaml): enable `"exactOptionalPropertyTypes"` option (#5892)
2024-09-04 05:15:01 +00:00
|
|
|
stringify(
|
|
|
|
[columns],
|
|
|
|
separator !== undefined
|
|
|
|
? { separator, headers: false }
|
|
|
|
: { headers: false },
|
|
|
|
),
|
2024-06-27 04:46:09 +00:00
|
|
|
);
|
|
|
|
} catch (error) {
|
|
|
|
controller.error(error);
|
|
|
|
}
|
2023-03-27 02:48:12 +00:00
|
|
|
}
|
|
|
|
},
|
2024-06-27 04:27:13 +00:00
|
|
|
transform(chunk, controller) {
|
2024-06-27 04:46:09 +00:00
|
|
|
try {
|
|
|
|
controller.enqueue(
|
refactor(archive,async,cli,csv,dotenv,encoding,expect,fmt,front-matter,fs,http,internal,log,net,path,semver,testing,text,webgpu,yaml): enable `"exactOptionalPropertyTypes"` option (#5892)
2024-09-04 05:15:01 +00:00
|
|
|
stringify(
|
|
|
|
[chunk],
|
|
|
|
separator !== undefined
|
|
|
|
? { separator, headers: false, columns }
|
|
|
|
: { headers: false, columns },
|
|
|
|
),
|
2024-06-27 04:46:09 +00:00
|
|
|
);
|
|
|
|
} catch (error) {
|
|
|
|
controller.error(error);
|
|
|
|
}
|
2024-06-27 04:27:13 +00:00
|
|
|
},
|
2023-03-27 02:48:12 +00:00
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|