initial commit.
This commit is contained in:
60
node_modules/json2csv/lib/JSON2CSVAsyncParser.js
generated
vendored
Normal file
60
node_modules/json2csv/lib/JSON2CSVAsyncParser.js
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
|
||||
const { Transform } = require('stream');
|
||||
const JSON2CSVTransform = require('./JSON2CSVTransform');
|
||||
const { fastJoin } = require('./utils');
|
||||
|
||||
class JSON2CSVAsyncParser {
|
||||
constructor(opts, transformOpts) {
|
||||
this.input = new Transform(transformOpts);
|
||||
this.input._read = () => {};
|
||||
|
||||
this.transform = new JSON2CSVTransform(opts, transformOpts);
|
||||
this.processor = this.input.pipe(this.transform);
|
||||
}
|
||||
|
||||
fromInput(input) {
|
||||
if (this._input) {
|
||||
throw new Error('Async parser already has an input.');
|
||||
}
|
||||
this._input = input;
|
||||
this.input = this._input.pipe(this.processor);
|
||||
return this;
|
||||
}
|
||||
|
||||
throughTransform(transform) {
|
||||
if (this._output) {
|
||||
throw new Error('Can\'t add transforms once an output has been added.');
|
||||
}
|
||||
this.processor = this.processor.pipe(transform);
|
||||
return this;
|
||||
}
|
||||
|
||||
toOutput(output) {
|
||||
if (this._output) {
|
||||
throw new Error('Async parser already has an output.');
|
||||
}
|
||||
this._output = output;
|
||||
this.processor = this.processor.pipe(output);
|
||||
return this;
|
||||
}
|
||||
|
||||
promise(returnCSV = true) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!returnCSV) {
|
||||
this.processor
|
||||
.on('finish', () => resolve())
|
||||
.on('error', err => reject(err));
|
||||
return;
|
||||
}
|
||||
|
||||
let csvBuffer = [];
|
||||
this.processor
|
||||
.on('data', chunk => csvBuffer.push(chunk.toString()))
|
||||
.on('finish', () => resolve(fastJoin(csvBuffer, '')))
|
||||
.on('error', err => reject(err));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = JSON2CSVAsyncParser
|
||||
188
node_modules/json2csv/lib/JSON2CSVBase.js
generated
vendored
Normal file
188
node_modules/json2csv/lib/JSON2CSVBase.js
generated
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
'use strict';
|
||||
|
||||
const os = require('os');
|
||||
const lodashGet = require('lodash.get');
|
||||
const { getProp, fastJoin, flattenReducer } = require('./utils');
|
||||
|
||||
class JSON2CSVBase {
|
||||
constructor(opts) {
|
||||
this.opts = this.preprocessOpts(opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check passing opts and set defaults.
|
||||
*
|
||||
* @param {Json2CsvOptions} opts Options object containing fields,
|
||||
* delimiter, default value, quote mark, header, etc.
|
||||
*/
|
||||
preprocessOpts(opts) {
|
||||
const processedOpts = Object.assign({}, opts);
|
||||
processedOpts.transforms = !Array.isArray(processedOpts.transforms)
|
||||
? (processedOpts.transforms ? [processedOpts.transforms] : [])
|
||||
: processedOpts.transforms
|
||||
processedOpts.delimiter = processedOpts.delimiter || ',';
|
||||
processedOpts.eol = processedOpts.eol || os.EOL;
|
||||
processedOpts.quote = typeof processedOpts.quote === 'string'
|
||||
? processedOpts.quote
|
||||
: '"';
|
||||
processedOpts.escapedQuote = typeof processedOpts.escapedQuote === 'string'
|
||||
? processedOpts.escapedQuote
|
||||
: `${processedOpts.quote}${processedOpts.quote}`;
|
||||
processedOpts.header = processedOpts.header !== false;
|
||||
processedOpts.includeEmptyRows = processedOpts.includeEmptyRows || false;
|
||||
processedOpts.withBOM = processedOpts.withBOM || false;
|
||||
|
||||
return processedOpts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check and normalize the fields configuration.
|
||||
*
|
||||
* @param {(string|object)[]} fields Fields configuration provided by the user
|
||||
* or inferred from the data
|
||||
* @returns {object[]} preprocessed FieldsInfo array
|
||||
*/
|
||||
preprocessFieldsInfo(fields) {
|
||||
return fields.map((fieldInfo) => {
|
||||
if (typeof fieldInfo === 'string') {
|
||||
return {
|
||||
label: fieldInfo,
|
||||
value: (fieldInfo.includes('.') || fieldInfo.includes('['))
|
||||
? row => lodashGet(row, fieldInfo, this.opts.defaultValue)
|
||||
: row => getProp(row, fieldInfo, this.opts.defaultValue),
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof fieldInfo === 'object') {
|
||||
const defaultValue = 'default' in fieldInfo
|
||||
? fieldInfo.default
|
||||
: this.opts.defaultValue;
|
||||
|
||||
if (typeof fieldInfo.value === 'string') {
|
||||
return {
|
||||
label: fieldInfo.label || fieldInfo.value,
|
||||
value: (fieldInfo.value.includes('.') || fieldInfo.value.includes('['))
|
||||
? row => lodashGet(row, fieldInfo.value, defaultValue)
|
||||
: row => getProp(row, fieldInfo.value, defaultValue),
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof fieldInfo.value === 'function') {
|
||||
const label = fieldInfo.label || fieldInfo.value.name || '';
|
||||
const field = { label, default: defaultValue };
|
||||
return {
|
||||
label,
|
||||
value(row) {
|
||||
const value = fieldInfo.value(row, field);
|
||||
return (value === null || value === undefined)
|
||||
? defaultValue
|
||||
: value;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Invalid field info option. ' + JSON.stringify(fieldInfo));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the title row with all the provided fields as column headings
|
||||
*
|
||||
* @returns {String} titles as a string
|
||||
*/
|
||||
getHeader() {
|
||||
return fastJoin(
|
||||
this.opts.fields.map(fieldInfo => this.processValue(fieldInfo.label)),
|
||||
this.opts.delimiter
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Preprocess each object according to the given transforms (unwind, flatten, etc.).
|
||||
* @param {Object} row JSON object to be converted in a CSV row
|
||||
*/
|
||||
preprocessRow(row) {
|
||||
return this.opts.transforms.reduce((rows, transform) =>
|
||||
rows.map(row => transform(row)).reduce(flattenReducer, []),
|
||||
[row]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the content of a specific CSV row
|
||||
*
|
||||
* @param {Object} row JSON object to be converted in a CSV row
|
||||
* @returns {String} CSV string (row)
|
||||
*/
|
||||
processRow(row) {
|
||||
if (!row) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const processedRow = this.opts.fields.map(fieldInfo => this.processCell(row, fieldInfo));
|
||||
|
||||
if (!this.opts.includeEmptyRows && processedRow.every(field => field === undefined)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return fastJoin(
|
||||
processedRow,
|
||||
this.opts.delimiter
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the content of a specfic CSV row cell
|
||||
*
|
||||
* @param {Object} row JSON object representing the CSV row that the cell belongs to
|
||||
* @param {FieldInfo} fieldInfo Details of the field to process to be a CSV cell
|
||||
* @returns {String} CSV string (cell)
|
||||
*/
|
||||
processCell(row, fieldInfo) {
|
||||
return this.processValue(fieldInfo.value(row));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the content of a specfic CSV row cell
|
||||
*
|
||||
* @param {Any} value Value to be included in a CSV cell
|
||||
* @returns {String} Value stringified and processed
|
||||
*/
|
||||
processValue(value) {
|
||||
if (value === null || value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const valueType = typeof value;
|
||||
if (valueType !== 'boolean' && valueType !== 'number' && valueType !== 'string') {
|
||||
value = JSON.stringify(value);
|
||||
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (value[0] === '"') {
|
||||
value = value.replace(/^"(.+)"$/,'$1');
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
if (this.opts.excelStrings) {
|
||||
if(value.includes(this.opts.quote)) {
|
||||
value = value.replace(new RegExp(this.opts.quote, 'g'), `${this.opts.escapedQuote}${this.opts.escapedQuote}`);
|
||||
}
|
||||
value = `"=""${value}"""`;
|
||||
} else {
|
||||
if(value.includes(this.opts.quote)) {
|
||||
value = value.replace(new RegExp(this.opts.quote, 'g'), this.opts.escapedQuote);
|
||||
}
|
||||
value = `${this.opts.quote}${value}${this.opts.quote}`;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = JSON2CSVBase;
|
||||
81
node_modules/json2csv/lib/JSON2CSVParser.js
generated
vendored
Normal file
81
node_modules/json2csv/lib/JSON2CSVParser.js
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
'use strict';
|
||||
|
||||
const JSON2CSVBase = require('./JSON2CSVBase');
|
||||
const { fastJoin, flattenReducer } = require('./utils');
|
||||
|
||||
class JSON2CSVParser extends JSON2CSVBase {
|
||||
constructor(opts) {
|
||||
super(opts);
|
||||
if (this.opts.fields) {
|
||||
this.opts.fields = this.preprocessFieldsInfo(this.opts.fields);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Main function that converts json to csv.
|
||||
*
|
||||
* @param {Array|Object} data Array of JSON objects to be converted to CSV
|
||||
* @returns {String} The CSV formated data as a string
|
||||
*/
|
||||
parse(data) {
|
||||
const processedData = this.preprocessData(data);
|
||||
|
||||
if (!this.opts.fields) {
|
||||
this.opts.fields = processedData
|
||||
.reduce((fields, item) => {
|
||||
Object.keys(item).forEach((field) => {
|
||||
if (!fields.includes(field)) {
|
||||
fields.push(field)
|
||||
}
|
||||
});
|
||||
|
||||
return fields
|
||||
}, []);
|
||||
|
||||
this.opts.fields = this.preprocessFieldsInfo(this.opts.fields);
|
||||
}
|
||||
|
||||
const header = this.opts.header ? this.getHeader() : '';
|
||||
const rows = this.processData(processedData);
|
||||
const csv = (this.opts.withBOM ? '\ufeff' : '')
|
||||
+ header
|
||||
+ ((header && rows) ? this.opts.eol : '')
|
||||
+ rows;
|
||||
|
||||
return csv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preprocess the data according to the give opts (unwind, flatten, etc.)
|
||||
and calculate the fields and field names if they are not provided.
|
||||
*
|
||||
* @param {Array|Object} data Array or object to be converted to CSV
|
||||
*/
|
||||
preprocessData(data) {
|
||||
const processedData = Array.isArray(data) ? data : [data];
|
||||
|
||||
if (!this.opts.fields && (processedData.length === 0 || typeof processedData[0] !== 'object')) {
|
||||
throw new Error('Data should not be empty or the "fields" option should be included');
|
||||
}
|
||||
|
||||
if (this.opts.transforms.length === 0) return processedData;
|
||||
|
||||
return processedData
|
||||
.map(row => this.preprocessRow(row))
|
||||
.reduce(flattenReducer, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the content row by row below the header
|
||||
*
|
||||
* @param {Array} data Array of JSON objects to be converted to CSV
|
||||
* @returns {String} CSV string (body)
|
||||
*/
|
||||
processData(data) {
|
||||
return fastJoin(
|
||||
data.map(row => this.processRow(row)).filter(row => row), // Filter empty rows
|
||||
this.opts.eol
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = JSON2CSVParser;
|
||||
203
node_modules/json2csv/lib/JSON2CSVTransform.js
generated
vendored
Normal file
203
node_modules/json2csv/lib/JSON2CSVTransform.js
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
'use strict';
|
||||
|
||||
const { Transform } = require('stream');
|
||||
const Parser = require('jsonparse');
|
||||
const JSON2CSVBase = require('./JSON2CSVBase');
|
||||
|
||||
class JSON2CSVTransform extends Transform {
|
||||
constructor(opts, transformOpts) {
|
||||
super(transformOpts);
|
||||
|
||||
// Inherit methods from JSON2CSVBase since extends doesn't
|
||||
// allow multiple inheritance and manually preprocess opts
|
||||
Object.getOwnPropertyNames(JSON2CSVBase.prototype)
|
||||
.forEach(key => (this[key] = JSON2CSVBase.prototype[key]));
|
||||
this.opts = this.preprocessOpts(opts);
|
||||
|
||||
this._data = '';
|
||||
this._hasWritten = false;
|
||||
|
||||
if (this._readableState.objectMode) {
|
||||
this.initObjectModeParse();
|
||||
} else if (this.opts.ndjson) {
|
||||
this.initNDJSONParse();
|
||||
} else {
|
||||
this.initJSONParser();
|
||||
}
|
||||
|
||||
if (this.opts.withBOM) {
|
||||
this.push('\ufeff');
|
||||
}
|
||||
|
||||
if (this.opts.fields) {
|
||||
this.opts.fields = this.preprocessFieldsInfo(this.opts.fields);
|
||||
this.pushHeader();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the transform with a parser to process data in object mode.
|
||||
* It receives JSON objects one by one and send them to `pushLine for processing.
|
||||
*/
|
||||
initObjectModeParse() {
|
||||
const transform = this;
|
||||
|
||||
this.parser = {
|
||||
write(line) {
|
||||
transform.pushLine(line);
|
||||
},
|
||||
getPendingData() {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the transform with a parser to process NDJSON data.
|
||||
* It maintains a buffer of received data, parses each line
|
||||
* as JSON and send it to `pushLine for processing.
|
||||
*/
|
||||
initNDJSONParse() {
|
||||
const transform = this;
|
||||
|
||||
this.parser = {
|
||||
_data: '',
|
||||
write(chunk) {
|
||||
this._data += chunk.toString();
|
||||
const lines = this._data
|
||||
.split('\n')
|
||||
.map(line => line.trim())
|
||||
.filter(line => line !== '');
|
||||
|
||||
let pendingData = false;
|
||||
lines
|
||||
.forEach((line, i) => {
|
||||
try {
|
||||
transform.pushLine(JSON.parse(line));
|
||||
} catch(e) {
|
||||
if (i === lines.length - 1) {
|
||||
pendingData = true;
|
||||
} else {
|
||||
e.message = `Invalid JSON (${line})`
|
||||
transform.emit('error', e);
|
||||
}
|
||||
}
|
||||
});
|
||||
this._data = pendingData
|
||||
? this._data.slice(this._data.lastIndexOf('\n'))
|
||||
: '';
|
||||
},
|
||||
getPendingData() {
|
||||
return this._data;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the transform with a parser to process JSON data.
|
||||
* It maintains a buffer of received data, parses each as JSON
|
||||
* item if the data is an array or the data itself otherwise
|
||||
* and send it to `pushLine` for processing.
|
||||
*/
|
||||
initJSONParser() {
|
||||
const transform = this;
|
||||
this.parser = new Parser();
|
||||
this.parser.onValue = function (value) {
|
||||
if (this.stack.length !== this.depthToEmit) return;
|
||||
transform.pushLine(value);
|
||||
}
|
||||
|
||||
this.parser._onToken = this.parser.onToken;
|
||||
|
||||
this.parser.onToken = function (token, value) {
|
||||
transform.parser._onToken(token, value);
|
||||
|
||||
if (this.stack.length === 0
|
||||
&& !transform.opts.fields
|
||||
&& this.mode !== Parser.C.ARRAY
|
||||
&& this.mode !== Parser.C.OBJECT) {
|
||||
this.onError(new Error('Data should not be empty or the "fields" option should be included'));
|
||||
}
|
||||
|
||||
if (this.stack.length === 1) {
|
||||
if(this.depthToEmit === undefined) {
|
||||
// If Array emit its content, else emit itself
|
||||
this.depthToEmit = (this.mode === Parser.C.ARRAY) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (this.depthToEmit !== 0 && this.stack.length === 1) {
|
||||
// No need to store the whole root array in memory
|
||||
this.value = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.parser.getPendingData = function () {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
this.parser.onError = function (err) {
|
||||
if(err.message.includes('Unexpected')) {
|
||||
err.message = `Invalid JSON (${err.message})`;
|
||||
}
|
||||
transform.emit('error', err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function that send data to the parse to be processed.
|
||||
*
|
||||
* @param {Buffer} chunk Incoming data
|
||||
* @param {String} encoding Encoding of the incoming data. Defaults to 'utf8'
|
||||
* @param {Function} done Called when the proceesing of the supplied chunk is done
|
||||
*/
|
||||
_transform(chunk, encoding, done) {
|
||||
this.parser.write(chunk);
|
||||
done();
|
||||
}
|
||||
|
||||
_flush(done) {
|
||||
if (this.parser.getPendingData()) {
|
||||
done(new Error('Invalid data received from stdin', this.parser.getPendingData()));
|
||||
}
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the csv header and pushes it downstream.
|
||||
*/
|
||||
pushHeader() {
|
||||
if (this.opts.header) {
|
||||
const header = this.getHeader();
|
||||
this.emit('header', header);
|
||||
this.push(header);
|
||||
this._hasWritten = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms an incoming json data to csv and pushes it downstream.
|
||||
*
|
||||
* @param {Object} data JSON object to be converted in a CSV row
|
||||
*/
|
||||
pushLine(data) {
|
||||
const processedData = this.preprocessRow(data);
|
||||
|
||||
if (!this._hasWritten) {
|
||||
this.opts.fields = this.opts.fields || this.preprocessFieldsInfo(Object.keys(processedData[0]));
|
||||
this.pushHeader();
|
||||
}
|
||||
|
||||
processedData.forEach(row => {
|
||||
const line = this.processRow(row, this.opts);
|
||||
if (line === undefined) return;
|
||||
this.emit('line', line);
|
||||
this.push(this._hasWritten ? this.opts.eol + line : line);
|
||||
this._hasWritten = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = JSON2CSVTransform;
|
||||
44
node_modules/json2csv/lib/json2csv.js
generated
vendored
Normal file
44
node_modules/json2csv/lib/json2csv.js
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
|
||||
const { Readable } = require('stream');
|
||||
const JSON2CSVParser = require('./JSON2CSVParser');
|
||||
const JSON2CSVAsyncParser = require('./JSON2CSVAsyncParser');
|
||||
const JSON2CSVTransform = require('./JSON2CSVTransform');
|
||||
const flatten = require('./transforms/flatten');
|
||||
const unwind = require('./transforms/unwind');
|
||||
|
||||
module.exports.Parser = JSON2CSVParser;
|
||||
module.exports.AsyncParser = JSON2CSVAsyncParser;
|
||||
module.exports.Transform = JSON2CSVTransform;
|
||||
|
||||
// Convenience method to keep the API similar to version 3.X
|
||||
module.exports.parse = (data, opts) => new JSON2CSVParser(opts).parse(data);
|
||||
module.exports.parseAsync = (data, opts, transformOpts) => {
|
||||
try {
|
||||
if (!(data instanceof Readable)) {
|
||||
transformOpts = Object.assign({}, transformOpts, { objectMode: true });
|
||||
}
|
||||
|
||||
const asyncParser = new JSON2CSVAsyncParser(opts, transformOpts);
|
||||
const promise = asyncParser.promise();
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
data.forEach(item => asyncParser.input.push(item));
|
||||
asyncParser.input.push(null);
|
||||
} else if (data instanceof Readable) {
|
||||
asyncParser.fromInput(data);
|
||||
} else {
|
||||
asyncParser.input.push(data);
|
||||
asyncParser.input.push(null);
|
||||
}
|
||||
|
||||
return promise;
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.transforms = {
|
||||
flatten,
|
||||
unwind,
|
||||
};
|
||||
37
node_modules/json2csv/lib/transforms/flatten.js
generated
vendored
Normal file
37
node_modules/json2csv/lib/transforms/flatten.js
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Performs the flattening of a data row recursively
|
||||
*
|
||||
* @param {String} separator Separator to be used as the flattened field name
|
||||
* @returns {Object => Object} Flattened object
|
||||
*/
|
||||
function flatten({ objects = true, arrays = false, separator = '.' } = {}) {
|
||||
function step (obj, flatDataRow, currentPath) {
|
||||
Object.keys(obj).forEach((key) => {
|
||||
const newPath = currentPath ? `${currentPath}${separator}${key}` : key;
|
||||
const value = obj[key];
|
||||
|
||||
if (objects
|
||||
&& typeof value === 'object'
|
||||
&& value !== null
|
||||
&& !Array.isArray(value)
|
||||
&& Object.prototype.toString.call(value.toJSON) !== '[object Function]'
|
||||
&& Object.keys(value).length) {
|
||||
step(value, flatDataRow, newPath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (arrays && Array.isArray(value)) {
|
||||
step(value, flatDataRow, newPath);
|
||||
return;
|
||||
}
|
||||
|
||||
flatDataRow[newPath] = value;
|
||||
});
|
||||
|
||||
return flatDataRow;
|
||||
}
|
||||
|
||||
return dataRow => step(dataRow, {});
|
||||
}
|
||||
|
||||
module.exports = flatten;
|
||||
63
node_modules/json2csv/lib/transforms/unwind.js
generated
vendored
Normal file
63
node_modules/json2csv/lib/transforms/unwind.js
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
|
||||
const lodashGet = require('lodash.get');
|
||||
const { setProp, unsetProp, flattenReducer } = require('../utils');
|
||||
|
||||
function getUnwindablePaths(obj, currentPath) {
|
||||
return Object.keys(obj).reduce((unwindablePaths, key) => {
|
||||
const newPath = currentPath ? `${currentPath}.${key}` : key;
|
||||
const value = obj[key];
|
||||
|
||||
if (typeof value === 'object'
|
||||
&& value !== null
|
||||
&& !Array.isArray(value)
|
||||
&& Object.prototype.toString.call(value.toJSON) !== '[object Function]'
|
||||
&& Object.keys(value).length) {
|
||||
unwindablePaths = unwindablePaths.concat(getUnwindablePaths(value, newPath));
|
||||
} else if (Array.isArray(value)) {
|
||||
unwindablePaths.push(newPath);
|
||||
unwindablePaths = unwindablePaths.concat(value
|
||||
.map(arrObj => getUnwindablePaths(arrObj, newPath))
|
||||
.reduce(flattenReducer, [])
|
||||
.filter((item, index, arr) => arr.indexOf(item) !== index));
|
||||
}
|
||||
|
||||
return unwindablePaths;
|
||||
}, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the unwind recursively in specified sequence
|
||||
*
|
||||
* @param {String[]} unwindPaths The paths as strings to be used to deconstruct the array
|
||||
* @returns {Object => Array} Array of objects containing all rows after unwind of chosen paths
|
||||
*/
|
||||
function unwind({ paths = undefined, blankOut = false } = {}) {
|
||||
function unwindReducer(rows, unwindPath) {
|
||||
return rows
|
||||
.map(row => {
|
||||
const unwindArray = lodashGet(row, unwindPath);
|
||||
|
||||
if (!Array.isArray(unwindArray)) {
|
||||
return row;
|
||||
}
|
||||
|
||||
if (!unwindArray.length) {
|
||||
return unsetProp(row, unwindPath);
|
||||
}
|
||||
|
||||
return unwindArray.map((unwindRow, index) => {
|
||||
const clonedRow = (blankOut && index > 0)
|
||||
? {}
|
||||
: row;
|
||||
|
||||
return setProp(clonedRow, unwindPath, unwindRow);
|
||||
});
|
||||
})
|
||||
.reduce(flattenReducer, []);
|
||||
}
|
||||
|
||||
paths = Array.isArray(paths) ? paths : (paths ? [paths] : undefined);
|
||||
return dataRow => (paths || getUnwindablePaths(dataRow)).reduce(unwindReducer, [dataRow]);
|
||||
}
|
||||
|
||||
module.exports = unwind;
|
||||
72
node_modules/json2csv/lib/utils.js
generated
vendored
Normal file
72
node_modules/json2csv/lib/utils.js
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
'use strict';
|
||||
|
||||
function getProp(obj, path, defaultValue) {
|
||||
return obj[path] === undefined ? defaultValue : obj[path];
|
||||
}
|
||||
|
||||
function setProp(obj, path, value) {
|
||||
const pathArray = Array.isArray(path) ? path : path.split('.');
|
||||
const [key, ...restPath] = pathArray;
|
||||
return {
|
||||
...obj,
|
||||
[key]: pathArray.length > 1 ? setProp(obj[key] || {}, restPath, value) : value
|
||||
};
|
||||
}
|
||||
|
||||
function unsetProp(obj, path) {
|
||||
const pathArray = Array.isArray(path) ? path : path.split('.');
|
||||
const [key, ...restPath] = pathArray;
|
||||
|
||||
if (typeof obj[key] !== 'object') {
|
||||
// This will never be hit in the current code because unwind does the check before calling unsetProp
|
||||
/* istanbul ignore next */
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (pathArray.length === 1) {
|
||||
return Object.keys(obj)
|
||||
.filter(prop => prop !== key)
|
||||
.reduce((acc, prop) => Object.assign(acc, { [prop]: obj[prop] }), {});
|
||||
}
|
||||
|
||||
return Object.keys(obj)
|
||||
.reduce((acc, prop) => ({
|
||||
...acc,
|
||||
[prop]: prop !== key ? obj[prop] : unsetProp(obj[key], restPath),
|
||||
}), {});
|
||||
}
|
||||
|
||||
function flattenReducer(acc, arr) {
|
||||
try {
|
||||
// This is faster but susceptible to `RangeError: Maximum call stack size exceeded`
|
||||
acc.push(...arr);
|
||||
return acc;
|
||||
} catch (err) {
|
||||
// Fallback to a slower but safer option
|
||||
return acc.concat(arr);
|
||||
}
|
||||
}
|
||||
|
||||
function fastJoin(arr, separator) {
|
||||
let isFirst = true;
|
||||
return arr.reduce((acc, elem) => {
|
||||
if (elem === null || elem === undefined) {
|
||||
elem = '';
|
||||
}
|
||||
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
return `${elem}`;
|
||||
}
|
||||
|
||||
return `${acc}${separator}${elem}`;
|
||||
}, '');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getProp,
|
||||
setProp,
|
||||
unsetProp,
|
||||
fastJoin,
|
||||
flattenReducer
|
||||
};
|
||||
Reference in New Issue
Block a user