[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.
--- /dev/null
+// ## 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));
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 = [];
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);