convert-csv-to-json 4.1.0 → 4.3.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.
Files changed (61) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc.json +48 -0
  3. package/.github/workflows/ci-cd.yml +28 -2
  4. package/SECURITY.md +2 -0
  5. package/docs/api/BrowserApi.html +2435 -0
  6. package/docs/api/BrowserApiError.html +522 -0
  7. package/docs/api/ConfigurationError.html +594 -0
  8. package/docs/api/CsvFormatError.html +530 -0
  9. package/docs/api/CsvParsingError.html +384 -0
  10. package/docs/api/CsvToJson.html +3136 -0
  11. package/docs/api/CsvToJsonAsync.html +2672 -0
  12. package/docs/api/FileOperationError.html +270 -0
  13. package/docs/api/FileUtils.html +1012 -0
  14. package/docs/api/InputValidationError.html +293 -0
  15. package/docs/api/JsonUtil.html +340 -0
  16. package/docs/api/JsonValidationError.html +247 -0
  17. package/docs/api/fonts/OpenSans-Bold-webfont.eot +0 -0
  18. package/docs/api/fonts/OpenSans-Bold-webfont.svg +1830 -0
  19. package/docs/api/fonts/OpenSans-Bold-webfont.woff +0 -0
  20. package/docs/api/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
  21. package/docs/api/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
  22. package/docs/api/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
  23. package/docs/api/fonts/OpenSans-Italic-webfont.eot +0 -0
  24. package/docs/api/fonts/OpenSans-Italic-webfont.svg +1830 -0
  25. package/docs/api/fonts/OpenSans-Italic-webfont.woff +0 -0
  26. package/docs/api/fonts/OpenSans-Light-webfont.eot +0 -0
  27. package/docs/api/fonts/OpenSans-Light-webfont.svg +1831 -0
  28. package/docs/api/fonts/OpenSans-Light-webfont.woff +0 -0
  29. package/docs/api/fonts/OpenSans-LightItalic-webfont.eot +0 -0
  30. package/docs/api/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
  31. package/docs/api/fonts/OpenSans-LightItalic-webfont.woff +0 -0
  32. package/docs/api/fonts/OpenSans-Regular-webfont.eot +0 -0
  33. package/docs/api/fonts/OpenSans-Regular-webfont.svg +1831 -0
  34. package/docs/api/fonts/OpenSans-Regular-webfont.woff +0 -0
  35. package/docs/api/global.html +3315 -0
  36. package/docs/api/index.html +326 -0
  37. package/docs/api/index.js.html +341 -0
  38. package/docs/api/scripts/linenumber.js +25 -0
  39. package/docs/api/scripts/prettify/Apache-License-2.0.txt +202 -0
  40. package/docs/api/scripts/prettify/lang-css.js +2 -0
  41. package/docs/api/scripts/prettify/prettify.js +28 -0
  42. package/docs/api/src_browserApi.js.html +271 -0
  43. package/docs/api/src_csvToJson.js.html +605 -0
  44. package/docs/api/src_csvToJsonAsync.js.html +244 -0
  45. package/docs/api/src_util_errors.js.html +374 -0
  46. package/docs/api/src_util_fileUtils.js.html +147 -0
  47. package/docs/api/src_util_jsonUtils.js.html +75 -0
  48. package/docs/api/src_util_stringUtils.js.html +212 -0
  49. package/docs/api/styles/jsdoc-default.css +358 -0
  50. package/docs/api/styles/prettify-jsdoc.css +111 -0
  51. package/docs/api/styles/prettify-tomorrow.css +132 -0
  52. package/index.js +109 -32
  53. package/jsdoc.json +17 -0
  54. package/package.json +10 -3
  55. package/src/browserApi.js +96 -4
  56. package/src/csvToJson.js +163 -2
  57. package/src/csvToJsonAsync.js +74 -14
  58. package/src/util/errors.js +96 -0
  59. package/src/util/fileUtils.js +34 -0
  60. package/src/util/jsonUtils.js +8 -0
  61. package/src/util/stringUtils.js +51 -0
@@ -0,0 +1,605 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>JSDoc: Source: src/csvToJson.js</title>
6
+
7
+ <script src="scripts/prettify/prettify.js"> </script>
8
+ <script src="scripts/prettify/lang-css.js"> </script>
9
+ <!--[if lt IE 9]>
10
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
11
+ <![endif]-->
12
+ <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
13
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
14
+ </head>
15
+
16
+ <body>
17
+
18
+ <div id="main">
19
+
20
+ <h1 class="page-title">Source: src/csvToJson.js</h1>
21
+
22
+
23
+
24
+
25
+
26
+
27
+ <section>
28
+ <article>
29
+ <pre class="prettyprint source linenums"><code>/* globals FileOperationError */
30
+ "use strict";
31
+
32
+ const fileUtils = require('./util/fileUtils');
33
+ const stringUtils = require('./util/stringUtils');
34
+ const jsonUtils = require('./util/jsonUtils');
35
+ const {
36
+ ConfigurationError,
37
+ CsvFormatError,
38
+ JsonValidationError
39
+ } = require('./util/errors');
40
+
41
+ const DEFAULT_FIELD_DELIMITER = ",";
42
+ const QUOTE_CHAR = '"';
43
+ const CRLF = '\r\n';
44
+ const LF = '\n';
45
+ const CR = '\r';
46
+
47
+ /**
48
+ * Main CSV to JSON converter class
49
+ * Provides chainable API for configuring and converting CSV data
50
+ */
51
+ class CsvToJson {
52
+
53
+ /**
54
+ * Enable or disable automatic type formatting for values
55
+ * When enabled, numeric strings are converted to numbers, 'true'/'false' to booleans
56
+ * @param {boolean} active - Whether to format values by type
57
+ * @returns {this} For method chaining
58
+ */
59
+ formatValueByType(active) {
60
+ this.printValueFormatByType = active;
61
+ return this;
62
+ }
63
+
64
+ /**
65
+ * Enable or disable support for RFC 4180 quoted fields
66
+ * When enabled, fields wrapped in double quotes can contain delimiters and newlines
67
+ * @param {boolean} active - Whether to support quoted fields
68
+ * @returns {this} For method chaining
69
+ */
70
+ supportQuotedField(active) {
71
+ this.isSupportQuotedField = active;
72
+ return this;
73
+ }
74
+
75
+ /**
76
+ * Set the field delimiter character
77
+ * @param {string} delimiter - Character(s) to use as field separator (default: ',')
78
+ * @returns {this} For method chaining
79
+ */
80
+ fieldDelimiter(delimiter) {
81
+ this.delimiter = delimiter;
82
+ return this;
83
+ }
84
+
85
+ /**
86
+ * Configure whitespace handling in header field names
87
+ * @param {boolean} active - If true, removes all whitespace from header names; if false, only trims edges
88
+ * @returns {this} For method chaining
89
+ */
90
+ trimHeaderFieldWhiteSpace(active) {
91
+ this.isTrimHeaderFieldWhiteSpace = active;
92
+ return this;
93
+ }
94
+
95
+ /**
96
+ * Set the row index where CSV headers are located
97
+ * @param {number} indexHeaderValue - Zero-based row index containing headers
98
+ * @returns {this} For method chaining
99
+ * @throws {ConfigurationError} If not a valid number
100
+ */
101
+ indexHeader(indexHeaderValue) {
102
+ if (isNaN(indexHeaderValue)) {
103
+ throw ConfigurationError.invalidHeaderIndex(indexHeaderValue);
104
+ }
105
+ this.indexHeaderValue = indexHeaderValue;
106
+ return this;
107
+ }
108
+
109
+
110
+ /**
111
+ * Configure sub-array parsing for special field values
112
+ * Fields bracketed by delimiter and containing separator are parsed into arrays
113
+ * @param {string} delimiter - Bracket character (default: '*')
114
+ * @param {string} separator - Item separator within brackets (default: ',')
115
+ * @returns {this} For method chaining
116
+ * @example
117
+ * // Input: "*val1,val2,val3*"
118
+ * // Output: ["val1", "val2", "val3"]
119
+ * .parseSubArray('*', ',')
120
+ */
121
+ parseSubArray(delimiter = '*',separator = ',') {
122
+ this.parseSubArrayDelimiter = delimiter;
123
+ this.parseSubArraySeparator = separator;
124
+ return this;
125
+ }
126
+
127
+ /**
128
+ * Set file encoding for reading CSV files
129
+ * @param {string} encoding - Node.js supported encoding (e.g., 'utf8', 'latin1', 'ascii')
130
+ * @returns {this} For method chaining
131
+ */
132
+ encoding(encoding){
133
+ this.encoding = encoding;
134
+ return this;
135
+ }
136
+
137
+ /**
138
+ * Sets a mapper function to transform each row after conversion
139
+ * @param {Function} mapperFn - Function that receives (row, index) and returns transformed row or null to filter out
140
+ * @returns {this} - For method chaining
141
+ */
142
+ mapRows(mapperFn) {
143
+ if (typeof mapperFn !== 'function') {
144
+ throw new TypeError('mapperFn must be a function');
145
+ }
146
+ this.rowMapper = mapperFn;
147
+ return this;
148
+ }
149
+
150
+ /**
151
+ * Read a CSV file and write the parsed JSON to an output file
152
+ * @param {string} fileInputName - Path to input CSV file
153
+ * @param {string} fileOutputName - Path to output JSON file
154
+ * @throws {FileOperationError} If file read or write fails
155
+ * @throws {CsvFormatError} If CSV is malformed
156
+ */
157
+ generateJsonFileFromCsv(fileInputName, fileOutputName) {
158
+ let jsonStringified = this.getJsonFromCsvStringified(fileInputName);
159
+ fileUtils.writeFile(jsonStringified, fileOutputName);
160
+ }
161
+
162
+ /**
163
+ * Read a CSV file and return parsed data as stringified JSON
164
+ * @param {string} fileInputName - Path to input CSV file
165
+ * @returns {string} JSON stringified array of objects
166
+ * @throws {FileOperationError} If file read fails
167
+ * @throws {CsvFormatError} If CSV is malformed
168
+ * @throws {JsonValidationError} If JSON generation fails
169
+ * @example
170
+ * const csvToJson = require('convert-csv-to-json');
171
+ * const jsonString = csvToJson.getJsonFromCsvStringified('resource/input.csv');
172
+ * console.log(jsonString);
173
+ */
174
+ getJsonFromCsvStringified(fileInputName) {
175
+ let json = this.getJsonFromCsv(fileInputName);
176
+ let jsonStringified = JSON.stringify(json, undefined, 1);
177
+ jsonUtils.validateJson(jsonStringified);
178
+ return jsonStringified;
179
+ }
180
+
181
+ /**
182
+ * Read a CSV file and return parsed data as JSON array of objects
183
+ * @param {string} fileInputName - Path to input CSV file
184
+ * @returns {Array&lt;object>} Array of objects representing CSV rows
185
+ * @throws {FileOperationError} If file read fails
186
+ * @throws {CsvFormatError} If CSV is malformed
187
+ * @example
188
+ * const csvToJson = require('convert-csv-to-json');
189
+ * const rows = csvToJson.getJsonFromCsv('resource/input.csv');
190
+ * console.log(rows);
191
+ */
192
+ getJsonFromCsv(fileInputName) {
193
+ let parsedCsv = fileUtils.readFile(fileInputName, this.encoding);
194
+ return this.csvToJson(parsedCsv);
195
+ }
196
+
197
+ /**
198
+ * Parse CSV string content and return as JSON array of objects
199
+ * @param {string} csvString - CSV content as string
200
+ * @returns {Array&lt;object>} Array of objects representing CSV rows
201
+ * @throws {CsvFormatError} If CSV is malformed
202
+ * @example
203
+ * const csvToJson = require('convert-csv-to-json');
204
+ * const rows = csvToJson.csvStringToJson('name,age\nAlice,30');
205
+ * console.log(rows); // [{ name: 'Alice', age: '30' }]
206
+ */
207
+ csvStringToJson(csvString) {
208
+ return this.csvToJson(csvString);
209
+ }
210
+
211
+ /**
212
+ * Parse CSV string content and return as stringified JSON
213
+ * @param {string} csvString - CSV content as string
214
+ * @returns {string} JSON stringified array of objects
215
+ * @throws {CsvFormatError} If CSV is malformed
216
+ * @throws {JsonValidationError} If JSON generation fails
217
+ * @example
218
+ * const csvToJson = require('convert-csv-to-json');
219
+ * const jsonString = csvToJson.csvStringToJsonStringified('name,age\nAlice,30');
220
+ * console.log(jsonString);
221
+ */
222
+ csvStringToJsonStringified(csvString) {
223
+ let json = this.csvStringToJson(csvString);
224
+ let jsonStringified = JSON.stringify(json, undefined, 1);
225
+ jsonUtils.validateJson(jsonStringified);
226
+ return jsonStringified;
227
+ }
228
+
229
+ /**
230
+ * Core CSV parsing logic - converts CSV string to JSON array
231
+ * Handles quoted fields per RFC 4180 when configured
232
+ * Applies row mapping and filtering when configured
233
+ * @param {string} parsedCsv - Raw CSV content as string
234
+ * @returns {Array&lt;object>} Array of objects with CSV data
235
+ * @private
236
+ */
237
+ csvToJson(parsedCsv) {
238
+ this.validateInputConfig();
239
+
240
+ // Parse CSV into individual records, respecting quoted fields that may contain newlines
241
+ let records = this.parseRecords(parsedCsv);
242
+
243
+ let fieldDelimiter = this.getFieldDelimiter();
244
+ let index = this.getIndexHeader();
245
+ let headers;
246
+
247
+ // Find the header row
248
+ while (index &lt; records.length) {
249
+ if (this.isSupportQuotedField) {
250
+ headers = this.split(records[index]);
251
+ } else {
252
+ headers = records[index].split(fieldDelimiter);
253
+ }
254
+
255
+ if (stringUtils.hasContent(headers)) {
256
+ break;
257
+ }
258
+ index++;
259
+ }
260
+
261
+ if (!headers) {
262
+ throw CsvFormatError.missingHeader();
263
+ }
264
+
265
+ let jsonResult = [];
266
+ for (let i = (index + 1); i &lt; records.length; i++) {
267
+ let currentLine;
268
+ if (this.isSupportQuotedField) {
269
+ currentLine = this.split(records[i]);
270
+ } else {
271
+ currentLine = records[i].split(fieldDelimiter);
272
+ }
273
+
274
+ if (stringUtils.hasContent(currentLine)) {
275
+ let row = this.buildJsonResult(headers, currentLine);
276
+
277
+ // Apply row mapper if defined
278
+ if (this.rowMapper) {
279
+ row = this.rowMapper(row, i - (index + 1)); // Pass row and 0-based row index
280
+ // If mapper returns null/undefined, skip this row (allows filtering)
281
+ if (row != null) {
282
+ jsonResult.push(row);
283
+ }
284
+ } else {
285
+ jsonResult.push(row);
286
+ }
287
+ }
288
+ }
289
+ return jsonResult;
290
+ }
291
+
292
+ /**
293
+ * Parse CSV content into individual records, respecting quoted fields that may span multiple lines.
294
+ * RFC 4180 compliant parsing - handles quoted fields that may contain newlines.
295
+ * @param {string} csvContent - The raw CSV content
296
+ * @returns {string[]} Array of record strings
297
+ */
298
+ parseRecords(csvContent) {
299
+ let records = [];
300
+ let currentRecord = '';
301
+ let insideQuotes = false;
302
+ let i = 0;
303
+
304
+ while (i &lt; csvContent.length) {
305
+ let char = csvContent[i];
306
+
307
+ // Handle quote characters
308
+ if (char === QUOTE_CHAR) {
309
+ if (insideQuotes &amp;&amp; i + 1 &lt; csvContent.length &amp;&amp; csvContent[i + 1] === QUOTE_CHAR) {
310
+ // Escaped quote: two consecutive quotes = single quote representation
311
+ currentRecord += QUOTE_CHAR + QUOTE_CHAR;
312
+ i += 2;
313
+ } else {
314
+ // Toggle quote state
315
+ insideQuotes = !insideQuotes;
316
+ currentRecord += char;
317
+ i++;
318
+ }
319
+ continue;
320
+ }
321
+
322
+ // Handle line endings (only outside quoted fields)
323
+ if (!insideQuotes) {
324
+ let lineEndingLength = this.getLineEndingLength(csvContent, i);
325
+ if (lineEndingLength > 0) {
326
+ records.push(currentRecord);
327
+ currentRecord = '';
328
+ i += lineEndingLength;
329
+ continue;
330
+ }
331
+ }
332
+
333
+ // Regular character
334
+ currentRecord += char;
335
+ i++;
336
+ }
337
+
338
+ // Add the last record if not empty
339
+ if (currentRecord.length > 0) {
340
+ records.push(currentRecord);
341
+ }
342
+
343
+ // Validate matching quotes
344
+ if (insideQuotes) {
345
+ throw CsvFormatError.mismatchedQuotes('CSV');
346
+ }
347
+
348
+ return records;
349
+ }
350
+
351
+ /**
352
+ * Get the length of line ending at current position (CRLF=2, LF=1, CR=1, or 0)
353
+ * @param {string} content - CSV content
354
+ * @param {number} index - Current index to check
355
+ * @returns {number} Length of line ending (0 if none)
356
+ */
357
+ getLineEndingLength(content, index) {
358
+ if (content.slice(index, index + 2) === CRLF) {
359
+ return 2;
360
+ }
361
+ if (content[index] === LF) {
362
+ return 1;
363
+ }
364
+ if (content[index] === CR &amp;&amp; content[index + 1] !== LF) {
365
+ return 1;
366
+ }
367
+ return 0;
368
+ }
369
+
370
+ /**
371
+ * Get the configured field delimiter, or default if not set
372
+ * @returns {string} Field delimiter character
373
+ * @private
374
+ */
375
+ getFieldDelimiter() {
376
+ if (this.delimiter) {
377
+ return this.delimiter;
378
+ }
379
+ return DEFAULT_FIELD_DELIMITER;
380
+ }
381
+
382
+ /**
383
+ * Get the configured header row index, or default (0) if not set
384
+ * @returns {number} Header row index
385
+ * @private
386
+ */
387
+ getIndexHeader(){
388
+ if(this.indexHeaderValue !== null &amp;&amp; !isNaN(this.indexHeaderValue)){
389
+ return this.indexHeaderValue;
390
+ }
391
+ return 0;
392
+ }
393
+
394
+ /**
395
+ * Build a JSON object from headers and field values
396
+ * Applies type formatting and sub-array parsing as configured
397
+ * @param {string[]} headers - Array of header field names
398
+ * @param {string[]} currentLine - Array of field values
399
+ * @returns {object} JSON object with header names as keys
400
+ * @private
401
+ */
402
+ buildJsonResult(headers, currentLine) {
403
+ let jsonObject = {};
404
+ for (let j = 0; j &lt; headers.length; j++) {
405
+ let propertyName = stringUtils.trimPropertyName(this.isTrimHeaderFieldWhiteSpace, headers[j]);
406
+ let value = currentLine[j];
407
+
408
+ if(this.isParseSubArray(value)){
409
+ value = this.buildJsonSubArray(value);
410
+ }
411
+
412
+ if (this.printValueFormatByType &amp;&amp; !Array.isArray(value)) {
413
+ value = stringUtils.getValueFormatByType(currentLine[j]);
414
+ }
415
+
416
+ jsonObject[propertyName] = value;
417
+ }
418
+ return jsonObject;
419
+ }
420
+
421
+ /**
422
+ * Parse a field value into a sub-array using configured delimiter and separator
423
+ * @param {string} value - Field value to parse
424
+ * @returns {Array&lt;string|number|boolean>} Array of parsed values
425
+ * @private
426
+ */
427
+ buildJsonSubArray(value) {
428
+ let extractedValues = value.substring(
429
+ value.indexOf(this.parseSubArrayDelimiter) + 1,
430
+ value.lastIndexOf(this.parseSubArrayDelimiter)
431
+ );
432
+ extractedValues.trim();
433
+ value = extractedValues.split(this.parseSubArraySeparator);
434
+ if(this.printValueFormatByType){
435
+ for(let i=0; i &lt; value.length; i++){
436
+ value[i] = stringUtils.getValueFormatByType(value[i]);
437
+ }
438
+ }
439
+ return value;
440
+ }
441
+
442
+ /**
443
+ * Check if a field value should be parsed as a sub-array
444
+ * @param {string} value - Field value to check
445
+ * @returns {boolean} True if value is bracketed with sub-array delimiter
446
+ * @private
447
+ */
448
+ isParseSubArray(value){
449
+ if(this.parseSubArrayDelimiter){
450
+ if (value &amp;&amp; (value.indexOf(this.parseSubArrayDelimiter) === 0 &amp;&amp; value.lastIndexOf(this.parseSubArrayDelimiter) === (value.length - 1))) {
451
+ return true;
452
+ }
453
+ }
454
+ return false;
455
+ }
456
+
457
+ /**
458
+ * Validate configuration for conflicts and incompatibilities
459
+ * @throws {ConfigurationError} If incompatible options are set
460
+ * @private
461
+ */
462
+ validateInputConfig(){
463
+ if(this.isSupportQuotedField) {
464
+ if(this.getFieldDelimiter() === '"'){
465
+ throw ConfigurationError.quotedFieldConflict('fieldDelimiter', '"');
466
+ }
467
+ if(this.parseSubArraySeparator === '"'){
468
+ throw ConfigurationError.quotedFieldConflict('parseSubArraySeparator', '"');
469
+ }
470
+ if(this.parseSubArrayDelimiter === '"'){
471
+ throw ConfigurationError.quotedFieldConflict('parseSubArrayDelimiter', '"');
472
+ }
473
+ }
474
+ }
475
+
476
+ /**
477
+ * Check if a line contains quote characters
478
+ * @param {string} line - Line to check
479
+ * @returns {boolean} True if line contains quotes
480
+ * @private
481
+ */
482
+ hasQuotes(line) {
483
+ return line.includes('"');
484
+ }
485
+
486
+ /**
487
+ * Split a CSV record line into fields, respecting quoted fields per RFC 4180.
488
+ * Handles:
489
+ * - Quoted fields with embedded delimiters and newlines
490
+ * - Escaped quotes (double quotes within quoted fields)
491
+ * - Empty quoted fields
492
+ * @param {string} line - A single CSV record line
493
+ * @returns {string[]} Array of field values
494
+ */
495
+ split(line) {
496
+ if (line.length === 0) {
497
+ return [];
498
+ }
499
+
500
+ let fields = [];
501
+ let currentField = '';
502
+ let insideQuotes = false;
503
+ let delimiter = this.getFieldDelimiter();
504
+
505
+ for (let i = 0; i &lt; line.length; i++) {
506
+ let char = line[i];
507
+
508
+ // Handle quote character
509
+ if (char === QUOTE_CHAR) {
510
+ if (this.isEscapedQuote(line, i, insideQuotes)) {
511
+ // Two consecutive quotes inside quoted field = escaped quote
512
+ currentField += QUOTE_CHAR;
513
+ i++; // Skip next quote
514
+ } else if (this.isEmptyQuotedField(line, i, insideQuotes, currentField, delimiter)) {
515
+ // Empty quoted field: "" at field start before delimiter/end
516
+ i++; // Skip closing quote
517
+ } else {
518
+ // Regular quote: toggle quoted state
519
+ insideQuotes = !insideQuotes;
520
+ }
521
+ } else if (char === delimiter &amp;&amp; !insideQuotes) {
522
+ // Delimiter outside quotes marks field boundary
523
+ fields.push(currentField);
524
+ currentField = '';
525
+ } else {
526
+ // Regular character (including embedded newlines in quoted fields)
527
+ currentField += char;
528
+ }
529
+ }
530
+
531
+ // Add final field
532
+ fields.push(currentField);
533
+
534
+ // Validate matching quotes
535
+ if (insideQuotes) {
536
+ throw CsvFormatError.mismatchedQuotes('row');
537
+ }
538
+
539
+ return fields;
540
+ }
541
+
542
+ /**
543
+ * Check if character at index is an escaped quote (double quote)
544
+ * Escaped quotes appear as "" within quoted fields per RFC 4180
545
+ * @param {string} line - Line being parsed
546
+ * @param {number} index - Character index to check
547
+ * @param {boolean} insideQuoted - Whether currently inside a quoted field
548
+ * @returns {boolean} True if character is an escaped quote
549
+ * @private
550
+ */
551
+ isEscapedQuote(line, index, insideQuoted) {
552
+ return insideQuoted &amp;&amp;
553
+ index + 1 &lt; line.length &amp;&amp;
554
+ line[index + 1] === QUOTE_CHAR;
555
+ }
556
+
557
+ /**
558
+ * Check if this is an empty quoted field: "" before delimiter or end of line
559
+ * @param {string} line - Line being parsed
560
+ * @param {number} index - Character index to check
561
+ * @param {boolean} insideQuoted - Whether currently inside a quoted field
562
+ * @param {string} currentField - Current field accumulation
563
+ * @param {string} delimiter - Field delimiter character
564
+ * @returns {boolean} True if this represents an empty quoted field
565
+ * @private
566
+ */
567
+ isEmptyQuotedField(line, index, insideQuoted, currentField, delimiter) {
568
+ if (insideQuoted || currentField !== '' || index + 1 >= line.length) {
569
+ return false;
570
+ }
571
+
572
+ let nextChar = line[index + 1];
573
+ if (nextChar !== QUOTE_CHAR) {
574
+ return false; // Not a quote pair
575
+ }
576
+
577
+ let afterQuotes = index + 2;
578
+ return afterQuotes === line.length || line[afterQuotes] === delimiter;
579
+ }
580
+ }
581
+
582
+ module.exports = new CsvToJson();
583
+ </code></pre>
584
+ </article>
585
+ </section>
586
+
587
+
588
+
589
+
590
+ </div>
591
+
592
+ <nav>
593
+ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="BrowserApi.html">BrowserApi</a></li><li><a href="BrowserApiError.html">BrowserApiError</a></li><li><a href="ConfigurationError.html">ConfigurationError</a></li><li><a href="CsvFormatError.html">CsvFormatError</a></li><li><a href="CsvParsingError.html">CsvParsingError</a></li><li><a href="CsvToJson.html">CsvToJson</a></li><li><a href="CsvToJsonAsync.html">CsvToJsonAsync</a></li><li><a href="FileOperationError.html">FileOperationError</a></li><li><a href="FileUtils.html">FileUtils</a></li><li><a href="InputValidationError.html">InputValidationError</a></li><li><a href="JsonUtil.html">JsonUtil</a></li><li><a href="JsonValidationError.html">JsonValidationError</a></li></ul><h3>Global</h3><ul><li><a href="global.html#asciiEncoding">asciiEncoding</a></li><li><a href="global.html#base64Encoding">base64Encoding</a></li><li><a href="global.html#browser">browser</a></li><li><a href="global.html#csvStringToJson">csvStringToJson</a></li><li><a href="global.html#csvStringToJsonStringified">csvStringToJsonStringified</a></li><li><a href="global.html#csvToJsonAsync">csvToJsonAsync</a></li><li><a href="global.html#customEncoding">customEncoding</a></li><li><a href="global.html#fieldDelimiter">fieldDelimiter</a></li><li><a href="global.html#formatValueByType">formatValueByType</a></li><li><a href="global.html#generateJsonFileFromCsv">generateJsonFileFromCsv</a></li><li><a href="global.html#getJsonFromCsv">getJsonFromCsv</a></li><li><a href="global.html#hexEncoding">hexEncoding</a></li><li><a href="global.html#indexHeader">indexHeader</a></li><li><a href="global.html#latin1Encoding">latin1Encoding</a></li><li><a href="global.html#mapRows">mapRows</a></li><li><a href="global.html#parseSubArray">parseSubArray</a></li><li><a href="global.html#supportQuotedField">supportQuotedField</a></li><li><a href="global.html#trimHeaderFieldWhiteSpace">trimHeaderFieldWhiteSpace</a></li><li><a href="global.html#ucs2Encoding">ucs2Encoding</a></li><li><a href="global.html#utf16leEncoding">utf16leEncoding</a></li><li><a href="global.html#utf8Encoding">utf8Encoding</a></li></ul>
594
+ </nav>
595
+
596
+ <br class="clear">
597
+
598
+ <footer>
599
+ Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.5</a> on Fri Mar 20 2026 14:48:29 GMT+0000 (Coordinated Universal Time)
600
+ </footer>
601
+
602
+ <script> prettyPrint(); </script>
603
+ <script src="scripts/linenumber.js"> </script>
604
+ </body>
605
+ </html>