jtcsv 2.2.7 → 3.0.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 +31 -1
- package/bin/jtcsv.js +891 -821
- package/bin/jtcsv.ts +2534 -0
- package/csv-to-json.js +168 -145
- package/dist/jtcsv-core.cjs.js +1407 -0
- package/dist/jtcsv-core.cjs.js.map +1 -0
- package/dist/jtcsv-core.esm.js +1379 -0
- package/dist/jtcsv-core.esm.js.map +1 -0
- package/dist/jtcsv-core.umd.js +1413 -0
- package/dist/jtcsv-core.umd.js.map +1 -0
- package/dist/jtcsv-full.cjs.js +1912 -0
- package/dist/jtcsv-full.cjs.js.map +1 -0
- package/dist/jtcsv-full.esm.js +1880 -0
- package/dist/jtcsv-full.esm.js.map +1 -0
- package/dist/jtcsv-full.umd.js +1918 -0
- package/dist/jtcsv-full.umd.js.map +1 -0
- package/dist/jtcsv-workers.esm.js +759 -0
- package/dist/jtcsv-workers.esm.js.map +1 -0
- package/dist/jtcsv-workers.umd.js +773 -0
- package/dist/jtcsv-workers.umd.js.map +1 -0
- package/dist/jtcsv.cjs.js +61 -19
- package/dist/jtcsv.cjs.js.map +1 -1
- package/dist/jtcsv.esm.js +61 -19
- package/dist/jtcsv.esm.js.map +1 -1
- package/dist/jtcsv.umd.js +61 -19
- package/dist/jtcsv.umd.js.map +1 -1
- package/errors.js +188 -2
- package/examples/advanced/conditional-transformations.js +446 -0
- package/examples/advanced/conditional-transformations.ts +446 -0
- package/examples/advanced/csv-parser.worker.js +89 -0
- package/examples/advanced/csv-parser.worker.ts +89 -0
- package/examples/advanced/nested-objects-example.js +306 -0
- package/examples/advanced/nested-objects-example.ts +306 -0
- package/examples/advanced/performance-optimization.js +504 -0
- package/examples/advanced/performance-optimization.ts +504 -0
- package/examples/advanced/run-demo-server.js +116 -0
- package/examples/advanced/run-demo-server.ts +116 -0
- package/examples/advanced/web-worker-usage.html +874 -0
- package/examples/async-multithreaded-example.ts +335 -0
- package/examples/cli-advanced-usage.md +288 -0
- package/examples/cli-batch-processing.ts +38 -0
- package/examples/cli-tool.js +0 -3
- package/examples/cli-tool.ts +183 -0
- package/examples/error-handling.js +21 -7
- package/examples/error-handling.ts +356 -0
- package/examples/express-api.js +0 -3
- package/examples/express-api.ts +164 -0
- package/examples/large-dataset-example.js +0 -3
- package/examples/large-dataset-example.ts +204 -0
- package/examples/ndjson-processing.js +1 -1
- package/examples/ndjson-processing.ts +456 -0
- package/examples/plugin-excel-exporter.js +3 -4
- package/examples/plugin-excel-exporter.ts +406 -0
- package/examples/react-integration.tsx +637 -0
- package/examples/schema-validation.ts +640 -0
- package/examples/simple-usage.js +254 -254
- package/examples/simple-usage.ts +194 -0
- package/examples/streaming-example.js +4 -5
- package/examples/streaming-example.ts +419 -0
- package/examples/web-workers-advanced.ts +28 -0
- package/index.d.ts +1 -3
- package/index.js +15 -1
- package/json-save.js +9 -3
- package/json-to-csv.js +168 -21
- package/package.json +69 -10
- package/plugins/express-middleware/README.md +21 -2
- package/plugins/express-middleware/example.js +3 -4
- package/plugins/express-middleware/example.ts +135 -0
- package/plugins/express-middleware/index.d.ts +1 -1
- package/plugins/express-middleware/index.js +270 -118
- package/plugins/express-middleware/index.ts +557 -0
- package/plugins/fastify-plugin/index.js +2 -4
- package/plugins/fastify-plugin/index.ts +443 -0
- package/plugins/hono/index.ts +226 -0
- package/plugins/nestjs/index.ts +201 -0
- package/plugins/nextjs-api/examples/ConverterComponent.tsx +386 -0
- package/plugins/nextjs-api/examples/api-convert.js +0 -2
- package/plugins/nextjs-api/examples/api-convert.ts +67 -0
- package/plugins/nextjs-api/index.tsx +339 -0
- package/plugins/nextjs-api/route.js +2 -3
- package/plugins/nextjs-api/route.ts +370 -0
- package/plugins/nuxt/index.ts +94 -0
- package/plugins/nuxt/runtime/composables/useJtcsv.ts +100 -0
- package/plugins/nuxt/runtime/plugin.ts +71 -0
- package/plugins/remix/index.js +1 -1
- package/plugins/remix/index.ts +260 -0
- package/plugins/sveltekit/index.js +1 -1
- package/plugins/sveltekit/index.ts +301 -0
- package/plugins/trpc/index.ts +267 -0
- package/src/browser/browser-functions.ts +402 -0
- package/src/browser/core.js +92 -0
- package/src/browser/core.ts +152 -0
- package/src/browser/csv-to-json-browser.d.ts +3 -0
- package/src/browser/csv-to-json-browser.js +36 -14
- package/src/browser/csv-to-json-browser.ts +264 -0
- package/src/browser/errors-browser.ts +303 -0
- package/src/browser/extensions/plugins.js +92 -0
- package/src/browser/extensions/plugins.ts +93 -0
- package/src/browser/extensions/workers.js +39 -0
- package/src/browser/extensions/workers.ts +39 -0
- package/src/browser/globals.d.ts +5 -0
- package/src/browser/index.ts +192 -0
- package/src/browser/json-to-csv-browser.d.ts +3 -0
- package/src/browser/json-to-csv-browser.js +13 -3
- package/src/browser/json-to-csv-browser.ts +262 -0
- package/src/browser/streams.js +12 -2
- package/src/browser/streams.ts +336 -0
- package/src/browser/workers/csv-parser.worker.ts +377 -0
- package/src/browser/workers/worker-pool.ts +548 -0
- package/src/core/delimiter-cache.js +22 -8
- package/src/core/delimiter-cache.ts +310 -0
- package/src/core/node-optimizations.ts +449 -0
- package/src/core/plugin-system.js +29 -11
- package/src/core/plugin-system.ts +400 -0
- package/src/core/transform-hooks.ts +558 -0
- package/src/engines/fast-path-engine-new.ts +347 -0
- package/src/engines/fast-path-engine.ts +854 -0
- package/src/errors.ts +72 -0
- package/src/formats/ndjson-parser.ts +469 -0
- package/src/formats/tsv-parser.ts +334 -0
- package/src/index-with-plugins.js +16 -9
- package/src/index-with-plugins.ts +395 -0
- package/src/types/index.ts +255 -0
- package/src/utils/bom-utils.js +259 -0
- package/src/utils/bom-utils.ts +373 -0
- package/src/utils/encoding-support.js +124 -0
- package/src/utils/encoding-support.ts +155 -0
- package/src/utils/schema-validator.js +19 -19
- package/src/utils/schema-validator.ts +819 -0
- package/src/utils/transform-loader.js +1 -1
- package/src/utils/transform-loader.ts +389 -0
- package/src/utils/zod-adapter.js +170 -0
- package/src/utils/zod-adapter.ts +280 -0
- package/src/web-server/index.js +10 -10
- package/src/web-server/index.ts +683 -0
- package/src/workers/csv-multithreaded.ts +310 -0
- package/src/workers/csv-parser.worker.ts +227 -0
- package/src/workers/worker-pool.ts +409 -0
- package/stream-csv-to-json.js +26 -8
- package/stream-json-to-csv.js +1 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TSV (Tab-Separated Values) парсер
|
|
3
|
+
* Специализированная поддержка TSV формата
|
|
4
|
+
*
|
|
5
|
+
* @version 1.0.0
|
|
6
|
+
* @date 2026-01-23
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { csvToJson } from "../../csv-to-json";
|
|
12
|
+
import { jsonToCsv } from "../../json-to-csv";
|
|
13
|
+
import { ValidationError, SecurityError, FileSystemError } from "../../errors";
|
|
14
|
+
import { createJsonToCsvStream } from "../../stream-json-to-csv";
|
|
15
|
+
import { createCsvToJsonStream } from "../../stream-csv-to-json";
|
|
16
|
+
|
|
17
|
+
function validateTsvFilePath(filePath) {
|
|
18
|
+
if (typeof filePath !== 'string' || filePath.trim() === '') {
|
|
19
|
+
throw new ValidationError('File path must be a non-empty string');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!filePath.toLowerCase().endsWith('.tsv')) {
|
|
23
|
+
throw new ValidationError('File must have .tsv extension');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const normalizedPath = path.normalize(filePath);
|
|
27
|
+
if (normalizedPath.includes('..') ||
|
|
28
|
+
/\\\.\.\\|\/\.\.\//.test(filePath) ||
|
|
29
|
+
filePath.startsWith('..') ||
|
|
30
|
+
filePath.includes('/..')) {
|
|
31
|
+
throw new SecurityError('Directory traversal detected in file path');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return path.resolve(filePath);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
class TsvParser {
|
|
38
|
+
/**
|
|
39
|
+
* Конвертирует массив объектов в TSV строку
|
|
40
|
+
* @param {Array} data - Массив объектов
|
|
41
|
+
* @param {Object} options - Опции форматирования
|
|
42
|
+
* @returns {string} TSV строка
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* const data = [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }];
|
|
46
|
+
* const tsv = TsvParser.jsonToTsv(data);
|
|
47
|
+
* // Результат: "id\tname\n1\tJohn\n2\tJane"
|
|
48
|
+
*/
|
|
49
|
+
static jsonToTsv(data, options = {}) {
|
|
50
|
+
const defaultOptions = {
|
|
51
|
+
delimiter: '\t',
|
|
52
|
+
includeHeaders: true,
|
|
53
|
+
...options
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return jsonToCsv(data, defaultOptions);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Конвертирует TSV строку в массив объектов
|
|
61
|
+
* @param {string} tsvString - TSV строка
|
|
62
|
+
* @param {Object} options - Опции парсинга
|
|
63
|
+
* @returns {Array} Массив объектов
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* const tsv = "id\tname\n1\tJohn\n2\tJane";
|
|
67
|
+
* const data = TsvParser.tsvToJson(tsv);
|
|
68
|
+
* // Результат: [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }]
|
|
69
|
+
*/
|
|
70
|
+
static tsvToJson(tsvString, options = {}) {
|
|
71
|
+
const defaultOptions = {
|
|
72
|
+
delimiter: '\t',
|
|
73
|
+
autoDetect: false,
|
|
74
|
+
hasHeaders: true,
|
|
75
|
+
...options
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return csvToJson(tsvString, defaultOptions);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Автоматически определяет является ли строка TSV
|
|
83
|
+
* @param {string} sample - Образец данных
|
|
84
|
+
* @returns {boolean} True если это TSV
|
|
85
|
+
*/
|
|
86
|
+
static isTsv(sample) {
|
|
87
|
+
if (!sample || typeof sample !== 'string') {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const lines = sample.split('\n').slice(0, 10);
|
|
92
|
+
let tabCount = 0;
|
|
93
|
+
let commaCount = 0;
|
|
94
|
+
let semicolonCount = 0;
|
|
95
|
+
|
|
96
|
+
for (const line of lines) {
|
|
97
|
+
if (line.trim() === '') {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Считаем разделители
|
|
102
|
+
tabCount += (line.match(/\t/g) || []).length;
|
|
103
|
+
commaCount += (line.match(/,/g) || []).length;
|
|
104
|
+
semicolonCount += (line.match(/;/g) || []).length;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Если табуляций больше чем других разделителей, считаем это TSV
|
|
108
|
+
return tabCount > commaCount && tabCount > semicolonCount;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Создает TransformStream для конвертации JSON в TSV
|
|
113
|
+
* @param {Object} options - Опции конвертации
|
|
114
|
+
* @returns {TransformStream} Transform stream
|
|
115
|
+
*/
|
|
116
|
+
static createJsonToTsvStream(options = {}) {
|
|
117
|
+
return createJsonToCsvStream({
|
|
118
|
+
delimiter: '\t',
|
|
119
|
+
...options
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Создает TransformStream для конвертации TSV в JSON
|
|
125
|
+
* @param {Object} options - Опции конвертации
|
|
126
|
+
* @returns {TransformStream} Transform stream
|
|
127
|
+
*/
|
|
128
|
+
static createTsvToJsonStream(options = {}) {
|
|
129
|
+
return createCsvToJsonStream({
|
|
130
|
+
delimiter: '\t',
|
|
131
|
+
autoDetect: false,
|
|
132
|
+
...options
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Читает TSV файл и конвертирует в JSON
|
|
138
|
+
* @param {string} filePath - Путь к TSV файлу
|
|
139
|
+
* @param {Object} options - Опции парсинга
|
|
140
|
+
* @returns {Promise<Array>} Promise с массивом объектов
|
|
141
|
+
*/
|
|
142
|
+
static async readTsvAsJson(filePath, options = {}) {
|
|
143
|
+
const safePath = validateTsvFilePath(filePath);
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
const tsvContent = await fs.promises.readFile(safePath, 'utf8');
|
|
147
|
+
return csvToJson(tsvContent, {
|
|
148
|
+
delimiter: '\t',
|
|
149
|
+
autoDetect: false,
|
|
150
|
+
...options
|
|
151
|
+
});
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (error instanceof ValidationError || error instanceof SecurityError) {
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
if (error.code === 'ENOENT') {
|
|
157
|
+
throw new FileSystemError(`File not found: ${safePath}`, error);
|
|
158
|
+
}
|
|
159
|
+
if (error.code === 'EACCES') {
|
|
160
|
+
throw new FileSystemError(`Permission denied: ${safePath}`, error);
|
|
161
|
+
}
|
|
162
|
+
if (error.code === 'EISDIR') {
|
|
163
|
+
throw new FileSystemError(`Path is a directory: ${safePath}`, error);
|
|
164
|
+
}
|
|
165
|
+
throw new FileSystemError(`Failed to read TSV file: ${error.message}`, error);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Синхронно читает TSV файл и конвертирует в JSON
|
|
171
|
+
* @param {string} filePath - Путь к TSV файлу
|
|
172
|
+
* @param {Object} options - Опции парсинга
|
|
173
|
+
* @returns {Array} Массив объектов
|
|
174
|
+
*/
|
|
175
|
+
static readTsvAsJsonSync(filePath, options = {}) {
|
|
176
|
+
const safePath = validateTsvFilePath(filePath);
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const tsvContent = fs.readFileSync(safePath, 'utf8');
|
|
180
|
+
return csvToJson(tsvContent, {
|
|
181
|
+
delimiter: '\t',
|
|
182
|
+
autoDetect: false,
|
|
183
|
+
...options
|
|
184
|
+
});
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (error instanceof ValidationError || error instanceof SecurityError) {
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
if (error.code === 'ENOENT') {
|
|
190
|
+
throw new FileSystemError(`File not found: ${safePath}`, error);
|
|
191
|
+
}
|
|
192
|
+
if (error.code === 'EACCES') {
|
|
193
|
+
throw new FileSystemError(`Permission denied: ${safePath}`, error);
|
|
194
|
+
}
|
|
195
|
+
if (error.code === 'EISDIR') {
|
|
196
|
+
throw new FileSystemError(`Path is a directory: ${safePath}`, error);
|
|
197
|
+
}
|
|
198
|
+
throw new FileSystemError(`Failed to read TSV file: ${error.message}`, error);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Сохраняет массив объектов как TSV файл
|
|
204
|
+
* @param {Array} data - Массив объектов
|
|
205
|
+
* @param {string} filePath - Путь для сохранения
|
|
206
|
+
* @param {Object} options - Опции сохранения
|
|
207
|
+
* @returns {Promise<void>}
|
|
208
|
+
*/
|
|
209
|
+
static async saveAsTsv(data, filePath, options = {}) {
|
|
210
|
+
const safePath = validateTsvFilePath(filePath);
|
|
211
|
+
const tsvContent = this.jsonToTsv(data, options);
|
|
212
|
+
const dir = path.dirname(safePath);
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
216
|
+
await fs.promises.writeFile(safePath, tsvContent, 'utf8');
|
|
217
|
+
return safePath;
|
|
218
|
+
} catch (error) {
|
|
219
|
+
if (error.code === 'ENOENT') {
|
|
220
|
+
throw new FileSystemError(`Directory does not exist: ${dir}`, error);
|
|
221
|
+
}
|
|
222
|
+
if (error.code === 'EACCES') {
|
|
223
|
+
throw new FileSystemError(`Permission denied: ${safePath}`, error);
|
|
224
|
+
}
|
|
225
|
+
if (error.code === 'ENOSPC') {
|
|
226
|
+
throw new FileSystemError(`No space left on device: ${safePath}`, error);
|
|
227
|
+
}
|
|
228
|
+
throw new FileSystemError(`Failed to save TSV file: ${error.message}`, error);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Синхронно сохраняет массив объектов как TSV файл
|
|
234
|
+
* @param {Array} data - Массив объектов
|
|
235
|
+
* @param {string} filePath - Путь для сохранения
|
|
236
|
+
* @param {Object} options - Опции сохранения
|
|
237
|
+
*/
|
|
238
|
+
static saveAsTsvSync(data, filePath, options = {}) {
|
|
239
|
+
const safePath = validateTsvFilePath(filePath);
|
|
240
|
+
const tsvContent = this.jsonToTsv(data, options);
|
|
241
|
+
|
|
242
|
+
fs.mkdirSync(path.dirname(safePath), { recursive: true });
|
|
243
|
+
fs.writeFileSync(safePath, tsvContent, 'utf8');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Валидирует TSV строку
|
|
248
|
+
* @param {string} tsvString - TSV строка для валидации
|
|
249
|
+
* @param {Object} options - Опции валидации
|
|
250
|
+
* @returns {Object} Результат валидации
|
|
251
|
+
*/
|
|
252
|
+
static validateTsv(tsvString, options: any = {}) {
|
|
253
|
+
const { requireConsistentColumns = true } = options;
|
|
254
|
+
|
|
255
|
+
if (!tsvString || typeof tsvString !== 'string') {
|
|
256
|
+
return {
|
|
257
|
+
valid: false,
|
|
258
|
+
error: 'Input must be a non-empty string',
|
|
259
|
+
details: { inputType: typeof tsvString }
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const lines = tsvString.split('\n').filter(line => line.trim() !== '');
|
|
264
|
+
|
|
265
|
+
if (lines.length === 0) {
|
|
266
|
+
return {
|
|
267
|
+
valid: false,
|
|
268
|
+
error: 'No data found in TSV',
|
|
269
|
+
details: { lineCount: 0 }
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const columnCounts = [];
|
|
274
|
+
const errors = [];
|
|
275
|
+
|
|
276
|
+
for (let i = 0; i < lines.length; i++) {
|
|
277
|
+
const line = lines[i];
|
|
278
|
+
const columns = line.split('\t');
|
|
279
|
+
columnCounts.push(columns.length);
|
|
280
|
+
|
|
281
|
+
// Проверяем наличие пустых полей (если требуется)
|
|
282
|
+
if (options.disallowEmptyFields) {
|
|
283
|
+
const emptyFields = columns.filter(field => field.trim() === '');
|
|
284
|
+
if (emptyFields.length > 0) {
|
|
285
|
+
errors.push({
|
|
286
|
+
line: i + 1,
|
|
287
|
+
error: `Found ${emptyFields.length} empty field(s)`,
|
|
288
|
+
fields: emptyFields.map((_, idx) => idx + 1)
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Проверяем консистентность колонки
|
|
295
|
+
if (requireConsistentColumns && columnCounts.length > 1) {
|
|
296
|
+
const firstCount = columnCounts[0];
|
|
297
|
+
const inconsistentLines = [];
|
|
298
|
+
|
|
299
|
+
for (let i = 1; i < columnCounts.length; i++) {
|
|
300
|
+
if (columnCounts[i] !== firstCount) {
|
|
301
|
+
inconsistentLines.push({
|
|
302
|
+
line: i + 1,
|
|
303
|
+
expected: firstCount,
|
|
304
|
+
actual: columnCounts[i]
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (inconsistentLines.length > 0) {
|
|
310
|
+
errors.push({
|
|
311
|
+
error: 'Inconsistent column count',
|
|
312
|
+
details: inconsistentLines
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* istanbul ignore next */
|
|
318
|
+
const totalColumns = columnCounts[0] || 0;
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
valid: errors.length === 0,
|
|
322
|
+
stats: {
|
|
323
|
+
totalLines: lines.length,
|
|
324
|
+
totalColumns,
|
|
325
|
+
minColumns: Math.min(...columnCounts),
|
|
326
|
+
maxColumns: Math.max(...columnCounts),
|
|
327
|
+
consistentColumns: new Set(columnCounts).size === 1
|
|
328
|
+
},
|
|
329
|
+
errors: errors.length > 0 ? errors : undefined
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export default TsvParser;
|
|
@@ -48,7 +48,9 @@ class JtcsvWithPlugins {
|
|
|
48
48
|
const structure = this.fastPathEngine.analyzeStructure(sample, context.options);
|
|
49
49
|
|
|
50
50
|
context.metadata.fastPathStructure = structure;
|
|
51
|
-
|
|
51
|
+
if (process.env.NODE_ENV === 'development') {
|
|
52
|
+
console.log(`🚀 Используется ${structure.recommendedEngine} парсер`);
|
|
53
|
+
}
|
|
52
54
|
}
|
|
53
55
|
return csv;
|
|
54
56
|
},
|
|
@@ -120,19 +122,27 @@ class JtcsvWithPlugins {
|
|
|
120
122
|
description: 'Логирование операций',
|
|
121
123
|
hooks: {
|
|
122
124
|
'before:csvToJson': (csv, context) => {
|
|
123
|
-
|
|
125
|
+
if (process.env.NODE_ENV === 'development') {
|
|
126
|
+
console.log(`📥 Начало csvToJson, размер: ${csv.length} байт`);
|
|
127
|
+
}
|
|
124
128
|
return csv;
|
|
125
129
|
},
|
|
126
130
|
'after:csvToJson': (result, context) => {
|
|
127
|
-
|
|
131
|
+
if (process.env.NODE_ENV === 'development') {
|
|
132
|
+
console.log(`📤 Завершение csvToJson, результат: ${result.length} записей`);
|
|
133
|
+
}
|
|
128
134
|
return result;
|
|
129
135
|
},
|
|
130
136
|
'before:jsonToCsv': (json, context) => {
|
|
131
|
-
|
|
137
|
+
if (process.env.NODE_ENV === 'development') {
|
|
138
|
+
console.log(`📥 Начало jsonToCsv, записей: ${json.length}`);
|
|
139
|
+
}
|
|
132
140
|
return json;
|
|
133
141
|
},
|
|
134
142
|
'after:jsonToCsv': (csv, context) => {
|
|
135
|
-
|
|
143
|
+
if (process.env.NODE_ENV === 'development') {
|
|
144
|
+
console.log(`📤 Завершение jsonToCsv, размер: ${csv.length} байт`);
|
|
145
|
+
}
|
|
136
146
|
return csv;
|
|
137
147
|
}
|
|
138
148
|
}
|
|
@@ -365,7 +375,4 @@ module.exports.FastPathEngine = FastPathEngine;
|
|
|
365
375
|
module.exports.NdjsonParser = NdjsonParser;
|
|
366
376
|
|
|
367
377
|
// Экспортируем фабричный метод
|
|
368
|
-
module.exports.create = JtcsvWithPlugins.create;
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
378
|
+
module.exports.create = JtcsvWithPlugins.create;
|