UNPKG

csv-stringify

Version:

CSV stringifier implementing the Node.js `stream.Transform` API

120 lines (114 loc) 3.35 kB
/* 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 };