convert-csv-to-json 4.14.0 → 4.16.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 (38) hide show
  1. package/.github/workflows/ci-cd.yml +1 -1
  2. package/docs/.nojekyll +0 -0
  3. package/docs/README.md +44 -0
  4. package/docs/demo.bundle.js +1708 -0
  5. package/docs/index.html +536 -0
  6. package/eslint.config.js +1 -1
  7. package/package.json +5 -1
  8. package/docs/api/BrowserApi.html +0 -2662
  9. package/docs/api/BrowserApiError.html +0 -669
  10. package/docs/api/ConfigurationError.html +0 -745
  11. package/docs/api/CsvFormatError.html +0 -677
  12. package/docs/api/CsvParsingError.html +0 -511
  13. package/docs/api/CsvToJson.html +0 -3367
  14. package/docs/api/CsvToJsonAsync.html +0 -2880
  15. package/docs/api/FileOperationError.html +0 -382
  16. package/docs/api/FileUtils.html +0 -1150
  17. package/docs/api/InputValidationError.html +0 -407
  18. package/docs/api/JsonUtil.html +0 -452
  19. package/docs/api/JsonValidationError.html +0 -357
  20. package/docs/api/StringUtils.html +0 -833
  21. package/docs/api/global.html +0 -3498
  22. package/docs/api/index.html +0 -414
  23. package/docs/api/index.js.html +0 -447
  24. package/docs/api/scripts/app.min.js +0 -1
  25. package/docs/api/scripts/linenumber.js +0 -26
  26. package/docs/api/scripts/search.js +0 -39
  27. package/docs/api/src_browserApi.js.html +0 -362
  28. package/docs/api/src_csvToJson.js.html +0 -696
  29. package/docs/api/src_csvToJsonAsync.js.html +0 -335
  30. package/docs/api/src_util_errors.js.html +0 -472
  31. package/docs/api/src_util_fileUtils.js.html +0 -238
  32. package/docs/api/src_util_jsonUtils.js.html +0 -166
  33. package/docs/api/src_util_stringUtils.js.html +0 -306
  34. package/docs/api/styles/app.min.css +0 -1
  35. package/docs/api/styles/iframe.css +0 -13
  36. package/docs/api/styles/prettify-jsdoc.css +0 -111
  37. package/docs/api/styles/prettify-tomorrow.css +0 -132
  38. package/docs/api/styles/reset.css +0 -44
@@ -1,696 +0,0 @@
1
-
2
-
3
- <!DOCTYPE html>
4
- <html lang="en">
5
-
6
- <head>
7
-
8
- <meta charset="utf-8">
9
- <meta name="viewport" content="width=device-width, initial-scale=1">
10
- <title> src/csvToJson.js</title>
11
-
12
- <script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js"></script>
13
- <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
14
- <script src="./build/entry.js"></script>
15
- <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
16
- <!--[if lt IE 9]>
17
- <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
18
- <![endif]-->
19
- <link href="https://fonts.googleapis.com/css?family=Roboto:100,400,700|Inconsolata,700" rel="stylesheet">
20
- <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
21
- <link type="text/css" rel="stylesheet" href="https://jmblog.github.io/color-themes-for-google-code-prettify/themes/tomorrow-night.min.css">
22
- <link type="text/css" rel="stylesheet" href="styles/app.min.css">
23
- <link type="text/css" rel="stylesheet" href="styles/iframe.css">
24
- <link type="text/css" rel="stylesheet" href="./node_modules/better-docs/css/better-docs.css">
25
- <script async defer src="https://buttons.github.io/buttons.js"></script>
26
-
27
-
28
- </head>
29
-
30
-
31
-
32
- <body class="layout small-header">
33
- <div id="stickyNavbarOverlay"></div>
34
-
35
-
36
- <div class="top-nav">
37
- <div class="inner">
38
- <a id="hamburger" role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
39
- <span aria-hidden="true"></span>
40
- <span aria-hidden="true"></span>
41
- <span aria-hidden="true"></span>
42
- </a>
43
- <div class="logo">
44
-
45
- <a class="image" href="index.html">
46
- <img src="https://raw.githubusercontent.com/iuccio/CSVtoJSON/master/CSVtoJSON.png" alt="logo">
47
- </a>
48
-
49
-
50
- <a href="index.html">
51
- <h1 class="navbar-item">Convert CSV to JSON</h1>
52
- </a>
53
-
54
- </div>
55
- <div class="menu">
56
-
57
- <div class="navigation">
58
- <a
59
- href="index.html"
60
- class="link"
61
- >
62
- Documentation
63
- </a>
64
-
65
-
66
-
67
- <a
68
- class="link user-link "
69
- href="https://github.com/iuccio/CSVtoJSON"
70
- >
71
- GitHub
72
- </a>
73
-
74
- <a
75
- class="link user-link "
76
- href="https://www.npmjs.com/package/convert-csv-to-json"
77
- >
78
- NPM
79
- </a>
80
-
81
-
82
-
83
- </div>
84
- </div>
85
- </div>
86
- </div>
87
- <div id="main">
88
- <div
89
- class="sidebar "
90
- id="sidebarNav"
91
- >
92
-
93
- <nav>
94
-
95
- <h2><a href="index.html">Documentation</a></h2><div class="category"></div><div class="category"><h2>1-Core API</h2><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#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></div><div class="category"><h2>2-Sync</h2><h3>Classes</h3><ul><li><a href="CsvToJson.html">CsvToJson</a></li></ul></div><div class="category"><h2>3-Async</h2><h3>Classes</h3><ul><li><a href="CsvToJsonAsync.html">CsvToJsonAsync</a></li></ul></div><div class="category"><h2>4-Browser</h2><h3>Classes</h3><ul><li><a href="BrowserApi.html">BrowserApi</a></li></ul></div><div class="category"><h2>Error Classes</h2><h3>Classes</h3><ul><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="FileOperationError.html">FileOperationError</a></li><li><a href="InputValidationError.html">InputValidationError</a></li><li><a href="JsonValidationError.html">JsonValidationError</a></li></ul></div><div class="category"><h2>Utilities</h2><h3>Classes</h3><ul><li><a href="FileUtils.html">FileUtils</a></li><li><a href="JsonUtil.html">JsonUtil</a></li><li><a href="StringUtils.html">StringUtils</a></li></ul></div>
96
-
97
- </nav>
98
- </div>
99
- <div class="core" id="main-content-wrapper">
100
- <div class="content">
101
- <header class="page-title">
102
- <p>Source</p>
103
- <h1>src/csvToJson.js</h1>
104
- </header>
105
-
106
-
107
-
108
-
109
-
110
- <section>
111
- <article>
112
- <pre class="prettyprint source linenums"><code>/* globals FileOperationError */
113
- "use strict";
114
-
115
- const fileUtils = require('./util/fileUtils');
116
- const stringUtils = require('./util/stringUtils');
117
- const jsonUtils = require('./util/jsonUtils');
118
- const {
119
- ConfigurationError,
120
- CsvFormatError,
121
- JsonValidationError
122
- } = require('./util/errors');
123
-
124
- const DEFAULT_FIELD_DELIMITER = ",";
125
- const QUOTE_CHAR = '"';
126
- const CRLF = '\r\n';
127
- const LF = '\n';
128
- const CR = '\r';
129
-
130
- /**
131
- * Main CSV to JSON converter class
132
- * Provides chainable API for configuring and converting CSV data
133
- * @category 2-Sync
134
- */
135
- class CsvToJson {
136
-
137
- /**
138
- * Enable or disable automatic type formatting for values
139
- * When enabled, numeric strings are converted to numbers, 'true'/'false' to booleans
140
- * @param {boolean} active - Whether to format values by type
141
- * @returns {this} For method chaining
142
- */
143
- formatValueByType(active) {
144
- this.printValueFormatByType = active;
145
- return this;
146
- }
147
-
148
- /**
149
- * Enable or disable support for RFC 4180 quoted fields
150
- * When enabled, fields wrapped in double quotes can contain delimiters and newlines
151
- * @param {boolean} active - Whether to support quoted fields
152
- * @returns {this} For method chaining
153
- */
154
- supportQuotedField(active) {
155
- this.isSupportQuotedField = active;
156
- return this;
157
- }
158
-
159
- /**
160
- * Set the field delimiter character
161
- * @param {string} delimiter - Character(s) to use as field separator (default: ',')
162
- * @returns {this} For method chaining
163
- */
164
- fieldDelimiter(delimiter) {
165
- this.delimiter = delimiter;
166
- return this;
167
- }
168
-
169
- /**
170
- * Configure whitespace handling in header field names
171
- * @param {boolean} active - If true, removes all whitespace from header names; if false, only trims edges
172
- * @returns {this} For method chaining
173
- */
174
- trimHeaderFieldWhiteSpace(active) {
175
- this.isTrimHeaderFieldWhiteSpace = active;
176
- return this;
177
- }
178
-
179
- /**
180
- * Set the row index where CSV headers are located
181
- * @param {number} indexHeaderValue - Zero-based row index containing headers
182
- * @returns {this} For method chaining
183
- * @throws {ConfigurationError} If not a valid number
184
- */
185
- indexHeader(indexHeaderValue) {
186
- if (isNaN(indexHeaderValue)) {
187
- throw ConfigurationError.invalidHeaderIndex(indexHeaderValue);
188
- }
189
- this.indexHeaderValue = indexHeaderValue;
190
- return this;
191
- }
192
-
193
-
194
- /**
195
- * Configure sub-array parsing for special field values
196
- * Fields bracketed by delimiter and containing separator are parsed into arrays
197
- * @param {string} delimiter - Bracket character (default: '*')
198
- * @param {string} separator - Item separator within brackets (default: ',')
199
- * @returns {this} For method chaining
200
- * @example
201
- * // Input: "*val1,val2,val3*"
202
- * // Output: ["val1", "val2", "val3"]
203
- * .parseSubArray('*', ',')
204
- */
205
- parseSubArray(delimiter = '*',separator = ',') {
206
- this.parseSubArrayDelimiter = delimiter;
207
- this.parseSubArraySeparator = separator;
208
- return this;
209
- }
210
-
211
- /**
212
- * Set file encoding for reading CSV files
213
- * @param {string} encoding - Node.js supported encoding (e.g., 'utf8', 'latin1', 'ascii')
214
- * @returns {this} For method chaining
215
- */
216
- encoding(encoding){
217
- this.encoding = encoding;
218
- return this;
219
- }
220
-
221
- /**
222
- * Sets a mapper function to transform each row after conversion
223
- * @param {function(object, number): (object|null)} mapperFn - Function that receives (row, index) and returns transformed row or null to filter out
224
- * @returns {this} For method chaining
225
- */
226
- mapRows(mapperFn) {
227
- if (typeof mapperFn !== 'function') {
228
- throw new TypeError('mapperFn must be a function');
229
- }
230
- this.rowMapper = mapperFn;
231
- return this;
232
- }
233
-
234
- /**
235
- * Read a CSV file and write the parsed JSON to an output file
236
- * @param {string} fileInputName - Path to input CSV file
237
- * @param {string} fileOutputName - Path to output JSON file
238
- * @throws {FileOperationError} If file read or write fails
239
- * @throws {CsvFormatError} If CSV is malformed
240
- */
241
- generateJsonFileFromCsv(fileInputName, fileOutputName) {
242
- let jsonStringified = this.getJsonFromCsvStringified(fileInputName);
243
- fileUtils.writeFile(jsonStringified, fileOutputName);
244
- }
245
-
246
- /**
247
- * Read a CSV file and return parsed data as stringified JSON
248
- * @param {string} fileInputName - Path to input CSV file
249
- * @returns {string} JSON stringified array of objects
250
- * @throws {FileOperationError} If file read fails
251
- * @throws {CsvFormatError} If CSV is malformed
252
- * @throws {JsonValidationError} If JSON generation fails
253
- * @example
254
- * const csvToJson = require('convert-csv-to-json');
255
- * const jsonString = csvToJson.getJsonFromCsvStringified('resource/input.csv');
256
- * console.log(jsonString);
257
- */
258
- getJsonFromCsvStringified(fileInputName) {
259
- let json = this.getJsonFromCsv(fileInputName);
260
- let jsonStringified = JSON.stringify(json, undefined, 1);
261
- jsonUtils.validateJson(jsonStringified);
262
- return jsonStringified;
263
- }
264
-
265
- /**
266
- * Read a CSV file and return parsed data as JSON array of objects
267
- * @param {string} fileInputName - Path to input CSV file
268
- * @returns {Array&lt;object>} Array of objects representing CSV rows
269
- * @throws {FileOperationError} If file read fails
270
- * @throws {CsvFormatError} If CSV is malformed
271
- * @example
272
- * const csvToJson = require('convert-csv-to-json');
273
- * const rows = csvToJson.getJsonFromCsv('resource/input.csv');
274
- * console.log(rows);
275
- */
276
- getJsonFromCsv(fileInputName) {
277
- let parsedCsv = fileUtils.readFile(fileInputName, this.encoding);
278
- return this.csvToJson(parsedCsv);
279
- }
280
-
281
- /**
282
- * Parse CSV string content and return as JSON array of objects
283
- * @param {string} csvString - CSV content as string
284
- * @returns {Array&lt;object>} Array of objects representing CSV rows
285
- * @throws {CsvFormatError} If CSV is malformed
286
- * @example
287
- * const csvToJson = require('convert-csv-to-json');
288
- * const rows = csvToJson.csvStringToJson('name,age\nAlice,30');
289
- * console.log(rows); // [{ name: 'Alice', age: '30' }]
290
- */
291
- csvStringToJson(csvString) {
292
- return this.csvToJson(csvString);
293
- }
294
-
295
- /**
296
- * Parse CSV string content and return as stringified JSON
297
- * @param {string} csvString - CSV content as string
298
- * @returns {string} JSON stringified array of objects
299
- * @throws {CsvFormatError} If CSV is malformed
300
- * @throws {JsonValidationError} If JSON generation fails
301
- * @example
302
- * const csvToJson = require('convert-csv-to-json');
303
- * const jsonString = csvToJson.csvStringToJsonStringified('name,age\nAlice,30');
304
- * console.log(jsonString);
305
- */
306
- csvStringToJsonStringified(csvString) {
307
- let json = this.csvStringToJson(csvString);
308
- let jsonStringified = JSON.stringify(json, undefined, 1);
309
- jsonUtils.validateJson(jsonStringified);
310
- return jsonStringified;
311
- }
312
-
313
- /**
314
- * Core CSV parsing logic - converts CSV string to JSON array
315
- * Handles quoted fields per RFC 4180 when configured
316
- * Applies row mapping and filtering when configured
317
- * @param {string} parsedCsv - Raw CSV content as string
318
- * @returns {Array&lt;object>} Array of objects with CSV data
319
- * @private
320
- */
321
- csvToJson(parsedCsv) {
322
- this.validateInputConfig();
323
-
324
- // Parse CSV into individual records, respecting quoted fields that may contain newlines
325
- let records = this.parseRecords(parsedCsv);
326
-
327
- let fieldDelimiter = this.getFieldDelimiter();
328
- let index = this.getIndexHeader();
329
- let headers;
330
-
331
- // Find the header row
332
- while (index &lt; records.length) {
333
- if (this.isSupportQuotedField) {
334
- headers = this.split(records[index]);
335
- } else {
336
- headers = records[index].split(fieldDelimiter);
337
- }
338
-
339
- if (stringUtils.hasContent(headers)) {
340
- break;
341
- }
342
- index++;
343
- }
344
-
345
- if (!headers) {
346
- throw CsvFormatError.missingHeader();
347
- }
348
-
349
- let jsonResult = [];
350
- for (let i = (index + 1); i &lt; records.length; i++) {
351
- let currentLine;
352
- if (this.isSupportQuotedField) {
353
- currentLine = this.split(records[i]);
354
- } else {
355
- currentLine = records[i].split(fieldDelimiter);
356
- }
357
-
358
- if (stringUtils.hasContent(currentLine)) {
359
- let row = this.buildJsonResult(headers, currentLine);
360
-
361
- // Apply row mapper if defined
362
- if (this.rowMapper) {
363
- row = this.rowMapper(row, i - (index + 1)); // Pass row and 0-based row index
364
- // If mapper returns null/undefined, skip this row (allows filtering)
365
- if (row != null) {
366
- jsonResult.push(row);
367
- }
368
- } else {
369
- jsonResult.push(row);
370
- }
371
- }
372
- }
373
- return jsonResult;
374
- }
375
-
376
- /**
377
- * Parse CSV content into individual records, respecting quoted fields that may span multiple lines.
378
- * RFC 4180 compliant parsing - handles quoted fields that may contain newlines.
379
- * @param {string} csvContent - The raw CSV content
380
- * @returns {string[]} Array of record strings
381
- */
382
- parseRecords(csvContent) {
383
- let records = [];
384
- let currentRecord = '';
385
- let insideQuotes = false;
386
- let i = 0;
387
-
388
- while (i &lt; csvContent.length) {
389
- let char = csvContent[i];
390
-
391
- // Handle quote characters
392
- if (char === QUOTE_CHAR) {
393
- if (insideQuotes &amp;&amp; i + 1 &lt; csvContent.length &amp;&amp; csvContent[i + 1] === QUOTE_CHAR) {
394
- // Escaped quote: two consecutive quotes = single quote representation
395
- currentRecord += QUOTE_CHAR + QUOTE_CHAR;
396
- i += 2;
397
- } else {
398
- // Toggle quote state
399
- insideQuotes = !insideQuotes;
400
- currentRecord += char;
401
- i++;
402
- }
403
- continue;
404
- }
405
-
406
- // Handle line endings (only outside quoted fields)
407
- if (!insideQuotes) {
408
- let lineEndingLength = this.getLineEndingLength(csvContent, i);
409
- if (lineEndingLength > 0) {
410
- records.push(currentRecord);
411
- currentRecord = '';
412
- i += lineEndingLength;
413
- continue;
414
- }
415
- }
416
-
417
- // Regular character
418
- currentRecord += char;
419
- i++;
420
- }
421
-
422
- // Add the last record if not empty
423
- if (currentRecord.length > 0) {
424
- records.push(currentRecord);
425
- }
426
-
427
- // Validate matching quotes
428
- if (insideQuotes) {
429
- throw CsvFormatError.mismatchedQuotes('CSV');
430
- }
431
-
432
- return records;
433
- }
434
-
435
- /**
436
- * Get the length of line ending at current position (CRLF=2, LF=1, CR=1, or 0)
437
- * @param {string} content - CSV content
438
- * @param {number} index - Current index to check
439
- * @returns {number} Length of line ending (0 if none)
440
- */
441
- getLineEndingLength(content, index) {
442
- if (content.slice(index, index + 2) === CRLF) {
443
- return 2;
444
- }
445
- if (content[index] === LF) {
446
- return 1;
447
- }
448
- if (content[index] === CR &amp;&amp; content[index + 1] !== LF) {
449
- return 1;
450
- }
451
- return 0;
452
- }
453
-
454
- /**
455
- * Get the configured field delimiter, or default if not set
456
- * @returns {string} Field delimiter character
457
- * @private
458
- */
459
- getFieldDelimiter() {
460
- if (this.delimiter) {
461
- return this.delimiter;
462
- }
463
- return DEFAULT_FIELD_DELIMITER;
464
- }
465
-
466
- /**
467
- * Get the configured header row index, or default (0) if not set
468
- * @returns {number} Header row index
469
- * @private
470
- */
471
- getIndexHeader(){
472
- if(this.indexHeaderValue !== null &amp;&amp; !isNaN(this.indexHeaderValue)){
473
- return this.indexHeaderValue;
474
- }
475
- return 0;
476
- }
477
-
478
- /**
479
- * Build a JSON object from headers and field values
480
- * Applies type formatting and sub-array parsing as configured
481
- * @param {string[]} headers - Array of header field names
482
- * @param {string[]} currentLine - Array of field values
483
- * @returns {object} JSON object with header names as keys
484
- * @private
485
- */
486
- buildJsonResult(headers, currentLine) {
487
- let jsonObject = {};
488
- for (let j = 0; j &lt; headers.length; j++) {
489
- let propertyName = stringUtils.trimPropertyName(this.isTrimHeaderFieldWhiteSpace, headers[j]);
490
- let value = currentLine[j];
491
-
492
- if(this.isParseSubArray(value)){
493
- value = this.buildJsonSubArray(value);
494
- }
495
-
496
- if (this.printValueFormatByType &amp;&amp; !Array.isArray(value)) {
497
- value = stringUtils.getValueFormatByType(currentLine[j]);
498
- }
499
-
500
- jsonObject[propertyName] = value;
501
- }
502
- return jsonObject;
503
- }
504
-
505
- /**
506
- * Parse a field value into a sub-array using configured delimiter and separator
507
- * @param {string} value - Field value to parse
508
- * @returns {Array&lt;string|number|boolean>} Array of parsed values
509
- * @private
510
- */
511
- buildJsonSubArray(value) {
512
- let extractedValues = value.substring(
513
- value.indexOf(this.parseSubArrayDelimiter) + 1,
514
- value.lastIndexOf(this.parseSubArrayDelimiter)
515
- );
516
- extractedValues.trim();
517
- value = extractedValues.split(this.parseSubArraySeparator);
518
- if(this.printValueFormatByType){
519
- for(let i=0; i &lt; value.length; i++){
520
- value[i] = stringUtils.getValueFormatByType(value[i]);
521
- }
522
- }
523
- return value;
524
- }
525
-
526
- /**
527
- * Check if a field value should be parsed as a sub-array
528
- * @param {string} value - Field value to check
529
- * @returns {boolean} True if value is bracketed with sub-array delimiter
530
- * @private
531
- */
532
- isParseSubArray(value){
533
- if(this.parseSubArrayDelimiter){
534
- if (value &amp;&amp; (value.indexOf(this.parseSubArrayDelimiter) === 0 &amp;&amp; value.lastIndexOf(this.parseSubArrayDelimiter) === (value.length - 1))) {
535
- return true;
536
- }
537
- }
538
- return false;
539
- }
540
-
541
- /**
542
- * Validate configuration for conflicts and incompatibilities
543
- * @throws {ConfigurationError} If incompatible options are set
544
- * @private
545
- */
546
- validateInputConfig(){
547
- if(this.isSupportQuotedField) {
548
- if(this.getFieldDelimiter() === '"'){
549
- throw ConfigurationError.quotedFieldConflict('fieldDelimiter', '"');
550
- }
551
- if(this.parseSubArraySeparator === '"'){
552
- throw ConfigurationError.quotedFieldConflict('parseSubArraySeparator', '"');
553
- }
554
- if(this.parseSubArrayDelimiter === '"'){
555
- throw ConfigurationError.quotedFieldConflict('parseSubArrayDelimiter', '"');
556
- }
557
- }
558
- }
559
-
560
- /**
561
- * Check if a line contains quote characters
562
- * @param {string} line - Line to check
563
- * @returns {boolean} True if line contains quotes
564
- * @private
565
- */
566
- hasQuotes(line) {
567
- return line.includes('"');
568
- }
569
-
570
- /**
571
- * Split a CSV record line into fields, respecting quoted fields per RFC 4180.
572
- * Handles:
573
- * - Quoted fields with embedded delimiters and newlines
574
- * - Escaped quotes (double quotes within quoted fields)
575
- * - Empty quoted fields
576
- * @param {string} line - A single CSV record line
577
- * @returns {string[]} Array of field values
578
- */
579
- split(line) {
580
- if (line.length === 0) {
581
- return [];
582
- }
583
-
584
- let fields = [];
585
- let currentField = '';
586
- let insideQuotes = false;
587
- let delimiter = this.getFieldDelimiter();
588
-
589
- for (let i = 0; i &lt; line.length; i++) {
590
- let char = line[i];
591
-
592
- // Handle quote character
593
- if (char === QUOTE_CHAR) {
594
- if (this.isEscapedQuote(line, i, insideQuotes)) {
595
- // Two consecutive quotes inside quoted field = escaped quote
596
- currentField += QUOTE_CHAR;
597
- i++; // Skip next quote
598
- } else if (this.isEmptyQuotedField(line, i, insideQuotes, currentField, delimiter)) {
599
- // Empty quoted field: "" at field start before delimiter/end
600
- i++; // Skip closing quote
601
- } else {
602
- // Regular quote: toggle quoted state
603
- insideQuotes = !insideQuotes;
604
- }
605
- } else if (char === delimiter &amp;&amp; !insideQuotes) {
606
- // Delimiter outside quotes marks field boundary
607
- fields.push(currentField);
608
- currentField = '';
609
- } else {
610
- // Regular character (including embedded newlines in quoted fields)
611
- currentField += char;
612
- }
613
- }
614
-
615
- // Add final field
616
- fields.push(currentField);
617
-
618
- // Validate matching quotes
619
- if (insideQuotes) {
620
- throw CsvFormatError.mismatchedQuotes('row');
621
- }
622
-
623
- return fields;
624
- }
625
-
626
- /**
627
- * Check if character at index is an escaped quote (double quote)
628
- * Escaped quotes appear as "" within quoted fields per RFC 4180
629
- * @param {string} line - Line being parsed
630
- * @param {number} index - Character index to check
631
- * @param {boolean} insideQuoted - Whether currently inside a quoted field
632
- * @returns {boolean} True if character is an escaped quote
633
- * @private
634
- */
635
- isEscapedQuote(line, index, insideQuoted) {
636
- return insideQuoted &amp;&amp;
637
- index + 1 &lt; line.length &amp;&amp;
638
- line[index + 1] === QUOTE_CHAR;
639
- }
640
-
641
- /**
642
- * Check if this is an empty quoted field: "" before delimiter or end of line
643
- * @param {string} line - Line being parsed
644
- * @param {number} index - Character index to check
645
- * @param {boolean} insideQuoted - Whether currently inside a quoted field
646
- * @param {string} currentField - Current field accumulation
647
- * @param {string} delimiter - Field delimiter character
648
- * @returns {boolean} True if this represents an empty quoted field
649
- * @private
650
- */
651
- isEmptyQuotedField(line, index, insideQuoted, currentField, delimiter) {
652
- if (insideQuoted || currentField !== '' || index + 1 >= line.length) {
653
- return false;
654
- }
655
-
656
- let nextChar = line[index + 1];
657
- if (nextChar !== QUOTE_CHAR) {
658
- return false; // Not a quote pair
659
- }
660
-
661
- let afterQuotes = index + 2;
662
- return afterQuotes === line.length || line[afterQuotes] === delimiter;
663
- }
664
- }
665
-
666
- module.exports = new CsvToJson();
667
- </code></pre>
668
- </article>
669
- </section>
670
-
671
-
672
-
673
-
674
- </div>
675
-
676
- <footer class="footer">
677
- <div class="content has-text-centered">
678
- <p>Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a></p>
679
- <p class="sidebar-created-by">
680
- <a href="https://github.com/SoftwareBrothers/better-docs" target="_blank">BetterDocs theme</a> provided with <i class="fas fa-heart"></i> by
681
- <a href="http://softwarebrothers.co" target="_blank">SoftwareBrothers - JavaScript Development Agency</a>
682
- </p>
683
- </div>
684
- </footer>
685
-
686
- </div>
687
- <div id="side-nav" class="side-nav">
688
- </div>
689
- </div>
690
- <script src="scripts/app.min.js"></script>
691
- <script>PR.prettyPrint();</script>
692
- <script src="scripts/linenumber.js"> </script>
693
-
694
-
695
- </body>
696
- </html>