csv-parse
Advanced tools
Comparing version 4.15.4 to 4.16.0
@@ -6,4 +6,6 @@ | ||
Please join and contribute: | ||
Below is the list of upgrades we are considering for the next major release. The majority of them are just simple refactoring. They will however introduce backward incompatibilities. | ||
We invite you to join and contribute but create an issue before engaging any work. Some tasks are scheduled for another time or might depends on another one. | ||
* `skip_lines_with_empty_values`: rename to skip_records_with_empty_values (easy) | ||
@@ -16,7 +18,16 @@ * `skip_lines_with_error`: rename to skip_records_with_error (easy) | ||
* encoding: new encoding_input and encoding_output options (medium) | ||
* context: isolate info properties at context root (easy) | ||
* context: merge record, raw, context, info, error into a single object (medium) | ||
* relax_column_count: rename INCONSISTENT_RECORD_LENGTH to RECORD_INCONSISTENT_FIELDS_LENGTH (easy) | ||
* relax_column_count: rename RECORD_DONT_MATCH_COLUMNS_LENGTH to RECORD_INCONSISTENT_COLUMNS (easy) | ||
* `columns_duplicates_to_array`: this is just too long but I don't have much insipiration for a better name | ||
* `relax_column_count`: rename INCONSISTENT_RECORD_LENGTH to RECORD_INCONSISTENT_FIELDS_LENGTH (easy) | ||
* `relax_column_count`: rename RECORD_DONT_MATCH_COLUMNS_LENGTH to RECORD_INCONSISTENT_COLUMNS (easy) | ||
* `info`: remove the `parser.info` object and move its properties to `state` | ||
* `info`: rename the `info` related properties and functions to `context` | ||
## Version 4.16.0 | ||
* fix: info print the number of encountered line when emited | ||
* feat: cast expose context.empty_lines | ||
* fix: handle empty column names properly | ||
* feat: enforce usage of columns with columns_duplicates_to_array | ||
* fix: update error message with invalid column type | ||
## Version 4.15.4 | ||
@@ -23,0 +34,0 @@ |
@@ -180,3 +180,3 @@ "use strict"; | ||
} else { | ||
throw new CsvError('CSV_INVALID_OPTION_COLUMNS', ['Invalid option columns:', 'expect an object, a function or true,', "got ".concat(JSON.stringify(options.columns))], options); | ||
throw new CsvError('CSV_INVALID_OPTION_COLUMNS', ['Invalid option columns:', 'expect an array, a function or true,', "got ".concat(JSON.stringify(options.columns))], options); | ||
} // Normalize option `columns_duplicates_to_array` | ||
@@ -189,2 +189,4 @@ | ||
throw new CsvError('CSV_INVALID_OPTION_COLUMNS_DUPLICATES_TO_ARRAY', ['Invalid option columns_duplicates_to_array:', 'expect an boolean,', "got ".concat(JSON.stringify(options.columns_duplicates_to_array))], options); | ||
} else if (options.columns === false) { | ||
throw new CsvError('CSV_INVALID_OPTION_COLUMNS_DUPLICATES_TO_ARRAY', ['Invalid option columns_duplicates_to_array:', 'the `columns` mode must be activated.'], options); | ||
} // Normalize option `comment` | ||
@@ -515,6 +517,6 @@ | ||
escapeIsQuote: Buffer.isBuffer(options.escape) && Buffer.isBuffer(options.quote) && Buffer.compare(options.escape, options.quote) === 0, | ||
expectedRecordLength: options.columns === null ? 0 : options.columns.length, | ||
// columns can be `false`, `true`, `Array` | ||
expectedRecordLength: Array.isArray(options.columns) ? options.columns.length : undefined, | ||
field: new ResizeableBuffer(20), | ||
firstLineToHeaders: fnFirstLineToHeaders, | ||
info: Object.assign({}, this.info), | ||
needMoreDataSize: Math.max.apply(Math, [// Skip if the remaining buffer smaller than comment | ||
@@ -577,3 +579,2 @@ options.comment !== null ? options.comment.length : 0].concat(_toConsumableArray(options.delimiter.map(function (delimiter) { | ||
from_line = _this$options.from_line, | ||
info = _this$options.info, | ||
ltrim = _this$options.ltrim, | ||
@@ -651,7 +652,2 @@ max_record_size = _this$options.max_record_size, | ||
this.info.lines++; | ||
if (info === true && this.state.record.length === 0 && this.state.field.length === 0 && this.state.wasQuoting === false) { | ||
this.state.info = Object.assign({}, this.info); | ||
} | ||
this.state.wasRowDelimiter = false; | ||
@@ -730,3 +726,3 @@ } | ||
} else if (relax === false) { | ||
var err = this.__error(new CsvError('CSV_INVALID_CLOSING_QUOTE', ['Invalid Closing Quote:', "got \"".concat(String.fromCharCode(nextChr), "\""), "at line ".concat(this.info.lines), 'instead of delimiter, record delimiter, trimable character', '(if activated) or comment'], this.options, this.__context())); | ||
var err = this.__error(new CsvError('CSV_INVALID_CLOSING_QUOTE', ['Invalid Closing Quote:', "got \"".concat(String.fromCharCode(nextChr), "\""), "at line ".concat(this.info.lines), 'instead of delimiter, record delimiter, trimable character', '(if activated) or comment'], this.options, this.__infoField())); | ||
@@ -744,3 +740,3 @@ if (err !== undefined) return err; | ||
if (relax === false) { | ||
var _err = this.__error(new CsvError('INVALID_OPENING_QUOTE', ['Invalid Opening Quote:', "a quote is found inside a field at line ".concat(this.info.lines)], this.options, this.__context(), { | ||
var _err = this.__error(new CsvError('INVALID_OPENING_QUOTE', ['Invalid Opening Quote:', "a quote is found inside a field at line ".concat(this.info.lines)], this.options, this.__infoField(), { | ||
field: this.state.field | ||
@@ -833,3 +829,3 @@ })); | ||
if (max_record_size !== 0 && this.state.record_length + this.state.field.length > max_record_size) { | ||
var _err2 = this.__error(new CsvError('CSV_MAX_RECORD_SIZE', ['Max Record Size:', 'record exceed the maximum number of tolerated bytes', "of ".concat(max_record_size), "at line ".concat(this.info.lines)], this.options, this.__context())); | ||
var _err2 = this.__error(new CsvError('CSV_MAX_RECORD_SIZE', ['Max Record Size:', 'record exceed the maximum number of tolerated bytes', "of ".concat(max_record_size), "at line ".concat(this.info.lines)], this.options, this.__infoField())); | ||
@@ -847,3 +843,3 @@ if (_err2 !== undefined) return _err2; | ||
} else if (rtrim === true && !this.__isCharTrimable(chr)) { | ||
var _err3 = this.__error(new CsvError('CSV_NON_TRIMABLE_CHAR_AFTER_CLOSING_QUOTE', ['Invalid Closing Quote:', 'found non trimable byte after quote', "at line ".concat(this.info.lines)], this.options, this.__context())); | ||
var _err3 = this.__error(new CsvError('CSV_NON_TRIMABLE_CHAR_AFTER_CLOSING_QUOTE', ['Invalid Closing Quote:', 'found non trimable byte after quote', "at line ".concat(this.info.lines)], this.options, this.__infoField())); | ||
@@ -857,3 +853,3 @@ if (_err3 !== undefined) return _err3; | ||
if (this.state.quoting === true) { | ||
var _err4 = this.__error(new CsvError('CSV_QUOTE_NOT_CLOSED', ['Quote Not Closed:', "the parsing is finished with an opening quote at line ".concat(this.info.lines)], this.options, this.__context())); | ||
var _err4 = this.__error(new CsvError('CSV_QUOTE_NOT_CLOSED', ['Quote Not Closed:', "the parsing is finished with an opening quote at line ".concat(this.info.lines)], this.options, this.__infoField())); | ||
@@ -912,3 +908,3 @@ if (_err4 !== undefined) return _err4; | ||
if (columns === true) { | ||
if (isRecordEmpty(record)) { | ||
if (skip_lines_with_empty_values === true && isRecordEmpty(record)) { | ||
this.__resetRecord(); | ||
@@ -929,3 +925,3 @@ | ||
// CSV_RECORD_INCONSISTENT_FIELDS_LENGTH | ||
new CsvError('CSV_INCONSISTENT_RECORD_LENGTH', ['Invalid Record Length:', "expect ".concat(this.state.expectedRecordLength, ","), "got ".concat(recordLength, " on line ").concat(this.info.lines)], this.options, this.__context(), { | ||
new CsvError('CSV_INCONSISTENT_RECORD_LENGTH', ['Invalid Record Length:', "expect ".concat(this.state.expectedRecordLength, ","), "got ".concat(recordLength, " on line ").concat(this.info.lines)], this.options, this.__infoField(), { | ||
record: record | ||
@@ -935,3 +931,3 @@ }) : // Todo: rename CSV_RECORD_DONT_MATCH_COLUMNS_LENGTH to | ||
new CsvError('CSV_RECORD_DONT_MATCH_COLUMNS_LENGTH', ['Invalid Record Length:', "columns length is ".concat(columns.length, ","), // rename columns | ||
"got ".concat(recordLength, " on line ").concat(this.info.lines)], this.options, this.__context(), { | ||
"got ".concat(recordLength, " on line ").concat(this.info.lines)], this.options, this.__infoField(), { | ||
record: record | ||
@@ -950,8 +946,6 @@ }); | ||
if (skip_lines_with_empty_values === true) { | ||
if (isRecordEmpty(record)) { | ||
this.__resetRecord(); | ||
if (skip_lines_with_empty_values === true && isRecordEmpty(record)) { | ||
this.__resetRecord(); | ||
return; | ||
} | ||
return; | ||
} | ||
@@ -969,2 +963,3 @@ | ||
if (from === 1 || this.info.records >= from) { | ||
// With columns, records are object | ||
if (columns !== false) { | ||
@@ -987,3 +982,3 @@ var obj = {}; // Transform record array to an object | ||
var objname = this.options.objname; | ||
var objname = this.options.objname; // Without objname (default) | ||
@@ -997,3 +992,3 @@ if (objname === undefined) { | ||
} : {}, info === true ? { | ||
info: this.state.info | ||
info: this.__infoRecord() | ||
} : {})); | ||
@@ -1010,3 +1005,4 @@ | ||
} | ||
} | ||
} // With objname (default) | ||
} else { | ||
@@ -1019,3 +1015,3 @@ if (raw === true || info === true) { | ||
} : {}, info === true ? { | ||
info: this.state.info | ||
info: this.__infoRecord() | ||
} : {})); | ||
@@ -1033,3 +1029,4 @@ | ||
} | ||
} | ||
} // Without columns, records are array | ||
} else { | ||
@@ -1042,3 +1039,3 @@ if (raw === true || info === true) { | ||
} : {}, info === true ? { | ||
info: this.state.info | ||
info: this.__infoRecord() | ||
} : {})); | ||
@@ -1070,3 +1067,3 @@ | ||
if (!Array.isArray(headers)) { | ||
return this.__error(new CsvError('CSV_INVALID_COLUMN_MAPPING', ['Invalid Column Mapping:', 'expect an array from column function,', "got ".concat(JSON.stringify(headers))], this.options, this.__context(), { | ||
return this.__error(new CsvError('CSV_INVALID_COLUMN_MAPPING', ['Invalid Column Mapping:', 'expect an array from column function,', "got ".concat(JSON.stringify(headers))], this.options, this.__infoField(), { | ||
headers: headers | ||
@@ -1111,3 +1108,2 @@ })); | ||
if (enabled === false) { | ||
/* this.options.columns !== true && */ | ||
return this.__resetField(); | ||
@@ -1152,6 +1148,6 @@ } | ||
if (on_record !== undefined) { | ||
var context = this.__context(); | ||
var info = this.__infoRecord(); | ||
try { | ||
record = on_record.call(null, record, context); | ||
record = on_record.call(null, record, info); | ||
} catch (err) { | ||
@@ -1183,7 +1179,7 @@ return err; | ||
var context = this.__context(); | ||
if (this.state.castField !== null) { | ||
try { | ||
return [undefined, this.state.castField.call(null, field, context)]; | ||
var info = this.__infoField(); | ||
return [undefined, this.state.castField.call(null, field, info)]; | ||
} catch (err) { | ||
@@ -1197,3 +1193,5 @@ return [err]; | ||
} else if (this.options.cast_date !== false) { | ||
return [undefined, this.options.cast_date.call(null, field, context)]; | ||
var _info = this.__infoField(); | ||
return [undefined, this.options.cast_date.call(null, field, _info)]; | ||
} | ||
@@ -1373,18 +1371,28 @@ | ||
}, { | ||
key: "__context", | ||
value: function __context() { | ||
key: "__infoDataSet", | ||
value: function __infoDataSet() { | ||
return _objectSpread(_objectSpread({}, this.info), {}, { | ||
columns: this.options.columns | ||
}); | ||
} | ||
}, { | ||
key: "__infoRecord", | ||
value: function __infoRecord() { | ||
var columns = this.options.columns; | ||
var isColumns = Array.isArray(columns); | ||
return { | ||
column: isColumns === true ? columns.length > this.state.record.length ? columns[this.state.record.length].name : null : this.state.record.length, | ||
empty_lines: this.info.empty_lines, | ||
return _objectSpread(_objectSpread({}, this.__infoDataSet()), {}, { | ||
error: this.state.error, | ||
header: columns === true, | ||
index: this.state.record.length, | ||
invalid_field_length: this.info.invalid_field_length, | ||
quoting: this.state.wasQuoting, | ||
lines: this.info.lines, | ||
records: this.info.records | ||
}; | ||
index: this.state.record.length | ||
}); | ||
} | ||
}, { | ||
key: "__infoField", | ||
value: function __infoField() { | ||
var columns = this.options.columns; | ||
var isColumns = Array.isArray(columns); | ||
return _objectSpread(_objectSpread({}, this.__infoRecord()), {}, { | ||
column: isColumns === true ? columns.length > this.state.record.length ? columns[this.state.record.length].name : null : this.state.record.length, | ||
quoting: this.state.wasQuoting | ||
}); | ||
} | ||
}]); | ||
@@ -1430,6 +1438,6 @@ | ||
parser.on('error', function (err) { | ||
callback(err, undefined, parser.info); | ||
callback(err, undefined, parser.__infoDataSet()); | ||
}); | ||
parser.on('end', function () { | ||
callback(undefined, records, parser.info); | ||
callback(undefined, records, parser.__infoDataSet()); | ||
}); | ||
@@ -1436,0 +1444,0 @@ } |
@@ -110,3 +110,3 @@ | ||
'Invalid option columns:', | ||
'expect an object, a function or true,', | ||
'expect an array, a function or true,', | ||
`got ${JSON.stringify(options.columns)}` | ||
@@ -124,2 +124,7 @@ ], options) | ||
], options) | ||
}else if(options.columns === false){ | ||
throw new CsvError('CSV_INVALID_OPTION_COLUMNS_DUPLICATES_TO_ARRAY', [ | ||
'Invalid option columns_duplicates_to_array:', | ||
'the `columns` mode must be activated.' | ||
], options) | ||
} | ||
@@ -439,6 +444,6 @@ // Normalize option `comment` | ||
escapeIsQuote: Buffer.isBuffer(options.escape) && Buffer.isBuffer(options.quote) && Buffer.compare(options.escape, options.quote) === 0, | ||
expectedRecordLength: options.columns === null ? 0 : options.columns.length, | ||
// columns can be `false`, `true`, `Array` | ||
expectedRecordLength: Array.isArray(options.columns) ? options.columns.length : undefined, | ||
field: new ResizeableBuffer(20), | ||
firstLineToHeaders: fnFirstLineToHeaders, | ||
info: Object.assign({}, this.info), | ||
needMoreDataSize: Math.max( | ||
@@ -486,3 +491,3 @@ // Skip if the remaining buffer smaller than comment | ||
__parse(nextBuf, end){ | ||
const {bom, comment, escape, from_line, info, ltrim, max_record_size, quote, raw, relax, rtrim, skip_empty_lines, to, to_line} = this.options | ||
const {bom, comment, escape, from_line, ltrim, max_record_size, quote, raw, relax, rtrim, skip_empty_lines, to, to_line} = this.options | ||
let {record_delimiter} = this.options | ||
@@ -538,5 +543,2 @@ const {bomSkipped, previousBuf, rawBuffer, escapeIsQuote} = this.state | ||
this.info.lines++ | ||
if(info === true && this.state.record.length === 0 && this.state.field.length === 0 && this.state.wasQuoting === false){ | ||
this.state.info = Object.assign({}, this.info) | ||
} | ||
this.state.wasRowDelimiter = false | ||
@@ -610,3 +612,3 @@ } | ||
'(if activated) or comment', | ||
], this.options, this.__context()) | ||
], this.options, this.__infoField()) | ||
) | ||
@@ -628,3 +630,3 @@ if(err !== undefined) return err | ||
`a quote is found inside a field at line ${this.info.lines}`, | ||
], this.options, this.__context(), { | ||
], this.options, this.__infoField(), { | ||
field: this.state.field, | ||
@@ -704,3 +706,3 @@ }) | ||
`at line ${this.info.lines}`, | ||
], this.options, this.__context()) | ||
], this.options, this.__infoField()) | ||
) | ||
@@ -710,3 +712,2 @@ if(err !== undefined) return err | ||
} | ||
const lappend = ltrim === false || this.state.quoting === true || this.state.field.length !== 0 || !this.__isCharTrimable(chr) | ||
@@ -723,3 +724,3 @@ // rtrim in non quoting is handle in __onField | ||
`at line ${this.info.lines}`, | ||
], this.options, this.__context()) | ||
], this.options, this.__infoField()) | ||
) | ||
@@ -736,3 +737,3 @@ if(err !== undefined) return err | ||
`the parsing is finished with an opening quote at line ${this.info.lines}`, | ||
], this.options, this.__context()) | ||
], this.options, this.__infoField()) | ||
) | ||
@@ -770,3 +771,3 @@ if(err !== undefined) return err | ||
if(columns === true){ | ||
if(isRecordEmpty(record)){ | ||
if(skip_lines_with_empty_values === true && isRecordEmpty(record)){ | ||
this.__resetRecord() | ||
@@ -788,3 +789,3 @@ return | ||
`got ${recordLength} on line ${this.info.lines}`, | ||
], this.options, this.__context(), { | ||
], this.options, this.__infoField(), { | ||
record: record, | ||
@@ -799,3 +800,3 @@ }) | ||
`got ${recordLength} on line ${this.info.lines}`, | ||
], this.options, this.__context(), { | ||
], this.options, this.__infoField(), { | ||
record: record, | ||
@@ -814,7 +815,5 @@ }) | ||
} | ||
if(skip_lines_with_empty_values === true){ | ||
if(isRecordEmpty(record)){ | ||
this.__resetRecord() | ||
return | ||
} | ||
if(skip_lines_with_empty_values === true && isRecordEmpty(record)){ | ||
this.__resetRecord() | ||
return | ||
} | ||
@@ -828,2 +827,3 @@ if(this.state.recordHasError === true){ | ||
if(from === 1 || this.info.records >= from){ | ||
// With columns, records are object | ||
if(columns !== false){ | ||
@@ -846,2 +846,3 @@ const obj = {} | ||
const {objname} = this.options | ||
// Without objname (default) | ||
if(objname === undefined){ | ||
@@ -852,3 +853,3 @@ if(raw === true || info === true){ | ||
(raw === true ? {raw: this.state.rawBuffer.toString(encoding)}: {}), | ||
(info === true ? {info: this.state.info}: {}) | ||
(info === true ? {info: this.__infoRecord()}: {}) | ||
)) | ||
@@ -864,2 +865,3 @@ if(err){ | ||
} | ||
// With objname (default) | ||
}else{ | ||
@@ -870,3 +872,3 @@ if(raw === true || info === true){ | ||
raw === true ? {raw: this.state.rawBuffer.toString(encoding)}: {}, | ||
info === true ? {info: this.state.info}: {} | ||
info === true ? {info: this.__infoRecord()}: {} | ||
)) | ||
@@ -883,2 +885,3 @@ if(err){ | ||
} | ||
// Without columns, records are array | ||
}else{ | ||
@@ -889,3 +892,3 @@ if(raw === true || info === true){ | ||
raw === true ? {raw: this.state.rawBuffer.toString(encoding)}: {}, | ||
info === true ? {info: this.state.info}: {} | ||
info === true ? {info: this.__infoRecord()}: {} | ||
)) | ||
@@ -915,3 +918,3 @@ if(err){ | ||
`got ${JSON.stringify(headers)}` | ||
], this.options, this.__context(), { | ||
], this.options, this.__infoField(), { | ||
headers: headers, | ||
@@ -942,3 +945,3 @@ }) | ||
// Short circuit for the from_line options | ||
if(enabled === false){ /* this.options.columns !== true && */ | ||
if(enabled === false){ | ||
return this.__resetField() | ||
@@ -969,5 +972,5 @@ } | ||
if(on_record !== undefined){ | ||
const context = this.__context() | ||
const info = this.__infoRecord() | ||
try{ | ||
record = on_record.call(null, record, context) | ||
record = on_record.call(null, record, info) | ||
}catch(err){ | ||
@@ -990,6 +993,6 @@ return err | ||
} | ||
const context = this.__context() | ||
if(this.state.castField !== null){ | ||
try{ | ||
return [undefined, this.state.castField.call(null, field, context)] | ||
const info = this.__infoField() | ||
return [undefined, this.state.castField.call(null, field, info)] | ||
}catch(err){ | ||
@@ -1002,3 +1005,4 @@ return [err] | ||
}else if(this.options.cast_date !== false){ | ||
return [undefined, this.options.cast_date.call(null, field, context)] | ||
const info = this.__infoField() | ||
return [undefined, this.options.cast_date.call(null, field, info)] | ||
} | ||
@@ -1135,6 +1139,22 @@ return [undefined, field] | ||
} | ||
__context(){ | ||
__infoDataSet(){ | ||
return { | ||
...this.info, | ||
columns: this.options.columns | ||
} | ||
} | ||
__infoRecord(){ | ||
const {columns} = this.options | ||
return { | ||
...this.__infoDataSet(), | ||
error: this.state.error, | ||
header: columns === true, | ||
index: this.state.record.length, | ||
} | ||
} | ||
__infoField(){ | ||
const {columns} = this.options | ||
const isColumns = Array.isArray(columns) | ||
return { | ||
...this.__infoRecord(), | ||
column: isColumns === true ? | ||
@@ -1146,10 +1166,3 @@ ( columns.length > this.state.record.length ? | ||
this.state.record.length, | ||
empty_lines: this.info.empty_lines, | ||
error: this.state.error, | ||
header: columns === true, | ||
index: this.state.record.length, | ||
invalid_field_length: this.info.invalid_field_length, | ||
quoting: this.state.wasQuoting, | ||
lines: this.info.lines, | ||
records: this.info.records | ||
} | ||
@@ -1191,6 +1204,6 @@ } | ||
parser.on('error', function(err){ | ||
callback(err, undefined, parser.info) | ||
callback(err, undefined, parser.__infoDataSet()) | ||
}) | ||
parser.on('end', function(){ | ||
callback(undefined, records, parser.info) | ||
callback(undefined, records, parser.__infoDataSet()) | ||
}) | ||
@@ -1197,0 +1210,0 @@ } |
{ | ||
"version": "4.15.4", | ||
"version": "4.16.0", | ||
"name": "csv-parse", | ||
@@ -4,0 +4,0 @@ "description": "CSV parsing implementing the Node.js `stream.Transform` API", |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
680948
16656