@naturalcycles/nodejs-lib 12.88.0 → 12.89.0

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.
@@ -0,0 +1,16 @@
1
+ import { AnyObject } from '@naturalcycles/js-lib';
2
+ export interface CSVReaderConfig {
3
+ /**
4
+ * Default: comma
5
+ */
6
+ /**
7
+ * Default: true
8
+ */
9
+ firstRowIsHeader?: boolean;
10
+ /**
11
+ * Array of columns, to be used if there is no header row.
12
+ */
13
+ columns?: string[];
14
+ }
15
+ export declare function csvStringParse<T extends AnyObject = any>(str: string, cfg?: CSVReaderConfig): T[];
16
+ export declare function csvStringToArray(str: string): string[][];
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ // Inspired by: https://gist.github.com/Jezternz/c8e9fafc2c114e079829974e3764db75
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.csvStringToArray = exports.csvStringParse = void 0;
5
+ const js_lib_1 = require("@naturalcycles/js-lib");
6
+ // export class CSVReader {
7
+ // constructor (cfg: CSVReaderConfig) {
8
+ // this.cfg = {
9
+ // delimiter: ',',
10
+ // includeHeader: true,
11
+ // ...cfg,
12
+ // }
13
+ // }
14
+ //
15
+ // public cfg: Required<CSVReaderConfig>
16
+ // }
17
+ function csvStringParse(str, cfg = {}) {
18
+ const { firstRowIsHeader = true, columns } = cfg;
19
+ const arr = csvStringToArray(str);
20
+ let header = columns;
21
+ if (firstRowIsHeader) {
22
+ const firstRow = arr.shift();
23
+ header ||= firstRow;
24
+ }
25
+ (0, js_lib_1._assert)(header, `firstRowIsHeader or columns is required`);
26
+ return arr.map(row => {
27
+ // eslint-disable-next-line unicorn/no-array-reduce
28
+ return header.reduce((obj, col, i) => {
29
+ ;
30
+ obj[col] = row[i];
31
+ return obj;
32
+ }, {});
33
+ });
34
+ }
35
+ exports.csvStringParse = csvStringParse;
36
+ function csvStringToArray(str) {
37
+ const objPattern = new RegExp('(,|\\r?\\n|\\r|^)(?:"([^"]*(?:""[^"]*)*)"|([^,\\r\\n]*))', 'gi');
38
+ let matches = null;
39
+ const arr = [[]];
40
+ // eslint-disable-next-line no-cond-assign
41
+ while ((matches = objPattern.exec(str))) {
42
+ if (matches[1].length && matches[1] !== ',') {
43
+ arr.push([]);
44
+ }
45
+ arr[arr.length - 1].push(matches[2] ? matches[2].replace(new RegExp('""', 'g'), '"') : matches[3]);
46
+ }
47
+ return arr;
48
+ }
49
+ exports.csvStringToArray = csvStringToArray;
@@ -0,0 +1,26 @@
1
+ import { AnyObject } from '@naturalcycles/js-lib';
2
+ export interface CSVWriterConfig {
3
+ /**
4
+ * Default: comma
5
+ */
6
+ delimiter?: string;
7
+ /**
8
+ * Array of columns
9
+ */
10
+ columns: string[];
11
+ /**
12
+ * Default: true
13
+ */
14
+ includeHeader?: boolean;
15
+ }
16
+ export declare class CSVWriter {
17
+ constructor(cfg: CSVWriterConfig);
18
+ cfg: Required<CSVWriterConfig>;
19
+ writeRows(rows: AnyObject[]): string;
20
+ writeHeader(): string;
21
+ writeRow(row: AnyObject): string;
22
+ private quoteIfNeeded;
23
+ private quote;
24
+ private shouldQuote;
25
+ }
26
+ export declare function arrayToCSVString(arr: AnyObject[], cfg: CSVWriterConfig): string;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ // Inspired by: https://github.com/ryu1kn/csv-writer/
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.arrayToCSVString = exports.CSVWriter = void 0;
5
+ class CSVWriter {
6
+ constructor(cfg) {
7
+ this.cfg = {
8
+ delimiter: ',',
9
+ includeHeader: true,
10
+ ...cfg,
11
+ };
12
+ }
13
+ writeRows(rows) {
14
+ let s = '';
15
+ if (this.cfg.includeHeader) {
16
+ s += this.writeHeader() + '\n';
17
+ }
18
+ return s + rows.map(row => this.writeRow(row)).join('\n');
19
+ }
20
+ writeHeader() {
21
+ return this.cfg.columns.map(col => this.quoteIfNeeded(col)).join(this.cfg.delimiter);
22
+ }
23
+ writeRow(row) {
24
+ return this.cfg.columns
25
+ .map(col => this.quoteIfNeeded(String(row[col] ?? '')))
26
+ .join(this.cfg.delimiter);
27
+ }
28
+ quoteIfNeeded(s) {
29
+ return this.shouldQuote(s) ? this.quote(s) : s;
30
+ }
31
+ quote(s) {
32
+ return `"${s.replace(/"/g, '""')}"`;
33
+ }
34
+ shouldQuote(s) {
35
+ return s.includes(this.cfg.delimiter) || s.includes('\r') || s.includes('\n') || s.includes('"');
36
+ }
37
+ }
38
+ exports.CSVWriter = CSVWriter;
39
+ function arrayToCSVString(arr, cfg) {
40
+ const writer = new CSVWriter(cfg);
41
+ return writer.writeRows(arr);
42
+ }
43
+ exports.arrayToCSVString = arrayToCSVString;
@@ -0,0 +1,15 @@
1
+ import { AnyObject } from '@naturalcycles/js-lib';
2
+ import { TransformTyped } from '../stream/stream.model';
3
+ import { CSVWriterConfig } from './csvWriter';
4
+ export interface TransformToCSVOptions extends CSVWriterConfig {
5
+ /**
6
+ * If true - will throw an error on stringify error
7
+ *
8
+ * @default true
9
+ */
10
+ strict?: boolean;
11
+ }
12
+ /**
13
+ * Transforms objects (objectMode=true) into chunks \n-terminated CSV strings (readableObjectMode=false).
14
+ */
15
+ export declare function transformToCSV<IN extends AnyObject = any>(opt: TransformToCSVOptions): TransformTyped<IN, string>;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transformToCSV = void 0;
4
+ const node_stream_1 = require("node:stream");
5
+ const csvWriter_1 = require("./csvWriter");
6
+ /**
7
+ * Transforms objects (objectMode=true) into chunks \n-terminated CSV strings (readableObjectMode=false).
8
+ */
9
+ function transformToCSV(opt) {
10
+ const { strict = true } = opt;
11
+ const writer = new csvWriter_1.CSVWriter(opt);
12
+ let firstRow = true;
13
+ return new node_stream_1.Transform({
14
+ writableObjectMode: true,
15
+ readableObjectMode: false,
16
+ transform(chunk, _, cb) {
17
+ try {
18
+ let s = '';
19
+ if (firstRow) {
20
+ s = writer.writeHeader() + '\n';
21
+ firstRow = false;
22
+ }
23
+ cb(null, s + writer.writeRow(chunk) + '\n');
24
+ }
25
+ catch (err) {
26
+ console.error(err);
27
+ if (strict) {
28
+ cb(err); // emit error
29
+ }
30
+ else {
31
+ cb(); // emit no error, but no result neither
32
+ }
33
+ }
34
+ },
35
+ });
36
+ }
37
+ exports.transformToCSV = transformToCSV;
package/dist/index.d.ts CHANGED
@@ -58,6 +58,9 @@ export * from './stream/writable/writableForEach';
58
58
  export * from './stream/writable/writableFork';
59
59
  export * from './stream/writable/writablePushToArray';
60
60
  export * from './stream/writable/writableVoid';
61
+ export * from './csv/csvWriter';
62
+ export * from './csv/csvReader';
63
+ export * from './csv/transformToCSV';
61
64
  export * from './string/inspectAny';
62
65
  export * from './util/env.util';
63
66
  export * from './util/lruMemoCache';
package/dist/index.js CHANGED
@@ -63,6 +63,9 @@ tslib_1.__exportStar(require("./stream/writable/writableForEach"), exports);
63
63
  tslib_1.__exportStar(require("./stream/writable/writableFork"), exports);
64
64
  tslib_1.__exportStar(require("./stream/writable/writablePushToArray"), exports);
65
65
  tslib_1.__exportStar(require("./stream/writable/writableVoid"), exports);
66
+ tslib_1.__exportStar(require("./csv/csvWriter"), exports);
67
+ tslib_1.__exportStar(require("./csv/csvReader"), exports);
68
+ tslib_1.__exportStar(require("./csv/transformToCSV"), exports);
66
69
  tslib_1.__exportStar(require("./string/inspectAny"), exports);
67
70
  tslib_1.__exportStar(require("./util/env.util"), exports);
68
71
  tslib_1.__exportStar(require("./util/lruMemoCache"), exports);
@@ -16,10 +16,6 @@ export interface TransformToNDJsonOptions {
16
16
  * @default `\n`
17
17
  */
18
18
  separator?: string;
19
- /**
20
- * @experimental
21
- */
22
- useFlatstr?: boolean;
23
19
  }
24
20
  /**
25
21
  * Transforms objects (objectMode=true) into chunks \n-terminated JSON strings (readableObjectMode=false).
@@ -7,7 +7,7 @@ const js_lib_1 = require("@naturalcycles/js-lib");
7
7
  * Transforms objects (objectMode=true) into chunks \n-terminated JSON strings (readableObjectMode=false).
8
8
  */
9
9
  function transformToNDJson(opt = {}) {
10
- const { strict = true, separator = '\n', sortObjects = false, useFlatstr = false } = opt;
10
+ const { strict = true, separator = '\n', sortObjects = false } = opt;
11
11
  return new node_stream_1.Transform({
12
12
  writableObjectMode: true,
13
13
  readableObjectMode: false,
@@ -16,12 +16,7 @@ function transformToNDJson(opt = {}) {
16
16
  if (sortObjects) {
17
17
  chunk = (0, js_lib_1._sortObjectDeep)(chunk);
18
18
  }
19
- if (useFlatstr) {
20
- cb(null, flatstr(JSON.stringify(chunk) + separator));
21
- }
22
- else {
23
- cb(null, JSON.stringify(chunk) + separator);
24
- }
19
+ cb(null, JSON.stringify(chunk) + separator);
25
20
  }
26
21
  catch (err) {
27
22
  console.error(err);
@@ -36,11 +31,3 @@ function transformToNDJson(opt = {}) {
36
31
  });
37
32
  }
38
33
  exports.transformToNDJson = transformToNDJson;
39
- /**
40
- * Based on: https://github.com/davidmarkclements/flatstr/blob/master/index.js
41
- */
42
- function flatstr(s) {
43
- // eslint-disable-next-line
44
- s | 0;
45
- return s;
46
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
- "version": "12.88.0",
3
+ "version": "12.89.0",
4
4
  "scripts": {
5
5
  "prepare": "husky install",
6
6
  "docs-serve": "vuepress dev docs",
@@ -0,0 +1,72 @@
1
+ // Inspired by: https://gist.github.com/Jezternz/c8e9fafc2c114e079829974e3764db75
2
+
3
+ import { _assert, AnyObject } from '@naturalcycles/js-lib'
4
+
5
+ export interface CSVReaderConfig {
6
+ /**
7
+ * Default: comma
8
+ */
9
+ // delimiter?: string
10
+
11
+ /**
12
+ * Default: true
13
+ */
14
+ firstRowIsHeader?: boolean
15
+
16
+ /**
17
+ * Array of columns, to be used if there is no header row.
18
+ */
19
+ columns?: string[]
20
+ }
21
+
22
+ // export class CSVReader {
23
+ // constructor (cfg: CSVReaderConfig) {
24
+ // this.cfg = {
25
+ // delimiter: ',',
26
+ // includeHeader: true,
27
+ // ...cfg,
28
+ // }
29
+ // }
30
+ //
31
+ // public cfg: Required<CSVReaderConfig>
32
+ // }
33
+
34
+ export function csvStringParse<T extends AnyObject = any>(
35
+ str: string,
36
+ cfg: CSVReaderConfig = {},
37
+ ): T[] {
38
+ const { firstRowIsHeader = true, columns } = cfg
39
+ const arr = csvStringToArray(str)
40
+
41
+ let header = columns
42
+ if (firstRowIsHeader) {
43
+ const firstRow = arr.shift()
44
+ header ||= firstRow
45
+ }
46
+
47
+ _assert(header, `firstRowIsHeader or columns is required`)
48
+
49
+ return arr.map(row => {
50
+ // eslint-disable-next-line unicorn/no-array-reduce
51
+ return header!.reduce((obj, col, i) => {
52
+ ;(obj as any)[col] = row[i]
53
+ return obj
54
+ }, {} as T)
55
+ })
56
+ }
57
+
58
+ export function csvStringToArray(str: string): string[][] {
59
+ const objPattern = new RegExp('(,|\\r?\\n|\\r|^)(?:"([^"]*(?:""[^"]*)*)"|([^,\\r\\n]*))', 'gi')
60
+ let matches = null
61
+ const arr: any[][] = [[]]
62
+ // eslint-disable-next-line no-cond-assign
63
+ while ((matches = objPattern.exec(str))) {
64
+ if (matches[1]!.length && matches[1] !== ',') {
65
+ arr.push([])
66
+ }
67
+ arr[arr.length - 1]!.push(
68
+ matches[2] ? matches[2].replace(new RegExp('""', 'g'), '"') : matches[3],
69
+ )
70
+ }
71
+ return arr
72
+ }
@@ -0,0 +1,67 @@
1
+ // Inspired by: https://github.com/ryu1kn/csv-writer/
2
+
3
+ import { AnyObject } from '@naturalcycles/js-lib'
4
+
5
+ export interface CSVWriterConfig {
6
+ /**
7
+ * Default: comma
8
+ */
9
+ delimiter?: string
10
+
11
+ /**
12
+ * Array of columns
13
+ */
14
+ columns: string[]
15
+
16
+ /**
17
+ * Default: true
18
+ */
19
+ includeHeader?: boolean
20
+ }
21
+
22
+ export class CSVWriter {
23
+ constructor(cfg: CSVWriterConfig) {
24
+ this.cfg = {
25
+ delimiter: ',',
26
+ includeHeader: true,
27
+ ...cfg,
28
+ }
29
+ }
30
+
31
+ public cfg: Required<CSVWriterConfig>
32
+
33
+ writeRows(rows: AnyObject[]): string {
34
+ let s = ''
35
+ if (this.cfg.includeHeader) {
36
+ s += this.writeHeader() + '\n'
37
+ }
38
+ return s + rows.map(row => this.writeRow(row)).join('\n')
39
+ }
40
+
41
+ writeHeader(): string {
42
+ return this.cfg.columns.map(col => this.quoteIfNeeded(col)).join(this.cfg.delimiter)
43
+ }
44
+
45
+ writeRow(row: AnyObject): string {
46
+ return this.cfg.columns
47
+ .map(col => this.quoteIfNeeded(String(row[col] ?? '')))
48
+ .join(this.cfg.delimiter)
49
+ }
50
+
51
+ private quoteIfNeeded(s: string): string {
52
+ return this.shouldQuote(s) ? this.quote(s) : s
53
+ }
54
+
55
+ private quote(s: string): string {
56
+ return `"${s.replace(/"/g, '""')}"`
57
+ }
58
+
59
+ private shouldQuote(s: string): boolean {
60
+ return s.includes(this.cfg.delimiter) || s.includes('\r') || s.includes('\n') || s.includes('"')
61
+ }
62
+ }
63
+
64
+ export function arrayToCSVString(arr: AnyObject[], cfg: CSVWriterConfig): string {
65
+ const writer = new CSVWriter(cfg)
66
+ return writer.writeRows(arr)
67
+ }
@@ -0,0 +1,49 @@
1
+ import { Transform } from 'node:stream'
2
+ import { AnyObject } from '@naturalcycles/js-lib'
3
+ import { TransformTyped } from '../stream/stream.model'
4
+ import { CSVWriter, CSVWriterConfig } from './csvWriter'
5
+
6
+ export interface TransformToCSVOptions extends CSVWriterConfig {
7
+ /**
8
+ * If true - will throw an error on stringify error
9
+ *
10
+ * @default true
11
+ */
12
+ strict?: boolean
13
+ }
14
+
15
+ /**
16
+ * Transforms objects (objectMode=true) into chunks \n-terminated CSV strings (readableObjectMode=false).
17
+ */
18
+ export function transformToCSV<IN extends AnyObject = any>(
19
+ opt: TransformToCSVOptions,
20
+ ): TransformTyped<IN, string> {
21
+ const { strict = true } = opt
22
+ const writer = new CSVWriter(opt)
23
+ let firstRow = true
24
+
25
+ return new Transform({
26
+ writableObjectMode: true,
27
+ readableObjectMode: false,
28
+ transform(chunk: IN, _, cb) {
29
+ try {
30
+ let s = ''
31
+
32
+ if (firstRow) {
33
+ s = writer.writeHeader() + '\n'
34
+ firstRow = false
35
+ }
36
+
37
+ cb(null, s + writer.writeRow(chunk) + '\n')
38
+ } catch (err) {
39
+ console.error(err)
40
+
41
+ if (strict) {
42
+ cb(err as Error) // emit error
43
+ } else {
44
+ cb() // emit no error, but no result neither
45
+ }
46
+ }
47
+ },
48
+ })
49
+ }
package/src/index.ts CHANGED
@@ -58,6 +58,9 @@ export * from './stream/writable/writableForEach'
58
58
  export * from './stream/writable/writableFork'
59
59
  export * from './stream/writable/writablePushToArray'
60
60
  export * from './stream/writable/writableVoid'
61
+ export * from './csv/csvWriter'
62
+ export * from './csv/csvReader'
63
+ export * from './csv/transformToCSV'
61
64
  export * from './string/inspectAny'
62
65
  export * from './util/env.util'
63
66
  export * from './util/lruMemoCache'
@@ -21,11 +21,6 @@ export interface TransformToNDJsonOptions {
21
21
  * @default `\n`
22
22
  */
23
23
  separator?: string
24
-
25
- /**
26
- * @experimental
27
- */
28
- useFlatstr?: boolean
29
24
  }
30
25
 
31
26
  /**
@@ -34,7 +29,7 @@ export interface TransformToNDJsonOptions {
34
29
  export function transformToNDJson<IN = any>(
35
30
  opt: TransformToNDJsonOptions = {},
36
31
  ): TransformTyped<IN, string> {
37
- const { strict = true, separator = '\n', sortObjects = false, useFlatstr = false } = opt
32
+ const { strict = true, separator = '\n', sortObjects = false } = opt
38
33
 
39
34
  return new Transform({
40
35
  writableObjectMode: true,
@@ -45,11 +40,7 @@ export function transformToNDJson<IN = any>(
45
40
  chunk = _sortObjectDeep(chunk as any)
46
41
  }
47
42
 
48
- if (useFlatstr) {
49
- cb(null, flatstr(JSON.stringify(chunk) + separator))
50
- } else {
51
- cb(null, JSON.stringify(chunk) + separator)
52
- }
43
+ cb(null, JSON.stringify(chunk) + separator)
53
44
  } catch (err) {
54
45
  console.error(err)
55
46
 
@@ -62,12 +53,3 @@ export function transformToNDJson<IN = any>(
62
53
  },
63
54
  })
64
55
  }
65
-
66
- /**
67
- * Based on: https://github.com/davidmarkclements/flatstr/blob/master/index.js
68
- */
69
- function flatstr(s: any): string {
70
- // eslint-disable-next-line
71
- s | 0
72
- return s
73
- }