]> git.ardis.lu - csvio/commitdiff
Refactor createCSVWritableStream to CSVWriter
authorArdis Lu <ardisglu@gmail.com>
Sun, 19 Jan 2025 07:17:16 +0000 (23:17 -0800)
committerArdis Lu <ardisglu@gmail.com>
Sun, 19 Jan 2025 07:17:16 +0000 (23:17 -0800)
README.md
examples/ex1.js
examples/ex2.js
examples/ex3.js
examples/ex4.js
examples/ex5.js
examples/ex6.js
examples/ex7.js
src/core.js
src/index.js
test/core.test.js

index d625dbfb76153f171f27aac50792f2e195701dc2..83d5abe8e1d6642c8fe3a999bb34d666655976e6 100644 (file)
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ Run:
 import {
   CSVReader,
   CSVTransformer,
-  createCSVWritableStream
+  CSVWriter
 } from './src/index.js';
 
 function timesTwo(row) {
@@ -30,7 +30,7 @@ function timesTwo(row) {
 
 await new CSVReader('./data/example-in.csv')
   .pipeThrough(new CSVTransformer(timesTwo))
-  .pipeTo(createCSVWritableStream('./data/example-out.csv'));
+  .pipeTo(new CSVWriter('./data/example-out.csv'));
 ```
 
 A new `./data/example-out.csv` file will be created:
@@ -93,14 +93,14 @@ function timesTwo(row) {
 const transformStream = new CSVTransformer(timesTwo);
 ```
 
-### `createCSVWritableStream`
+### `CSVWriter`
 
-Create a `WritableStream` to save CSV row data to disk.
+A `WritableStream` to save CSV row data to disk.
 
 ```javascript
-import { createCSVWritableStream } from './src/index.js';
+import { CSVWriter } from './src/index.js';
 
-const writableStream = createCSVWritableStream('./data/example-out.csv');
+const writableStream = new CSVWriter('./data/example-out.csv');
 ```
 
 ## Extended API
index 7af1e200e9b97eaf2d0b0f2217fa3544a1aa77ba..a81f060d0b17f263da27db8435c80c25525511a2 100644 (file)
@@ -8,7 +8,7 @@
 // - Each call to the transformation function passes 1 row of the input CSV.
 // - The expected output of the transformation function is another array representing 1 row of the output CSV.
 
-import { CSVReader, CSVTransformer, createCSVWritableStream } from '../src/index.js';
+import { CSVReader, CSVTransformer, CSVWriter } from '../src/index.js';
 
 function timesTwo(row) {
   return [Number(row[0]) * 2, Number(row[1]) * 2];
@@ -16,4 +16,4 @@ function timesTwo(row) {
 
 await new CSVReader(new URL('./data/ex1-in.csv', import.meta.url))
   .pipeThrough(new CSVTransformer(timesTwo))
-  .pipeTo(createCSVWritableStream(new URL('./data/ex1-out.csv', import.meta.url)));
\ No newline at end of file
+  .pipeTo(new CSVWriter(new URL('./data/ex1-out.csv', import.meta.url)));
\ No newline at end of file
index 25f4c20c43804be03ffbe385bd757cfbd6079b2b..de5cdf96910447da6adf38883876203216351d06 100644 (file)
@@ -7,7 +7,7 @@
 // - If your input CSV does not have a header row, set `includeHeaders: true` and process the header row (first row) normally.
 // - Otherwise, you will need distinct logic to handle the header row vs. data rows. A simple boolean variable is a good way to switch between this logic. 
 
-import { CSVReader, CSVTransformer, createCSVWritableStream } from '../src/index.js';
+import { CSVReader, CSVTransformer, CSVWriter } from '../src/index.js';
 
 let firstRow = true;
 function sum(row) {
@@ -20,4 +20,4 @@ function sum(row) {
 
 await new CSVReader(new URL('./data/ex2-in.csv', import.meta.url))
   .pipeThrough(new CSVTransformer(sum, { includeHeaders: true }))
-  .pipeTo(createCSVWritableStream(new URL('./data/ex2-out.csv', import.meta.url)));
\ No newline at end of file
+  .pipeTo(new CSVWriter(new URL('./data/ex2-out.csv', import.meta.url)));
\ No newline at end of file
index 8e44dc3fc76a3e290d22351a1d8260bb5abad47e..42223afcee3e8cfa90872c9b21018ad63fea50e7 100644 (file)
@@ -5,7 +5,7 @@
 // transformations (e.g., transformations spread across multiple functions or lower-level transformations directly
 // on byte arrays).
 
-import { CSVReader, CSVTransformer, createCSVWritableStream } from '../src/index.js';
+import { CSVReader, CSVTransformer, CSVWriter } from '../src/index.js';
 
 // Parse the raw CSV data and transform the data into an object for convenience
 function parse(row) {
@@ -25,4 +25,4 @@ function calculate(obj) {
 await new CSVReader(new URL('./data/ex3-in.csv', import.meta.url))
   .pipeThrough(new CSVTransformer(parse, { includeHeaders: true, rawOutput: true }))
   .pipeThrough(new CSVTransformer(calculate, { includeHeaders: true, rawInput: true }))
-  .pipeTo(createCSVWritableStream(new URL('./data/ex3-out.csv', import.meta.url)));
\ No newline at end of file
+  .pipeTo(new CSVWriter(new URL('./data/ex3-out.csv', import.meta.url)));
\ No newline at end of file
index 59695c9f2fa1402b313d63be54c22c479480a805..2d46f866f05e1cfb1798d40f280531605be8e3a1 100644 (file)
@@ -8,7 +8,7 @@
 // `createCSVNormalizationStream` performs transformations to un-mangle the above using only a declaration of the header names and data types.
 // `createCSVDenormalizationStream` converts the data stream back into a CSV row.
 
-import { CSVReader, createCSVWritableStream, createCSVNormalizationStream, createCSVDenormalizationStream } from '../src/index.js';
+import { CSVReader, CSVWriter, createCSVNormalizationStream, createCSVDenormalizationStream } from '../src/index.js';
 
 // The order of items in `headers` is significant, it determines the order of the output CSV columns.
 // If an input column is not provided in `headers`, the column is removed from the output CSV.
@@ -33,4 +33,4 @@ const headers = [
 await new CSVReader(new URL('./data/ex4-in.csv', import.meta.url))
   .pipeThrough(createCSVNormalizationStream(headers))
   .pipeThrough(createCSVDenormalizationStream())
-  .pipeTo(createCSVWritableStream(new URL('./data/ex4-out.csv', import.meta.url)));
\ No newline at end of file
+  .pipeTo(new CSVWriter(new URL('./data/ex4-out.csv', import.meta.url)));
\ No newline at end of file
index 75130fb491be35dfa45e414304961fd271ec9bcf..d949fcfdd7768c7a29efb04d812cdd43554c71d4 100644 (file)
@@ -5,7 +5,7 @@
 //
 // Return `null` in the transformation function to consume an input row without emitting an output row.
 
-import { CSVReader, CSVTransformer, createCSVWritableStream } from '../src/index.js';
+import { CSVReader, CSVTransformer, CSVWriter } from '../src/index.js';
 
 let i = 0;
 let report, id, date;
@@ -26,4 +26,4 @@ function parse(row) {
 
 await new CSVReader(new URL('./data/ex5-in.csv', import.meta.url))
   .pipeThrough(new CSVTransformer(parse, { includeHeaders: true }))
-  .pipeTo(createCSVWritableStream(new URL('./data/ex5-out.csv', import.meta.url)));
\ No newline at end of file
+  .pipeTo(new CSVWriter(new URL('./data/ex5-out.csv', import.meta.url)));
\ No newline at end of file
index 31aca2f22a96faaccc2df7135955c0b7a10b9eb3..25b6dfa72ef0f9d5ca99f6b342436f1d81cc37c6 100644 (file)
@@ -6,7 +6,7 @@
 // The `onError` function may be used to implement graceful failure strategies (e.g., set the output row to a
 // placeholder), retry strategies (e.g., a truncated exponential backoff for flaky network requests), logging, etc.
 
-import { CSVReader, CSVTransformer, createCSVWritableStream } from '../src/index.js';
+import { CSVReader, CSVTransformer, CSVWriter } from '../src/index.js';
 
 // Will immediately retry calling `fn` a maximum of `iterations` times
 function retry(row, fn, iterations) {
@@ -27,4 +27,4 @@ function flaky(row) {
 
 await new CSVReader(new URL('./data/ex6-in.csv', import.meta.url))
   .pipeThrough(new CSVTransformer(flaky, { onError: (row, e, fn) => retry(row, fn, 1000) }))
-  .pipeTo(createCSVWritableStream(new URL('./data/ex6-out.csv', import.meta.url)));
\ No newline at end of file
+  .pipeTo(new CSVWriter(new URL('./data/ex6-out.csv', import.meta.url)));
\ No newline at end of file
index 3f2b52a744ec28bc51f26779b2d1d287fa9ada14..21e55a786beac51b8969c8b9a2a72180e65d86d4 100644 (file)
@@ -6,7 +6,7 @@
 // This functionality is useful if you need to collect data from multiple rows before processing (e.g., performing an
 // expensive network request where it is more efficient to batch multiple rows into one request).
 
-import { CSVReader, CSVTransformer, createCSVWritableStream } from '../src/index.js';
+import { CSVReader, CSVTransformer, CSVWriter } from '../src/index.js';
 
 let firstChunk = true;
 let batchNumber = 0;
@@ -30,4 +30,4 @@ function process(batch) {
 
 await new CSVReader(new URL('./data/ex7-in.csv', import.meta.url))
   .pipeThrough(new CSVTransformer(process, { includeHeaders: true, maxBatchSize: 5 }))
-  .pipeTo(createCSVWritableStream(new URL('./data/ex7-out.csv', import.meta.url)));
\ No newline at end of file
+  .pipeTo(new CSVWriter(new URL('./data/ex7-out.csv', import.meta.url)));
\ No newline at end of file
index da558b61f592d6491c2127589964e363208017c9..f9f476226cc31b65d52c927df9b3d0c67ebe51c0 100644 (file)
@@ -284,32 +284,35 @@ export class CSVTransformer extends TransformStream {
  * A simple wrapper around Node.js's [`fs.writeFile`](https://nodejs.org/api/fs.html#fspromiseswritefilefile-data-options) to write
  * streamed data to a file. If the input data is JSON, the data is converted to a string representing a RFC 4180 CSV record. If the
  * data is not JSON (e.g., if it is already converted to a CSV string), the data is written to the CSV file directly.
- * @param {PathLike} path A `string`, `Buffer`, or `URL` representing a path to a local CSV file destination. If the file already
- * exists, its **data will be overwritten**.
- * @returns {WritableStream} A `WritableStream` to write data to a file on disk.
+ * @extends WritableStream
  */
-export function createCSVWritableStream(path) {
-  const fullPath = parsePathLike(path);
-  const dir = dirname(fullPath);
-  let handle;
-
-  return new WritableStream({
-    async start() {
-      await mkdir(dir, { recursive: true });
-      handle = await open(fullPath, 'w');
-    },
-    async write(chunk) {
-      let data;
-      try {
-        data = arrayToCSVString(JSON.parse(chunk));
-      }
-      catch {
-        data = chunk;
+export class CSVWriter extends WritableStream {
+  /**
+   * @param {PathLike} path A `string`, `Buffer`, or `URL` representing a path to a local CSV file destination. If the file already
+   * exists, its **data will be overwritten**.
+   */
+  constructor(path) {
+    const fullPath = parsePathLike(path);
+    const dir = dirname(fullPath);
+    let handle;
+    super({
+      async start() {
+        await mkdir(dir, { recursive: true });
+        handle = await open(fullPath, 'w');
+      },
+      async write(chunk) {
+        let data;
+        try {
+          data = arrayToCSVString(JSON.parse(chunk));
+        }
+        catch {
+          data = chunk;
+        }
+        await handle.write(data);
+      },
+      async close() {
+        await handle.close();
       }
-      await handle.write(data);
-    },
-    async close() {
-      await handle.close();
-    }
-  });
+    });
+  }
 }
index c75b66bdec6be6b86f6403310512b0c590a88ff2..c78b4f10ac6601e2c2a4f3baf516544bc5fa908e 100644 (file)
@@ -1,2 +1,2 @@
-export { CSVReader, CSVTransformer, createCSVWritableStream } from './core.js';
+export { CSVReader, CSVTransformer, CSVWriter } from './core.js';
 export { createCSVNormalizationStream, createCSVDenormalizationStream } from './normalization.js';
index e850c27a36beb5765f71ab9da02fc3484c4a7d94..eddc5d197cf7fa799bbd74d4b5689904660d4e4d 100644 (file)
@@ -5,7 +5,7 @@ import { normalize, resolve } from 'node:path';
 import { pathToFileURL } from 'node:url';
 
 import { csvStreamEqualWritable, csvStreamNotEqualWritable, createCSVMockStream, createTempFile } from './utils.js';
-import { arrayToCSVString, parsePathLike, CSVReader, CSVTransformer, createCSVWritableStream } from '../src/core.js';
+import { arrayToCSVString, parsePathLike, CSVReader, CSVTransformer, CSVWriter } from '../src/core.js';
 
 suite('arrayToCSVString', { concurrency: true }, () => {
   const vectors = [
@@ -319,7 +319,7 @@ suite('CSVTransformer', { concurrency: true }, () => {
   });
 });
 
-suite('createCSVWritableStream', { concurrency: true }, () => {
+suite('CSVWriter', { concurrency: true }, () => {
   const vectors = [
     {
       name: 'writes simple CSV',
@@ -421,7 +421,7 @@ suite('createCSVWritableStream', { concurrency: true }, () => {
     test(name, { concurrency: true }, async (t) => {
       const temp = await createTempFile();
       t.after(async () => await unlink(temp));
-      await createCSVMockStream(csv).pipeTo(createCSVWritableStream(temp));
+      await createCSVMockStream(csv).pipeTo(new CSVWriter(temp));
       await new CSVReader(temp).pipeTo(csvStreamEqualWritable(csv));
     });
   }
This page took 0.057057 seconds and 4 git commands to generate.