jtcsv 1.2.0 → 2.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/README.md +252 -337
- package/bin/jtcsv.js +167 -85
- package/cli-tui.js +0 -0
- package/dist/jtcsv.cjs.js +1619 -0
- package/dist/jtcsv.cjs.js.map +1 -0
- package/dist/jtcsv.esm.js +1599 -0
- package/dist/jtcsv.esm.js.map +1 -0
- package/dist/jtcsv.umd.js +1625 -0
- package/dist/jtcsv.umd.js.map +1 -0
- package/examples/cli-tool.js +186 -0
- package/examples/express-api.js +167 -0
- package/examples/large-dataset-example.js +185 -0
- package/examples/plugin-excel-exporter.js +407 -0
- package/examples/simple-usage.js +280 -0
- package/examples/streaming-example.js +419 -0
- package/index.d.ts +4 -0
- package/json-save.js +1 -1
- package/package.json +128 -14
- package/plugins/README.md +373 -0
- package/plugins/express-middleware/README.md +306 -0
- package/plugins/express-middleware/example.js +136 -0
- package/plugins/express-middleware/index.d.ts +114 -0
- package/plugins/express-middleware/index.js +360 -0
- package/plugins/express-middleware/package.json +52 -0
- package/plugins/fastify-plugin/index.js +406 -0
- package/plugins/fastify-plugin/package.json +55 -0
- package/plugins/nextjs-api/README.md +452 -0
- package/plugins/nextjs-api/examples/ConverterComponent.jsx +386 -0
- package/plugins/nextjs-api/examples/api-convert.js +69 -0
- package/plugins/nextjs-api/index.js +388 -0
- package/plugins/nextjs-api/package.json +63 -0
- package/plugins/nextjs-api/route.js +372 -0
- package/src/browser/browser-functions.js +189 -0
- package/src/browser/csv-to-json-browser.js +442 -0
- package/src/browser/errors-browser.js +194 -0
- package/src/browser/index.js +79 -0
- package/src/browser/json-to-csv-browser.js +309 -0
- package/src/browser/workers/csv-parser.worker.js +359 -0
- package/src/browser/workers/worker-pool.js +467 -0
- package/src/core/plugin-system.js +472 -0
- package/src/engines/fast-path-engine-new.js +338 -0
- package/src/engines/fast-path-engine.js +347 -0
- package/src/formats/ndjson-parser.js +419 -0
- package/src/index-with-plugins.js +349 -0
- package/stream-csv-to-json.js +1 -1
- package/stream-json-to-csv.js +1 -1
package/bin/jtcsv.js
CHANGED
|
@@ -41,8 +41,8 @@ ${color('USAGE:', 'bright')}
|
|
|
41
41
|
jtcsv [command] [options] [file...]
|
|
42
42
|
|
|
43
43
|
${color('COMMANDS:', 'bright')}
|
|
44
|
-
${color('
|
|
45
|
-
${color('
|
|
44
|
+
${color('json-to-csv', 'green')} Convert JSON to CSV (alias: json2csv)
|
|
45
|
+
${color('csv-to-json', 'green')} Convert CSV to JSON (alias: csv2json)
|
|
46
46
|
${color('stream', 'yellow')} Streaming conversion for large files
|
|
47
47
|
${color('batch', 'yellow')} Batch process multiple files
|
|
48
48
|
${color('tui', 'magenta')} Launch Terminal User Interface (requires blessed)
|
|
@@ -51,23 +51,29 @@ ${color('COMMANDS:', 'bright')}
|
|
|
51
51
|
|
|
52
52
|
${color('EXAMPLES:', 'bright')}
|
|
53
53
|
${color('Convert JSON file to CSV:', 'dim')}
|
|
54
|
-
jtcsv
|
|
54
|
+
jtcsv json-to-csv input.json output.csv --delimiter=,
|
|
55
55
|
|
|
56
56
|
${color('Convert CSV file to JSON:', 'dim')}
|
|
57
|
-
jtcsv
|
|
57
|
+
jtcsv csv-to-json input.csv output.json --parse-numbers --auto-detect
|
|
58
58
|
|
|
59
59
|
${color('Stream large JSON file to CSV:', 'dim')}
|
|
60
|
-
jtcsv stream
|
|
60
|
+
jtcsv stream json-to-csv large.json output.csv --max-records=1000000
|
|
61
61
|
|
|
62
62
|
${color('Launch TUI interface:', 'dim')}
|
|
63
63
|
jtcsv tui
|
|
64
64
|
|
|
65
65
|
${color('OPTIONS:', 'bright')}
|
|
66
66
|
${color('--delimiter=', 'cyan')}CHAR CSV delimiter (default: ;)
|
|
67
|
+
${color('--auto-detect', 'cyan')} Auto-detect delimiter (default: true)
|
|
68
|
+
${color('--candidates=', 'cyan')}LIST Delimiter candidates (default: ;,\t|)
|
|
67
69
|
${color('--no-headers', 'cyan')} Exclude headers from CSV output
|
|
68
70
|
${color('--parse-numbers', 'cyan')} Parse numeric values in CSV
|
|
69
71
|
${color('--parse-booleans', 'cyan')} Parse boolean values in CSV
|
|
72
|
+
${color('--no-trim', 'cyan')} Don't trim whitespace from CSV values
|
|
73
|
+
${color('--rename=', 'cyan')}JSON Rename columns (JSON map)
|
|
74
|
+
${color('--template=', 'cyan')}JSON Column order template (JSON object)
|
|
70
75
|
${color('--no-injection-protection', 'cyan')} Disable CSV injection protection
|
|
76
|
+
${color('--no-rfc4180', 'cyan')} Disable RFC 4180 compliance
|
|
71
77
|
${color('--max-records=', 'cyan')}N Maximum records to process (optional, no limit by default)
|
|
72
78
|
${color('--max-rows=', 'cyan')}N Maximum rows to process (optional, no limit by default)
|
|
73
79
|
${color('--pretty', 'cyan')} Pretty print JSON output
|
|
@@ -109,8 +115,19 @@ async function convertJsonToCsv(inputFile, outputFile, options) {
|
|
|
109
115
|
|
|
110
116
|
console.log(color(`Converting ${jsonData.length} records...`, 'dim'));
|
|
111
117
|
|
|
118
|
+
// Prepare options for jtcsv
|
|
119
|
+
const jtcsvOptions = {
|
|
120
|
+
delimiter: options.delimiter,
|
|
121
|
+
includeHeaders: options.includeHeaders,
|
|
122
|
+
renameMap: options.renameMap,
|
|
123
|
+
template: options.template,
|
|
124
|
+
maxRecords: options.maxRecords,
|
|
125
|
+
preventCsvInjection: options.preventCsvInjection,
|
|
126
|
+
rfc4180Compliant: options.rfc4180Compliant
|
|
127
|
+
};
|
|
128
|
+
|
|
112
129
|
// Convert to CSV
|
|
113
|
-
const csvData = jtcsv.jsonToCsv(jsonData,
|
|
130
|
+
const csvData = jtcsv.jsonToCsv(jsonData, jtcsvOptions);
|
|
114
131
|
|
|
115
132
|
// Write output file
|
|
116
133
|
await fs.promises.writeFile(outputFile, csvData, 'utf8');
|
|
@@ -129,10 +146,23 @@ async function convertCsvToJson(inputFile, outputFile, options) {
|
|
|
129
146
|
const startTime = Date.now();
|
|
130
147
|
|
|
131
148
|
try {
|
|
132
|
-
console.log(color(
|
|
149
|
+
console.log(color('Reading CSV file...', 'dim'));
|
|
150
|
+
|
|
151
|
+
// Prepare options for jtcsv
|
|
152
|
+
const jtcsvOptions = {
|
|
153
|
+
delimiter: options.delimiter,
|
|
154
|
+
autoDetect: options.autoDetect,
|
|
155
|
+
candidates: options.candidates,
|
|
156
|
+
hasHeaders: options.hasHeaders,
|
|
157
|
+
renameMap: options.renameMap,
|
|
158
|
+
trim: options.trim,
|
|
159
|
+
parseNumbers: options.parseNumbers,
|
|
160
|
+
parseBooleans: options.parseBooleans,
|
|
161
|
+
maxRows: options.maxRows
|
|
162
|
+
};
|
|
133
163
|
|
|
134
164
|
// Read and convert CSV
|
|
135
|
-
const jsonData = await jtcsv.readCsvAsJson(inputFile,
|
|
165
|
+
const jsonData = await jtcsv.readCsvAsJson(inputFile, jtcsvOptions);
|
|
136
166
|
|
|
137
167
|
// Format JSON
|
|
138
168
|
const jsonOutput = options.pretty
|
|
@@ -156,7 +186,7 @@ async function streamJsonToCsv(inputFile, outputFile, options) {
|
|
|
156
186
|
const startTime = Date.now();
|
|
157
187
|
|
|
158
188
|
try {
|
|
159
|
-
console.log(color(
|
|
189
|
+
console.log(color('Streaming conversion started...', 'dim'));
|
|
160
190
|
|
|
161
191
|
// Create streams
|
|
162
192
|
const readStream = fs.createReadStream(inputFile, 'utf8');
|
|
@@ -235,10 +265,17 @@ async function launchTUI() {
|
|
|
235
265
|
function parseOptions(args) {
|
|
236
266
|
const options = {
|
|
237
267
|
delimiter: ';',
|
|
268
|
+
autoDetect: true,
|
|
269
|
+
candidates: [';', ',', '\t', '|'],
|
|
270
|
+
hasHeaders: true,
|
|
238
271
|
includeHeaders: true,
|
|
272
|
+
renameMap: undefined,
|
|
273
|
+
template: undefined,
|
|
274
|
+
trim: true,
|
|
239
275
|
parseNumbers: false,
|
|
240
276
|
parseBooleans: false,
|
|
241
277
|
preventCsvInjection: true,
|
|
278
|
+
rfc4180Compliant: true,
|
|
242
279
|
maxRecords: undefined,
|
|
243
280
|
maxRows: undefined,
|
|
244
281
|
pretty: false,
|
|
@@ -255,36 +292,72 @@ function parseOptions(args) {
|
|
|
255
292
|
const [key, value] = arg.slice(2).split('=');
|
|
256
293
|
|
|
257
294
|
switch (key) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
295
|
+
case 'delimiter':
|
|
296
|
+
options.delimiter = value || ',';
|
|
297
|
+
options.autoDetect = false; // Disable auto-detect if delimiter is specified
|
|
298
|
+
break;
|
|
299
|
+
case 'auto-detect':
|
|
300
|
+
options.autoDetect = value !== 'false';
|
|
301
|
+
break;
|
|
302
|
+
case 'candidates':
|
|
303
|
+
options.candidates = value ? value.split(',') : [';', ',', '\t', '|'];
|
|
304
|
+
break;
|
|
305
|
+
case 'no-headers':
|
|
306
|
+
options.includeHeaders = false;
|
|
307
|
+
options.hasHeaders = false;
|
|
308
|
+
break;
|
|
309
|
+
case 'parse-numbers':
|
|
310
|
+
options.parseNumbers = true;
|
|
311
|
+
break;
|
|
312
|
+
case 'parse-booleans':
|
|
313
|
+
options.parseBooleans = true;
|
|
314
|
+
break;
|
|
315
|
+
case 'no-trim':
|
|
316
|
+
options.trim = false;
|
|
317
|
+
break;
|
|
318
|
+
case 'rename':
|
|
319
|
+
try {
|
|
320
|
+
// Handle both quoted and unquoted JSON
|
|
321
|
+
const jsonStr = value || '{}';
|
|
322
|
+
// Remove surrounding single quotes if present
|
|
323
|
+
const cleanStr = jsonStr.replace(/^'|'$/g, '').replace(/^"|"$/g, '');
|
|
324
|
+
options.renameMap = JSON.parse(cleanStr);
|
|
325
|
+
} catch (e) {
|
|
326
|
+
throw new Error(`Invalid JSON in --rename option: ${e.message}. Value: ${value}`);
|
|
327
|
+
}
|
|
328
|
+
break;
|
|
329
|
+
case 'template':
|
|
330
|
+
try {
|
|
331
|
+
// Handle both quoted and unquoted JSON
|
|
332
|
+
const jsonStr = value || '{}';
|
|
333
|
+
// Remove surrounding single quotes if present
|
|
334
|
+
const cleanStr = jsonStr.replace(/^'|'$/g, '').replace(/^"|"$/g, '');
|
|
335
|
+
options.template = JSON.parse(cleanStr);
|
|
336
|
+
} catch (e) {
|
|
337
|
+
throw new Error(`Invalid JSON in --template option: ${e.message}. Value: ${value}`);
|
|
338
|
+
}
|
|
339
|
+
break;
|
|
340
|
+
case 'no-injection-protection':
|
|
341
|
+
options.preventCsvInjection = false;
|
|
342
|
+
break;
|
|
343
|
+
case 'no-rfc4180':
|
|
344
|
+
options.rfc4180Compliant = false;
|
|
345
|
+
break;
|
|
346
|
+
case 'max-records':
|
|
347
|
+
options.maxRecords = parseInt(value, 10);
|
|
348
|
+
break;
|
|
349
|
+
case 'max-rows':
|
|
350
|
+
options.maxRows = parseInt(value, 10);
|
|
351
|
+
break;
|
|
352
|
+
case 'pretty':
|
|
353
|
+
options.pretty = true;
|
|
354
|
+
break;
|
|
355
|
+
case 'silent':
|
|
356
|
+
options.silent = true;
|
|
357
|
+
break;
|
|
358
|
+
case 'verbose':
|
|
359
|
+
options.verbose = true;
|
|
360
|
+
break;
|
|
288
361
|
}
|
|
289
362
|
} else if (!arg.startsWith('-')) {
|
|
290
363
|
files.push(arg);
|
|
@@ -312,57 +385,59 @@ async function main() {
|
|
|
312
385
|
}
|
|
313
386
|
|
|
314
387
|
switch (command) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
388
|
+
case 'json-to-csv':
|
|
389
|
+
case 'json2csv': // Backward compatibility
|
|
390
|
+
if (files.length < 2) {
|
|
391
|
+
console.error(color('Error: Input and output files required', 'red'));
|
|
392
|
+
console.log(color('Usage: jtcsv json-to-csv input.json output.csv', 'cyan'));
|
|
393
|
+
process.exit(1);
|
|
394
|
+
}
|
|
395
|
+
await convertJsonToCsv(files[0], files[1], options);
|
|
396
|
+
break;
|
|
323
397
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
398
|
+
case 'csv-to-json':
|
|
399
|
+
case 'csv2json': // Backward compatibility
|
|
400
|
+
if (files.length < 2) {
|
|
401
|
+
console.error(color('Error: Input and output files required', 'red'));
|
|
402
|
+
console.log(color('Usage: jtcsv csv-to-json input.csv output.json', 'cyan'));
|
|
403
|
+
process.exit(1);
|
|
404
|
+
}
|
|
405
|
+
await convertCsvToJson(files[0], files[1], options);
|
|
406
|
+
break;
|
|
332
407
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
408
|
+
case 'stream':
|
|
409
|
+
if (args.length < 2) {
|
|
410
|
+
console.error(color('Error: Streaming mode requires subcommand', 'red'));
|
|
411
|
+
console.log(color('Usage: jtcsv stream [json2csv|csv2json] input output', 'cyan'));
|
|
412
|
+
process.exit(1);
|
|
413
|
+
}
|
|
414
|
+
const streamCommand = args[1].toLowerCase();
|
|
415
|
+
if (streamCommand === 'json2csv' && files.length >= 2) {
|
|
416
|
+
await streamJsonToCsv(files[0], files[1], options);
|
|
417
|
+
} else {
|
|
418
|
+
console.error(color('Error: Invalid streaming command', 'red'));
|
|
419
|
+
process.exit(1);
|
|
420
|
+
}
|
|
421
|
+
break;
|
|
347
422
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
423
|
+
case 'tui':
|
|
424
|
+
await launchTUI();
|
|
425
|
+
break;
|
|
351
426
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
427
|
+
case 'help':
|
|
428
|
+
showHelp();
|
|
429
|
+
break;
|
|
355
430
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
431
|
+
case 'version':
|
|
432
|
+
case '-v':
|
|
433
|
+
case '--version':
|
|
434
|
+
showVersion();
|
|
435
|
+
break;
|
|
361
436
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
437
|
+
default:
|
|
438
|
+
console.error(color(`Error: Unknown command '${command}'`, 'red'));
|
|
439
|
+
console.log(color('Use jtcsv help for available commands', 'cyan'));
|
|
440
|
+
process.exit(1);
|
|
366
441
|
}
|
|
367
442
|
}
|
|
368
443
|
|
|
@@ -391,4 +466,11 @@ if (require.main === module) {
|
|
|
391
466
|
});
|
|
392
467
|
}
|
|
393
468
|
|
|
394
|
-
module.exports = { main };
|
|
469
|
+
module.exports = { main };
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
|
package/cli-tui.js
CHANGED
|
Binary file
|