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(''); };