convert-csv-to-json 4.45.0 → 4.47.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/README.md +9 -0
- package/csvToJson-mockup.html +309 -0
- package/index.d.ts +53 -6
- package/index.js +81 -60
- package/package.json +3 -1
- package/src/browserApi.js +39 -105
- package/src/core/configurable.js +126 -0
- package/src/{util → core}/errors.js +21 -20
- package/src/core/parserConfig.js +37 -0
- package/src/{streamProcessor.js → core/streamProcessor.js} +121 -9
- package/src/csvToJson.js +96 -181
- package/src/csvToJsonAsync.js +15 -102
- package/src/util/fileUtils.js +161 -38
- package/src/util/jsonUtils.js +1 -1
package/index.js
CHANGED
|
@@ -19,6 +19,24 @@ const encodingOps = {
|
|
|
19
19
|
hex: 'hex'
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
+
const csvToJsonAsync = require('./src/csvToJsonAsync');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Apply the same parser configuration update to every parser client.
|
|
26
|
+
* This keeps root-level chainable API calls in sync across sync, async, and browser clients.
|
|
27
|
+
* @param {function(object): void} configFn - Function that applies configuration to a client instance
|
|
28
|
+
* @returns {object} Module exports for chaining
|
|
29
|
+
* @private
|
|
30
|
+
*/
|
|
31
|
+
function applyConfigToAllClients(configFn) {
|
|
32
|
+
configFn(csvToJson);
|
|
33
|
+
configFn(csvToJsonAsync);
|
|
34
|
+
if (exports.browser) {
|
|
35
|
+
configFn(exports.browser);
|
|
36
|
+
}
|
|
37
|
+
return exports;
|
|
38
|
+
}
|
|
39
|
+
|
|
22
40
|
/**
|
|
23
41
|
* Enable or disable automatic type formatting for values
|
|
24
42
|
* Converts numeric strings to numbers, 'true'/'false' to booleans
|
|
@@ -27,8 +45,7 @@ const encodingOps = {
|
|
|
27
45
|
* @returns {object} Module context for method chaining
|
|
28
46
|
*/
|
|
29
47
|
exports.formatValueByType = function (active = true) {
|
|
30
|
-
|
|
31
|
-
return this;
|
|
48
|
+
return applyConfigToAllClients(client => client.formatValueByType(active));
|
|
32
49
|
};
|
|
33
50
|
|
|
34
51
|
/**
|
|
@@ -39,8 +56,7 @@ exports.formatValueByType = function (active = true) {
|
|
|
39
56
|
* @returns {object} Module context for method chaining
|
|
40
57
|
*/
|
|
41
58
|
exports.supportQuotedField = function (active = false) {
|
|
42
|
-
|
|
43
|
-
return this;
|
|
59
|
+
return applyConfigToAllClients(client => client.supportQuotedField(active));
|
|
44
60
|
};
|
|
45
61
|
/**
|
|
46
62
|
* Set the field delimiter character used to separate CSV fields
|
|
@@ -49,8 +65,7 @@ exports.supportQuotedField = function (active = false) {
|
|
|
49
65
|
* @returns {object} Module context for method chaining
|
|
50
66
|
*/
|
|
51
67
|
exports.fieldDelimiter = function (delimiter) {
|
|
52
|
-
|
|
53
|
-
return this;
|
|
68
|
+
return applyConfigToAllClients(client => client.fieldDelimiter(delimiter));
|
|
54
69
|
};
|
|
55
70
|
|
|
56
71
|
/**
|
|
@@ -62,8 +77,7 @@ exports.fieldDelimiter = function (delimiter) {
|
|
|
62
77
|
* @returns {object} Module context for method chaining
|
|
63
78
|
*/
|
|
64
79
|
exports.trimHeaderFieldWhiteSpace = function (active = false) {
|
|
65
|
-
|
|
66
|
-
return this;
|
|
80
|
+
return applyConfigToAllClients(client => client.trimHeaderFieldWhiteSpace(active));
|
|
67
81
|
};
|
|
68
82
|
|
|
69
83
|
/**
|
|
@@ -74,8 +88,7 @@ exports.trimHeaderFieldWhiteSpace = function (active = false) {
|
|
|
74
88
|
* @returns {object} Module context for method chaining
|
|
75
89
|
*/
|
|
76
90
|
exports.indexHeader = function (index) {
|
|
77
|
-
|
|
78
|
-
return this;
|
|
91
|
+
return applyConfigToAllClients(client => client.indexHeader(index));
|
|
79
92
|
};
|
|
80
93
|
|
|
81
94
|
/**
|
|
@@ -91,8 +104,7 @@ exports.indexHeader = function (index) {
|
|
|
91
104
|
* csvToJson.parseSubArray('*', ',')
|
|
92
105
|
*/
|
|
93
106
|
exports.parseSubArray = function (delimiter, separator) {
|
|
94
|
-
|
|
95
|
-
return this;
|
|
107
|
+
return applyConfigToAllClients(client => client.parseSubArray(delimiter, separator));
|
|
96
108
|
};
|
|
97
109
|
|
|
98
110
|
/**
|
|
@@ -111,8 +123,7 @@ exports.ignoreColumnIndexes = function (indexes) {
|
|
|
111
123
|
if (!indexes.every(idx => Number.isInteger(idx) && idx >= 0)) {
|
|
112
124
|
throw new TypeError('All elements in indexes must be valid non-negative numbers (>= 0)');
|
|
113
125
|
}
|
|
114
|
-
|
|
115
|
-
return this;
|
|
126
|
+
return applyConfigToAllClients(client => client.ignoreColumnIndexes(indexes));
|
|
116
127
|
};
|
|
117
128
|
|
|
118
129
|
/**
|
|
@@ -123,8 +134,7 @@ exports.ignoreColumnIndexes = function (indexes) {
|
|
|
123
134
|
* @returns {object} Module context for method chaining
|
|
124
135
|
*/
|
|
125
136
|
exports.customEncoding = function (encoding) {
|
|
126
|
-
|
|
127
|
-
return this;
|
|
137
|
+
return applyConfigToAllClients(client => client.encoding(encoding));
|
|
128
138
|
};
|
|
129
139
|
|
|
130
140
|
/**
|
|
@@ -133,8 +143,7 @@ exports.customEncoding = function (encoding) {
|
|
|
133
143
|
* @returns {object} Module context for method chaining
|
|
134
144
|
*/
|
|
135
145
|
exports.utf8Encoding = function utf8Encoding() {
|
|
136
|
-
|
|
137
|
-
return this;
|
|
146
|
+
return applyConfigToAllClients(client => client.encoding(encodingOps.utf8));
|
|
138
147
|
};
|
|
139
148
|
|
|
140
149
|
/**
|
|
@@ -143,8 +152,7 @@ exports.utf8Encoding = function utf8Encoding() {
|
|
|
143
152
|
* @returns {object} Module context for method chaining
|
|
144
153
|
*/
|
|
145
154
|
exports.ucs2Encoding = function () {
|
|
146
|
-
|
|
147
|
-
return this;
|
|
155
|
+
return applyConfigToAllClients(client => client.encoding(encodingOps.ucs2));
|
|
148
156
|
};
|
|
149
157
|
|
|
150
158
|
/**
|
|
@@ -153,8 +161,7 @@ exports.ucs2Encoding = function () {
|
|
|
153
161
|
* @returns {object} Module context for method chaining
|
|
154
162
|
*/
|
|
155
163
|
exports.utf16leEncoding = function () {
|
|
156
|
-
|
|
157
|
-
return this;
|
|
164
|
+
return applyConfigToAllClients(client => client.encoding(encodingOps.utf16le));
|
|
158
165
|
};
|
|
159
166
|
|
|
160
167
|
/**
|
|
@@ -163,8 +170,7 @@ exports.utf16leEncoding = function () {
|
|
|
163
170
|
* @returns {object} Module context for method chaining
|
|
164
171
|
*/
|
|
165
172
|
exports.latin1Encoding = function () {
|
|
166
|
-
|
|
167
|
-
return this;
|
|
173
|
+
return applyConfigToAllClients(client => client.encoding(encodingOps.latin1));
|
|
168
174
|
};
|
|
169
175
|
|
|
170
176
|
/**
|
|
@@ -173,8 +179,7 @@ exports.latin1Encoding = function () {
|
|
|
173
179
|
* @returns {object} Module context for method chaining
|
|
174
180
|
*/
|
|
175
181
|
exports.asciiEncoding = function () {
|
|
176
|
-
|
|
177
|
-
return this;
|
|
182
|
+
return applyConfigToAllClients(client => client.encoding(encodingOps.ascii));
|
|
178
183
|
};
|
|
179
184
|
|
|
180
185
|
/**
|
|
@@ -183,8 +188,7 @@ exports.asciiEncoding = function () {
|
|
|
183
188
|
* @returns {object} Module context for method chaining
|
|
184
189
|
*/
|
|
185
190
|
exports.base64Encoding = function () {
|
|
186
|
-
|
|
187
|
-
return this;
|
|
191
|
+
return applyConfigToAllClients(client => client.encoding(encodingOps.base64));
|
|
188
192
|
};
|
|
189
193
|
|
|
190
194
|
/**
|
|
@@ -193,8 +197,7 @@ exports.base64Encoding = function () {
|
|
|
193
197
|
* @returns {object} Module context for method chaining
|
|
194
198
|
*/
|
|
195
199
|
exports.hexEncoding = function () {
|
|
196
|
-
|
|
197
|
-
return this;
|
|
200
|
+
return applyConfigToAllClients(client => client.encoding(encodingOps.hex));
|
|
198
201
|
};
|
|
199
202
|
|
|
200
203
|
/**
|
|
@@ -210,8 +213,7 @@ exports.hexEncoding = function () {
|
|
|
210
213
|
* .getJsonFromCsv('input.csv')
|
|
211
214
|
*/
|
|
212
215
|
exports.mapRows = function (mapperFn) {
|
|
213
|
-
|
|
214
|
-
return this;
|
|
216
|
+
return applyConfigToAllClients(client => client.mapRows(mapperFn));
|
|
215
217
|
};
|
|
216
218
|
|
|
217
219
|
/**
|
|
@@ -257,8 +259,8 @@ exports.getJsonFromCsv = function(inputFileName) {
|
|
|
257
259
|
};
|
|
258
260
|
|
|
259
261
|
/**
|
|
260
|
-
* Parse CSV file asynchronously and return parsed data as JSON array
|
|
261
|
-
* @param {string}
|
|
262
|
+
* Parse CSV file asynchronously and return parsed data as JSON array.
|
|
263
|
+
* @param {string} input - Path to file or CSV string
|
|
262
264
|
* @param {object} options - Configuration options
|
|
263
265
|
* @param {boolean} options.raw - If true, treats first param as CSV content; if false, reads from file
|
|
264
266
|
* @returns {Promise<Array<object>>} Promise resolving to array of objects
|
|
@@ -271,11 +273,46 @@ exports.getJsonFromCsv = function(inputFileName) {
|
|
|
271
273
|
* const data = await csvToJson.getJsonFromCsvAsync('resource/input.csv');
|
|
272
274
|
* console.log(data);
|
|
273
275
|
*/
|
|
274
|
-
|
|
276
|
+
exports.getJsonFromCsvAsync = function(input, options) {
|
|
277
|
+
return csvToJsonAsync.getJsonFromCsvAsync(input, options);
|
|
278
|
+
};
|
|
275
279
|
|
|
276
280
|
/**
|
|
277
|
-
* Parse
|
|
278
|
-
*
|
|
281
|
+
* Parse a raw CSV string asynchronously and return parsed JSON objects.
|
|
282
|
+
* @param {string} input - CSV content as a string
|
|
283
|
+
* @param {object} [options] - Configuration options
|
|
284
|
+
* @param {boolean} [options.raw] - If true, treats input as CSV content
|
|
285
|
+
* @returns {Promise<Array<object>>} Promise resolving to array of parsed objects
|
|
286
|
+
* @category 1-Core API
|
|
287
|
+
*/
|
|
288
|
+
exports.csvStringToJsonAsync = function(input, options) {
|
|
289
|
+
return csvToJsonAsync.csvStringToJsonAsync(input, options);
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Parse a raw CSV string asynchronously and return a JSON string.
|
|
294
|
+
* @param {string} input - CSV content as a string
|
|
295
|
+
* @returns {Promise<string>} Promise resolving to JSON string
|
|
296
|
+
* @category 1-Core API
|
|
297
|
+
*/
|
|
298
|
+
exports.csvStringToJsonStringifiedAsync = function(input) {
|
|
299
|
+
return csvToJsonAsync.csvStringToJsonStringifiedAsync(input);
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Parse a CSV file asynchronously and write the parsed JSON to an output file.
|
|
304
|
+
* @param {string} input - Path to the input CSV file
|
|
305
|
+
* @param {string} output - Path to the output JSON file
|
|
306
|
+
* @returns {Promise<void>} Promise resolving when file is written
|
|
307
|
+
* @category 1-Core API
|
|
308
|
+
*/
|
|
309
|
+
exports.generateJsonFileFromCsvAsync = function(input, output) {
|
|
310
|
+
return csvToJsonAsync.generateJsonFileFromCsv(input, output);
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Parse CSV from a Readable stream and return parsed data as JSON array.
|
|
315
|
+
* Processes data in chunks for memory-efficient handling of large files.
|
|
279
316
|
* @param {object} stream - Node.js Readable stream containing CSV data
|
|
280
317
|
* @returns {Promise<Array<object>>} Promise resolving to array of objects representing CSV rows
|
|
281
318
|
* @throws {InputValidationError} If stream is invalid
|
|
@@ -288,9 +325,12 @@ const csvToJsonAsync = require('./src/csvToJsonAsync');
|
|
|
288
325
|
* const data = await csvToJson.getJsonFromStreamAsync(stream);
|
|
289
326
|
* console.log(data);
|
|
290
327
|
*/
|
|
328
|
+
exports.getJsonFromStreamAsync = function(stream) {
|
|
329
|
+
return csvToJsonAsync.getJsonFromStreamAsync(stream);
|
|
330
|
+
};
|
|
291
331
|
|
|
292
332
|
/**
|
|
293
|
-
* Parse CSV from a file path using streaming for memory-efficient processing
|
|
333
|
+
* Parse CSV from a file path using streaming for memory-efficient processing.
|
|
294
334
|
* @param {string} filePath - Path to the CSV file
|
|
295
335
|
* @returns {Promise<Array<object>>} Promise resolving to array of objects representing CSV rows
|
|
296
336
|
* @throws {InputValidationError} If filePath is invalid
|
|
@@ -302,28 +342,9 @@ const csvToJsonAsync = require('./src/csvToJsonAsync');
|
|
|
302
342
|
* const data = await csvToJson.getJsonFromFileStreamingAsync('large.csv');
|
|
303
343
|
* console.log(data);
|
|
304
344
|
*/
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
getJsonFromCsvAsync: function(input, options) {
|
|
309
|
-
return csvToJsonAsync.getJsonFromCsvAsync(input, options);
|
|
310
|
-
},
|
|
311
|
-
csvStringToJsonAsync: function(input, options) {
|
|
312
|
-
return csvToJsonAsync.csvStringToJsonAsync(input, options);
|
|
313
|
-
},
|
|
314
|
-
csvStringToJsonStringifiedAsync: function(input) {
|
|
315
|
-
return csvToJsonAsync.csvStringToJsonStringifiedAsync(input);
|
|
316
|
-
},
|
|
317
|
-
generateJsonFileFromCsvAsync: function(input, output) {
|
|
318
|
-
return csvToJsonAsync.generateJsonFileFromCsv(input, output);
|
|
319
|
-
},
|
|
320
|
-
getJsonFromStreamAsync: function(stream) {
|
|
321
|
-
return csvToJsonAsync.getJsonFromStreamAsync(stream);
|
|
322
|
-
},
|
|
323
|
-
getJsonFromFileStreamingAsync: function(filePath) {
|
|
324
|
-
return csvToJsonAsync.getJsonFromFileStreamingAsync(filePath);
|
|
325
|
-
}
|
|
326
|
-
});
|
|
345
|
+
exports.getJsonFromFileStreamingAsync = function(filePath) {
|
|
346
|
+
return csvToJsonAsync.getJsonFromFileStreamingAsync(filePath);
|
|
347
|
+
};
|
|
327
348
|
|
|
328
349
|
/**
|
|
329
350
|
* Parse a CSV string and return as JSON array of objects (synchronous)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "convert-csv-to-json",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.47.0",
|
|
4
4
|
"description": "Convert CSV to JSON",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"lint": "eslint .",
|
|
11
11
|
"docs": "jsdoc -c jsdoc.json",
|
|
12
12
|
"docs:api": "jsdoc -c jsdoc.json -d demo/api",
|
|
13
|
+
"demo": "npx http-server demo -p 8000",
|
|
13
14
|
"prepublishOnly": "npm run docs:api",
|
|
14
15
|
"version-patch": "npm version patch",
|
|
15
16
|
"version-minor": "npm version minor",
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
"browserify": "^17.0.1",
|
|
56
57
|
"eslint": "^10.1.0",
|
|
57
58
|
"eslint-plugin-jsdoc": "^63.0.0",
|
|
59
|
+
"http-server": "^14.1.1",
|
|
58
60
|
"jest": "^30.2.0",
|
|
59
61
|
"jsdoc": "^4.0.5",
|
|
60
62
|
"ts-jest": "^29.4.5",
|
package/src/browserApi.js
CHANGED
|
@@ -3,104 +3,52 @@
|
|
|
3
3
|
"use strict";
|
|
4
4
|
|
|
5
5
|
const csvToJson = require('./csvToJson');
|
|
6
|
-
const
|
|
7
|
-
const
|
|
6
|
+
const Configurable = require('./core/configurable');
|
|
7
|
+
const { InputValidationError, BrowserApiError } = require('./core/errors');
|
|
8
|
+
const StreamProcessor = require('./core/streamProcessor');
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Browser-friendly CSV to JSON API
|
|
11
12
|
* Provides methods for parsing CSV strings and File/Blob objects in browser environments
|
|
12
|
-
*
|
|
13
|
+
* Uses isolated parser configuration via ParserConfig snapshots
|
|
13
14
|
* @category 4-Browser
|
|
14
15
|
*/
|
|
15
|
-
class BrowserApi {
|
|
16
|
+
class BrowserApi extends Configurable {
|
|
16
17
|
/**
|
|
17
18
|
* Constructor initializes proxy to sync csvToJson instance
|
|
18
19
|
*/
|
|
19
20
|
constructor() {
|
|
20
|
-
|
|
21
|
+
super();
|
|
22
|
+
// reuse the existing csvToJson instance for parsing
|
|
21
23
|
this.csvToJson = csvToJson;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
/**
|
|
25
|
-
*
|
|
26
|
-
* @param {
|
|
27
|
-
* @
|
|
28
|
-
|
|
29
|
-
formatValueByType(active = true) {
|
|
30
|
-
this.csvToJson.formatValueByType(active);
|
|
31
|
-
return this;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Enable or disable support for RFC 4180 quoted fields
|
|
36
|
-
* @param {boolean} active - Whether to support quoted fields (default: false)
|
|
37
|
-
* @returns {this} For method chaining
|
|
38
|
-
*/
|
|
39
|
-
supportQuotedField(active = false) {
|
|
40
|
-
this.csvToJson.supportQuotedField(active);
|
|
41
|
-
return this;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Set the field delimiter character
|
|
46
|
-
* @param {string} delimiter - Character(s) to use as field separator
|
|
47
|
-
* @returns {this} For method chaining
|
|
48
|
-
*/
|
|
49
|
-
fieldDelimiter(delimiter) {
|
|
50
|
-
this.csvToJson.fieldDelimiter(delimiter);
|
|
51
|
-
return this;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Configure whitespace handling in header field names
|
|
56
|
-
* @param {boolean} active - If true, removes all whitespace; if false, only trims edges (default: false)
|
|
57
|
-
* @returns {this} For method chaining
|
|
58
|
-
*/
|
|
59
|
-
trimHeaderFieldWhiteSpace(active = false) {
|
|
60
|
-
this.csvToJson.trimHeaderFieldWhiteSpace(active);
|
|
61
|
-
return this;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Set the row index where CSV headers are located
|
|
66
|
-
* @param {number} index - Zero-based row index containing headers
|
|
67
|
-
* @returns {this} For method chaining
|
|
68
|
-
*/
|
|
69
|
-
indexHeader(index) {
|
|
70
|
-
this.csvToJson.indexHeader(index);
|
|
71
|
-
return this;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Configure columns to exclude from output
|
|
76
|
-
* @param {Array<number>} indexes - Column indexes to ignore
|
|
77
|
-
* @returns {this} For method chaining
|
|
78
|
-
* @private Used internally after validation in index.js
|
|
79
|
-
*/
|
|
80
|
-
ignoreColumnIndexes(indexes) {
|
|
81
|
-
this.csvToJson.ignoreColumnIndexes(indexes);
|
|
82
|
-
return this;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Configure sub-array parsing for special field values
|
|
87
|
-
* @param {string} delimiter - Bracket character (default: '*')
|
|
88
|
-
* @param {string} separator - Item separator within brackets (default: ',')
|
|
89
|
-
* @returns {this} For method chaining
|
|
27
|
+
* Validate CSV text input for browser methods.
|
|
28
|
+
* @param {string} csvString - CSV content as string
|
|
29
|
+
* @throws {InputValidationError} If the input is not a valid string
|
|
30
|
+
* @private
|
|
90
31
|
*/
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
32
|
+
_validateCsvString(csvString) {
|
|
33
|
+
if (csvString === undefined || csvString === null) {
|
|
34
|
+
throw new InputValidationError(
|
|
35
|
+
'csvString',
|
|
36
|
+
'string',
|
|
37
|
+
`${typeof csvString}`,
|
|
38
|
+
'Provide valid CSV content as a string to parse.'
|
|
39
|
+
);
|
|
40
|
+
}
|
|
94
41
|
}
|
|
95
42
|
|
|
96
43
|
/**
|
|
97
|
-
*
|
|
98
|
-
* @param {
|
|
99
|
-
* @returns {
|
|
44
|
+
* Parse CSV text using a frozen parser configuration snapshot.
|
|
45
|
+
* @param {string} csvString - CSV content as string
|
|
46
|
+
* @returns {Array<object>} Parsed CSV rows
|
|
47
|
+
* @private
|
|
100
48
|
*/
|
|
101
|
-
|
|
102
|
-
this.
|
|
103
|
-
return this;
|
|
49
|
+
_parseCsvText(csvString) {
|
|
50
|
+
const config = this.getParserConfig();
|
|
51
|
+
return this.csvToJson.csvToJsonWithConfig(String(csvString), config);
|
|
104
52
|
}
|
|
105
53
|
|
|
106
54
|
/**
|
|
@@ -115,15 +63,8 @@ class BrowserApi {
|
|
|
115
63
|
* console.log(rows); // [{ name: 'Alice', age: '30' }]
|
|
116
64
|
*/
|
|
117
65
|
csvStringToJson(csvString) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
'csvString',
|
|
121
|
-
'string',
|
|
122
|
-
`${typeof csvString}`,
|
|
123
|
-
'Provide valid CSV content as a string to parse.'
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
return this.csvToJson.csvToJson(csvString);
|
|
66
|
+
this._validateCsvString(csvString);
|
|
67
|
+
return this._parseCsvText(csvString);
|
|
127
68
|
}
|
|
128
69
|
|
|
129
70
|
/**
|
|
@@ -138,15 +79,9 @@ class BrowserApi {
|
|
|
138
79
|
* console.log(jsonString);
|
|
139
80
|
*/
|
|
140
81
|
csvStringToJsonStringified(csvString) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
'string',
|
|
145
|
-
`${typeof csvString}`,
|
|
146
|
-
'Provide valid CSV content as a string to parse.'
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
return this.csvToJson.csvStringToJsonStringified(csvString);
|
|
82
|
+
this._validateCsvString(csvString);
|
|
83
|
+
const rows = this._parseCsvText(csvString);
|
|
84
|
+
return JSON.stringify(rows, undefined, 1);
|
|
150
85
|
}
|
|
151
86
|
|
|
152
87
|
/**
|
|
@@ -212,9 +147,7 @@ class BrowserApi {
|
|
|
212
147
|
));
|
|
213
148
|
reader.onload = () => {
|
|
214
149
|
try {
|
|
215
|
-
|
|
216
|
-
const result = this.csvToJson.csvToJson(String(text));
|
|
217
|
-
resolve(result);
|
|
150
|
+
resolve(this._parseCsvText(reader.result));
|
|
218
151
|
} catch (err) {
|
|
219
152
|
reject(BrowserApiError.parseFileError(err));
|
|
220
153
|
}
|
|
@@ -257,7 +190,8 @@ class BrowserApi {
|
|
|
257
190
|
);
|
|
258
191
|
}
|
|
259
192
|
|
|
260
|
-
const
|
|
193
|
+
const config = this.getParserConfig();
|
|
194
|
+
const streamProcessor = new StreamProcessor(config, { isBrowser: true });
|
|
261
195
|
return streamProcessor.processStream(stream);
|
|
262
196
|
}
|
|
263
197
|
|
|
@@ -302,7 +236,7 @@ class BrowserApi {
|
|
|
302
236
|
* @param {function(Array<object>, number, number): void} options.onChunk - Callback for each chunk of processed rows
|
|
303
237
|
* @param {function(Array<object>): void} [options.onComplete] - Callback when processing is complete
|
|
304
238
|
* @param {function(Error): void} [options.onError] - Callback for errors
|
|
305
|
-
* @param {number} [options.chunkSize
|
|
239
|
+
* @param {number} [options.chunkSize] - Number of rows per chunk (default: 1000)
|
|
306
240
|
* @returns {Promise<void>} Promise that resolves when streaming starts
|
|
307
241
|
* @throws {InputValidationError} If file or options are invalid
|
|
308
242
|
* @example
|
|
@@ -343,7 +277,8 @@ class BrowserApi {
|
|
|
343
277
|
}
|
|
344
278
|
|
|
345
279
|
const chunkSize = options.chunkSize || 1000;
|
|
346
|
-
const
|
|
280
|
+
const config = this.getParserConfig();
|
|
281
|
+
const streamProcessor = new StreamProcessor(config, {
|
|
347
282
|
isBrowser: true,
|
|
348
283
|
chunkSize,
|
|
349
284
|
onChunk: options.onChunk,
|
|
@@ -393,8 +328,7 @@ class BrowserApi {
|
|
|
393
328
|
|
|
394
329
|
reader.onload = () => {
|
|
395
330
|
try {
|
|
396
|
-
const
|
|
397
|
-
const allRows = this.csvToJson.csvToJson(String(text));
|
|
331
|
+
const allRows = this._parseCsvText(reader.result);
|
|
398
332
|
|
|
399
333
|
// Process in chunks
|
|
400
334
|
let processed = 0;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { ConfigurationError } = require('./errors');
|
|
4
|
+
const ParserConfig = require('./parserConfig');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Shared configuration builder for CSV parser clients.
|
|
8
|
+
* Provides chainable methods for parser options and snapshots.
|
|
9
|
+
*/
|
|
10
|
+
class Configurable {
|
|
11
|
+
/**
|
|
12
|
+
* Initialize a configurable parser instance.
|
|
13
|
+
* @param {object} [initialConfig] - Initial parser configuration values
|
|
14
|
+
*/
|
|
15
|
+
constructor(initialConfig = {}) {
|
|
16
|
+
this.config = { ...initialConfig };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Enable or disable automatic type formatting for values.
|
|
21
|
+
* @param {boolean} active - Whether to format values by type
|
|
22
|
+
* @returns {this} For method chaining
|
|
23
|
+
*/
|
|
24
|
+
formatValueByType(active = true) {
|
|
25
|
+
this.config.printValueFormatByType = active;
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Enable or disable support for RFC 4180 quoted fields.
|
|
31
|
+
* @param {boolean} active - Whether to support quoted fields
|
|
32
|
+
* @returns {this} For method chaining
|
|
33
|
+
*/
|
|
34
|
+
supportQuotedField(active = false) {
|
|
35
|
+
this.config.isSupportQuotedField = active;
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Set the field delimiter character.
|
|
41
|
+
* @param {string} delimiter - Character(s) to use as field separator
|
|
42
|
+
* @returns {this} For method chaining
|
|
43
|
+
*/
|
|
44
|
+
fieldDelimiter(delimiter) {
|
|
45
|
+
this.config.delimiter = delimiter;
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Configure whitespace handling in header field names.
|
|
51
|
+
* @param {boolean} active - Whether to trim whitespace in header names
|
|
52
|
+
* @returns {this} For method chaining
|
|
53
|
+
*/
|
|
54
|
+
trimHeaderFieldWhiteSpace(active = false) {
|
|
55
|
+
this.config.isTrimHeaderFieldWhiteSpace = active;
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Set the row index where CSV headers are located.
|
|
61
|
+
* @param {number} indexHeaderValue - Zero-based row index containing headers
|
|
62
|
+
* @returns {this} For method chaining
|
|
63
|
+
*/
|
|
64
|
+
indexHeader(indexHeaderValue) {
|
|
65
|
+
if (isNaN(indexHeaderValue)) {
|
|
66
|
+
throw ConfigurationError.invalidHeaderIndex(indexHeaderValue);
|
|
67
|
+
}
|
|
68
|
+
this.config.indexHeaderValue = indexHeaderValue;
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Configure sub-array parsing for special field values.
|
|
74
|
+
* @param {string} delimiter - Bracket character
|
|
75
|
+
* @param {string} separator - Item separator within brackets
|
|
76
|
+
* @returns {this} For method chaining
|
|
77
|
+
*/
|
|
78
|
+
parseSubArray(delimiter = '*', separator = ',') {
|
|
79
|
+
this.config.parseSubArrayDelimiter = delimiter;
|
|
80
|
+
this.config.parseSubArraySeparator = separator;
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Set a mapper function for row transformation.
|
|
86
|
+
* @param {function(object, number): object|null} mapperFn - Function receiving (row, index)
|
|
87
|
+
* @returns {this} For method chaining
|
|
88
|
+
*/
|
|
89
|
+
mapRows(mapperFn) {
|
|
90
|
+
if (typeof mapperFn !== 'function') {
|
|
91
|
+
throw new TypeError('mapperFn must be a function');
|
|
92
|
+
}
|
|
93
|
+
this.config.rowMapper = mapperFn;
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Configure column indexes to exclude from output.
|
|
99
|
+
* @param {Array<number>} indexes - Column indexes to ignore
|
|
100
|
+
* @returns {this} For method chaining
|
|
101
|
+
*/
|
|
102
|
+
ignoreColumnIndexes(indexes) {
|
|
103
|
+
this.config.indexesToIgnore = Array.isArray(indexes) ? [...indexes] : [...indexes];
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Set the file encoding for reading CSV files.
|
|
109
|
+
* @param {string} encoding - Node.js supported encoding
|
|
110
|
+
* @returns {this} For method chaining
|
|
111
|
+
*/
|
|
112
|
+
encoding(encoding) {
|
|
113
|
+
this.config.encoding = encoding;
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Create an immutable parser configuration snapshot.
|
|
119
|
+
* @returns {ParserConfig} Frozen parser configuration
|
|
120
|
+
*/
|
|
121
|
+
getParserConfig() {
|
|
122
|
+
return new ParserConfig(this.config);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = Configurable;
|