Socket
Socket
Sign inDemoInstall

csv-stringify

Package Overview
Dependencies
Maintainers
1
Versions
84
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

csv-stringify - npm Package Compare versions

Comparing version 5.6.5 to 6.0.0

dist/cjs/index.cjs

188

lib/index.d.ts
/// <reference types="node" />
import * as stream from "stream";
export = stringify
declare function stringify(callback?: stringify.Callback): stringify.Stringifier
declare function stringify(options: stringify.Options, callback?: stringify.Callback): stringify.Stringifier
declare function stringify(input: stringify.Input, callback?: stringify.Callback): stringify.Stringifier
declare function stringify(input: stringify.Input, options?: stringify.Options, callback?: stringify.Callback): stringify.Stringifier
declare namespace stringify {
type Callback = (err: Error | undefined, output: string) => void
type RecordDelimiter = string | Buffer | 'auto' | 'unix' | 'mac' | 'windows' | 'ascii' | 'unicode'
type Cast<T> = (value: T, context: CastingContext) => string
type PlainObject<T> = Record<string, T>
type Input = any[]
interface ColumnOption {
key: string
header?: string
}
interface CastingContext {
readonly column?: number | string;
readonly header: boolean;
readonly index: number;
readonly records: number;
}
interface Options {
export type Callback = (err: Error | undefined, output: string) => void
export type RecordDelimiter = string | Buffer | 'auto' | 'unix' | 'mac' | 'windows' | 'ascii' | 'unicode'
export type Cast<T> = (value: T, context: CastingContext) => string
export type PlainObject<T> = Record<string, T>
export type Input = any[]
export interface ColumnOption {
key: string
header?: string
}
export interface CastingContext {
readonly column?: number | string;
readonly header: boolean;
readonly index: number;
readonly records: number;
}
export interface Options extends stream.TransformOptions {
/**
* Prepend the byte order mark (BOM) to the output stream.
*/
bom?: boolean
/**
* Key-value object which defines custom cast for certain data types
*/
cast?: {
boolean?: Cast<boolean>
date?: Cast<Date>
number?: Cast<number>
/**
* Prepend the byte order mark (BOM) to the output stream.
* Custom formatter for generic object values
*/
bom?: boolean
/**
* Key-value object which defines custom cast for certain data types
*/
cast?: {
boolean?: Cast<boolean>
date?: Cast<Date>
number?: Cast<number>
/**
* Custom formatter for generic object values
*/
object?: Cast<Record<string, any>>
string?: Cast<string>
}
/**
* List of fields, applied when `transform` returns an object
* order matters
* read the transformer documentation for additionnal information
* columns are auto discovered in the first record when the user write objects
* can refer to nested properties of the input JSON
* see the "header" option on how to print columns names on the first line
*/
columns?: string[] | PlainObject<string> | ColumnOption[]
/**
* Set the field delimiter, one character only, defaults to a comma.
*/
delimiter?: string | Buffer
/**
* Add the value of "options.RecordDelimiter" on the last line, default to true.
*/
eof?: boolean
/**
* Defaults to the escape read option.
*/
escape?: string | Buffer
/**
* Display the column names on the first line if the columns option is provided or discovered.
*/
header?: boolean
/**
* The quote characters, defaults to the ", an empty quote value will preserve the original field.
*/
quote?: string | Buffer | boolean
/**
* Boolean, default to false, quote all the non-empty fields even if not required.
*/
quoted?: boolean
object?: Cast<Record<string, any>>
string?: Cast<string>
}
/**
* List of fields, applied when `transform` returns an object
* order matters
* read the transformer documentation for additionnal information
* columns are auto discovered in the first record when the user write objects
* can refer to nested properties of the input JSON
* see the "header" option on how to print columns names on the first line
*/
columns?: string[] | PlainObject<string> | ColumnOption[]
/**
* Set the field delimiter, one character only, defaults to a comma.
*/
delimiter?: string | Buffer
/**
* Add the value of "options.RecordDelimiter" on the last line, default to true.
*/
eof?: boolean
/**
* Defaults to the escape read option.
*/
escape?: string | Buffer
/**
* Display the column names on the first line if the columns option is provided or discovered.
*/
header?: boolean
/**
* The quote characters, defaults to the ", an empty quote value will preserve the original field.
*/
quote?: string | Buffer | boolean
/**
* Boolean, default to false, quote all the non-empty fields even if not required.
*/
quoted?: boolean
/**
* Boolean, no default, quote empty fields and overrides `quoted_string` on empty strings when defined.
*/
quoted_empty?: boolean
/**
* String or RegExp, no default, quote all fields matching a regular expression.
*/
quoted_match?: string | RegExp
/**
* Boolean, default to false, quote all fields of type string even if not required.
*/
quoted_string?: boolean
/**
* String used to delimit record rows or a special value
* special values are 'auto', 'unix', 'mac', 'windows', 'ascii', 'unicode'
* defaults to 'auto' (discovered in source or 'unix' if no source is specified).
*/
record_delimiter?: RecordDelimiter
}
class Stringifier extends stream.Transform {
constructor(options: Options)
readonly options: Options
}
/**
* Boolean, no default, quote empty fields and overrides `quoted_string` on empty strings when defined.
*/
quoted_empty?: boolean
/**
* String or RegExp, no default, quote all fields matching a regular expression.
*/
quoted_match?: string | RegExp
/**
* Boolean, default to false, quote all fields of type string even if not required.
*/
quoted_string?: boolean
/**
* String used to delimit record rows or a special value
* special values are 'auto', 'unix', 'mac', 'windows', 'ascii', 'unicode'
* defaults to 'auto' (discovered in source or 'unix' if no source is specified).
*/
record_delimiter?: RecordDelimiter
}
export class Stringifier extends stream.Transform {
constructor(options: Options)
readonly options: Options
}
declare function stringify(callback?: Callback): Stringifier
declare function stringify(options: Options, callback?: Callback): Stringifier
declare function stringify(input: Input, callback?: Callback): Stringifier
declare function stringify(input: Input, options?: Options, callback?: Callback): Stringifier
// export default stringify
export { stringify }

@@ -9,45 +9,151 @@

const { Transform } = require('stream')
const bom_utf8 = Buffer.from([239, 187, 191])
import { Transform } from 'stream';
const bom_utf8 = Buffer.from([239, 187, 191]);
class CsvError extends Error {
constructor(code, message, ...contexts) {
if(Array.isArray(message)) message = message.join(' ');
super(message);
if(Error.captureStackTrace !== undefined){
Error.captureStackTrace(this, CsvError);
}
this.code = code;
for(const context of contexts){
for(const key in context){
const value = context[key];
this[key] = Buffer.isBuffer(value) ? value.toString() : value == null ? value : JSON.parse(JSON.stringify(value));
}
}
}
}
const isObject = function(obj){
return typeof obj === 'object' && obj !== null && ! Array.isArray(obj);
};
const underscore = function(str){
return str.replace(/([A-Z])/g, function(_, match){
return '_' + match.toLowerCase();
});
};
// Lodash implementation of `get`
const charCodeOfDot = '.'.charCodeAt(0);
const reEscapeChar = /\\(\\)?/g;
const rePropName = RegExp(
// Match anything that isn't a dot or bracket.
'[^.[\\]]+' + '|' +
// Or match property names within brackets.
'\\[(?:' +
// Match a non-string expression.
'([^"\'][^[]*)' + '|' +
// Or match strings (supports escaping characters).
'(["\'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2' +
')\\]'+ '|' +
// Or match "" as the space between consecutive dots or empty brackets.
'(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))'
, 'g');
const reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/;
const reIsPlainProp = /^\w*$/;
const getTag = function(value){
if(!value)
value === undefined ? '[object Undefined]' : '[object Null]';
return Object.prototype.toString.call(value);
};
const isSymbol = function(value){
const type = typeof value;
return type === 'symbol' || (type === 'object' && value && getTag(value) === '[object Symbol]');
};
const isKey = function(value, object){
if(Array.isArray(value)){
return false;
}
const type = typeof value;
if(type === 'number' || type === 'symbol' || type === 'boolean' || !value || isSymbol(value)){
return true;
}
return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
(object != null && value in Object(object));
};
const stringToPath = function(string){
const result = [];
if(string.charCodeAt(0) === charCodeOfDot){
result.push('');
}
string.replace(rePropName, function(match, expression, quote, subString){
let key = match;
if(quote){
key = subString.replace(reEscapeChar, '$1');
}else if(expression){
key = expression.trim();
}
result.push(key);
});
return result;
};
const castPath = function(value, object){
if(Array.isArray(value)){
return value;
} else {
return isKey(value, object) ? [value] : stringToPath(value);
}
};
const toKey = function(value){
if(typeof value === 'string' || isSymbol(value))
return value;
const result = `${value}`;
// eslint-disable-next-line
return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
};
const get = function(object, path){
path = castPath(path, object);
let index = 0;
const length = path.length;
while(object != null && index < length){
object = object[toKey(path[index++])];
}
return (index && index === length) ? object : undefined;
};
class Stringifier extends Transform {
constructor(opts = {}){
super({...{writableObjectMode: true}, ...opts})
const options = {}
let err
super({...{writableObjectMode: true}, ...opts});
const options = {};
let err;
// Merge with user options
for(let opt in opts){
options[underscore(opt)] = opts[opt]
for(const opt in opts){
options[underscore(opt)] = opts[opt];
}
if(err = this.normalize(options)) throw err
if((err = this.normalize(options)) !== undefined) throw err;
switch(options.record_delimiter){
case 'auto':
options.record_delimiter = null
break
case 'unix':
options.record_delimiter = "\n"
break
case 'mac':
options.record_delimiter = "\r"
break
case 'windows':
options.record_delimiter = "\r\n"
break
case 'ascii':
options.record_delimiter = "\u001e"
break
case 'unicode':
options.record_delimiter = "\u2028"
break
case 'auto':
options.record_delimiter = null;
break;
case 'unix':
options.record_delimiter = "\n";
break;
case 'mac':
options.record_delimiter = "\r";
break;
case 'windows':
options.record_delimiter = "\r\n";
break;
case 'ascii':
options.record_delimiter = "\u001e";
break;
case 'unicode':
options.record_delimiter = "\u2028";
break;
}
// Expose options
this.options = options
this.options = options;
// Internal state
this.state = {
stop: false
}
};
// Information
this.info = {
records: 0
}
};
}

@@ -57,3 +163,3 @@ normalize(options){

if(options.bom === undefined || options.bom === null || options.bom === false){
options.bom = false
options.bom = false;
}else if(options.bom !== true){

@@ -63,9 +169,9 @@ return new CsvError('CSV_OPTION_BOOLEAN_INVALID_TYPE', [

`got ${JSON.stringify(options.bom)}`
])
]);
}
// Normalize option `delimiter`
if(options.delimiter === undefined || options.delimiter === null){
options.delimiter = ','
options.delimiter = ',';
}else if(Buffer.isBuffer(options.delimiter)){
options.delimiter = options.delimiter.toString()
options.delimiter = options.delimiter.toString();
}else if(typeof options.delimiter !== 'string'){

@@ -75,13 +181,13 @@ return new CsvError('CSV_OPTION_DELIMITER_INVALID_TYPE', [

`got ${JSON.stringify(options.delimiter)}`
])
]);
}
// Normalize option `quote`
if(options.quote === undefined || options.quote === null){
options.quote = '"'
options.quote = '"';
}else if(options.quote === true){
options.quote = '"'
options.quote = '"';
}else if(options.quote === false){
options.quote = ''
options.quote = '';
}else if (Buffer.isBuffer(options.quote)){
options.quote = options.quote.toString()
options.quote = options.quote.toString();
}else if(typeof options.quote !== 'string'){

@@ -91,7 +197,7 @@ return new CsvError('CSV_OPTION_QUOTE_INVALID_TYPE', [

`got ${JSON.stringify(options.quote)}`
])
]);
}
// Normalize option `quoted`
if(options.quoted === undefined || options.quoted === null){
options.quoted = false
options.quoted = false;
}else{

@@ -102,3 +208,3 @@ // todo

if(options.quoted_empty === undefined || options.quoted_empty === null){
options.quoted_empty = undefined
options.quoted_empty = undefined;
}else{

@@ -109,12 +215,12 @@ // todo

if(options.quoted_match === undefined || options.quoted_match === null || options.quoted_match === false){
options.quoted_match = null
options.quoted_match = null;
}else if(!Array.isArray(options.quoted_match)){
options.quoted_match = [options.quoted_match]
options.quoted_match = [options.quoted_match];
}
if(options.quoted_match){
for(let quoted_match of options.quoted_match){
const isString = typeof quoted_match === 'string'
const isRegExp = quoted_match instanceof RegExp
for(const quoted_match of options.quoted_match){
const isString = typeof quoted_match === 'string';
const isRegExp = quoted_match instanceof RegExp;
if(!isString && !isRegExp){
return Error(`Invalid Option: quoted_match must be a string or a regex, got ${JSON.stringify(quoted_match)}`)
return Error(`Invalid Option: quoted_match must be a string or a regex, got ${JSON.stringify(quoted_match)}`);
}

@@ -125,3 +231,3 @@ }

if(options.quoted_string === undefined || options.quoted_string === null){
options.quoted_string = false
options.quoted_string = false;
}else{

@@ -132,3 +238,3 @@ // todo

if(options.eof === undefined || options.eof === null){
options.eof = true
options.eof = true;
}else{

@@ -139,14 +245,14 @@ // todo

if(options.escape === undefined || options.escape === null){
options.escape = '"'
options.escape = '"';
}else if(Buffer.isBuffer(options.escape)){
options.escape = options.escape.toString()
options.escape = options.escape.toString();
}else if(typeof options.escape !== 'string'){
return Error(`Invalid Option: escape must be a buffer or a string, got ${JSON.stringify(options.escape)}`)
return Error(`Invalid Option: escape must be a buffer or a string, got ${JSON.stringify(options.escape)}`);
}
if (options.escape.length > 1){
return Error(`Invalid Option: escape must be one character, got ${options.escape.length} characters`)
return Error(`Invalid Option: escape must be one character, got ${options.escape.length} characters`);
}
// Normalize option `header`
if(options.header === undefined || options.header === null){
options.header = false
options.header = false;
}else{

@@ -156,6 +262,6 @@ // todo

// Normalize option `columns`
options.columns = this.normalize_columns(options.columns)
options.columns = this.normalize_columns(options.columns);
// Normalize option `quoted`
if(options.quoted === undefined || options.quoted === null){
options.quoted = false
options.quoted = false;
}else{

@@ -166,3 +272,3 @@ // todo

if(options.cast === undefined || options.cast === null){
options.cast = {}
options.cast = {};
}else{

@@ -174,3 +280,3 @@ // todo

// Cast boolean to string by default
options.cast.bigint = value => '' + value
options.cast.bigint = value => '' + value;
}

@@ -180,3 +286,3 @@ // Normalize option cast.boolean

// Cast boolean to string by default
options.cast.boolean = value => value ? '1' : ''
options.cast.boolean = value => value ? '1' : '';
}

@@ -186,3 +292,3 @@ // Normalize option cast.date

// Cast date to timestamp string by default
options.cast.date = value => '' + value.getTime()
options.cast.date = value => '' + value.getTime();
}

@@ -192,3 +298,3 @@ // Normalize option cast.number

// Cast number to string using native casting by default
options.cast.number = value => '' + value
options.cast.number = value => '' + value;
}

@@ -198,3 +304,3 @@ // Normalize option cast.object

// Stringify object as JSON by default
options.cast.object = value => JSON.stringify(value)
options.cast.object = value => JSON.stringify(value);
}

@@ -204,11 +310,11 @@ // Normalize option cast.string

// Leave string untouched
options.cast.string = function(value){return value}
options.cast.string = function(value){return value;};
}
// Normalize option `record_delimiter`
if(options.record_delimiter === undefined || options.record_delimiter === null){
options.record_delimiter = '\n'
options.record_delimiter = '\n';
}else if(Buffer.isBuffer(options.record_delimiter)){
options.record_delimiter = options.record_delimiter.toString()
options.record_delimiter = options.record_delimiter.toString();
}else if(typeof options.record_delimiter !== 'string'){
return Error(`Invalid Option: record_delimiter must be a buffer or a string, got ${JSON.stringify(options.record_delimiter)}`)
return Error(`Invalid Option: record_delimiter must be a buffer or a string, got ${JSON.stringify(options.record_delimiter)}`);
}

@@ -218,8 +324,8 @@ }

if(this.state.stop === true){
return
return;
}
// 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)}`))
this.state.stop = true;
return callback(Error(`Invalid Record: expect an array or an object, got ${JSON.stringify(chunk)}`));
}

@@ -229,8 +335,8 @@ // Detect columns from the first record

if(Array.isArray(chunk)){
if(this.options.header === true && !this.options.columns){
this.state.stop = true
return callback(Error('Undiscoverable Columns: header option requires column option or object records'))
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'));
}
}else if(this.options.columns === undefined || this.options.columns === null){
this.options.columns = this.normalize_columns(Object.keys(chunk))
}else if(this.options.columns === undefined){
this.options.columns = this.normalize_columns(Object.keys(chunk));
}

@@ -240,28 +346,28 @@ }

if(this.info.records === 0){
this.bom()
this.headers()
this.bom();
this.headers();
}
// Emit and stringify the record if an object or an array
try{
this.emit('record', chunk, this.info.records)
this.emit('record', chunk, this.info.records);
}catch(err){
this.state.stop = true
return this.emit('error', err)
this.state.stop = true;
return this.emit('error', err);
}
// Convert the record into a string
let chunk_string
let chunk_string;
if(this.options.eof){
chunk_string = this.stringify(chunk)
chunk_string = this.stringify(chunk);
if(chunk_string === undefined){
return
return;
}else{
chunk_string = chunk_string + this.options.record_delimiter
chunk_string = chunk_string + this.options.record_delimiter;
}
}else{
chunk_string = this.stringify(chunk)
chunk_string = this.stringify(chunk);
if(chunk_string === undefined){
return
return;
}else{
if(this.options.header || this.info.records){
chunk_string = this.options.record_delimiter + chunk_string
chunk_string = this.options.record_delimiter + chunk_string;
}

@@ -271,19 +377,19 @@ }

// Emit the csv
this.info.records++
this.push(chunk_string)
callback()
this.info.records++;
this.push(chunk_string);
callback();
}
_flush(callback){
if(this.info.records === 0){
this.bom()
this.headers()
this.bom();
this.headers();
}
callback()
callback();
}
stringify(chunk, chunkIsHeader=false){
if(typeof chunk !== 'object'){
return chunk
return chunk;
}
const {columns, header} = this.options
const record = []
const {columns} = this.options;
const record = [];
// Record is an array

@@ -294,154 +400,142 @@ if(Array.isArray(chunk)){

if(columns){
chunk.splice(columns.length)
chunk.splice(columns.length);
}
// Cast record elements
for(let i=0; i<chunk.length; i++){
const field = chunk[i]
const field = chunk[i];
const [err, value] = this.__cast(field, {
index: i, column: i, records: this.info.records, header: chunkIsHeader
})
});
if(err){
this.emit('error', err)
return
this.emit('error', err);
return;
}
record[i] = [value, field]
record[i] = [value, field];
}
// Record is a literal object
// `columns` is always defined: it is either provided or discovered.
}else{
if(columns){
for(let i=0; i<columns.length; i++){
const field = get(chunk, columns[i].key)
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
}
record[i] = [value, field]
for(let i=0; i<columns.length; i++){
const field = get(chunk, columns[i].key);
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;
}
}else{
for(let column of chunk){
const field = chunk[column]
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
}
record.push([value, field])
}
record[i] = [value, field];
}
}
let csvrecord = ''
let csvrecord = '';
for(let i=0; i<record.length; i++){
let options, err
let [value, field] = record[i]
let options, err;
// eslint-disable-next-line
let [value, field] = record[i];
if(typeof value === "string"){
options = this.options
options = this.options;
}else if(isObject(value)){
// let { value, ...options } = value
options = value
value = options.value
delete 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
this.emit("error", Error(`Invalid Casting Value: returned value must return a string, null or undefined, got ${JSON.stringify(value)}`));
return;
}
options = {...this.options, ...options}
if(err = this.normalize(options)){
this.emit("error", err)
return
options = {...this.options, ...options};
if((err = this.normalize(options)) !== undefined){
this.emit("error", err);
return;
}
}else if(value === undefined || value === null){
options = this.options
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
this.emit("error", Error(`Invalid Casting Value: returned value must return a string, an object, null or undefined, got ${JSON.stringify(value)}`));
return;
}
const {delimiter, escape, quote, quoted, quoted_empty, quoted_string, quoted_match, record_delimiter} = options
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
this.emit("error", Error(`Formatter must return a string, null or undefined, got ${JSON.stringify(value)}`));
return null;
}
const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0
const containsQuote = (quote !== '') && value.indexOf(quote) >= 0
const containsEscape = value.indexOf(escape) >= 0 && (escape !== quote)
const containsRecordDelimiter = value.indexOf(record_delimiter) >= 0
const quotedString = quoted_string && typeof field === 'string'
let quotedMatch = quoted_match && quoted_match.filter( quoted_match => {
const containsdelimiter = delimiter.length && value.indexOf(delimiter) >= 0;
const containsQuote = (quote !== '') && value.indexOf(quote) >= 0;
const containsEscape = value.indexOf(escape) >= 0 && (escape !== quote);
const containsRecordDelimiter = value.indexOf(record_delimiter) >= 0;
const quotedString = quoted_string && typeof field === 'string';
let quotedMatch = quoted_match && quoted_match.filter(quoted_match => {
if(typeof quoted_match === 'string'){
return value.indexOf(quoted_match) !== -1
return value.indexOf(quoted_match) !== -1;
}else{
return quoted_match.test(value)
return quoted_match.test(value);
}
})
quotedMatch = quotedMatch && quotedMatch.length > 0
const shouldQuote = containsQuote === true || containsdelimiter || containsRecordDelimiter || quoted || quotedString || quotedMatch
});
quotedMatch = quotedMatch && quotedMatch.length > 0;
const shouldQuote = containsQuote === true || containsdelimiter || containsRecordDelimiter || quoted || quotedString || quotedMatch;
if(shouldQuote === true && containsEscape === true){
const regexp = escape === '\\'
? new RegExp(escape + escape, 'g')
: new RegExp(escape, 'g')
value = value.replace(regexp, escape + escape)
? new RegExp(escape + escape, 'g')
: new RegExp(escape, 'g');
value = value.replace(regexp, escape + escape);
}
if(containsQuote === true){
const regexp = new RegExp(quote,'g')
value = value.replace(regexp, escape + quote)
const regexp = new RegExp(quote,'g');
value = value.replace(regexp, escape + quote);
}
if(shouldQuote === true){
value = quote + value + quote
value = quote + value + quote;
}
csvrecord += value
csvrecord += value;
}else if(quoted_empty === true || (field === '' && quoted_string === true && quoted_empty !== false)){
csvrecord += quote + quote
csvrecord += quote + quote;
}
if(i !== record.length - 1){
csvrecord += delimiter
csvrecord += delimiter;
}
}
return csvrecord
return csvrecord;
}
bom(){
if(this.options.bom !== true){
return
return;
}
this.push(bom_utf8)
this.push(bom_utf8);
}
headers(){
if(this.options.header === false){
return
return;
}
if(this.options.columns === undefined){
return
return;
}
let headers = this.options.columns.map(column => column.header)
let headers = this.options.columns.map(column => column.header);
if(this.options.eof){
headers = this.stringify(headers, true) + this.options.record_delimiter
headers = this.stringify(headers, true) + this.options.record_delimiter;
}else{
headers = this.stringify(headers)
headers = this.stringify(headers);
}
this.push(headers)
this.push(headers);
}
__cast(value, context){
const type = typeof value
const type = typeof value;
try{
if(type === 'string'){ // Fine for 99% of the cases
return [undefined, this.options.cast.string(value, context)]
return [undefined, this.options.cast.string(value, context)];
}else if(type === 'bigint'){
return [undefined, this.options.cast.bigint(value, context)]
return [undefined, this.options.cast.bigint(value, context)];
}else if(type === 'number'){
return [undefined, this.options.cast.number(value, context)]
return [undefined, this.options.cast.number(value, context)];
}else if(type === 'boolean'){
return [undefined, this.options.cast.boolean(value, context)]
return [undefined, this.options.cast.boolean(value, context)];
}else if(value instanceof Date){
return [undefined, this.options.cast.date(value, context)]
return [undefined, this.options.cast.date(value, context)];
}else if(type === 'object' && value !== null){
return [undefined, this.options.cast.object(value, context)]
return [undefined, this.options.cast.object(value, context)];
}else{
return [undefined, value, value]
return [undefined, value, value];
}
}catch(err){
return [err]
return [err];
}

@@ -451,19 +545,19 @@ }

if(columns === undefined || columns === null){
return undefined
return undefined;
}
if(typeof columns !== 'object'){
throw Error('Invalid option "columns": expect an array or an object')
throw Error('Invalid option "columns": expect an array or an object');
}
if(!Array.isArray(columns)){
const newcolumns = []
for(let k in columns){
const newcolumns = [];
for(const k in columns){
newcolumns.push({
key: k,
header: columns[k]
})
});
}
columns = newcolumns
columns = newcolumns;
}else{
const newcolumns = []
for(let column of columns){
const newcolumns = [];
for(const column of columns){
if(typeof column === 'string'){

@@ -473,18 +567,18 @@ newcolumns.push({

header: column
})
});
}else if(typeof column === 'object' && column !== undefined && !Array.isArray(column)){
if(!column.key){
throw Error('Invalid column definition: property "key" is required')
throw Error('Invalid column definition: property "key" is required');
}
if(column.header === undefined){
column.header = column.key
column.header = column.key;
}
newcolumns.push(column)
newcolumns.push(column);
}else{
throw Error('Invalid column definition: expect a string or an object')
throw Error('Invalid column definition: expect a string or an object');
}
}
columns = newcolumns
columns = newcolumns;
}
return columns
return columns;
}

@@ -494,12 +588,12 @@ }

const stringify = function(){
let data, options, callback
for(let i in arguments){
const argument = arguments[i]
const type = typeof argument
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
data = argument;
}else if(options === undefined && isObject(argument)){
options = argument
options = argument;
}else if(callback === undefined && type === 'function'){
callback = argument
callback = argument;
}else{

@@ -509,149 +603,39 @@ throw new CsvError('CSV_INVALID_ARGUMENT', [

`got ${JSON.stringify(argument)} at index ${i}`
])
]);
}
}
const stringifier = new Stringifier(options)
const stringifier = new Stringifier(options);
if(callback){
const chunks = []
const chunks = [];
stringifier.on('readable', function(){
let chunk
let chunk;
while((chunk = this.read()) !== null){
chunks.push(chunk)
chunks.push(chunk);
}
})
});
stringifier.on('error', function(err){
callback(err)
})
callback(err);
});
stringifier.on('end', function(){
callback(undefined, chunks.join(''))
})
callback(undefined, chunks.join(''));
});
}
if(data !== undefined){
// Give a chance for events to be registered later
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(function(){
for(let record of data){
stringifier.write(record)
}
stringifier.end()
})
setImmediate(writer);
}else{
for(let record of data){
stringifier.write(record)
}
stringifier.end()
setTimeout(writer, 0);
}
}
return stringifier
}
return stringifier;
};
class CsvError extends Error {
constructor(code, message, ...contexts) {
if(Array.isArray(message)) message = message.join(' ')
super(message)
if(Error.captureStackTrace !== undefined){
Error.captureStackTrace(this, CsvError)
}
this.code = code
for(const context of contexts){
for(const key in context){
const value = context[key]
this[key] = Buffer.isBuffer(value) ? value.toString() : value == null ? value : JSON.parse(JSON.stringify(value))
}
}
}
}
stringify.Stringifier = Stringifier
stringify.CsvError = CsvError
module.exports = stringify
const isObject = function(obj){
return typeof obj === 'object' && obj !== null && ! Array.isArray(obj)
}
const underscore = function(str){
return str.replace(/([A-Z])/g, function(_, match){
return '_' + match.toLowerCase()
})
}
// Lodash implementation of `get`
const charCodeOfDot = '.'.charCodeAt(0)
const reEscapeChar = /\\(\\)?/g
const rePropName = RegExp(
// Match anything that isn't a dot or bracket.
'[^.[\\]]+' + '|' +
// Or match property names within brackets.
'\\[(?:' +
// Match a non-string expression.
'([^"\'][^[]*)' + '|' +
// Or match strings (supports escaping characters).
'(["\'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2' +
')\\]'+ '|' +
// Or match "" as the space between consecutive dots or empty brackets.
'(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))'
, 'g')
const reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/
const reIsPlainProp = /^\w*$/
const getTag = function(value){
if(!value)
value === undefined ? '[object Undefined]' : '[object Null]'
return Object.prototype.toString.call(value)
}
const isKey = function(value, object){
if(Array.isArray(value)){
return false
}
const type = typeof value
if(type === 'number' || type === 'symbol' || type === 'boolean' || !value || isSymbol(value)){
return true
}
return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
(object != null && value in Object(object))
}
const isSymbol = function(value){
const type = typeof value
return type === 'symbol' || (type === 'object' && value && getTag(value) === '[object Symbol]')
}
const stringToPath = function(string){
const result = []
if(string.charCodeAt(0) === charCodeOfDot){
result.push('')
}
string.replace(rePropName, function(match, expression, quote, subString){
let key = match
if(quote){
key = subString.replace(reEscapeChar, '$1')
}else if(expression){
key = expression.trim()
}
result.push(key)
})
return result
}
const castPath = function(value, object){
if(Array.isArray(value)){
return value
} else {
return isKey(value, object) ? [value] : stringToPath(value)
}
}
const toKey = function(value){
if(typeof value === 'string' || isSymbol(value))
return value
const result = `${value}`
return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result
}
const get = function(object, path){
path = castPath(path, object)
let index = 0
const length = path.length
while(object != null && index < length){
object = object[toKey(path[index++])]
}
return (index && index === length) ? object : undefined
}
// export default stringify
export { stringify, CsvError, Stringifier };

@@ -1,6 +0,12 @@

import * as csvStringify from './index'
export = stringify
import { Input, Options } from './index'
declare function stringify(input: csvStringify.Input, options?: csvStringify.Options): string
declare namespace stringify {}
declare function stringify(input: Input, options?: Options): string
// export default stringify;
export { stringify };
export {
RecordDelimiter, Cast, PlainObject, Input, ColumnOption, CastingContext,
Options
} from './index';
const stringify = require('.')
const {StringDecoder} = require('string_decoder')
import { Stringifier } from './index.js';
import { StringDecoder } from 'string_decoder';
module.exports = function(records, options={}){
const data = []
const stringify = function(records, options={}){
const data = [];
if(Buffer.isBuffer(records)){
const decoder = new StringDecoder()
records = decoder.write(records)
const decoder = new StringDecoder();
records = decoder.write(records);
}
function onData(record){
if(record){
data.push(record.toString())
data.push(record.toString());
}
}
let stringifier = new stringify.Stringifier(options)
const stringifier = new Stringifier(options);
stringifier.on('data', onData);
for(let record of records){
stringifier.write(record)
for(const record of records){
stringifier.write(record);
}
stringifier.end()
stringifier.off('data', onData);
return data.join('')
}
stringifier.end();
stringifier.removeListener('data', onData);
return data.join('');
};
// export default stringify
export { stringify };
{
"version": "5.6.5",
"version": "6.0.0",
"name": "csv-stringify",

@@ -10,66 +10,82 @@ "description": "CSV stringifier implementing the Node.js `stream.Transform` API",

],
"license": "MIT",
"repository": {
"type": "git",
"url": "http://www.github.com/adaltas/node-csv-stringify"
},
"homepage": "https://csv.js.org/stringify/",
"author": "David Worms <david@adaltas.com> (https://www.adaltas.com)",
"coffeelintConfig": {
"indentation": {
"level": "error",
"value": 2
},
"line_endings": {
"level": "error",
"value": "unix"
},
"max_line_length": {
"level": "ignore"
}
},
"devDependencies": {
"@babel/cli": "^7.14.8",
"@babel/core": "^7.15.0",
"@babel/preset-env": "^7.15.0",
"@rollup/plugin-eslint": "^8.0.1",
"@rollup/plugin-node-resolve": "^13.0.6",
"@types/mocha": "^9.0.0",
"@types/node": "^16.7.8",
"@types/node": "^16.11.7",
"@types/should": "^13.0.0",
"babelify": "^10.0.0",
"browserify": "^17.0.0",
"coffeescript": "~2.5.1",
"csv-generate": "^3.4.3",
"coffeescript": "~2.6.1",
"csv-generate": "^4.0.0",
"each": "^1.2.2",
"eslint": "^8.2.0",
"express": "^4.17.1",
"mocha": "~9.1.1",
"mocha": "~9.1.3",
"rollup": "^2.60.0",
"rollup-plugin-node-builtins": "^2.1.2",
"rollup-plugin-node-globals": "^1.4.0",
"should": "~13.2.3",
"ts-node": "^10.2.1",
"typescript": "^4.4.2"
"ts-node": "^10.4.0",
"typescript": "^4.4.4"
},
"exports": {
".": {
"import": "./lib/index.js",
"require": "./dist/cjs/index.cjs"
},
"./sync": {
"import": "./lib/sync.js",
"require": "./dist/cjs/sync.cjs"
}
},
"files": [
"/lib"
"dist",
"lib",
"samples"
],
"main": "./lib",
"homepage": "https://csv.js.org/stringify/",
"license": "MIT",
"main": "./dist/cjs/index.cjs",
"mocha": {
"throw-deprecation": true,
"inline-diffs": true,
"loader": "./test/loaders/all.mjs",
"recursive": true,
"reporter": "spec",
"require": [
"should",
"coffeescript/register",
"ts-node/register"
"should"
],
"inline-diffs": true,
"timeout": 40000,
"reporter": "spec",
"recursive": true
"throw-deprecation": true,
"timeout": 40000
},
"repository": {
"type": "git",
"url": "https://github.com/adaltas/node-csv.git",
"directory": "packages/csv-stringify"
},
"scripts": {
"build:babel": "cd lib && babel *.js -d es5 && cd ..",
"build:browserify": "browserify lib/index.js --transform babelify --standalone stringify > lib/browser/index.js && browserify lib/sync.js --transform babelify --standalone stringify > lib/browser/sync.js",
"build": "npm run build:babel && npm run build:browserify",
"preversion": "cp lib/*.ts lib/es5 && git add lib/es5/*.ts",
"build": "npm run build:rollup && npm run build:ts",
"build:rollup": "npx rollup -c",
"build:ts": "cp lib/*.ts dist/cjs && cp lib/*.ts dist/esm",
"lint": "npm run lint:lib && npm run lint:samples && npm run lint:test",
"lint:lib": "eslint --fix lib/*.js",
"lint:samples": "eslint --fix samples/*.js",
"lint:test": "coffeelint --fix test/*.coffee",
"preversion": "npm run build && git add dist",
"pretest": "npm run build",
"test": "mocha test/**/*.{coffee,ts}"
"test": "mocha 'test/**/*.{coffee,ts}'",
"test:legacy": "mocha --loader=./test/loaders/legacy/all.mjs 'test/**/*.{coffee,ts}'"
},
"types": "./lib/index.d.ts",
"gitHead": "0fd5209b6862655c384cda052abf38019b959e70"
"type": "module",
"types": "dist/esm/index.d.ts",
"typesVersions": {
"*": {
".": [
"dist/esm/index.d.ts"
],
"sync": [
"dist/esm/sync.d.ts"
]
}
},
"gitHead": "d95c35dc7fd698e8e3278f539c8f1d43fb77640b"
}
[![Build Status](https://api.travis-ci.org/adaltas/node-csv-stringify.svg)](https://travis-ci.org/#!/adaltas/node-csv-stringify) [![NPM](https://img.shields.io/npm/dm/csv-stringify)](https://www.npmjs.com/package/csv-stringify) [![NPM](https://img.shields.io/npm/v/csv-stringify)](https://www.npmjs.com/package/csv-stringify)
# CSV stringifier for Node.js and the web
This package is a stringifier converting records into a CSV text and
[![Build Status](https://img.shields.io/github/workflow/status/adaltas/node-csv/Node.js)](https://github.com/adaltas/node-csv/actions)
[![NPM](https://img.shields.io/npm/dm/csv-stringify)](https://www.npmjs.com/package/csv-stringify)
[![NPM](https://img.shields.io/npm/v/csv-stringify)](https://www.npmjs.com/package/csv-stringify)
The [`csv-stringify` package](https://csv.js.org/stringify/) is a stringifier converting records into a CSV text and
implementing the Node.js [`stream.Transform`

@@ -13,6 +17,6 @@ API](https://nodejs.org/api/stream.html). It also provides the easier

* [Project homepage](http://csv.js.org/stringify/)
* [API](http://csv.js.org/stringify/api/)
* [Options](http://csv.js.org/stringify/options/)
* [Examples](http://csv.js.org/stringify/examples/)
* [Project homepage](https://csv.js.org/stringify/)
* [API](https://csv.js.org/stringify/api/)
* [Options](https://csv.js.org/stringify/options/)
* [Examples](https://csv.js.org/stringify/examples/)

@@ -32,18 +36,20 @@ ## Main features

The module is built on the Node.js Stream API. For the sake of simplicity, a
simple callback API is also provided. To give you a quick look, here's an
example of the callback API:
Run `npm install csv` to install the full csv module or run `npm install csv-stringify` if you are only interested by the CSV stringifier.
```javascript
const stringify = require('csv-stringify')
const assert = require('assert')
// import stringify from 'csv-stringify'
// import assert from 'assert/strict'
The module is built on the Node.js Stream API. Use the callback and sync APIs for simplicity or the stream based API for scalability.
const input = [ [ '1', '2', '3', '4' ], [ 'a', 'b', 'c', 'd' ] ]
stringify(input, function(err, output) {
const expected = '1,2,3,4\na,b,c,d\n'
assert.strictEqual(output, expected, `output.should.eql ${expected}`)
console.log("Passed.", output)
})
## Example
The [API](https://csv.js.org/stringify/api/) is available in multiple flavors. This example illustrates the sync API.
```js
import { stringify } from 'csv-stringify/sync';
import assert from 'assert';
const output = stringify([
[ '1', '2', '3', '4' ],
[ 'a', 'b', 'c', 'd' ]
]);
assert.equal(output, '1,2,3,4\na,b,c,d\n');
```

@@ -68,2 +74,4 @@

The project is sponsored by [Adaltas](https://www.adaltas.com), an Big Data consulting firm based in Paris, France.
* David Worms: <https://github.com/wdavidw>

@@ -73,3 +81,3 @@

[stream_transform]: http://nodejs.org/api/stream.html#stream_class_stream_transform
[examples]: http://csv.js.org/stringify/examples/
[examples]: https://csv.js.org/stringify/examples/
[csv]: https://github.com/adaltas/node-csv
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc