jtc-utils
Version:
Utilities for Japanese Traditional Companies
164 lines (163 loc) • 5.97 kB
JavaScript
;
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;