From: Ardis Lu Date: Mon, 15 Sep 2025 03:22:11 +0000 (-0700) Subject: Add example of processing `.csv.gz` files X-Git-Tag: v0.1.6~6 X-Git-Url: https://git.ardis.lu/?a=commitdiff_plain;h=e26c05420bc0c458b90929abe89b40da5806eb85;p=csvio Add example of processing `.csv.gz` files --- diff --git a/examples/README.md b/examples/README.md index 607878a..4238659 100644 --- a/examples/README.md +++ b/examples/README.md @@ -30,6 +30,8 @@ These examples demonstrate the most common, expected usage. [Example 1.8: Read from `stdin` and write to `stdout`](./ex1_8.js) ([input](./data/ex1_8-in.csv), [output](./data/ex1_8-out.csv)) +[Example 1.9: Read and write a `.csv.gz` file](./ex1_9.js) ([input](./data/ex1_9-in.csv.gz), [output](./data/ex1_9-out.csv.gz)) + ### Section 2: Error strategies These examples demonstrate using the `onError` option to handle transformations that may throw errors. diff --git a/examples/data/ex1_9-in.csv.gz b/examples/data/ex1_9-in.csv.gz new file mode 100644 index 0000000..70028c2 Binary files /dev/null and b/examples/data/ex1_9-in.csv.gz differ diff --git a/examples/data/ex1_9-out.csv.gz b/examples/data/ex1_9-out.csv.gz new file mode 100644 index 0000000..53ca670 Binary files /dev/null and b/examples/data/ex1_9-out.csv.gz differ diff --git a/examples/ex1_9.js b/examples/ex1_9.js new file mode 100644 index 0000000..e4c10a9 --- /dev/null +++ b/examples/ex1_9.js @@ -0,0 +1,33 @@ +// ## Example 1.9: Read and write a `.csv.gz` file +// +// If your input or output files are not direct CSV files, you can create CSVReader and CSVWriter as +// TransformStream instances by passing nothing to the constructors. This usage is convenient for +// interoperating with other Web APIs, such as the Compression Streams API to read and write `.gz` files. +// +// Note there is no need to save the CSVs to intermediate temporary working files, streams allow you to +// directly process the `.gz` files. + +import { createWriteStream } from 'node:fs'; +import { Writable } from 'node:stream'; + +import { CSVReader, CSVTransformer, CSVWriter } from '../src/index.js'; + +import { createFileStream } from '../src/core.js'; +import { setGzipOSByteToUnknown } from '../test/utils.js'; + +function timesTwo(row) { + return [Number(row[0]) * 2, Number(row[1]) * 2]; +} + +await createFileStream(new URL('./data/ex1_9-in.csv.gz', import.meta.url)) + .pipeThrough(new DecompressionStream('gzip')) + .pipeThrough(new TextDecoderStream()) + .pipeThrough(new CSVReader()) + .pipeThrough(new CSVTransformer(timesTwo)) + .pipeThrough(new CSVWriter()) // No need to use TextEncoderStream on this end, CompressionStream can handle text. + .pipeThrough(new CompressionStream('gzip')) + .pipeTo(Writable.toWeb(createWriteStream(new URL('./data/ex1_9-out.csv.gz', import.meta.url)))); + +// Overriding platform-specific metadata on the `.gz` file so this test is consistent across platforms. This step is +// NOT necessary to actually use the `.gz` file in normal situations. +await setGzipOSByteToUnknown(new URL('./data/ex1_9-out.csv.gz', import.meta.url)); diff --git a/test/examples.test.js b/test/examples.test.js index 8e49070..0476c3c 100644 --- a/test/examples.test.js +++ b/test/examples.test.js @@ -11,15 +11,19 @@ import { createTempFolder, assertConsole } from './utils.js'; const dir = await createTempFolder(); const tempExamples = pathToFileURL(`${dir}/examples/`); // Trailing forward slash is REQUIRED for using this URL as a URL base later const tempSrc = pathToFileURL(`${dir}/src/`); +const tempTest = pathToFileURL(`${dir}/test/`); const actualExamples = new URL('../examples/', import.meta.url); const actualSrc = new URL('../src/', import.meta.url); +const actualTest = new URL('../test/', import.meta.url); await Promise.all([ mkdir(tempExamples, { recursive: true }), - mkdir(tempSrc, { recursive: true }) + mkdir(tempSrc, { recursive: true }), + mkdir(tempTest, { recursive: true }) ]); await Promise.all([ cp(actualExamples, tempExamples, { recursive: true }), - cp(actualSrc, tempSrc, { recursive: true }) + cp(actualSrc, tempSrc, { recursive: true }), + cp(actualTest, tempTest, { recursive: true }) ]); const tests = []; @@ -58,6 +62,15 @@ suite('examples', { concurrency: true }, () => { ok(actual.equals(expected)); }); } + else if (name === 'ex1_9') { + test(name, { concurrency: true }, async () => { + const output = new URL(`./data/${name}-out.csv.gz`, tempExamples); + const expected = await readFile(output); + await import(code); + const actual = await readFile(output); + ok(actual.equals(expected)); + }); + } else { test(name, { concurrency: true }, async () => { const expected = await readFile(output);