jtcsv 3.0.0 → 3.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 +205 -146
- package/bin/jtcsv.ts +280 -202
- package/browser.d.ts +142 -0
- package/dist/benchmark.js +446 -0
- package/dist/benchmark.js.map +1 -0
- package/dist/bin/jtcsv.js +1940 -0
- package/dist/bin/jtcsv.js.map +1 -0
- package/dist/csv-to-json.js +1262 -0
- package/dist/csv-to-json.js.map +1 -0
- package/dist/errors.js +291 -0
- package/dist/errors.js.map +1 -0
- package/dist/eslint.config.js +147 -0
- package/dist/eslint.config.js.map +1 -0
- package/dist/index-core.js +95 -0
- package/dist/index-core.js.map +1 -0
- package/dist/index.js +93 -0
- package/dist/index.js.map +1 -0
- package/dist/json-save.js +229 -0
- package/dist/json-save.js.map +1 -0
- package/dist/json-to-csv.js +576 -0
- package/dist/json-to-csv.js.map +1 -0
- package/dist/jtcsv-core.cjs.js +336 -7
- package/dist/jtcsv-core.cjs.js.map +1 -1
- package/dist/jtcsv-core.esm.js +336 -7
- package/dist/jtcsv-core.esm.js.map +1 -1
- package/dist/jtcsv-core.umd.js +336 -7
- package/dist/jtcsv-core.umd.js.map +1 -1
- package/dist/jtcsv-full.cjs.js +336 -7
- package/dist/jtcsv-full.cjs.js.map +1 -1
- package/dist/jtcsv-full.esm.js +336 -7
- package/dist/jtcsv-full.esm.js.map +1 -1
- package/dist/jtcsv-full.umd.js +336 -7
- package/dist/jtcsv-full.umd.js.map +1 -1
- package/dist/jtcsv-workers.esm.js +9 -0
- package/dist/jtcsv-workers.esm.js.map +1 -1
- package/dist/jtcsv-workers.umd.js +9 -0
- package/dist/jtcsv-workers.umd.js.map +1 -1
- package/dist/jtcsv.cjs.js +1998 -2092
- package/dist/jtcsv.cjs.js.map +1 -1
- package/dist/jtcsv.esm.js +1994 -2092
- package/dist/jtcsv.esm.js.map +1 -1
- package/dist/jtcsv.umd.js +2157 -2251
- package/dist/jtcsv.umd.js.map +1 -1
- package/dist/plugins/express-middleware/index.js +350 -0
- package/dist/plugins/express-middleware/index.js.map +1 -0
- package/dist/plugins/fastify-plugin/index.js +315 -0
- package/dist/plugins/fastify-plugin/index.js.map +1 -0
- package/dist/plugins/hono/index.js +111 -0
- package/dist/plugins/hono/index.js.map +1 -0
- package/dist/plugins/nestjs/index.js +112 -0
- package/dist/plugins/nestjs/index.js.map +1 -0
- package/dist/plugins/nuxt/index.js +53 -0
- package/dist/plugins/nuxt/index.js.map +1 -0
- package/dist/plugins/remix/index.js +133 -0
- package/dist/plugins/remix/index.js.map +1 -0
- package/dist/plugins/sveltekit/index.js +155 -0
- package/dist/plugins/sveltekit/index.js.map +1 -0
- package/dist/plugins/trpc/index.js +136 -0
- package/dist/plugins/trpc/index.js.map +1 -0
- package/dist/run-demo.js +49 -0
- package/dist/run-demo.js.map +1 -0
- package/dist/src/browser/browser-functions.js +193 -0
- package/dist/src/browser/browser-functions.js.map +1 -0
- package/dist/src/browser/core.js +123 -0
- package/dist/src/browser/core.js.map +1 -0
- package/dist/src/browser/csv-to-json-browser.js +353 -0
- package/dist/src/browser/csv-to-json-browser.js.map +1 -0
- package/dist/src/browser/errors-browser.js +219 -0
- package/dist/src/browser/errors-browser.js.map +1 -0
- package/dist/src/browser/extensions/plugins.js +106 -0
- package/dist/src/browser/extensions/plugins.js.map +1 -0
- package/dist/src/browser/extensions/workers.js +66 -0
- package/dist/src/browser/extensions/workers.js.map +1 -0
- package/dist/src/browser/index.js +140 -0
- package/dist/src/browser/index.js.map +1 -0
- package/dist/src/browser/json-to-csv-browser.js +225 -0
- package/dist/src/browser/json-to-csv-browser.js.map +1 -0
- package/dist/src/browser/streams.js +340 -0
- package/dist/src/browser/streams.js.map +1 -0
- package/dist/src/browser/workers/csv-parser.worker.js +264 -0
- package/dist/src/browser/workers/csv-parser.worker.js.map +1 -0
- package/dist/src/browser/workers/worker-pool.js +338 -0
- package/dist/src/browser/workers/worker-pool.js.map +1 -0
- package/dist/src/core/delimiter-cache.js +196 -0
- package/dist/src/core/delimiter-cache.js.map +1 -0
- package/dist/src/core/node-optimizations.js +279 -0
- package/dist/src/core/node-optimizations.js.map +1 -0
- package/dist/src/core/plugin-system.js +399 -0
- package/dist/src/core/plugin-system.js.map +1 -0
- package/dist/src/core/transform-hooks.js +348 -0
- package/dist/src/core/transform-hooks.js.map +1 -0
- package/dist/src/engines/fast-path-engine-new.js +262 -0
- package/dist/src/engines/fast-path-engine-new.js.map +1 -0
- package/dist/src/engines/fast-path-engine.js +671 -0
- package/dist/src/engines/fast-path-engine.js.map +1 -0
- package/dist/src/errors.js +18 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/formats/ndjson-parser.js +332 -0
- package/dist/src/formats/ndjson-parser.js.map +1 -0
- package/dist/src/formats/tsv-parser.js +230 -0
- package/dist/src/formats/tsv-parser.js.map +1 -0
- package/dist/src/index-with-plugins.js +259 -0
- package/dist/src/index-with-plugins.js.map +1 -0
- package/dist/src/types/index.js +3 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/utils/bom-utils.js +267 -0
- package/dist/src/utils/bom-utils.js.map +1 -0
- package/dist/src/utils/encoding-support.js +77 -0
- package/dist/src/utils/encoding-support.js.map +1 -0
- package/dist/src/utils/schema-validator.js +609 -0
- package/dist/src/utils/schema-validator.js.map +1 -0
- package/dist/src/utils/transform-loader.js +281 -0
- package/dist/src/utils/transform-loader.js.map +1 -0
- package/dist/src/utils/validators.js +40 -0
- package/dist/src/utils/validators.js.map +1 -0
- package/dist/src/utils/zod-adapter.js +144 -0
- package/dist/src/utils/zod-adapter.js.map +1 -0
- package/{src → dist/src}/web-server/index.js +251 -286
- package/dist/src/web-server/index.js.map +1 -0
- package/dist/src/workers/csv-multithreaded.js +211 -0
- package/dist/src/workers/csv-multithreaded.js.map +1 -0
- package/dist/src/workers/csv-parser.worker.js +179 -0
- package/dist/src/workers/csv-parser.worker.js.map +1 -0
- package/dist/src/workers/worker-pool.js +228 -0
- package/dist/src/workers/worker-pool.js.map +1 -0
- package/dist/stream-csv-to-json.js +665 -0
- package/dist/stream-csv-to-json.js.map +1 -0
- package/dist/stream-json-to-csv.js +389 -0
- package/dist/stream-json-to-csv.js.map +1 -0
- package/examples/advanced/conditional-transformations.ts +2 -2
- package/examples/advanced/performance-optimization.ts +2 -2
- package/examples/cli-advanced-usage.md +2 -0
- package/examples/cli-tool.ts +1 -1
- package/examples/large-dataset-example.ts +2 -2
- package/examples/simple-usage.ts +2 -2
- package/examples/streaming-example.ts +1 -1
- package/index.d.ts +186 -15
- package/package.json +43 -108
- package/plugins.d.ts +37 -0
- package/schema.d.ts +103 -0
- package/src/browser/csv-to-json-browser.ts +233 -3
- package/src/browser/errors-browser.ts +45 -28
- package/src/browser/json-to-csv-browser.ts +81 -5
- package/src/browser/streams.ts +73 -6
- package/src/core/delimiter-cache.ts +21 -11
- package/src/core/plugin-system.ts +343 -155
- package/src/core/transform-hooks.ts +20 -12
- package/src/engines/fast-path-engine.ts +48 -32
- package/src/errors.ts +1 -72
- package/src/formats/ndjson-parser.ts +6 -0
- package/src/formats/tsv-parser.ts +6 -0
- package/src/types/index.ts +21 -1
- package/src/utils/validators.ts +35 -0
- package/src/web-server/index.ts +1 -1
- package/bin/jtcsv.js +0 -2532
- package/csv-to-json.js +0 -711
- package/errors.js +0 -394
- package/examples/advanced/conditional-transformations.js +0 -446
- package/examples/advanced/csv-parser.worker.js +0 -89
- package/examples/advanced/nested-objects-example.js +0 -306
- package/examples/advanced/performance-optimization.js +0 -504
- package/examples/advanced/run-demo-server.js +0 -116
- package/examples/cli-batch-processing.js +0 -38
- package/examples/cli-tool.js +0 -183
- package/examples/error-handling.js +0 -338
- package/examples/express-api.js +0 -164
- package/examples/large-dataset-example.js +0 -182
- package/examples/ndjson-processing.js +0 -434
- package/examples/plugin-excel-exporter.js +0 -406
- package/examples/schema-validation.js +0 -640
- package/examples/simple-usage.js +0 -282
- package/examples/streaming-example.js +0 -418
- package/examples/web-workers-advanced.js +0 -28
- package/index.js +0 -82
- package/json-save.js +0 -255
- package/json-to-csv.js +0 -668
- package/plugins/README.md +0 -91
- package/plugins/express-middleware/README.md +0 -83
- package/plugins/express-middleware/example.js +0 -135
- package/plugins/express-middleware/example.ts +0 -135
- package/plugins/express-middleware/index.d.ts +0 -114
- package/plugins/express-middleware/index.js +0 -512
- package/plugins/express-middleware/index.ts +0 -557
- package/plugins/express-middleware/package.json +0 -52
- package/plugins/fastify-plugin/index.js +0 -404
- package/plugins/fastify-plugin/index.ts +0 -443
- package/plugins/fastify-plugin/package.json +0 -55
- package/plugins/hono/README.md +0 -28
- package/plugins/hono/index.d.ts +0 -12
- package/plugins/hono/index.js +0 -36
- package/plugins/hono/index.ts +0 -226
- package/plugins/hono/package.json +0 -35
- package/plugins/nestjs/README.md +0 -35
- package/plugins/nestjs/index.d.ts +0 -25
- package/plugins/nestjs/index.js +0 -77
- package/plugins/nestjs/index.ts +0 -201
- package/plugins/nestjs/package.json +0 -37
- package/plugins/nextjs-api/README.md +0 -57
- package/plugins/nextjs-api/examples/ConverterComponent.jsx +0 -386
- package/plugins/nextjs-api/examples/ConverterComponent.tsx +0 -386
- package/plugins/nextjs-api/examples/api-convert.js +0 -67
- package/plugins/nextjs-api/examples/api-convert.ts +0 -67
- package/plugins/nextjs-api/index.js +0 -387
- package/plugins/nextjs-api/index.tsx +0 -339
- package/plugins/nextjs-api/package.json +0 -63
- package/plugins/nextjs-api/route.js +0 -370
- package/plugins/nextjs-api/route.ts +0 -370
- package/plugins/nuxt/README.md +0 -24
- package/plugins/nuxt/index.js +0 -21
- package/plugins/nuxt/index.ts +0 -94
- package/plugins/nuxt/package.json +0 -35
- package/plugins/nuxt/runtime/composables/useJtcsv.js +0 -6
- package/plugins/nuxt/runtime/composables/useJtcsv.ts +0 -100
- package/plugins/nuxt/runtime/plugin.js +0 -6
- package/plugins/nuxt/runtime/plugin.ts +0 -71
- package/plugins/remix/README.md +0 -26
- package/plugins/remix/index.d.ts +0 -16
- package/plugins/remix/index.js +0 -62
- package/plugins/remix/index.ts +0 -260
- package/plugins/remix/package.json +0 -35
- package/plugins/sveltekit/README.md +0 -28
- package/plugins/sveltekit/index.d.ts +0 -17
- package/plugins/sveltekit/index.js +0 -54
- package/plugins/sveltekit/index.ts +0 -301
- package/plugins/sveltekit/package.json +0 -33
- package/plugins/trpc/README.md +0 -25
- package/plugins/trpc/index.d.ts +0 -7
- package/plugins/trpc/index.js +0 -32
- package/plugins/trpc/index.ts +0 -267
- package/plugins/trpc/package.json +0 -34
- package/src/browser/browser-functions.js +0 -219
- package/src/browser/core.js +0 -92
- package/src/browser/csv-to-json-browser.js +0 -722
- package/src/browser/errors-browser.js +0 -212
- package/src/browser/extensions/plugins.js +0 -92
- package/src/browser/extensions/workers.js +0 -39
- package/src/browser/index.js +0 -113
- package/src/browser/json-to-csv-browser.js +0 -319
- package/src/browser/streams.js +0 -403
- package/src/browser/workers/csv-parser.worker.js +0 -377
- package/src/browser/workers/worker-pool.js +0 -527
- package/src/core/delimiter-cache.js +0 -200
- package/src/core/node-optimizations.js +0 -408
- package/src/core/plugin-system.js +0 -494
- package/src/core/transform-hooks.js +0 -350
- package/src/engines/fast-path-engine-new.js +0 -338
- package/src/engines/fast-path-engine.js +0 -844
- package/src/errors.js +0 -26
- package/src/formats/ndjson-parser.js +0 -467
- package/src/formats/tsv-parser.js +0 -339
- package/src/index-with-plugins.js +0 -378
- package/src/utils/bom-utils.js +0 -259
- package/src/utils/encoding-support.js +0 -124
- package/src/utils/schema-validator.js +0 -594
- package/src/utils/transform-loader.js +0 -205
- package/src/utils/zod-adapter.js +0 -170
- package/stream-csv-to-json.js +0 -560
- package/stream-json-to-csv.js +0 -465
package/bin/jtcsv.ts
CHANGED
|
@@ -11,7 +11,7 @@ import * as fs from 'fs';
|
|
|
11
11
|
import * as path from 'path';
|
|
12
12
|
import * as readline from 'readline';
|
|
13
13
|
const { pipeline } = require('stream/promises');
|
|
14
|
-
import * as jtcsv from '../index
|
|
14
|
+
import * as jtcsv from '../index';
|
|
15
15
|
const transformLoader = require('../src/utils/transform-loader');
|
|
16
16
|
const schemaValidator = require('../src/utils/schema-validator');
|
|
17
17
|
|
|
@@ -88,7 +88,7 @@ function color(text, colorName: any): any {
|
|
|
88
88
|
function showHelp(): void {
|
|
89
89
|
console.log(`
|
|
90
90
|
${color('jtcsv CLI v' + VERSION, 'cyan')}
|
|
91
|
-
${color('The Complete JSON
|
|
91
|
+
${color('The Complete JSON<->CSV Converter for Node.js', 'dim')}
|
|
92
92
|
|
|
93
93
|
${color('USAGE:', 'bright')}
|
|
94
94
|
jtcsv [command] [options] [file...]
|
|
@@ -106,7 +106,7 @@ ${color('MAIN COMMANDS:', 'bright')}
|
|
|
106
106
|
${color('batch', 'yellow')} Batch process multiple files
|
|
107
107
|
${color('preprocess', 'magenta')} Preprocess JSON with deep unwrapping
|
|
108
108
|
${color('unwrap', 'magenta')} Flatten nested JSON structures (alias: flatten)
|
|
109
|
-
|
|
109
|
+
${color('tui', 'magenta')} Launch Terminal User Interface (@jtcsv/tui)
|
|
110
110
|
${color('web', 'magenta')} Launch Web Interface (http://localhost:3000)
|
|
111
111
|
${color('help', 'blue')} Show this help message
|
|
112
112
|
${color('version', 'blue')} Show version information
|
|
@@ -147,7 +147,7 @@ ${color('EXAMPLES:', 'bright')}
|
|
|
147
147
|
${color('Batch convert JSON files:', 'dim')}
|
|
148
148
|
jtcsv batch json-to-csv "data/*.json" "output/" --delimiter=;
|
|
149
149
|
|
|
150
|
-
|
|
150
|
+
${color('Launch TUI interface:', 'dim')}
|
|
151
151
|
jtcsv tui
|
|
152
152
|
|
|
153
153
|
${color('Launch Web interface:', 'dim')}
|
|
@@ -160,10 +160,12 @@ ${color('CONVERSION OPTIONS:', 'bright')}
|
|
|
160
160
|
${color('--no-headers', 'cyan')} Exclude headers from CSV output
|
|
161
161
|
${color('--parse-numbers', 'cyan')} Parse numeric values in CSV
|
|
162
162
|
${color('--parse-booleans', 'cyan')} Parse boolean values in CSV
|
|
163
|
-
${color('--no-trim', 'cyan')} Don't trim whitespace from CSV values
|
|
164
|
-
${color('--no-fast-path', 'cyan')} Disable fast-path parser (force quote-aware)
|
|
165
|
-
${color('--fast-path-mode=', 'cyan')}MODE Fast path output mode (objects|compact)
|
|
166
|
-
${color('--
|
|
163
|
+
${color('--no-trim', 'cyan')} Don't trim whitespace from CSV values
|
|
164
|
+
${color('--no-fast-path', 'cyan')} Disable fast-path parser (force quote-aware)
|
|
165
|
+
${color('--fast-path-mode=', 'cyan')}MODE Fast path output mode (objects|compact)
|
|
166
|
+
${color('--repair-row-shifts', 'cyan')} Repair shifted rows with trailing empty fields
|
|
167
|
+
${color('--normalize-quotes', 'cyan')} Normalize excessive quotes in parsed fields
|
|
168
|
+
${color('--rename=', 'cyan')}JSON Rename columns (JSON map)
|
|
167
169
|
${color('--template=', 'cyan')}JSON Column order template (JSON object)
|
|
168
170
|
${color('--no-injection-protection', 'cyan')} Disable CSV injection protection
|
|
169
171
|
${color('--no-rfc4180', 'cyan')} Disable RFC 4180 compliance
|
|
@@ -196,17 +198,17 @@ ${color('CONVERSION OPTIONS:', 'bright')}
|
|
|
196
198
|
${color('--debug', 'cyan')} Show debug information
|
|
197
199
|
${color('--dry-run', 'cyan')} Show what would be done without actually doing it
|
|
198
200
|
${color('SECURITY FEATURES:', 'bright')}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
201
|
+
- CSV injection protection (enabled by default)
|
|
202
|
+
- Path traversal protection
|
|
203
|
+
- Input validation and sanitization
|
|
204
|
+
- Size limits to prevent DoS attacks
|
|
205
|
+
- Schema validation support
|
|
204
206
|
|
|
205
207
|
${color('PERFORMANCE FEATURES:', 'bright')}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
208
|
+
- Streaming for files >100MB
|
|
209
|
+
- Batch processing with parallel execution
|
|
210
|
+
- Memory-efficient preprocessing
|
|
211
|
+
- Configurable buffer sizes
|
|
210
212
|
|
|
211
213
|
${color('LEARN MORE:', 'dim')}
|
|
212
214
|
GitHub: https://github.com/Linol-Hamelton/jtcsv
|
|
@@ -215,57 +217,74 @@ ${color('CONVERSION OPTIONS:', 'bright')}
|
|
|
215
217
|
`);
|
|
216
218
|
}
|
|
217
219
|
|
|
218
|
-
function showVersion(): void {
|
|
219
|
-
console.log(`jtcsv v${VERSION}`);
|
|
220
|
-
console.log(`Node.js ${process.version}`);
|
|
221
|
-
console.log(`Platform: ${process.platform} ${process.arch}`);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
220
|
+
function showVersion(): void {
|
|
221
|
+
console.log(`jtcsv v${VERSION}`);
|
|
222
|
+
console.log(`Node.js ${process.version}`);
|
|
223
|
+
console.log(`Platform: ${process.platform} ${process.arch}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function readStdin(): Promise<string> {
|
|
227
|
+
return new Promise((resolve, reject) => {
|
|
228
|
+
let data = '';
|
|
229
|
+
process.stdin.setEncoding('utf8');
|
|
230
|
+
process.stdin.on('data', (chunk) => {
|
|
231
|
+
data += chunk;
|
|
232
|
+
});
|
|
233
|
+
process.stdin.on('end', () => resolve(data));
|
|
234
|
+
process.stdin.on('error', reject);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ============================================================================
|
|
239
|
+
// CONVERSION FUNCTIONS
|
|
240
|
+
// ============================================================================
|
|
227
241
|
|
|
228
242
|
async function convertJsonToCsv(inputFile, outputFile, options: any): Promise<ConversionResult> {
|
|
229
|
-
const startTime = Date.now();
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
243
|
+
const startTime = Date.now();
|
|
244
|
+
const useStdin = inputFile === '-';
|
|
245
|
+
const useStdout = outputFile === '-';
|
|
246
|
+
const shouldLog = !options.silent && !useStdout;
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
// Read input
|
|
250
|
+
const inputData = useStdin
|
|
251
|
+
? await readStdin()
|
|
252
|
+
: await fs.promises.readFile(inputFile, 'utf8');
|
|
253
|
+
const jsonData = JSON.parse(inputData);
|
|
254
|
+
|
|
255
|
+
if (!Array.isArray(jsonData)) {
|
|
256
|
+
throw new Error('JSON data must be an array of objects');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (shouldLog) {
|
|
260
|
+
console.log(
|
|
261
|
+
color(
|
|
262
|
+
`Converting ${jsonData.length.toLocaleString()} records...`,
|
|
263
|
+
'dim'
|
|
264
|
+
)
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (options.transform) {
|
|
269
|
+
if (shouldLog) {
|
|
270
|
+
console.log(
|
|
271
|
+
color(`Applying transform from: ${options.transform}`, 'dim')
|
|
272
|
+
);
|
|
273
|
+
}
|
|
255
274
|
let transformedData;
|
|
256
275
|
try {
|
|
257
276
|
transformedData = transformLoader.applyTransform(
|
|
258
277
|
jsonData,
|
|
259
278
|
options.transform
|
|
260
279
|
);
|
|
261
|
-
if (
|
|
262
|
-
console.log(
|
|
263
|
-
color(
|
|
264
|
-
`✓ Transform applied to ${transformedData.length} records`,
|
|
265
|
-
'green'
|
|
266
|
-
)
|
|
267
|
-
);
|
|
268
|
-
}
|
|
280
|
+
if (shouldLog) {
|
|
281
|
+
console.log(
|
|
282
|
+
color(
|
|
283
|
+
`✓ Transform applied to ${transformedData.length} records`,
|
|
284
|
+
'green'
|
|
285
|
+
)
|
|
286
|
+
);
|
|
287
|
+
}
|
|
269
288
|
} catch (transformError: any) {
|
|
270
289
|
console.error(
|
|
271
290
|
color(`✗ Transform error: ${transformError.message}`, 'red')
|
|
@@ -278,19 +297,20 @@ try {
|
|
|
278
297
|
}
|
|
279
298
|
|
|
280
299
|
// Prepare options for jtcsv
|
|
281
|
-
const jtcsvOptions = {
|
|
282
|
-
delimiter: options.delimiter,
|
|
283
|
-
includeHeaders: options.includeHeaders,
|
|
284
|
-
renameMap: options.renameMap,
|
|
285
|
-
template: options.template,
|
|
286
|
-
maxRecords: options.maxRecords,
|
|
287
|
-
preventCsvInjection: options.preventCsvInjection,
|
|
288
|
-
rfc4180Compliant: options.rfc4180Compliant,
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
300
|
+
const jtcsvOptions = {
|
|
301
|
+
delimiter: options.delimiter,
|
|
302
|
+
includeHeaders: options.includeHeaders,
|
|
303
|
+
renameMap: options.renameMap,
|
|
304
|
+
template: options.template,
|
|
305
|
+
maxRecords: options.maxRecords,
|
|
306
|
+
preventCsvInjection: options.preventCsvInjection,
|
|
307
|
+
rfc4180Compliant: options.rfc4180Compliant,
|
|
308
|
+
normalizeQuotes: options.normalizeQuotes,
|
|
309
|
+
schema: options.schema, // Add schema option
|
|
310
|
+
flatten: options.flatten,
|
|
311
|
+
flattenSeparator: options.flattenSeparator,
|
|
312
|
+
flattenMaxDepth: options.flattenMaxDepth,
|
|
313
|
+
arrayHandling: options.arrayHandling
|
|
294
314
|
};
|
|
295
315
|
|
|
296
316
|
// Apply transform function if provided
|
|
@@ -302,24 +322,30 @@ try {
|
|
|
302
322
|
// Convert to CSV
|
|
303
323
|
const csvData = jtcsv.jsonToCsv(transformedData, jtcsvOptions);
|
|
304
324
|
|
|
305
|
-
// Write output
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
325
|
+
// Write output
|
|
326
|
+
if (useStdout) {
|
|
327
|
+
process.stdout.write(csvData);
|
|
328
|
+
} else {
|
|
329
|
+
await fs.promises.writeFile(outputFile, csvData, 'utf8');
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const elapsed = Date.now() - startTime;
|
|
333
|
+
if (shouldLog) {
|
|
334
|
+
console.log(
|
|
335
|
+
color(
|
|
336
|
+
`✓ Converted ${transformedData.length.toLocaleString()} records in ${elapsed}ms`,
|
|
337
|
+
'green'
|
|
338
|
+
)
|
|
339
|
+
);
|
|
340
|
+
if (!useStdout) {
|
|
341
|
+
console.log(
|
|
342
|
+
color(
|
|
343
|
+
` Output: ${outputFile} (${csvData.length.toLocaleString()} bytes)`,
|
|
344
|
+
'dim'
|
|
345
|
+
)
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
323
349
|
|
|
324
350
|
return {
|
|
325
351
|
records: transformedData.length,
|
|
@@ -336,12 +362,15 @@ try {
|
|
|
336
362
|
}
|
|
337
363
|
|
|
338
364
|
async function convertCsvToJson(inputFile, outputFile, options: any): Promise<ConversionResult> {
|
|
339
|
-
const startTime = Date.now();
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
365
|
+
const startTime = Date.now();
|
|
366
|
+
const useStdin = inputFile === '-';
|
|
367
|
+
const useStdout = outputFile === '-';
|
|
368
|
+
const shouldLog = !options.silent && !useStdout;
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
if (shouldLog) {
|
|
372
|
+
console.log(color('Reading CSV file...', 'dim'));
|
|
373
|
+
}
|
|
345
374
|
|
|
346
375
|
// Prepare options for jtcsv
|
|
347
376
|
const jtcsvOptions = {
|
|
@@ -351,38 +380,46 @@ async function convertCsvToJson(inputFile, outputFile, options: any): Promise<Co
|
|
|
351
380
|
hasHeaders: options.hasHeaders,
|
|
352
381
|
renameMap: options.renameMap,
|
|
353
382
|
trim: options.trim,
|
|
354
|
-
parseNumbers: options.parseNumbers,
|
|
355
|
-
parseBooleans: options.parseBooleans,
|
|
356
|
-
maxRows: options.maxRows,
|
|
357
|
-
useFastPath: options.useFastPath,
|
|
358
|
-
fastPathMode: options.fastPathMode,
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
383
|
+
parseNumbers: options.parseNumbers,
|
|
384
|
+
parseBooleans: options.parseBooleans,
|
|
385
|
+
maxRows: options.maxRows,
|
|
386
|
+
useFastPath: options.useFastPath,
|
|
387
|
+
fastPathMode: options.fastPathMode,
|
|
388
|
+
repairRowShifts: options.repairRowShifts,
|
|
389
|
+
normalizeQuotes: options.normalizeQuotes,
|
|
390
|
+
schema: options.schema // Add schema option if supported
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
// Read and convert CSV
|
|
394
|
+
let jsonData: any;
|
|
395
|
+
if (useStdin) {
|
|
396
|
+
const csvContent = await readStdin();
|
|
397
|
+
jsonData = jtcsv.csvToJson(csvContent, jtcsvOptions);
|
|
398
|
+
} else {
|
|
399
|
+
jsonData = await jtcsv.readCsvAsJson(inputFile, jtcsvOptions);
|
|
400
|
+
}
|
|
364
401
|
|
|
365
402
|
// Apply transform if specified
|
|
366
403
|
let transformedData = jsonData;
|
|
367
|
-
if (options.transform) {
|
|
368
|
-
if (
|
|
369
|
-
console.log(
|
|
370
|
-
color(`Applying transform from: ${options.transform}`, 'dim')
|
|
371
|
-
);
|
|
372
|
-
}
|
|
404
|
+
if (options.transform) {
|
|
405
|
+
if (shouldLog) {
|
|
406
|
+
console.log(
|
|
407
|
+
color(`Applying transform from: ${options.transform}`, 'dim')
|
|
408
|
+
);
|
|
409
|
+
}
|
|
373
410
|
try {
|
|
374
411
|
transformedData = transformLoader.applyTransform(
|
|
375
412
|
jsonData,
|
|
376
413
|
options.transform
|
|
377
414
|
);
|
|
378
|
-
if (
|
|
379
|
-
console.log(
|
|
380
|
-
color(
|
|
381
|
-
`✓ Transform applied to ${transformedData.length} rows`,
|
|
382
|
-
'green'
|
|
383
|
-
)
|
|
384
|
-
);
|
|
385
|
-
}
|
|
415
|
+
if (shouldLog) {
|
|
416
|
+
console.log(
|
|
417
|
+
color(
|
|
418
|
+
`✓ Transform applied to ${transformedData.length} rows`,
|
|
419
|
+
'green'
|
|
420
|
+
)
|
|
421
|
+
);
|
|
422
|
+
}
|
|
386
423
|
} catch (transformError: any) {
|
|
387
424
|
console.error(
|
|
388
425
|
color(`✗ Transform error: ${transformError.message}`, 'red')
|
|
@@ -399,24 +436,30 @@ async function convertCsvToJson(inputFile, outputFile, options: any): Promise<Co
|
|
|
399
436
|
? JSON.stringify(transformedData, null, 2)
|
|
400
437
|
: JSON.stringify(transformedData);
|
|
401
438
|
|
|
402
|
-
// Write output
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
439
|
+
// Write output
|
|
440
|
+
if (useStdout) {
|
|
441
|
+
process.stdout.write(jsonOutput);
|
|
442
|
+
} else {
|
|
443
|
+
await fs.promises.writeFile(outputFile, jsonOutput, 'utf8');
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const elapsed = Date.now() - startTime;
|
|
447
|
+
if (shouldLog) {
|
|
448
|
+
console.log(
|
|
449
|
+
color(
|
|
450
|
+
`✓ Converted ${transformedData.length.toLocaleString()} rows in ${elapsed}ms`,
|
|
451
|
+
'green'
|
|
452
|
+
)
|
|
453
|
+
);
|
|
454
|
+
if (!useStdout) {
|
|
455
|
+
console.log(
|
|
456
|
+
color(
|
|
457
|
+
` Output: ${outputFile} (${jsonOutput.length.toLocaleString()} bytes)`,
|
|
458
|
+
'dim'
|
|
459
|
+
)
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
420
463
|
|
|
421
464
|
return {
|
|
422
465
|
rows: transformedData.length,
|
|
@@ -453,14 +496,16 @@ async function saveAsCsv(inputFile, outputFile, options: any): Promise<Conversio
|
|
|
453
496
|
}
|
|
454
497
|
try {
|
|
455
498
|
// Для CSV нужно сначала распарсить, применить трансформацию, затем снова сериализовать
|
|
456
|
-
const parsedData = jtcsv.csvToJson(inputData, {
|
|
457
|
-
delimiter: options.delimiter,
|
|
458
|
-
autoDetect: options.autoDetect,
|
|
459
|
-
hasHeaders: options.hasHeaders,
|
|
460
|
-
trim: options.trim,
|
|
461
|
-
parseNumbers: options.parseNumbers,
|
|
462
|
-
parseBooleans: options.parseBooleans
|
|
463
|
-
|
|
499
|
+
const parsedData = jtcsv.csvToJson(inputData, {
|
|
500
|
+
delimiter: options.delimiter,
|
|
501
|
+
autoDetect: options.autoDetect,
|
|
502
|
+
hasHeaders: options.hasHeaders,
|
|
503
|
+
trim: options.trim,
|
|
504
|
+
parseNumbers: options.parseNumbers,
|
|
505
|
+
parseBooleans: options.parseBooleans,
|
|
506
|
+
repairRowShifts: options.repairRowShifts,
|
|
507
|
+
normalizeQuotes: options.normalizeQuotes
|
|
508
|
+
});
|
|
464
509
|
|
|
465
510
|
const transformedJson = transformLoader.applyTransform(
|
|
466
511
|
parsedData,
|
|
@@ -468,10 +513,11 @@ async function saveAsCsv(inputFile, outputFile, options: any): Promise<Conversio
|
|
|
468
513
|
);
|
|
469
514
|
|
|
470
515
|
// Конвертировать обратно в CSV
|
|
471
|
-
transformedData = jtcsv.jsonToCsv(transformedJson, {
|
|
472
|
-
delimiter: options.delimiter,
|
|
473
|
-
includeHeaders: options.includeHeaders
|
|
474
|
-
|
|
516
|
+
transformedData = jtcsv.jsonToCsv(transformedJson, {
|
|
517
|
+
delimiter: options.delimiter,
|
|
518
|
+
includeHeaders: options.includeHeaders,
|
|
519
|
+
normalizeQuotes: options.normalizeQuotes
|
|
520
|
+
});
|
|
475
521
|
|
|
476
522
|
if (!options.silent) {
|
|
477
523
|
console.log(color('✓ Transform applied', 'green'));
|
|
@@ -597,14 +643,15 @@ async function convertNdjsonToCsv(inputFile, outputFile, options: any): Promise<
|
|
|
597
643
|
}
|
|
598
644
|
|
|
599
645
|
// Prepare options for jtcsv
|
|
600
|
-
const jtcsvOptions = {
|
|
601
|
-
delimiter: options.delimiter,
|
|
602
|
-
includeHeaders: options.includeHeaders,
|
|
603
|
-
renameMap: options.renameMap,
|
|
604
|
-
template: options.template,
|
|
605
|
-
preventCsvInjection: options.preventCsvInjection,
|
|
606
|
-
rfc4180Compliant: options.rfc4180Compliant
|
|
607
|
-
|
|
646
|
+
const jtcsvOptions = {
|
|
647
|
+
delimiter: options.delimiter,
|
|
648
|
+
includeHeaders: options.includeHeaders,
|
|
649
|
+
renameMap: options.renameMap,
|
|
650
|
+
template: options.template,
|
|
651
|
+
preventCsvInjection: options.preventCsvInjection,
|
|
652
|
+
rfc4180Compliant: options.rfc4180Compliant,
|
|
653
|
+
normalizeQuotes: options.normalizeQuotes
|
|
654
|
+
};
|
|
608
655
|
|
|
609
656
|
// Convert to CSV
|
|
610
657
|
const csvData = jtcsv.jsonToCsv(jsonData, jtcsvOptions);
|
|
@@ -1866,10 +1913,12 @@ function parseOptions(args: any): any {
|
|
|
1866
1913
|
template: undefined,
|
|
1867
1914
|
trim: true,
|
|
1868
1915
|
parseNumbers: false,
|
|
1869
|
-
parseBooleans: false,
|
|
1870
|
-
useFastPath: true,
|
|
1871
|
-
fastPathMode: 'objects',
|
|
1872
|
-
|
|
1916
|
+
parseBooleans: false,
|
|
1917
|
+
useFastPath: true,
|
|
1918
|
+
fastPathMode: 'objects',
|
|
1919
|
+
repairRowShifts: true,
|
|
1920
|
+
normalizeQuotes: true,
|
|
1921
|
+
preventCsvInjection: true,
|
|
1873
1922
|
rfc4180Compliant: true,
|
|
1874
1923
|
maxRecords: undefined,
|
|
1875
1924
|
maxRows: undefined,
|
|
@@ -1882,11 +1931,13 @@ function parseOptions(args: any): any {
|
|
|
1882
1931
|
addBOM: false,
|
|
1883
1932
|
unwrapArrays: false,
|
|
1884
1933
|
stringifyObjects: false,
|
|
1885
|
-
recursive: false,
|
|
1886
|
-
pattern: '**/*',
|
|
1887
|
-
outputDir: './output',
|
|
1888
|
-
|
|
1889
|
-
|
|
1934
|
+
recursive: false,
|
|
1935
|
+
pattern: '**/*',
|
|
1936
|
+
outputDir: './output',
|
|
1937
|
+
patternProvided: false,
|
|
1938
|
+
outputDirProvided: false,
|
|
1939
|
+
overwrite: false,
|
|
1940
|
+
parallel: 4,
|
|
1890
1941
|
chunkSize: 65536,
|
|
1891
1942
|
bufferSize: 1000,
|
|
1892
1943
|
schema: undefined,
|
|
@@ -1938,16 +1989,22 @@ function parseOptions(args: any): any {
|
|
|
1938
1989
|
case 'fast-path':
|
|
1939
1990
|
options.useFastPath = value !== 'false';
|
|
1940
1991
|
break;
|
|
1941
|
-
case 'fast-path-mode':
|
|
1942
|
-
options.fastPathMode = value || 'objects';
|
|
1943
|
-
if (
|
|
1944
|
-
options.fastPathMode !== 'objects' &&
|
|
1945
|
-
options.fastPathMode !== 'compact'
|
|
1946
|
-
) {
|
|
1947
|
-
throw new Error('Invalid --fast-path-mode value (objects|compact)');
|
|
1948
|
-
}
|
|
1949
|
-
break;
|
|
1950
|
-
case '
|
|
1992
|
+
case 'fast-path-mode':
|
|
1993
|
+
options.fastPathMode = value || 'objects';
|
|
1994
|
+
if (
|
|
1995
|
+
options.fastPathMode !== 'objects' &&
|
|
1996
|
+
options.fastPathMode !== 'compact'
|
|
1997
|
+
) {
|
|
1998
|
+
throw new Error('Invalid --fast-path-mode value (objects|compact)');
|
|
1999
|
+
}
|
|
2000
|
+
break;
|
|
2001
|
+
case 'repair-row-shifts':
|
|
2002
|
+
options.repairRowShifts = true;
|
|
2003
|
+
break;
|
|
2004
|
+
case 'normalize-quotes':
|
|
2005
|
+
options.normalizeQuotes = true;
|
|
2006
|
+
break;
|
|
2007
|
+
case 'rename':
|
|
1951
2008
|
try {
|
|
1952
2009
|
const jsonStr = value || '{}';
|
|
1953
2010
|
const cleanStr = jsonStr
|
|
@@ -2026,12 +2083,14 @@ function parseOptions(args: any): any {
|
|
|
2026
2083
|
case 'recursive':
|
|
2027
2084
|
options.recursive = true;
|
|
2028
2085
|
break;
|
|
2029
|
-
case 'pattern':
|
|
2030
|
-
options.pattern = value || '**/*';
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2086
|
+
case 'pattern':
|
|
2087
|
+
options.pattern = value || '**/*';
|
|
2088
|
+
options.patternProvided = true;
|
|
2089
|
+
break;
|
|
2090
|
+
case 'output-dir':
|
|
2091
|
+
options.outputDir = value || './output';
|
|
2092
|
+
options.outputDirProvided = true;
|
|
2093
|
+
break;
|
|
2035
2094
|
case 'overwrite':
|
|
2036
2095
|
options.overwrite = true;
|
|
2037
2096
|
break;
|
|
@@ -2065,10 +2124,10 @@ function parseOptions(args: any): any {
|
|
|
2065
2124
|
options.host = value || 'localhost';
|
|
2066
2125
|
break;
|
|
2067
2126
|
}
|
|
2068
|
-
} else if (!arg.startsWith('-')) {
|
|
2069
|
-
files.push(arg);
|
|
2070
|
-
}
|
|
2071
|
-
}
|
|
2127
|
+
} else if (arg === '-' || !arg.startsWith('-')) {
|
|
2128
|
+
files.push(arg);
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2072
2131
|
|
|
2073
2132
|
return { options, files };
|
|
2074
2133
|
}
|
|
@@ -2453,20 +2512,37 @@ async function main(): Promise<void> {
|
|
|
2453
2512
|
|
|
2454
2513
|
const batchCommand = args[1].toLowerCase();
|
|
2455
2514
|
// Для batch команд нужно парсить опции начиная с 3-го аргумента (после batch и подкоманды)
|
|
2456
|
-
const batchArgs = args.slice(2);
|
|
2457
|
-
const { options: batchOptions, files: batchFiles } =
|
|
2458
|
-
parseOptions(batchArgs);
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2515
|
+
const batchArgs = args.slice(2);
|
|
2516
|
+
const { options: batchOptions, files: batchFiles } =
|
|
2517
|
+
parseOptions(batchArgs);
|
|
2518
|
+
const inputPattern = batchFiles[0]
|
|
2519
|
+
|| (batchOptions.patternProvided ? batchOptions.pattern : undefined);
|
|
2520
|
+
const outputDir = batchFiles[1]
|
|
2521
|
+
|| (batchOptions.outputDirProvided ? batchOptions.outputDir : undefined);
|
|
2522
|
+
|
|
2523
|
+
if (!inputPattern || !outputDir) {
|
|
2524
|
+
console.error(
|
|
2525
|
+
color('Error: Batch mode requires input pattern and output directory', 'red')
|
|
2526
|
+
);
|
|
2527
|
+
console.log(
|
|
2528
|
+
color('Usage: jtcsv batch [json-to-csv|csv-to-json|process] "<pattern>" "<outputDir>"', 'cyan')
|
|
2529
|
+
);
|
|
2530
|
+
console.log(
|
|
2531
|
+
color('Or: jtcsv batch <subcommand> --pattern="<pattern>" --output-dir="<outputDir>"', 'cyan')
|
|
2532
|
+
);
|
|
2533
|
+
process.exit(1);
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
if (batchCommand === 'json-to-csv') {
|
|
2537
|
+
await batchJsonToCsv(inputPattern, outputDir, batchOptions);
|
|
2538
|
+
} else if (batchCommand === 'csv-to-json') {
|
|
2539
|
+
await batchCsvToJson(inputPattern, outputDir, batchOptions);
|
|
2540
|
+
} else if (batchCommand === 'process') {
|
|
2541
|
+
await batchProcessMixed(inputPattern, outputDir, batchOptions);
|
|
2542
|
+
} else {
|
|
2543
|
+
console.error(
|
|
2544
|
+
color('Error: Invalid batch command or missing files', 'red')
|
|
2545
|
+
);
|
|
2470
2546
|
process.exit(1);
|
|
2471
2547
|
}
|
|
2472
2548
|
break;
|
|
@@ -2532,3 +2608,5 @@ if (require.main === module) {
|
|
|
2532
2608
|
}
|
|
2533
2609
|
|
|
2534
2610
|
module.exports = { main };
|
|
2611
|
+
|
|
2612
|
+
|