convert-csv-to-json 3.28.0 → 4.1.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/CHANGELOG.md +41 -1
- package/README.md +173 -833
- package/docs/ASYNC.md +485 -0
- package/docs/BROWSER.md +425 -0
- package/docs/ERROR_HANDLING.md +367 -0
- package/docs/MAPROWS.md +196 -0
- package/docs/SYNC.md +239 -0
- package/index.d.ts +10 -8
- package/index.js +13 -10
- package/migration/RFC4180_MIGRATION_GUIDE.md +737 -0
- package/package.json +1 -1
- package/release-notes/RELEASE_NOTES_v4.0.0.md +320 -0
- package/src/browserApi.js +29 -6
- package/src/csvToJson.js +233 -82
- package/src/csvToJsonAsync.js +15 -1
- package/src/util/errors.js +227 -0
- package/src/util/fileUtils.js +18 -7
- package/src/util/jsonUtils.js +3 -1
- /package/{MIGRATION.md → migration/MIGRATION_TO_ASYNC.md} +0 -0
package/docs/ASYNC.md
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
# Asynchronous API Documentation
|
|
2
|
+
|
|
3
|
+
Promise-based async/await API for modern Node.js applications. Perfect for handling large files, concurrent operations, and integration with async workflows.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
- [Basic Usage](#basic-usage)
|
|
7
|
+
- [File Operations](#file-operations)
|
|
8
|
+
- [Working with Raw CSV Data](#working-with-raw-csv-data)
|
|
9
|
+
- [Processing Large Files](#processing-large-files)
|
|
10
|
+
- [Batch Processing](#batch-processing)
|
|
11
|
+
- [Error Handling](#error-handling)
|
|
12
|
+
- [Method Chaining](#method-chaining)
|
|
13
|
+
- [Row Mapping and Transformation](#row-mapping-and-transformation)
|
|
14
|
+
- [TypeScript Support](#typescript-support)
|
|
15
|
+
|
|
16
|
+
## Basic Usage
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
const csvToJson = require('convert-csv-to-json');
|
|
20
|
+
|
|
21
|
+
// Using async/await
|
|
22
|
+
async function parseCSV() {
|
|
23
|
+
const json = await csvToJson.getJsonFromCsvAsync('input.csv');
|
|
24
|
+
console.log(json);
|
|
25
|
+
}
|
|
26
|
+
parseCSV();
|
|
27
|
+
|
|
28
|
+
// Using Promises
|
|
29
|
+
csvToJson.getJsonFromCsvAsync('input.csv')
|
|
30
|
+
.then(json => console.log(json))
|
|
31
|
+
.catch(err => console.error('Error:', err));
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## File Operations
|
|
35
|
+
|
|
36
|
+
### Read CSV File
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
const csvToJson = require('convert-csv-to-json');
|
|
40
|
+
|
|
41
|
+
async function readCSV() {
|
|
42
|
+
try {
|
|
43
|
+
const json = await csvToJson.getJsonFromCsvAsync('input.csv');
|
|
44
|
+
console.log(`Parsed ${json.length} records`);
|
|
45
|
+
return json;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error('Error reading CSV:', error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Generate JSON File from CSV
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
async function convertAndSave() {
|
|
56
|
+
await csvToJson
|
|
57
|
+
.fieldDelimiter(',')
|
|
58
|
+
.formatValueByType()
|
|
59
|
+
.generateJsonFileFromCsvAsync('input.csv', 'output.json');
|
|
60
|
+
|
|
61
|
+
console.log('JSON file created successfully');
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Parse CSV String
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
const csv = 'name,age\nAlice,30\nBob,25';
|
|
69
|
+
|
|
70
|
+
// Parse to array
|
|
71
|
+
const json = await csvToJson.csvStringToJsonAsync(csv);
|
|
72
|
+
|
|
73
|
+
// Parse to JSON string (validated)
|
|
74
|
+
const jsonString = await csvToJson.csvStringToJsonStringifiedAsync(csv);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Working with Raw CSV Data
|
|
78
|
+
|
|
79
|
+
### Parse API Response
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
async function parseRemoteCSV(url) {
|
|
83
|
+
const response = await fetch(url);
|
|
84
|
+
const csvText = await response.text();
|
|
85
|
+
|
|
86
|
+
const json = await csvToJson
|
|
87
|
+
.formatValueByType()
|
|
88
|
+
.getJsonFromCsvAsync(csvText, { raw: true });
|
|
89
|
+
|
|
90
|
+
return json;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Parse from Buffer
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
async function parseBuffer(buffer) {
|
|
98
|
+
const csvString = buffer.toString('utf8');
|
|
99
|
+
return csvToJson.getJsonFromCsvAsync(csvString, { raw: true });
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Processing Large Files
|
|
104
|
+
|
|
105
|
+
### Stream Processing
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
const { createReadStream } = require('fs');
|
|
109
|
+
const { createInterface } = require('readline');
|
|
110
|
+
|
|
111
|
+
async function* processLargeFile(filePath) {
|
|
112
|
+
const fileStream = createReadStream(filePath);
|
|
113
|
+
const lines = createInterface({
|
|
114
|
+
input: fileStream,
|
|
115
|
+
crlfDelay: Infinity
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const headers = (await lines[Symbol.asyncIterator]().next()).value;
|
|
119
|
+
|
|
120
|
+
for await (const line of lines) {
|
|
121
|
+
const fullCSV = headers + '\n' + line;
|
|
122
|
+
const record = await csvToJson.getJsonFromCsvAsync(fullCSV, { raw: true });
|
|
123
|
+
yield record[0];
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Usage
|
|
128
|
+
async function process() {
|
|
129
|
+
for await (const record of processLargeFile('large.csv')) {
|
|
130
|
+
console.log(record);
|
|
131
|
+
// Process one record at a time
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Chunked Processing
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
const fs = require('fs');
|
|
140
|
+
|
|
141
|
+
async function processInChunks(filePath, chunkSize = 1000) {
|
|
142
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
143
|
+
const lines = content.split('\n');
|
|
144
|
+
const headers = lines[0];
|
|
145
|
+
|
|
146
|
+
for (let i = 1; i < lines.length; i += chunkSize) {
|
|
147
|
+
const chunk = lines.slice(i, i + chunkSize);
|
|
148
|
+
const csvChunk = headers + '\n' + chunk.join('\n');
|
|
149
|
+
|
|
150
|
+
const json = await csvToJson.getJsonFromCsvAsync(csvChunk, { raw: true });
|
|
151
|
+
await processChunk(json);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function processChunk(records) {
|
|
156
|
+
// Your processing logic here
|
|
157
|
+
console.log(`Processing ${records.length} records`);
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Batch Processing
|
|
162
|
+
|
|
163
|
+
### Sequential Processing
|
|
164
|
+
|
|
165
|
+
```js
|
|
166
|
+
async function processFilesSequentially(files) {
|
|
167
|
+
const results = [];
|
|
168
|
+
|
|
169
|
+
for (const file of files) {
|
|
170
|
+
const json = await csvToJson.getJsonFromCsvAsync(file);
|
|
171
|
+
results.push({ file, data: json });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return results;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Usage
|
|
178
|
+
const files = ['data1.csv', 'data2.csv', 'data3.csv'];
|
|
179
|
+
const results = await processFilesSequentially(files);
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Parallel Processing
|
|
183
|
+
|
|
184
|
+
```js
|
|
185
|
+
async function processFilesParallel(files, concurrency = 3) {
|
|
186
|
+
const results = new Map();
|
|
187
|
+
|
|
188
|
+
// Process in chunks of concurrent operations
|
|
189
|
+
for (let i = 0; i < files.length; i += concurrency) {
|
|
190
|
+
const batch = files.slice(i, i + concurrency);
|
|
191
|
+
|
|
192
|
+
const promises = batch.map(async file => {
|
|
193
|
+
const json = await csvToJson.getJsonFromCsvAsync(file);
|
|
194
|
+
return [file, json];
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const batchResults = await Promise.all(promises);
|
|
198
|
+
batchResults.forEach(([file, json]) => results.set(file, json));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return results;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Usage
|
|
205
|
+
const results = await processFilesParallel(['file1.csv', 'file2.csv', 'file3.csv'], 2);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Error Handling
|
|
209
|
+
|
|
210
|
+
### Basic Try-Catch
|
|
211
|
+
|
|
212
|
+
```js
|
|
213
|
+
async function safeRead(filePath) {
|
|
214
|
+
try {
|
|
215
|
+
const json = await csvToJson.getJsonFromCsvAsync(filePath);
|
|
216
|
+
return json;
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.error(`Failed to read ${filePath}:`, error.message);
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Retry Logic
|
|
225
|
+
|
|
226
|
+
```js
|
|
227
|
+
async function readWithRetry(filePath, maxRetries = 3) {
|
|
228
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
229
|
+
try {
|
|
230
|
+
return await csvToJson.getJsonFromCsvAsync(filePath);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
if (attempt === maxRetries) throw error;
|
|
233
|
+
|
|
234
|
+
// Exponential backoff
|
|
235
|
+
const delay = Math.pow(2, attempt - 1) * 1000;
|
|
236
|
+
console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
|
|
237
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Error Recovery
|
|
244
|
+
|
|
245
|
+
```js
|
|
246
|
+
async function processWithFallback(primaryFile, fallbackFile) {
|
|
247
|
+
try {
|
|
248
|
+
return await csvToJson.getJsonFromCsvAsync(primaryFile);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.warn(`Failed to read ${primaryFile}, using fallback...`);
|
|
251
|
+
return csvToJson.getJsonFromCsvAsync(fallbackFile);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Method Chaining
|
|
257
|
+
|
|
258
|
+
Combine configuration options with async operations:
|
|
259
|
+
|
|
260
|
+
```js
|
|
261
|
+
const json = await csvToJson
|
|
262
|
+
.fieldDelimiter(';')
|
|
263
|
+
.formatValueByType()
|
|
264
|
+
.supportQuotedField(true)
|
|
265
|
+
.trimHeaderFieldWhiteSpace(true)
|
|
266
|
+
.getJsonFromCsvAsync('file.csv');
|
|
267
|
+
|
|
268
|
+
// Or use async file generation
|
|
269
|
+
await csvToJson
|
|
270
|
+
.fieldDelimiter(',')
|
|
271
|
+
.formatValueByType()
|
|
272
|
+
.generateJsonFileFromCsvAsync('input.csv', 'output.json');
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
All configuration methods from the [Sync API](SYNC.md) are available with async operations.
|
|
276
|
+
|
|
277
|
+
## Row Mapping and Transformation
|
|
278
|
+
|
|
279
|
+
The `mapRows()` method allows you to transform, filter, or enrich each row after parsing. The mapping function is applied within the async operation, making it perfect for data transformation pipelines.
|
|
280
|
+
|
|
281
|
+
### Basic Row Transformation
|
|
282
|
+
|
|
283
|
+
```js
|
|
284
|
+
async function transformRows() {
|
|
285
|
+
const json = await csvToJson
|
|
286
|
+
.fieldDelimiter(',')
|
|
287
|
+
.mapRows((row, index) => {
|
|
288
|
+
// Add computed fields
|
|
289
|
+
row.id = index + 1;
|
|
290
|
+
row.email = row.email.toLowerCase();
|
|
291
|
+
return row;
|
|
292
|
+
})
|
|
293
|
+
.csvStringToJsonAsync('firstName,lastName,email\nJohn,Doe,John.Doe@example.com');
|
|
294
|
+
|
|
295
|
+
console.log(json);
|
|
296
|
+
// Output: [{ id: 1, firstName: 'John', lastName: 'Doe', email: 'john.doe@example.com' }]
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Filtering Rows
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
async function filterRows() {
|
|
304
|
+
const json = await csvToJson
|
|
305
|
+
.fieldDelimiter(',')
|
|
306
|
+
.mapRows((row) => {
|
|
307
|
+
// Only keep rows where status is 'active'
|
|
308
|
+
if (row.status === 'active') {
|
|
309
|
+
return row;
|
|
310
|
+
}
|
|
311
|
+
return null; // Filter out inactive rows
|
|
312
|
+
})
|
|
313
|
+
.getJsonFromCsvAsync('data.csv');
|
|
314
|
+
|
|
315
|
+
return json; // Contains only active records
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Data Enrichment
|
|
320
|
+
|
|
321
|
+
```js
|
|
322
|
+
async function enrichData() {
|
|
323
|
+
const json = await csvToJson
|
|
324
|
+
.fieldDelimiter(';')
|
|
325
|
+
.mapRows((row, index) => {
|
|
326
|
+
// Add metadata and computed properties
|
|
327
|
+
const age = parseInt(row.age);
|
|
328
|
+
return {
|
|
329
|
+
...row,
|
|
330
|
+
rowId: index,
|
|
331
|
+
ageGroup: age < 18 ? 'minor' : age < 65 ? 'adult' : 'senior',
|
|
332
|
+
processed: new Date().toISOString()
|
|
333
|
+
};
|
|
334
|
+
})
|
|
335
|
+
.getJsonFromCsvAsync('people.csv');
|
|
336
|
+
|
|
337
|
+
return json;
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Complex Transformations
|
|
342
|
+
|
|
343
|
+
```js
|
|
344
|
+
async function complexTransformation() {
|
|
345
|
+
const sales = await csvToJson
|
|
346
|
+
.fieldDelimiter(',')
|
|
347
|
+
.formatValueByType()
|
|
348
|
+
.mapRows((row, index) => {
|
|
349
|
+
const amount = typeof row.amount === 'number' ? row.amount : parseFloat(row.amount);
|
|
350
|
+
const quantity = typeof row.quantity === 'number' ? row.quantity : parseInt(row.quantity);
|
|
351
|
+
|
|
352
|
+
return {
|
|
353
|
+
transactionId: `TXN-${String(index + 1).padStart(6, '0')}`,
|
|
354
|
+
customer: row.customer_name,
|
|
355
|
+
product: row.product_id,
|
|
356
|
+
quantity: quantity,
|
|
357
|
+
unitPrice: amount / quantity,
|
|
358
|
+
totalAmount: amount,
|
|
359
|
+
taxable: amount > 100,
|
|
360
|
+
timestamp: new Date().toISOString()
|
|
361
|
+
};
|
|
362
|
+
})
|
|
363
|
+
.getJsonFromCsvAsync('sales.csv');
|
|
364
|
+
|
|
365
|
+
return sales;
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Validation and Filtering Pipeline
|
|
370
|
+
|
|
371
|
+
```js
|
|
372
|
+
async function validateAndFilter() {
|
|
373
|
+
const json = await csvToJson
|
|
374
|
+
.fieldDelimiter(',')
|
|
375
|
+
.mapRows((row) => {
|
|
376
|
+
// Validate email format
|
|
377
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
378
|
+
if (!emailRegex.test(row.email)) {
|
|
379
|
+
return null; // Skip invalid rows
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Trim whitespace
|
|
383
|
+
row.name = row.name.trim();
|
|
384
|
+
row.email = row.email.trim();
|
|
385
|
+
|
|
386
|
+
return row;
|
|
387
|
+
})
|
|
388
|
+
.getJsonFromCsvAsync('contacts.csv');
|
|
389
|
+
|
|
390
|
+
return json;
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## TypeScript Support
|
|
395
|
+
|
|
396
|
+
```ts
|
|
397
|
+
import csvToJson from 'convert-csv-to-json';
|
|
398
|
+
|
|
399
|
+
interface Product {
|
|
400
|
+
id: number;
|
|
401
|
+
name: string;
|
|
402
|
+
price: number;
|
|
403
|
+
inStock: boolean;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Read with type assertion
|
|
407
|
+
async function loadProducts(file: string): Promise<Product[]> {
|
|
408
|
+
return csvToJson
|
|
409
|
+
.formatValueByType()
|
|
410
|
+
.getJsonFromCsvAsync(file) as Promise<Product[]>;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Parse string with types
|
|
414
|
+
async function parseCSV(csv: string): Promise<Product[]> {
|
|
415
|
+
return csvToJson
|
|
416
|
+
.csvStringToJsonAsync(csv) as Promise<Product[]>;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Error handling
|
|
420
|
+
async function safeLoad(file: string): Promise<Product[] | null> {
|
|
421
|
+
try {
|
|
422
|
+
return await loadProducts(file);
|
|
423
|
+
} catch (error) {
|
|
424
|
+
console.error('Failed to load products:', error);
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
## Common Patterns
|
|
431
|
+
|
|
432
|
+
### Transform Data Pipeline
|
|
433
|
+
|
|
434
|
+
```js
|
|
435
|
+
async function processAndTransform(csvFile) {
|
|
436
|
+
// 1. Parse CSV
|
|
437
|
+
const raw = await csvToJson
|
|
438
|
+
.formatValueByType()
|
|
439
|
+
.getJsonFromCsvAsync(csvFile);
|
|
440
|
+
|
|
441
|
+
// 2. Filter and transform
|
|
442
|
+
const processed = raw
|
|
443
|
+
.filter(record => record.active)
|
|
444
|
+
.map(record => ({
|
|
445
|
+
id: record.id,
|
|
446
|
+
name: record.name.toUpperCase(),
|
|
447
|
+
displayPrice: `$${record.price.toFixed(2)}`
|
|
448
|
+
}));
|
|
449
|
+
|
|
450
|
+
return processed;
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Concurrent File Processing with Logging
|
|
455
|
+
|
|
456
|
+
```js
|
|
457
|
+
async function processMultipleWithLogging(files) {
|
|
458
|
+
const results = [];
|
|
459
|
+
|
|
460
|
+
const promises = files.map(async (file, index) => {
|
|
461
|
+
console.log(`[${index + 1}/${files.length}] Processing ${file}...`);
|
|
462
|
+
const startTime = Date.now();
|
|
463
|
+
|
|
464
|
+
try {
|
|
465
|
+
const json = await csvToJson.getJsonFromCsvAsync(file);
|
|
466
|
+
const duration = Date.now() - startTime;
|
|
467
|
+
|
|
468
|
+
console.log(`[✓] ${file} completed in ${duration}ms (${json.length} records)`);
|
|
469
|
+
return { file, json, success: true };
|
|
470
|
+
} catch (error) {
|
|
471
|
+
console.error(`[✗] ${file} failed:`, error.message);
|
|
472
|
+
return { file, error: error.message, success: false };
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
return Promise.all(promises);
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
## See Also
|
|
481
|
+
|
|
482
|
+
- [Main README](../README.md) - Overview and installation
|
|
483
|
+
- [Sync API](SYNC.md) - Synchronous operations
|
|
484
|
+
- [Browser API](BROWSER.md) - Client-side CSV parsing
|
|
485
|
+
- [MIGRATION.md](../migration/MIGRATION_TO_ASYNC.md) - Guide for migrating from Sync to Async
|