@cfworker/csv 1.12.2 → 1.12.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/encode.d.ts CHANGED
@@ -1 +1,9 @@
1
- export declare function encode(rows: any[]): ReadableStream;
1
+ export interface EncodeOptions<T> {
2
+ columns?: Column<T, keyof T>[];
3
+ }
4
+ export interface Column<T, K extends keyof T> {
5
+ key: K;
6
+ label: string;
7
+ format: (value: T[K], row: T) => unknown;
8
+ }
9
+ export declare function encode<T>(rows: T[], { columns }?: EncodeOptions<T>): ReadableStream;
package/dist/encode.js CHANGED
@@ -3,30 +3,35 @@ const quoteRegex = /"/g;
3
3
  const delimiter = ',';
4
4
  const rowDelimiter = '\r\n';
5
5
  const requiresQuoteRegex = /["\r\n,]/;
6
- export function encode(rows) {
6
+ export function encode(rows, { columns } = {}) {
7
7
  const { readable, writable } = new TransformStream();
8
8
  (async () => {
9
9
  const writer = writable.getWriter();
10
- await writeRows(writer, rows);
10
+ if (rows.length > 0) {
11
+ if (!columns) {
12
+ columns = Object.keys(rows[0]).map(key => ({
13
+ key: key,
14
+ label: key,
15
+ format: (x) => x
16
+ }));
17
+ }
18
+ await writeRows(writer, rows, columns);
19
+ }
11
20
  await writer.close();
12
21
  })();
13
22
  return readable;
14
23
  }
15
- async function writeRows(writer, rows) {
24
+ async function writeRows(writer, rows, columns) {
16
25
  const text = new TextEncoder();
17
26
  const utf8 = text.encode.bind(text);
18
27
  const write = writer.write.bind(writer);
19
- let keys = undefined;
28
+ await write(utf8(columns.map(c => encodeValue(c.label)).join(delimiter)));
20
29
  for (const row of rows) {
21
- if (keys === undefined) {
22
- keys = Object.keys(row);
23
- await write(utf8(keys.map(encodeValue).join(delimiter)));
24
- }
25
30
  await write(utf8(rowDelimiter));
26
31
  let s = '';
27
32
  let first = true;
28
- for (const key of keys) {
29
- s += (first ? '' : ',') + encodeValue(row[key]);
33
+ for (const { key, format } of columns) {
34
+ s += (first ? '' : ',') + encodeValue(format(row[key], row));
30
35
  first = false;
31
36
  }
32
37
  await write(utf8(s));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cfworker/csv",
3
- "version": "1.12.2",
3
+ "version": "1.12.5",
4
4
  "description": "Streaming CSV encoding for Cloudflare Workers and service workers",
5
5
  "sideEffects": false,
6
6
  "main": "dist/index.js",
@@ -27,11 +27,11 @@
27
27
  "test": "cfworker test test/**/*.spec.ts"
28
28
  },
29
29
  "devDependencies": {
30
- "@cfworker/dev": "^1.14.0",
31
- "@types/chai": "^4.3.0",
32
- "@types/mocha": "^9.1.0",
30
+ "@cfworker/dev": "^1.14.1",
31
+ "@types/chai": "^4.3.3",
32
+ "@types/mocha": "^9.1.1",
33
33
  "chai": "^4.3.6",
34
- "mocha": "^9.2.1",
35
- "typescript": "^4.5.5"
34
+ "mocha": "^10.0.0",
35
+ "typescript": "^4.7.4"
36
36
  }
37
37
  }
package/src/encode.ts CHANGED
@@ -4,40 +4,69 @@ const delimiter = ',';
4
4
  const rowDelimiter = '\r\n';
5
5
  const requiresQuoteRegex = /["\r\n,]/;
6
6
 
7
- export function encode(rows: any[]): ReadableStream {
7
+ export interface EncodeOptions<T> {
8
+ /**
9
+ * Properties to include
10
+ */
11
+ columns?: Column<T, keyof T>[];
12
+ }
13
+
14
+ export interface Column<T, K extends keyof T> {
15
+ key: K;
16
+ label: string;
17
+ format: (value: T[K], row: T) => unknown;
18
+ }
19
+
20
+ /**
21
+ * Encode an array of objects as a CSV
22
+ * @param rows An array of objects. Each item in the array represents a row.
23
+ * @param options
24
+ * @returns
25
+ */
26
+ export function encode<T>(
27
+ rows: T[],
28
+ { columns }: EncodeOptions<T> = {}
29
+ ): ReadableStream {
8
30
  // Cloudflare Workers cannot construct a ReadableStream
9
31
  const { readable, writable } = new TransformStream();
10
-
11
32
  (async () => {
12
33
  const writer = writable.getWriter();
13
- await writeRows(writer, rows);
34
+ if (rows.length > 0) {
35
+ if (!columns) {
36
+ columns = Object.keys(rows[0]).map(key => ({
37
+ key: key as keyof T,
38
+ label: key,
39
+ format: (x: any) => x
40
+ }));
41
+ }
42
+ await writeRows(writer, rows, columns);
43
+ }
14
44
  await writer.close();
15
45
  })();
16
46
 
17
47
  return readable;
18
48
  }
19
49
 
20
- async function writeRows(writer: WritableStreamDefaultWriter, rows: any[]) {
50
+ async function writeRows<T>(
51
+ writer: WritableStreamDefaultWriter,
52
+ rows: T[],
53
+ columns: Column<T, keyof T>[]
54
+ ) {
21
55
  const text = new TextEncoder();
22
56
  const utf8 = text.encode.bind(text);
23
57
  const write = writer.write.bind(writer);
24
- let keys: string[] | undefined = undefined;
58
+ // write header row
59
+ await write(utf8(columns.map(c => encodeValue(c.label)).join(delimiter)));
25
60
 
26
61
  for (const row of rows) {
27
- // write header row
28
- if (keys === undefined) {
29
- keys = Object.keys(row);
30
- await write(utf8(keys.map(encodeValue).join(delimiter)));
31
- }
32
-
33
62
  // write row delimiter
34
63
  await write(utf8(rowDelimiter));
35
64
 
36
65
  // write data row
37
66
  let s = '';
38
67
  let first = true;
39
- for (const key of keys) {
40
- s += (first ? '' : ',') + encodeValue(row[key]);
68
+ for (const { key, format } of columns) {
69
+ s += (first ? '' : ',') + encodeValue(format(row[key], row));
41
70
  first = false;
42
71
  }
43
72
  await write(utf8(s));