121 lines
3.2 KiB
JavaScript
121 lines
3.2 KiB
JavaScript
|
|
/*
|
|
CSV Stringify
|
|
|
|
Please look at the [project documentation](https://csv.js.org/stringify/) for
|
|
additional information.
|
|
*/
|
|
|
|
import { Transform } from 'stream';
|
|
import { CsvError } from './api/CsvError.js';
|
|
import { is_object } from './utils/is_object.js';
|
|
import { stringifier } from './api/index.js';
|
|
import { normalize_options } from './api/normalize_options.js';
|
|
|
|
class Stringifier extends Transform {
|
|
constructor(opts = {}){
|
|
super({...{writableObjectMode: true}, ...opts});
|
|
const [err, options] = normalize_options(opts);
|
|
if(err !== undefined) throw err;
|
|
// Expose options
|
|
this.options = options;
|
|
// Internal state
|
|
this.state = {
|
|
stop: false
|
|
};
|
|
// Information
|
|
this.info = {
|
|
records: 0
|
|
};
|
|
this.api = stringifier(this.options, this.state, this.info);
|
|
this.api.options.on_record = (...args) => {
|
|
this.emit('record', ...args);
|
|
};
|
|
}
|
|
_transform(chunk, encoding, callback){
|
|
if(this.state.stop === true){
|
|
return;
|
|
}
|
|
const err = this.api.__transform(chunk, this.push.bind(this));
|
|
if(err !== undefined){
|
|
this.state.stop = true;
|
|
}
|
|
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.api.bom(this.push.bind(this));
|
|
const err = this.api.headers(this.push.bind(this));
|
|
if(err) callback(err);
|
|
}
|
|
callback();
|
|
}
|
|
}
|
|
|
|
const stringify = function(){
|
|
let data, options, callback;
|
|
for(const i in arguments){
|
|
const argument = arguments[i];
|
|
const type = typeof argument;
|
|
if(data === undefined && (Array.isArray(argument))){
|
|
data = argument;
|
|
}else if(options === undefined && is_object(argument)){
|
|
options = argument;
|
|
}else if(callback === undefined && type === 'function'){
|
|
callback = argument;
|
|
}else{
|
|
throw new CsvError('CSV_INVALID_ARGUMENT', [
|
|
'Invalid argument:',
|
|
`got ${JSON.stringify(argument)} at index ${i}`
|
|
]);
|
|
}
|
|
}
|
|
const stringifier = new Stringifier(options);
|
|
if(callback){
|
|
const chunks = [];
|
|
stringifier.on('readable', function(){
|
|
let chunk;
|
|
while((chunk = this.read()) !== null){
|
|
chunks.push(chunk);
|
|
}
|
|
});
|
|
stringifier.on('error', function(err){
|
|
callback(err);
|
|
});
|
|
stringifier.on('end', function(){
|
|
try {
|
|
callback(undefined, chunks.join(''));
|
|
} catch (err) {
|
|
// This can happen if the `chunks` is extremely long; it may throw
|
|
// "Cannot create a string longer than 0x1fffffe8 characters"
|
|
// See [#386](https://github.com/adaltas/node-csv/pull/386)
|
|
callback(err);
|
|
return;
|
|
}
|
|
});
|
|
}
|
|
if(data !== undefined){
|
|
const writer = function(){
|
|
for(const record of data){
|
|
stringifier.write(record);
|
|
}
|
|
stringifier.end();
|
|
};
|
|
// Support Deno, Rollup doesnt provide a shim for setImmediate
|
|
if(typeof setImmediate === 'function'){
|
|
setImmediate(writer);
|
|
}else{
|
|
setTimeout(writer, 0);
|
|
}
|
|
}
|
|
return stringifier;
|
|
};
|
|
|
|
// export default stringify
|
|
export { stringify, CsvError, Stringifier };
|