@naturalcycles/nodejs-lib 12.87.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.
- package/dist/csv/csvReader.d.ts +16 -0
- package/dist/csv/csvReader.js +49 -0
- package/dist/csv/csvWriter.d.ts +26 -0
- package/dist/csv/csvWriter.js +43 -0
- package/dist/csv/transformToCSV.d.ts +15 -0
- package/dist/csv/transformToCSV.js +37 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/stream/ndjson/transformToNDJson.d.ts +0 -4
- package/dist/stream/ndjson/transformToNDJson.js +2 -15
- package/dist/validation/joi/joi.model.d.ts +7 -0
- package/package.json +1 -1
- package/src/csv/csvReader.ts +72 -0
- package/src/csv/csvWriter.ts +67 -0
- package/src/csv/transformToCSV.ts +49 -0
- package/src/index.ts +3 -0
- package/src/stream/ndjson/transformToNDJson.ts +2 -20
- package/src/validation/joi/joi.model.ts +8 -0
|
@@ -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
|
|
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
|
-
|
|
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
|
-
}
|
|
@@ -25,3 +25,10 @@ export interface ObjectSchemaTyped<IN, OUT = IN> extends ObjectSchema<IN>, AnySc
|
|
|
25
25
|
}
|
|
26
26
|
export interface StringSchemaTyped extends StringSchema, AnySchemaTyped<string> {
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* This type is useful to allow "joi schema merging".
|
|
30
|
+
* Because by default Joi doesn't allow normal merging.
|
|
31
|
+
* E.g `joiSchema.concat` doesn't play well when some property exists
|
|
32
|
+
* in both left and right side.
|
|
33
|
+
*/
|
|
34
|
+
export type JoiSchemaObject<T> = Partial<Record<keyof T, any>>;
|
package/package.json
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
}
|
|
@@ -42,3 +42,11 @@ export interface ObjectSchemaTyped<IN, OUT = IN>
|
|
|
42
42
|
extends ObjectSchema<IN>,
|
|
43
43
|
AnySchemaTyped<IN, OUT> {}
|
|
44
44
|
export interface StringSchemaTyped extends StringSchema, AnySchemaTyped<string> {}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* This type is useful to allow "joi schema merging".
|
|
48
|
+
* Because by default Joi doesn't allow normal merging.
|
|
49
|
+
* E.g `joiSchema.concat` doesn't play well when some property exists
|
|
50
|
+
* in both left and right side.
|
|
51
|
+
*/
|
|
52
|
+
export type JoiSchemaObject<T> = Partial<Record<keyof T, any>>
|