diff --git a/README.md b/README.md index bfd4b31..64eca77 100644 --- a/README.md +++ b/README.md @@ -241,6 +241,13 @@ Default: `0` Specifies the number of lines at the beginning of a data file that the parser should skip over, prior to parsing headers. +#### skipEmptyLines + +Type: `Boolean`
+Default: `false` + +If `true`, empty lines are skipped. + #### maxRowBytes Type: `Number`
@@ -259,6 +266,13 @@ if `false`: the headers are mapped to the column index less columns: any missing column in the middle will result in a wrong property mapping! more columns: the aditional columns will create a "_"+index properties - eg. "_10":"value" +#### extendedRangeError + +Type: `Boolean`
+Default: `false` + +If `true`, then `lineNumber`, `cells`, `line` are added to the error object. + ## Events The following events are emitted during parsing: @@ -305,6 +319,7 @@ Usage: csv-parser [filename?] [options] --separator,-s Set the separator character ("," by default) --skipComments,-c Skip CSV comments that begin with '#'. Set a value to change the comment character. --skipLines,-l Set the number of lines to skip to before parsing headers + --skipEmptyLines Skip empty lines --strict Require column length match headers length --version,-v Print out the installed version ``` diff --git a/bin/csv-parser b/bin/csv-parser index f895a8e..f3b23fb 100755 --- a/bin/csv-parser +++ b/bin/csv-parser @@ -44,6 +44,7 @@ if (argv.help || (process.stdin.isTTY && !filename)) { --separator,-s Set the separator character ("," by default) --skipComments,-c Skip CSV comments that begin with '#'. Set a value to change the comment character. --skipLines,-l Set the number of lines to skip to before parsing headers + --skipEmptyLines Skip empty lines --strict Require column length match headers length --version,-v Print out the installed version `) @@ -56,7 +57,8 @@ const options = { separator: argv.separator, strict: argv.strict, skipComments: argv.skipComments, - skipLines: argv.skipLines + skipLines: argv.skipLines, + skipEmptyLines: argv.skipEmptyLines } if (argv.headers) { diff --git a/index.d.ts b/index.d.ts index a6a400d..ad2f529 100644 --- a/index.d.ts +++ b/index.d.ts @@ -96,6 +96,13 @@ Bugs Bunny,22 */ readonly skipLines?: number; + /** + * If true empty lines are skipped. + * + * @default false + */ + readonly skipEmptyLines?: boolean; + /** * Maximum number of bytes per row. An error is thrown if a line exeeds this value. The default value is on 8 peta byte. * @@ -107,6 +114,11 @@ Bugs Bunny,22 * If `true`, instructs the parser that the number of columns in each row must match the number of `headers` specified. */ readonly strict?: boolean; + + /** + * If `true`, then `lineNumber`, `cells`, `line` are added to the error object. + */ + readonly extendedRangeError?: boolean; } } diff --git a/index.js b/index.js index 2a6a2da..7b41d9a 100644 --- a/index.js +++ b/index.js @@ -159,11 +159,21 @@ class CsvParser extends Transform { return } - if (!skip && this.options.strict && cells.length !== this.headers.length) { - const e = new RangeError('Row length does not match headers') - this.emit('error', e) - } else { - if (!skip) this.writeRow(cells) + if (!skip) { + if (start === end && this.options.skipEmptyLines) { + return + } + if (this.options.strict && cells.length !== this.headers.length) { + const e = new RangeError('Row length does not match headers') + if (this.options.extendedRangeError) { + e.lineNumber = this.state.lineNumber + e.cells = cells + e.line = buffer.toString('utf-8', start, end) + } + this.emit('error', e) + } else { + this.writeRow(cells) + } } } diff --git a/index.test-d.ts b/index.test-d.ts index 703330e..9b46bd3 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -36,5 +36,7 @@ expectType(csvParser({ separator: ',' })); expectType(csvParser({ skipComments: true })); expectType(csvParser({ skipComments: '#' })); expectType(csvParser({ skipLines: 1 })); +expectType(csvParser({ skipEmptyLines: false })); expectType(csvParser({ maxRowBytes: 1 })); expectType(csvParser({ strict: true })); +expectType(csvParser({ extendedRangeError: false })); diff --git a/test/extendedRangeError.test.js b/test/extendedRangeError.test.js new file mode 100644 index 0000000..c02b28c --- /dev/null +++ b/test/extendedRangeError.test.js @@ -0,0 +1,14 @@ +const test = require('ava') + +const { collect } = require('./helpers/helper') + +test.cb('extended range error', (t) => { + const verify = (err, lines) => { + t.is(err.lineNumber, 2, 'lineNumber set') + t.is(err.cells.length, 1, 'cells returned') + t.is(err.line, '1', 'line returned') + t.end() + } + + collect('option-extendedRangeError', { columns: true, strict: true, extendedRangeError: true }, verify) +}) diff --git a/test/fixtures/option-extendedRangeError.csv b/test/fixtures/option-extendedRangeError.csv new file mode 100644 index 0000000..1fc698d --- /dev/null +++ b/test/fixtures/option-extendedRangeError.csv @@ -0,0 +1,2 @@ +A,B +1 diff --git a/test/fixtures/option-skipEmptyLines.csv b/test/fixtures/option-skipEmptyLines.csv new file mode 100644 index 0000000..96083f6 --- /dev/null +++ b/test/fixtures/option-skipEmptyLines.csv @@ -0,0 +1,7 @@ +A + +1 +2 + + +3 diff --git a/test/skipEmptyLines.test.js b/test/skipEmptyLines.test.js new file mode 100644 index 0000000..90ab778 --- /dev/null +++ b/test/skipEmptyLines.test.js @@ -0,0 +1,17 @@ +const test = require('ava') + +const { collect } = require('./helpers/helper') + +test.cb('skip empty lines', (t) => { + const verify = (err, lines) => { + console.log(lines) + t.false(err, 'no err') + t.is(lines.length, 3, '3 row') + t.is(JSON.stringify(lines[0]), JSON.stringify({ A: '1' })) + t.is(JSON.stringify(lines[1]), JSON.stringify({ A: '2' })) + t.is(JSON.stringify(lines[2]), JSON.stringify({ A: '3' })) + t.end() + } + + collect('option-skipEmptyLines', { skipEmptyLines: true, columns: true, strict: true }, verify) +})