UNPKG

jtc-utils

Version:
164 lines (163 loc) 5.97 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CsvReader = void 0; const utf8_cjs_1 = require("./charset/utf8.cjs"); const escapeRegExp_cjs_1 = require("./util/escapeRegExp.cjs"); class CsvReader { reader; fieldSeparator; skipEmptyLine; reSeparator; endsWithCR = false; buf = ""; index = 0; constructor(src, options) { const charset = options?.charset ?? utf8_cjs_1.utf8; this.fieldSeparator = options?.fieldSeparator ?? ","; this.skipEmptyLine = options?.skipEmptyLine ?? false; this.reSeparator = new RegExp(`\n|\r\n?|${(0, escapeRegExp_cjs_1.escapeRegExp)(this.fieldSeparator)}`, "g"); if (typeof src === "string") { this.reader = Promise.resolve(new ReadableStream({ start(controller) { controller.enqueue(src); controller.close(); }, }).getReader()); } else { let stream; if (src instanceof Uint8Array) { stream = Promise.resolve(new ReadableStream({ start(controller) { controller.enqueue(src); controller.close(); }, })); } else if (src instanceof Blob) { stream = Promise.resolve(src.stream()); } else if (src instanceof ReadableStream) { stream = Promise.resolve(src); } else { const readable = "createReadStream" in src ? src.createReadStream() : src; if ("constructor" in readable && "toWeb" in readable.constructor && typeof readable.constructor.toWeb === "function") { stream = Promise.resolve(readable.constructor.toWeb(readable)); } else { throw new TypeError(`Unsuppoted source: ${src}`); } } const decoder = charset.createDecoder({ fatal: options?.fatal ?? true, ignoreBOM: options?.bom != null ? !options.bom : false, }); this.reader = stream.then((value) => value .pipeThrough(new TransformStream({ transform(chunk, controller) { controller.enqueue(decoder.decode(chunk, { stream: true })); }, flush() { decoder.decode(new Uint8Array()); }, })) .getReader()); } } async read() { const items = new Array(); let buf = this.buf; let pos = 0; let quoted = false; let done = false; const reader = await this.reader; loop: do { const readed = await reader.read(); done = readed.done; if (readed.value) { let value = readed.value; if (this.endsWithCR && value.startsWith("\n")) { value = value.substring(1); this.endsWithCR = false; } if (value.endsWith("\r")) { this.endsWithCR = true; } buf = buf ? buf + value : value; } while (pos < buf.length) { if (!quoted && buf.startsWith('"')) { if (pos === 0) { pos = 1; } const lpos = buf.indexOf('"', pos); if (lpos === -1) { pos = buf.length; continue loop; } else if (buf.startsWith('"', lpos + 1)) { pos = lpos + 2; continue; } else { const unquoted = buf.substring(1, lpos).replaceAll('""', '"'); buf = unquoted + buf.substring(lpos + 1); pos = unquoted.length; quoted = true; continue; } } this.reSeparator.lastIndex = pos; if (!this.reSeparator.test(buf)) { pos = buf.length; continue loop; } const item = buf.substring(0, this.reSeparator.lastIndex - (buf.endsWith("\r\n", this.reSeparator.lastIndex) ? 2 : 1)); if (!buf.endsWith(this.fieldSeparator, this.reSeparator.lastIndex)) { if (item || items.length > 0 || quoted) { items.push(item); } if (items.length > 0 || !this.skipEmptyLine) { this.buf = buf.substring(this.reSeparator.lastIndex); this.index++; return items; } } else { items.push(item); } buf = buf.substring(this.reSeparator.lastIndex); pos = 0; quoted = false; } if (done) { if (buf || items.length > 0 || quoted) { items.push(buf); } if (items.length > 0) { this.buf = ""; this.index++; return items; } } } while (buf || !done); } async *[Symbol.asyncIterator]() { let record; while ((record = await this.read()) != null) { yield record; } } get count() { return this.index; } async close() { const reader = await this.reader; await reader.cancel(); } } exports.CsvReader = CsvReader;