From 378dd58841ad1a8963cfae2c1824cd4f29c10e4c Mon Sep 17 00:00:00 2001 From: David Worms Date: Fri, 19 Nov 2021 13:20:43 +0100 Subject: [PATCH 1/7] docs(csv-demo-webpack): reflect latest changes in readme --- demo/webpack/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/demo/webpack/README.md b/demo/webpack/README.md index 3e7e1fd95..626788cee 100644 --- a/demo/webpack/README.md +++ b/demo/webpack/README.md @@ -1,13 +1,13 @@ # `webpack` bunder demonstration -The project is built with `webpack` version 5 and follow the official [getting started](https://webpack.js.org/guides/getting-started/). +The project is built with `webpack` version 5. It follows the official [getting started](https://webpack.js.org/guides/getting-started/). ## Architecture The application serves one [HTML page](./dist/index.html) on port `8080` available at `http://localhost:8080`. -The JavaScript source code is located in [`./src/index.js`](./src/index.js) and import the various packages of the [CSV project](https://csv.js.org/). Following the getting started instructions, `webpack` reads this file and generates a new bundle in `./lib/main.js`. +The JavaScript source code is located in [`./src` folder](./src/). Choose the script according to your usage. Each script imports the necessary package from the [CSV project](https://csv.js.org/). Following the getting started instructions, `webpack` reads this file and generates a new bundle in [`./lib/` folder](./lib/). The `webpack` configuration file is located in [`./webpack.config.js`](./webpack.config.js). @@ -35,3 +35,5 @@ npm run start # Or npx http-server ./dist -p 8080 ``` + +The web application is now accessible on [`https://localhost:8080`](https://localhost:8080). From 7358f9d2b150655579dadf2af1aa64206fc7e2fa Mon Sep 17 00:00:00 2001 From: David Worms Date: Fri, 19 Nov 2021 13:32:15 +0100 Subject: [PATCH 2/7] fix(csv-issues-esm): illustrate issue #300 fix #300 --- demo/issues-cjs/lib/300.js | 68 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 demo/issues-cjs/lib/300.js diff --git a/demo/issues-cjs/lib/300.js b/demo/issues-cjs/lib/300.js new file mode 100644 index 000000000..7fca19afc --- /dev/null +++ b/demo/issues-cjs/lib/300.js @@ -0,0 +1,68 @@ +const {stringify} = require('csv-stringify') + +/** + * Testing CSV bug. When length of csv data exceeds 16500, stringifier + * fails. This version fails. Shorten the string on line 22 by one + * character and the generation succeeds. + */ + +async function main() { + const numberOfRows = 140; + + // Generate test data + let generatedRows = []; + for (let i = 0; i < numberOfRows; i++) { + let row = ['row ' + i]; + for (let field = 0; field < 24; field++) { + row.push(`f ${field}`); + } + + // fine-tune data length + if (i == 0) { + row[0] = 'customlengthfield customlengthfield customlengthfield customlengthfield customlengthfield custom.'; + } + + let rowString = row.join('\t'); + generatedRows.push(rowString); + } + const csvData = generatedRows.join('\n'); + + // At this point we have a simple CSV string, delimited by tabs and newlines. + + // Split data so we can feed it to the stringifier + const rows = csvData.split('\n'); + + // Generate CSV + const data = []; + const stringifier = stringify({ + delimiter: '\t', + }); + + stringifier.on('readable', function(){ + let row; + while(row = stringifier.read()){ + data.push(row); + } + }) + + rows.forEach((row) => { + const fields = row.split('\t'); + console.log('>', stringifier.write(fields)); + }); + + stringifier.end(); + + stringifier.on('finish', function(){ + console.log('Generated contents of the CSV file should be shown between this line and "^data".'); + + console.log(data.toString('utf8')); + console.log('^data'); + + console.log(csvData.length); + console.log('^data length'); + + process.exit(); + }); +} + +main(); From e157f407eeffe5bcfb179cb20476169037bfb4f1 Mon Sep 17 00:00:00 2001 From: David Worms Date: Fri, 19 Nov 2021 15:31:02 +0100 Subject: [PATCH 3/7] fix(csv-stringify): catch error with sync api, fix #296 --- demo/issues-cjs/lib/296.js | 11 +++ packages/csv-stringify/dist/cjs/index.cjs | 95 +++++++++--------- packages/csv-stringify/dist/cjs/sync.cjs | 112 +++++++++++----------- packages/csv-stringify/dist/esm/index.js | 95 +++++++++--------- packages/csv-stringify/dist/esm/sync.js | 112 +++++++++++----------- packages/csv-stringify/dist/iife/index.js | 95 +++++++++--------- packages/csv-stringify/dist/iife/sync.js | 112 +++++++++++----------- packages/csv-stringify/dist/umd/index.js | 95 +++++++++--------- packages/csv-stringify/dist/umd/sync.js | 112 +++++++++++----------- packages/csv-stringify/lib/index.js | 95 +++++++++--------- packages/csv-stringify/lib/sync.js | 17 ++-- packages/csv-stringify/test/sync.coffee | 5 + 12 files changed, 497 insertions(+), 459 deletions(-) create mode 100644 demo/issues-cjs/lib/296.js diff --git a/demo/issues-cjs/lib/296.js b/demo/issues-cjs/lib/296.js new file mode 100644 index 000000000..c9576a0f2 --- /dev/null +++ b/demo/issues-cjs/lib/296.js @@ -0,0 +1,11 @@ + +const {stringify} = require('csv-stringify/sync'); +const assert = require('assert'); + +try{ + stringify([ 1, {a: '3', b: '4'}]); + process.exit(1); +}catch(err){ + console.info(err.message); + assert.equal(err.message, 'Invalid Record: expect an array or an object, got 1'); +} diff --git a/packages/csv-stringify/dist/cjs/index.cjs b/packages/csv-stringify/dist/cjs/index.cjs index e5c618c7a..33dac9bd1 100644 --- a/packages/csv-stringify/dist/cjs/index.cjs +++ b/packages/csv-stringify/dist/cjs/index.cjs @@ -234,7 +234,9 @@ class Stringifier extends stream.Transform { options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -286,45 +288,62 @@ class Stringifier extends stream.Transform { if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -336,18 +355,10 @@ class Stringifier extends stream.Transform { // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -364,10 +375,7 @@ class Stringifier extends stream.Transform { const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -378,10 +386,7 @@ class Stringifier extends stream.Transform { const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -393,30 +398,25 @@ class Stringifier extends stream.Transform { if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -453,7 +453,7 @@ class Stringifier extends stream.Transform { csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -468,12 +468,15 @@ class Stringifier extends stream.Transform { if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -500,10 +503,10 @@ class Stringifier extends stream.Transform { } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -524,19 +527,19 @@ class Stringifier extends stream.Transform { }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } diff --git a/packages/csv-stringify/dist/cjs/sync.cjs b/packages/csv-stringify/dist/cjs/sync.cjs index 3f5725f6b..d7b2ecabc 100644 --- a/packages/csv-stringify/dist/cjs/sync.cjs +++ b/packages/csv-stringify/dist/cjs/sync.cjs @@ -235,7 +235,9 @@ class Stringifier extends stream.Transform { options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -287,45 +289,62 @@ class Stringifier extends stream.Transform { if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -337,18 +356,10 @@ class Stringifier extends stream.Transform { // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -365,10 +376,7 @@ class Stringifier extends stream.Transform { const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -379,10 +387,7 @@ class Stringifier extends stream.Transform { const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -394,30 +399,25 @@ class Stringifier extends stream.Transform { if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -454,7 +454,7 @@ class Stringifier extends stream.Transform { csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -469,12 +469,15 @@ class Stringifier extends stream.Transform { if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -501,10 +504,10 @@ class Stringifier extends stream.Transform { } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -525,19 +528,19 @@ class Stringifier extends stream.Transform { }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } @@ -547,18 +550,17 @@ const stringify = function(records, options={}){ const decoder = new string_decoder.StringDecoder(); records = decoder.write(records); } - function onData(record){ - if(record){ - data.push(record.toString()); - } - } const stringifier = new Stringifier(options); - stringifier.on('data', onData); + stringifier.push = function(record){ + if(record === null){ + return; + } + data.push(record.toString()); + }; for(const record of records){ - stringifier.write(record); + const err = stringifier.__transform(record, null); + if(err !== undefined) throw err; } - stringifier.end(); - stringifier.removeListener('data', onData); return data.join(''); }; diff --git a/packages/csv-stringify/dist/esm/index.js b/packages/csv-stringify/dist/esm/index.js index 9e5550f70..8d6d20678 100644 --- a/packages/csv-stringify/dist/esm/index.js +++ b/packages/csv-stringify/dist/esm/index.js @@ -5196,7 +5196,9 @@ class Stringifier extends Transform { options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -5248,45 +5250,62 @@ class Stringifier extends Transform { if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -5298,18 +5317,10 @@ class Stringifier extends Transform { // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -5326,10 +5337,7 @@ class Stringifier extends Transform { const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -5340,10 +5348,7 @@ class Stringifier extends Transform { const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -5355,30 +5360,25 @@ class Stringifier extends Transform { if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -5415,7 +5415,7 @@ class Stringifier extends Transform { csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -5430,12 +5430,15 @@ class Stringifier extends Transform { if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -5462,10 +5465,10 @@ class Stringifier extends Transform { } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -5486,19 +5489,19 @@ class Stringifier extends Transform { }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } diff --git a/packages/csv-stringify/dist/esm/sync.js b/packages/csv-stringify/dist/esm/sync.js index 6be5977fb..0f35278e0 100644 --- a/packages/csv-stringify/dist/esm/sync.js +++ b/packages/csv-stringify/dist/esm/sync.js @@ -5196,7 +5196,9 @@ class Stringifier extends Transform { options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -5248,45 +5250,62 @@ class Stringifier extends Transform { if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -5298,18 +5317,10 @@ class Stringifier extends Transform { // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -5326,10 +5337,7 @@ class Stringifier extends Transform { const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -5340,10 +5348,7 @@ class Stringifier extends Transform { const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -5355,30 +5360,25 @@ class Stringifier extends Transform { if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -5415,7 +5415,7 @@ class Stringifier extends Transform { csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -5430,12 +5430,15 @@ class Stringifier extends Transform { if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -5462,10 +5465,10 @@ class Stringifier extends Transform { } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -5486,19 +5489,19 @@ class Stringifier extends Transform { }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } @@ -5508,18 +5511,17 @@ const stringify = function(records, options={}){ const decoder = new StringDecoder(); records = decoder.write(records); } - function onData(record){ - if(record){ - data.push(record.toString()); - } - } const stringifier = new Stringifier(options); - stringifier.on('data', onData); + stringifier.push = function(record){ + if(record === null){ + return; + } + data.push(record.toString()); + }; for(const record of records){ - stringifier.write(record); + const err = stringifier.__transform(record, null); + if(err !== undefined) throw err; } - stringifier.end(); - stringifier.removeListener('data', onData); return data.join(''); }; diff --git a/packages/csv-stringify/dist/iife/index.js b/packages/csv-stringify/dist/iife/index.js index 0adaf3ee1..3d52ab0c9 100644 --- a/packages/csv-stringify/dist/iife/index.js +++ b/packages/csv-stringify/dist/iife/index.js @@ -5199,7 +5199,9 @@ var csv_stringify = (function (exports) { options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -5251,45 +5253,62 @@ var csv_stringify = (function (exports) { if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -5301,18 +5320,10 @@ var csv_stringify = (function (exports) { // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -5329,10 +5340,7 @@ var csv_stringify = (function (exports) { const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -5343,10 +5351,7 @@ var csv_stringify = (function (exports) { const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -5358,30 +5363,25 @@ var csv_stringify = (function (exports) { if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -5418,7 +5418,7 @@ var csv_stringify = (function (exports) { csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -5433,12 +5433,15 @@ var csv_stringify = (function (exports) { if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -5465,10 +5468,10 @@ var csv_stringify = (function (exports) { } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -5489,19 +5492,19 @@ var csv_stringify = (function (exports) { }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } diff --git a/packages/csv-stringify/dist/iife/sync.js b/packages/csv-stringify/dist/iife/sync.js index 04f77fb33..76307d457 100644 --- a/packages/csv-stringify/dist/iife/sync.js +++ b/packages/csv-stringify/dist/iife/sync.js @@ -5199,7 +5199,9 @@ var csv_stringify_sync = (function (exports) { options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -5251,45 +5253,62 @@ var csv_stringify_sync = (function (exports) { if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -5301,18 +5320,10 @@ var csv_stringify_sync = (function (exports) { // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -5329,10 +5340,7 @@ var csv_stringify_sync = (function (exports) { const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -5343,10 +5351,7 @@ var csv_stringify_sync = (function (exports) { const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -5358,30 +5363,25 @@ var csv_stringify_sync = (function (exports) { if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -5418,7 +5418,7 @@ var csv_stringify_sync = (function (exports) { csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -5433,12 +5433,15 @@ var csv_stringify_sync = (function (exports) { if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -5465,10 +5468,10 @@ var csv_stringify_sync = (function (exports) { } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -5489,19 +5492,19 @@ var csv_stringify_sync = (function (exports) { }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } @@ -5511,18 +5514,17 @@ var csv_stringify_sync = (function (exports) { const decoder = new StringDecoder(); records = decoder.write(records); } - function onData(record){ - if(record){ - data.push(record.toString()); - } - } const stringifier = new Stringifier(options); - stringifier.on('data', onData); + stringifier.push = function(record){ + if(record === null){ + return; + } + data.push(record.toString()); + }; for(const record of records){ - stringifier.write(record); + const err = stringifier.__transform(record, null); + if(err !== undefined) throw err; } - stringifier.end(); - stringifier.removeListener('data', onData); return data.join(''); }; diff --git a/packages/csv-stringify/dist/umd/index.js b/packages/csv-stringify/dist/umd/index.js index 09558047f..92ad6d348 100644 --- a/packages/csv-stringify/dist/umd/index.js +++ b/packages/csv-stringify/dist/umd/index.js @@ -5202,7 +5202,9 @@ options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -5254,45 +5256,62 @@ if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -5304,18 +5323,10 @@ // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -5332,10 +5343,7 @@ const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -5346,10 +5354,7 @@ const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -5361,30 +5366,25 @@ if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -5421,7 +5421,7 @@ csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -5436,12 +5436,15 @@ if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -5468,10 +5471,10 @@ } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -5492,19 +5495,19 @@ }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } diff --git a/packages/csv-stringify/dist/umd/sync.js b/packages/csv-stringify/dist/umd/sync.js index e579c0ea4..38222ed28 100644 --- a/packages/csv-stringify/dist/umd/sync.js +++ b/packages/csv-stringify/dist/umd/sync.js @@ -5202,7 +5202,9 @@ options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -5254,45 +5256,62 @@ if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -5304,18 +5323,10 @@ // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -5332,10 +5343,7 @@ const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -5346,10 +5354,7 @@ const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -5361,30 +5366,25 @@ if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -5421,7 +5421,7 @@ csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -5436,12 +5436,15 @@ if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -5468,10 +5471,10 @@ } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -5492,19 +5495,19 @@ }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } @@ -5514,18 +5517,17 @@ const decoder = new StringDecoder(); records = decoder.write(records); } - function onData(record){ - if(record){ - data.push(record.toString()); - } - } const stringifier = new Stringifier(options); - stringifier.on('data', onData); + stringifier.push = function(record){ + if(record === null){ + return; + } + data.push(record.toString()); + }; for(const record of records){ - stringifier.write(record); + const err = stringifier.__transform(record, null); + if(err !== undefined) throw err; } - stringifier.end(); - stringifier.removeListener('data', onData); return data.join(''); }; diff --git a/packages/csv-stringify/lib/index.js b/packages/csv-stringify/lib/index.js index 74bbf7367..c25fc8af0 100644 --- a/packages/csv-stringify/lib/index.js +++ b/packages/csv-stringify/lib/index.js @@ -249,7 +249,9 @@ class Stringifier extends Transform { // todo } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -305,45 +307,62 @@ class Stringifier extends Transform { if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else{ chunk_string = chunk_string + this.options.record_delimiter; } }else{ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else{ @@ -355,18 +374,10 @@ class Stringifier extends Transform { // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -383,10 +394,7 @@ class Stringifier extends Transform { const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -397,10 +405,7 @@ class Stringifier extends Transform { const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -412,30 +417,25 @@ class Stringifier extends Transform { if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else{ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -472,7 +472,7 @@ class Stringifier extends Transform { csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -487,12 +487,15 @@ class Stringifier extends Transform { if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else{ - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -519,10 +522,10 @@ class Stringifier extends Transform { } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -543,19 +546,19 @@ class Stringifier extends Transform { }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else{ - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } diff --git a/packages/csv-stringify/lib/sync.js b/packages/csv-stringify/lib/sync.js index 7d1e6e052..22f46c73e 100644 --- a/packages/csv-stringify/lib/sync.js +++ b/packages/csv-stringify/lib/sync.js @@ -8,18 +8,17 @@ const stringify = function(records, options={}){ const decoder = new StringDecoder(); records = decoder.write(records); } - function onData(record){ - if(record){ - data.push(record.toString()); - } - } const stringifier = new Stringifier(options); - stringifier.on('data', onData); + stringifier.push = function(record){ + if(record === null){ + return; + } + data.push(record.toString()); + }; for(const record of records){ - stringifier.write(record); + const err = stringifier.__transform(record, null); + if(err !== undefined) throw err; } - stringifier.end(); - stringifier.removeListener('data', onData); return data.join(''); }; diff --git a/packages/csv-stringify/test/sync.coffee b/packages/csv-stringify/test/sync.coffee index ee8661107..ab7e861ef 100644 --- a/packages/csv-stringify/test/sync.coffee +++ b/packages/csv-stringify/test/sync.coffee @@ -14,3 +14,8 @@ describe 'sync', -> it 'pass options', -> data = stringify [ {a: '1', b: '2'}, {a: '3', b: '4'}], quoted: true, eof: false data.should.eql '"1","2"\n"3","4"' + + it 'catch error', -> + (-> + stringify([ 1, {a: '3', b: '4'}]) + ).should.throw 'Invalid Record: expect an array or an object, got 1' From bfcbd5b0e2ec0c2dd1d767060195a09da7edb8d6 Mon Sep 17 00:00:00 2001 From: David Worms Date: Fri, 19 Nov 2021 15:32:06 +0100 Subject: [PATCH 4/7] refactor(csv-stringify): move api.sync test --- packages/csv-stringify/test/{sync.coffee => api.sync.coffee} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/csv-stringify/test/{sync.coffee => api.sync.coffee} (100%) diff --git a/packages/csv-stringify/test/sync.coffee b/packages/csv-stringify/test/api.sync.coffee similarity index 100% rename from packages/csv-stringify/test/sync.coffee rename to packages/csv-stringify/test/api.sync.coffee From 30f7c84cbfe6d0bb3891e81e6dbc09fd4a9819bc Mon Sep 17 00:00:00 2001 From: David Worms Date: Fri, 19 Nov 2021 15:39:25 +0100 Subject: [PATCH 5/7] refactor(csv-stringify): remove unused dependency --- packages/csv-stringify/dist/cjs/sync.cjs | 5 - packages/csv-stringify/dist/esm/sync.js | 4 - packages/csv-stringify/dist/iife/sync.js | 4 - packages/csv-stringify/dist/umd/sync.js | 4 - packages/csv-stringify/lib/sync.js | 5 - packages/csv/dist/cjs/index.cjs | 95 +++++++++--------- packages/csv/dist/cjs/sync.cjs | 117 +++++++++++------------ packages/csv/dist/esm/index.js | 95 +++++++++--------- packages/csv/dist/esm/sync.js | 116 +++++++++++----------- packages/csv/dist/iife/index.js | 95 +++++++++--------- packages/csv/dist/iife/sync.js | 116 +++++++++++----------- packages/csv/dist/umd/index.js | 95 +++++++++--------- packages/csv/dist/umd/sync.js | 116 +++++++++++----------- 13 files changed, 424 insertions(+), 443 deletions(-) diff --git a/packages/csv-stringify/dist/cjs/sync.cjs b/packages/csv-stringify/dist/cjs/sync.cjs index d7b2ecabc..2638c19b6 100644 --- a/packages/csv-stringify/dist/cjs/sync.cjs +++ b/packages/csv-stringify/dist/cjs/sync.cjs @@ -3,7 +3,6 @@ Object.defineProperty(exports, '__esModule', { value: true }); var stream = require('stream'); -var string_decoder = require('string_decoder'); const bom_utf8 = Buffer.from([239, 187, 191]); @@ -546,10 +545,6 @@ class Stringifier extends stream.Transform { const stringify = function(records, options={}){ const data = []; - if(Buffer.isBuffer(records)){ - const decoder = new string_decoder.StringDecoder(); - records = decoder.write(records); - } const stringifier = new Stringifier(options); stringifier.push = function(record){ if(record === null){ diff --git a/packages/csv-stringify/dist/esm/sync.js b/packages/csv-stringify/dist/esm/sync.js index 0f35278e0..90135ed88 100644 --- a/packages/csv-stringify/dist/esm/sync.js +++ b/packages/csv-stringify/dist/esm/sync.js @@ -5507,10 +5507,6 @@ class Stringifier extends Transform { const stringify = function(records, options={}){ const data = []; - if(isBuffer(records)){ - const decoder = new StringDecoder(); - records = decoder.write(records); - } const stringifier = new Stringifier(options); stringifier.push = function(record){ if(record === null){ diff --git a/packages/csv-stringify/dist/iife/sync.js b/packages/csv-stringify/dist/iife/sync.js index 76307d457..4319ec924 100644 --- a/packages/csv-stringify/dist/iife/sync.js +++ b/packages/csv-stringify/dist/iife/sync.js @@ -5510,10 +5510,6 @@ var csv_stringify_sync = (function (exports) { const stringify = function(records, options={}){ const data = []; - if(isBuffer(records)){ - const decoder = new StringDecoder(); - records = decoder.write(records); - } const stringifier = new Stringifier(options); stringifier.push = function(record){ if(record === null){ diff --git a/packages/csv-stringify/dist/umd/sync.js b/packages/csv-stringify/dist/umd/sync.js index 38222ed28..5cfc92ca7 100644 --- a/packages/csv-stringify/dist/umd/sync.js +++ b/packages/csv-stringify/dist/umd/sync.js @@ -5513,10 +5513,6 @@ const stringify = function(records, options={}){ const data = []; - if(isBuffer(records)){ - const decoder = new StringDecoder(); - records = decoder.write(records); - } const stringifier = new Stringifier(options); stringifier.push = function(record){ if(record === null){ diff --git a/packages/csv-stringify/lib/sync.js b/packages/csv-stringify/lib/sync.js index 22f46c73e..2251947d5 100644 --- a/packages/csv-stringify/lib/sync.js +++ b/packages/csv-stringify/lib/sync.js @@ -1,13 +1,8 @@ import { Stringifier } from './index.js'; -import { StringDecoder } from 'string_decoder'; const stringify = function(records, options={}){ const data = []; - if(Buffer.isBuffer(records)){ - const decoder = new StringDecoder(); - records = decoder.write(records); - } const stringifier = new Stringifier(options); stringifier.push = function(record){ if(record === null){ diff --git a/packages/csv/dist/cjs/index.cjs b/packages/csv/dist/cjs/index.cjs index b6b0aa3ea..ddaec3184 100644 --- a/packages/csv/dist/cjs/index.cjs +++ b/packages/csv/dist/cjs/index.cjs @@ -1926,7 +1926,9 @@ class Stringifier extends stream.Transform { options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -1978,45 +1980,62 @@ class Stringifier extends stream.Transform { if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -2028,18 +2047,10 @@ class Stringifier extends stream.Transform { // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -2056,10 +2067,7 @@ class Stringifier extends stream.Transform { const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -2070,10 +2078,7 @@ class Stringifier extends stream.Transform { const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -2085,30 +2090,25 @@ class Stringifier extends stream.Transform { if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -2145,7 +2145,7 @@ class Stringifier extends stream.Transform { csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -2160,12 +2160,15 @@ class Stringifier extends stream.Transform { if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -2192,10 +2195,10 @@ class Stringifier extends stream.Transform { } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -2216,19 +2219,19 @@ class Stringifier extends stream.Transform { }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } diff --git a/packages/csv/dist/cjs/sync.cjs b/packages/csv/dist/cjs/sync.cjs index e85a3f03f..f73ab412c 100644 --- a/packages/csv/dist/cjs/sync.cjs +++ b/packages/csv/dist/cjs/sync.cjs @@ -4,7 +4,6 @@ Object.defineProperty(exports, '__esModule', { value: true }); var stream = require('stream'); var util = require('util'); -var string_decoder = require('string_decoder'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } @@ -1749,7 +1748,9 @@ class Stringifier extends stream.Transform { options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -1801,45 +1802,62 @@ class Stringifier extends stream.Transform { if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -1851,18 +1869,10 @@ class Stringifier extends stream.Transform { // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -1879,10 +1889,7 @@ class Stringifier extends stream.Transform { const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -1893,10 +1900,7 @@ class Stringifier extends stream.Transform { const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -1908,30 +1912,25 @@ class Stringifier extends stream.Transform { if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -1968,7 +1967,7 @@ class Stringifier extends stream.Transform { csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -1983,12 +1982,15 @@ class Stringifier extends stream.Transform { if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -2015,10 +2017,10 @@ class Stringifier extends stream.Transform { } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -2039,40 +2041,35 @@ class Stringifier extends stream.Transform { }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } const stringify = function(records, options={}){ const data = []; - if(Buffer.isBuffer(records)){ - const decoder = new string_decoder.StringDecoder(); - records = decoder.write(records); - } - function onData(record){ - if(record){ - data.push(record.toString()); - } - } const stringifier = new Stringifier(options); - stringifier.on('data', onData); + stringifier.push = function(record){ + if(record === null){ + return; + } + data.push(record.toString()); + }; for(const record of records){ - stringifier.write(record); + const err = stringifier.__transform(record, null); + if(err !== undefined) throw err; } - stringifier.end(); - stringifier.removeListener('data', onData); return data.join(''); }; diff --git a/packages/csv/dist/esm/index.js b/packages/csv/dist/esm/index.js index e6c115256..b6879a2b8 100644 --- a/packages/csv/dist/esm/index.js +++ b/packages/csv/dist/esm/index.js @@ -6952,7 +6952,9 @@ class Stringifier extends Transform { options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -7004,45 +7006,62 @@ class Stringifier extends Transform { if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -7054,18 +7073,10 @@ class Stringifier extends Transform { // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -7082,10 +7093,7 @@ class Stringifier extends Transform { const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -7096,10 +7104,7 @@ class Stringifier extends Transform { const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -7111,30 +7116,25 @@ class Stringifier extends Transform { if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -7171,7 +7171,7 @@ class Stringifier extends Transform { csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -7186,12 +7186,15 @@ class Stringifier extends Transform { if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -7218,10 +7221,10 @@ class Stringifier extends Transform { } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -7242,19 +7245,19 @@ class Stringifier extends Transform { }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } diff --git a/packages/csv/dist/esm/sync.js b/packages/csv/dist/esm/sync.js index ed8b0856b..3ba895d2b 100644 --- a/packages/csv/dist/esm/sync.js +++ b/packages/csv/dist/esm/sync.js @@ -6774,7 +6774,9 @@ class Stringifier extends Transform { options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -6826,45 +6828,62 @@ class Stringifier extends Transform { if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -6876,18 +6895,10 @@ class Stringifier extends Transform { // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -6904,10 +6915,7 @@ class Stringifier extends Transform { const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -6918,10 +6926,7 @@ class Stringifier extends Transform { const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -6933,30 +6938,25 @@ class Stringifier extends Transform { if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -6993,7 +6993,7 @@ class Stringifier extends Transform { csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -7008,12 +7008,15 @@ class Stringifier extends Transform { if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -7040,10 +7043,10 @@ class Stringifier extends Transform { } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -7064,40 +7067,35 @@ class Stringifier extends Transform { }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } const stringify = function(records, options={}){ const data = []; - if(isBuffer$1(records)){ - const decoder = new StringDecoder(); - records = decoder.write(records); - } - function onData(record){ - if(record){ - data.push(record.toString()); - } - } const stringifier = new Stringifier(options); - stringifier.on('data', onData); + stringifier.push = function(record){ + if(record === null){ + return; + } + data.push(record.toString()); + }; for(const record of records){ - stringifier.write(record); + const err = stringifier.__transform(record, null); + if(err !== undefined) throw err; } - stringifier.end(); - stringifier.removeListener('data', onData); return data.join(''); }; diff --git a/packages/csv/dist/iife/index.js b/packages/csv/dist/iife/index.js index bbd4cef31..72fd40b59 100644 --- a/packages/csv/dist/iife/index.js +++ b/packages/csv/dist/iife/index.js @@ -6955,7 +6955,9 @@ var csv = (function (exports) { options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -7007,45 +7009,62 @@ var csv = (function (exports) { if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -7057,18 +7076,10 @@ var csv = (function (exports) { // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -7085,10 +7096,7 @@ var csv = (function (exports) { const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -7099,10 +7107,7 @@ var csv = (function (exports) { const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -7114,30 +7119,25 @@ var csv = (function (exports) { if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -7174,7 +7174,7 @@ var csv = (function (exports) { csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -7189,12 +7189,15 @@ var csv = (function (exports) { if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -7221,10 +7224,10 @@ var csv = (function (exports) { } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -7245,19 +7248,19 @@ var csv = (function (exports) { }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } diff --git a/packages/csv/dist/iife/sync.js b/packages/csv/dist/iife/sync.js index 695e530f4..7ff6f5715 100644 --- a/packages/csv/dist/iife/sync.js +++ b/packages/csv/dist/iife/sync.js @@ -6777,7 +6777,9 @@ var csv_sync = (function (exports) { options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -6829,45 +6831,62 @@ var csv_sync = (function (exports) { if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -6879,18 +6898,10 @@ var csv_sync = (function (exports) { // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -6907,10 +6918,7 @@ var csv_sync = (function (exports) { const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -6921,10 +6929,7 @@ var csv_sync = (function (exports) { const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -6936,30 +6941,25 @@ var csv_sync = (function (exports) { if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -6996,7 +6996,7 @@ var csv_sync = (function (exports) { csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -7011,12 +7011,15 @@ var csv_sync = (function (exports) { if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -7043,10 +7046,10 @@ var csv_sync = (function (exports) { } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -7067,40 +7070,35 @@ var csv_sync = (function (exports) { }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } const stringify = function(records, options={}){ const data = []; - if(isBuffer$1(records)){ - const decoder = new StringDecoder(); - records = decoder.write(records); - } - function onData(record){ - if(record){ - data.push(record.toString()); - } - } const stringifier = new Stringifier(options); - stringifier.on('data', onData); + stringifier.push = function(record){ + if(record === null){ + return; + } + data.push(record.toString()); + }; for(const record of records){ - stringifier.write(record); + const err = stringifier.__transform(record, null); + if(err !== undefined) throw err; } - stringifier.end(); - stringifier.removeListener('data', onData); return data.join(''); }; diff --git a/packages/csv/dist/umd/index.js b/packages/csv/dist/umd/index.js index fdd6d4bc6..4f7d95345 100644 --- a/packages/csv/dist/umd/index.js +++ b/packages/csv/dist/umd/index.js @@ -6958,7 +6958,9 @@ options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -7010,45 +7012,62 @@ if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -7060,18 +7079,10 @@ // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -7088,10 +7099,7 @@ const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -7102,10 +7110,7 @@ const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -7117,30 +7122,25 @@ if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -7177,7 +7177,7 @@ csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -7192,12 +7192,15 @@ if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -7224,10 +7227,10 @@ } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -7248,19 +7251,19 @@ }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } diff --git a/packages/csv/dist/umd/sync.js b/packages/csv/dist/umd/sync.js index 83608ded4..07674a18f 100644 --- a/packages/csv/dist/umd/sync.js +++ b/packages/csv/dist/umd/sync.js @@ -6780,7 +6780,9 @@ options.header = false; } // Normalize option `columns` - options.columns = this.normalize_columns(options.columns); + const [err, columns] = this.normalize_columns(options.columns); + if(err) return err; + options.columns = columns; // Normalize option `quoted` if(options.quoted === undefined || options.quoted === null){ options.quoted = false; @@ -6832,45 +6834,62 @@ if(this.state.stop === true){ return; } + const err = this.__transform(chunk); + if(err !== undefined){ + this.state.stop = true; + } + callback(err); + } + _flush(callback){ + if(this.info.records === 0){ + this.bom(); + const err = this.headers(); + if(err) callback(err); + } + callback(); + } + __transform(chunk){ // Chunk validation if(!Array.isArray(chunk) && typeof chunk !== 'object'){ - this.state.stop = true; - return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`)); + return Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`); } // Detect columns from the first record if(this.info.records === 0){ if(Array.isArray(chunk)){ if(this.options.header === true && this.options.columns === undefined){ - this.state.stop = true; - return callback(Error('Undiscoverable Columns: header option requires column option or object records')); + return Error('Undiscoverable Columns: header option requires column option or object records'); } }else if(this.options.columns === undefined){ - this.options.columns = this.normalize_columns(Object.keys(chunk)); + const [err, columns] = this.normalize_columns(Object.keys(chunk)); + if(err) return; + this.options.columns = columns; } } // Emit the header if(this.info.records === 0){ this.bom(); - this.headers(); + const err = this.headers(); + if(err) return err; } // Emit and stringify the record if an object or an array try{ this.emit('record', chunk, this.info.records); }catch(err){ - this.state.stop = true; - return this.emit('error', err); + return err; } // Convert the record into a string - let chunk_string; + let err, chunk_string; if(this.options.eof){ - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { chunk_string = chunk_string + this.options.record_delimiter; } }else { - chunk_string = this.stringify(chunk); + [err, chunk_string] = this.stringify(chunk); + if(err) return err; if(chunk_string === undefined){ return; }else { @@ -6882,18 +6901,10 @@ // Emit the csv this.info.records++; this.push(chunk_string); - callback(); - } - _flush(callback){ - if(this.info.records === 0){ - this.bom(); - this.headers(); - } - callback(); } stringify(chunk, chunkIsHeader=false){ if(typeof chunk !== 'object'){ - return chunk; + return [undefined, chunk]; } const {columns} = this.options; const record = []; @@ -6910,10 +6921,7 @@ const [err, value] = this.__cast(field, { index: i, column: i, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } // Record is a literal object @@ -6924,10 +6932,7 @@ const [err, value] = this.__cast(field, { index: i, column: columns[i].key, records: this.info.records, header: chunkIsHeader }); - if(err){ - this.emit('error', err); - return; - } + if(err) return [err]; record[i] = [value, field]; } } @@ -6939,30 +6944,25 @@ if(typeof value === "string"){ options = this.options; }else if(isObject(value)){ - // let { value, ...options } = value options = value; value = options.value; delete options.value; if(typeof value !== "string" && value !== undefined && value !== null){ - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return; + if(err) return [Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } options = {...this.options, ...options}; if((err = this.normalize(options)) !== undefined){ - this.emit("error", err); - return; + return [err]; } }else if(value === undefined || value === null){ options = this.options; }else { - this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)); - return; + return [Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`)]; } const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options; if(value){ if(typeof value !== 'string'){ - this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)); - return null; + return [Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`)]; } const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0; const containsQuote = (quote !== '') && value.indexOf(quote) >= 0; @@ -6999,7 +6999,7 @@ csvrecord += delimiter; } } - return csvrecord; + return [undefined, csvrecord]; } bom(){ if(this.options.bom !== true){ @@ -7014,12 +7014,15 @@ if(this.options.columns === undefined){ return; } + let err; let headers = this.options.columns.map(column => column.header); if(this.options.eof){ - headers = this.stringify(headers, true) + this.options.record_delimiter; + [err, headers] = this.stringify(headers, true); + headers += this.options.record_delimiter; }else { - headers = this.stringify(headers); + [err, headers] = this.stringify(headers); } + if(err) return err; this.push(headers); } __cast(value, context){ @@ -7046,10 +7049,10 @@ } normalize_columns(columns){ if(columns === undefined || columns === null){ - return undefined; + return []; } if(typeof columns !== 'object'){ - throw Error('Invalid option "columns": expect an array or an object'); + return [Error('Invalid option "columns": expect an array or an object')]; } if(!Array.isArray(columns)){ const newcolumns = []; @@ -7070,40 +7073,35 @@ }); }else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){ if(!column.key){ - throw Error('Invalid column definition: property "key" is required'); + return [Error('Invalid column definition: property "key" is required')]; } if(column.header === undefined){ column.header = column.key; } newcolumns.push(column); }else { - throw Error('Invalid column definition: expect a string or an object'); + return [Error('Invalid column definition: expect a string or an object')]; } } columns = newcolumns; } - return columns; + return [undefined, columns]; } } const stringify = function(records, options={}){ const data = []; - if(isBuffer$1(records)){ - const decoder = new StringDecoder(); - records = decoder.write(records); - } - function onData(record){ - if(record){ - data.push(record.toString()); - } - } const stringifier = new Stringifier(options); - stringifier.on('data', onData); + stringifier.push = function(record){ + if(record === null){ + return; + } + data.push(record.toString()); + }; for(const record of records){ - stringifier.write(record); + const err = stringifier.__transform(record, null); + if(err !== undefined) throw err; } - stringifier.end(); - stringifier.removeListener('data', onData); return data.join(''); }; From 9145b75012ec71a0b4152036af2275bf28c460e0 Mon Sep 17 00:00:00 2001 From: David Worms Date: Fri, 19 Nov 2021 16:05:28 +0100 Subject: [PATCH 6/7] fix(csv-stringify): node 12 compatibility in flush --- packages/csv-stringify/dist/cjs/index.cjs | 5 +++++ packages/csv-stringify/dist/cjs/sync.cjs | 5 +++++ packages/csv-stringify/dist/esm/index.js | 5 +++++ packages/csv-stringify/dist/esm/sync.js | 5 +++++ packages/csv-stringify/dist/iife/index.js | 5 +++++ packages/csv-stringify/dist/iife/sync.js | 5 +++++ packages/csv-stringify/dist/umd/index.js | 5 +++++ packages/csv-stringify/dist/umd/sync.js | 5 +++++ packages/csv-stringify/lib/index.js | 5 +++++ packages/csv/dist/cjs/index.cjs | 5 +++++ packages/csv/dist/cjs/sync.cjs | 5 +++++ packages/csv/dist/esm/index.js | 5 +++++ packages/csv/dist/esm/sync.js | 5 +++++ packages/csv/dist/iife/index.js | 5 +++++ packages/csv/dist/iife/sync.js | 5 +++++ packages/csv/dist/umd/index.js | 5 +++++ packages/csv/dist/umd/sync.js | 5 +++++ 17 files changed, 85 insertions(+) diff --git a/packages/csv-stringify/dist/cjs/index.cjs b/packages/csv-stringify/dist/cjs/index.cjs index 33dac9bd1..23fce222f 100644 --- a/packages/csv-stringify/dist/cjs/index.cjs +++ b/packages/csv-stringify/dist/cjs/index.cjs @@ -295,6 +295,11 @@ class Stringifier extends stream.Transform { callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv-stringify/dist/cjs/sync.cjs b/packages/csv-stringify/dist/cjs/sync.cjs index 2638c19b6..17adf23dc 100644 --- a/packages/csv-stringify/dist/cjs/sync.cjs +++ b/packages/csv-stringify/dist/cjs/sync.cjs @@ -295,6 +295,11 @@ class Stringifier extends stream.Transform { callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv-stringify/dist/esm/index.js b/packages/csv-stringify/dist/esm/index.js index 8d6d20678..46e8b2a8a 100644 --- a/packages/csv-stringify/dist/esm/index.js +++ b/packages/csv-stringify/dist/esm/index.js @@ -5257,6 +5257,11 @@ class Stringifier extends Transform { callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv-stringify/dist/esm/sync.js b/packages/csv-stringify/dist/esm/sync.js index 90135ed88..c4168f0e5 100644 --- a/packages/csv-stringify/dist/esm/sync.js +++ b/packages/csv-stringify/dist/esm/sync.js @@ -5257,6 +5257,11 @@ class Stringifier extends Transform { callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv-stringify/dist/iife/index.js b/packages/csv-stringify/dist/iife/index.js index 3d52ab0c9..3bbd95e9b 100644 --- a/packages/csv-stringify/dist/iife/index.js +++ b/packages/csv-stringify/dist/iife/index.js @@ -5260,6 +5260,11 @@ var csv_stringify = (function (exports) { callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv-stringify/dist/iife/sync.js b/packages/csv-stringify/dist/iife/sync.js index 4319ec924..452aa65b7 100644 --- a/packages/csv-stringify/dist/iife/sync.js +++ b/packages/csv-stringify/dist/iife/sync.js @@ -5260,6 +5260,11 @@ var csv_stringify_sync = (function (exports) { callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv-stringify/dist/umd/index.js b/packages/csv-stringify/dist/umd/index.js index 92ad6d348..9691bcd43 100644 --- a/packages/csv-stringify/dist/umd/index.js +++ b/packages/csv-stringify/dist/umd/index.js @@ -5263,6 +5263,11 @@ callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv-stringify/dist/umd/sync.js b/packages/csv-stringify/dist/umd/sync.js index 5cfc92ca7..33c7680d9 100644 --- a/packages/csv-stringify/dist/umd/sync.js +++ b/packages/csv-stringify/dist/umd/sync.js @@ -5263,6 +5263,11 @@ callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv-stringify/lib/index.js b/packages/csv-stringify/lib/index.js index c25fc8af0..175049843 100644 --- a/packages/csv-stringify/lib/index.js +++ b/packages/csv-stringify/lib/index.js @@ -314,6 +314,11 @@ class Stringifier extends Transform { callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv/dist/cjs/index.cjs b/packages/csv/dist/cjs/index.cjs index ddaec3184..4dba281f5 100644 --- a/packages/csv/dist/cjs/index.cjs +++ b/packages/csv/dist/cjs/index.cjs @@ -1987,6 +1987,11 @@ class Stringifier extends stream.Transform { callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv/dist/cjs/sync.cjs b/packages/csv/dist/cjs/sync.cjs index f73ab412c..60de4bfbd 100644 --- a/packages/csv/dist/cjs/sync.cjs +++ b/packages/csv/dist/cjs/sync.cjs @@ -1809,6 +1809,11 @@ class Stringifier extends stream.Transform { callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv/dist/esm/index.js b/packages/csv/dist/esm/index.js index b6879a2b8..6bd538035 100644 --- a/packages/csv/dist/esm/index.js +++ b/packages/csv/dist/esm/index.js @@ -7013,6 +7013,11 @@ class Stringifier extends Transform { callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv/dist/esm/sync.js b/packages/csv/dist/esm/sync.js index 3ba895d2b..5dc6b198f 100644 --- a/packages/csv/dist/esm/sync.js +++ b/packages/csv/dist/esm/sync.js @@ -6835,6 +6835,11 @@ class Stringifier extends Transform { callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv/dist/iife/index.js b/packages/csv/dist/iife/index.js index 72fd40b59..7cb7e456a 100644 --- a/packages/csv/dist/iife/index.js +++ b/packages/csv/dist/iife/index.js @@ -7016,6 +7016,11 @@ var csv = (function (exports) { callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv/dist/iife/sync.js b/packages/csv/dist/iife/sync.js index 7ff6f5715..945d26f8f 100644 --- a/packages/csv/dist/iife/sync.js +++ b/packages/csv/dist/iife/sync.js @@ -6838,6 +6838,11 @@ var csv_sync = (function (exports) { callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv/dist/umd/index.js b/packages/csv/dist/umd/index.js index 4f7d95345..792fcb39c 100644 --- a/packages/csv/dist/umd/index.js +++ b/packages/csv/dist/umd/index.js @@ -7019,6 +7019,11 @@ callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); diff --git a/packages/csv/dist/umd/sync.js b/packages/csv/dist/umd/sync.js index 07674a18f..c77783a85 100644 --- a/packages/csv/dist/umd/sync.js +++ b/packages/csv/dist/umd/sync.js @@ -6841,6 +6841,11 @@ callback(err); } _flush(callback){ + if(this.state.stop === true){ + // Note, Node.js 12 call flush even after an error, we must prevent + // `callback` from being called in flush without any error. + return; + } if(this.info.records === 0){ this.bom(); const err = this.headers(); From 8bdbe7ef8b4ff626fcd79d39b273a8bee7f278fb Mon Sep 17 00:00:00 2001 From: David Worms Date: Fri, 19 Nov 2021 16:06:03 +0100 Subject: [PATCH 7/7] chore(release): publish - csv-issues-cjs@0.0.2 - csv-demo-webpack@0.1.1 - csv-stringify@6.0.4 - csv@6.0.4 --- demo/issues-cjs/CHANGELOG.md | 12 ++++++++++++ demo/issues-cjs/package.json | 2 +- demo/webpack/CHANGELOG.md | 8 ++++++++ demo/webpack/package.json | 2 +- packages/csv-stringify/CHANGELOG.md | 12 ++++++++++++ packages/csv-stringify/package.json | 2 +- packages/csv/CHANGELOG.md | 11 +++++++++++ packages/csv/package.json | 4 ++-- 8 files changed, 48 insertions(+), 5 deletions(-) diff --git a/demo/issues-cjs/CHANGELOG.md b/demo/issues-cjs/CHANGELOG.md index 01c89665b..a8200653a 100644 --- a/demo/issues-cjs/CHANGELOG.md +++ b/demo/issues-cjs/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.0.2](https://github.com/adaltas/node-csv/compare/csv-issues-cjs@0.0.1...csv-issues-cjs@0.0.2) (2021-11-19) + + +### Bug Fixes + +* **csv-issues-esm:** illustrate issue [#300](https://github.com/adaltas/node-csv/issues/300) ([7358f9d](https://github.com/adaltas/node-csv/commit/7358f9d2b150655579dadf2af1aa64206fc7e2fa)) +* **csv-stringify:** catch error with sync api, fix [#296](https://github.com/adaltas/node-csv/issues/296) ([e157f40](https://github.com/adaltas/node-csv/commit/e157f407eeffe5bcfb179cb20476169037bfb4f1)) + + + + + ## 0.0.1 (2021-11-19) diff --git a/demo/issues-cjs/package.json b/demo/issues-cjs/package.json index e0472073a..f48054540 100644 --- a/demo/issues-cjs/package.json +++ b/demo/issues-cjs/package.json @@ -1,6 +1,6 @@ { "name": "csv-issues-cjs", - "version": "0.0.1", + "version": "0.0.2", "main": "index.js", "license": "MIT", "private": true, diff --git a/demo/webpack/CHANGELOG.md b/demo/webpack/CHANGELOG.md index 82be71643..e310c2a80 100644 --- a/demo/webpack/CHANGELOG.md +++ b/demo/webpack/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [0.1.1](https://github.com/adaltas/node-csv/compare/csv-demo-webpack@0.1.0...csv-demo-webpack@0.1.1) (2021-11-19) + +**Note:** Version bump only for package csv-demo-webpack + + + + + # 0.1.0 (2021-11-19) diff --git a/demo/webpack/package.json b/demo/webpack/package.json index c302b63ef..069ac56e5 100644 --- a/demo/webpack/package.json +++ b/demo/webpack/package.json @@ -1,6 +1,6 @@ { "name": "csv-demo-webpack", - "version": "0.1.0", + "version": "0.1.1", "description": "", "private": true, "scripts": { diff --git a/packages/csv-stringify/CHANGELOG.md b/packages/csv-stringify/CHANGELOG.md index e33c16e92..d21235729 100644 --- a/packages/csv-stringify/CHANGELOG.md +++ b/packages/csv-stringify/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [6.0.4](https://github.com/adaltas/node-csv/compare/csv-stringify@6.0.3...csv-stringify@6.0.4) (2021-11-19) + + +### Bug Fixes + +* **csv-stringify:** catch error with sync api, fix [#296](https://github.com/adaltas/node-csv/issues/296) ([e157f40](https://github.com/adaltas/node-csv/commit/e157f407eeffe5bcfb179cb20476169037bfb4f1)) +* **csv-stringify:** node 12 compatibility in flush ([9145b75](https://github.com/adaltas/node-csv/commit/9145b75012ec71a0b4152036af2275bf28c460e0)) + + + + + ## [6.0.3](https://github.com/adaltas/node-csv/compare/csv-stringify@6.0.2...csv-stringify@6.0.3) (2021-11-19) diff --git a/packages/csv-stringify/package.json b/packages/csv-stringify/package.json index f06d19a9a..0e0ac9e4e 100644 --- a/packages/csv-stringify/package.json +++ b/packages/csv-stringify/package.json @@ -1,5 +1,5 @@ { - "version": "6.0.3", + "version": "6.0.4", "name": "csv-stringify", "description": "CSV stringifier implementing the Node.js `stream.Transform` API", "keywords": [ diff --git a/packages/csv/CHANGELOG.md b/packages/csv/CHANGELOG.md index befaba5a6..e06366511 100644 --- a/packages/csv/CHANGELOG.md +++ b/packages/csv/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [6.0.4](https://github.com/adaltas/node-csv/compare/csv@6.0.3...csv@6.0.4) (2021-11-19) + + +### Bug Fixes + +* **csv-stringify:** node 12 compatibility in flush ([9145b75](https://github.com/adaltas/node-csv/commit/9145b75012ec71a0b4152036af2275bf28c460e0)) + + + + + ## [6.0.3](https://github.com/adaltas/node-csv/compare/csv@6.0.2...csv@6.0.3) (2021-11-19) diff --git a/packages/csv/package.json b/packages/csv/package.json index ae14129f0..c768186bd 100644 --- a/packages/csv/package.json +++ b/packages/csv/package.json @@ -1,6 +1,6 @@ { "name": "csv", - "version": "6.0.3", + "version": "6.0.4", "description": "A mature CSV toolset with simple api, full of options and tested against large datasets.", "keywords": [ "node", @@ -23,7 +23,7 @@ "dependencies": { "csv-generate": "^4.0.3", "csv-parse": "^5.0.3", - "csv-stringify": "^6.0.3", + "csv-stringify": "^6.0.4", "stream-transform": "^3.0.3" }, "devDependencies": {