"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.streamLineByLineAsync = exports.streamFileLineByLineAsync = exports.lineReaderAsync = exports.readFile = void 0; // cSpell:ignore curr // cSpell:words zlib iconv const fs = require("fs"); const iconv = require("iconv-lite"); const zlib = require("zlib"); const readline = require("readline"); const defaultEncoding = 'utf8'; function readFile(filename, encoding = defaultEncoding) { return new Promise((resolve, reject) => { const data = []; const stream = prepareFileStream(filename, encoding, reject); let resolved = false; function complete() { resolve(data.join('')); resolved = resolved || (resolve(data.join('')), true); } stream.on('error', reject); stream.on('data', (d) => data.push(d)); stream.on('close', complete); stream.on('end', complete); }); } exports.readFile = readFile; /** * Reads a file line by line. The last value emitted by the Observable is always an empty string. * @param filename * @param encoding defaults to 'utf8' */ function lineReaderAsync(filename, encoding = defaultEncoding) { return streamFileLineByLineAsync(filename, encoding); } exports.lineReaderAsync = lineReaderAsync; function prepareFileStream(filename, encoding, fnError) { const pipes = []; if (filename.match(/\.gz$/i)) { pipes.push(zlib.createGunzip()); } pipes.push(iconv.decodeStream(encoding)); const fileStream = fs.createReadStream(filename); fileStream.on('error', fnError); const stream = pipes.reduce((s, p) => s.pipe(p).on('error', fnError), fileStream); return stream; } /** * Emit a file line by line * @param filename full path to the file to read. * @param encoding defaults to 'utf8' */ function streamFileLineByLineAsync(filename, encoding = defaultEncoding) { const fnError = (e) => { iter.throw && iter.throw(e); }; const stream = prepareFileStream(filename, encoding, fnError); const iter = streamLineByLineAsync(stream); return iter; } exports.streamFileLineByLineAsync = streamFileLineByLineAsync; /** * Emit a file line by line * @param filename full path to the file to read. * @param encoding defaults to 'utf8' */ function streamLineByLineAsync(stream, encoding = defaultEncoding) { let data = '.'; let done = false; let error; const buffer = []; const pending = []; const fnError = (e) => { error = e; }; const fnComplete = () => { // readline will consume the last newline without emitting an empty last line. // If the last data read contains a new line, then emit an empty string. if (data.match(/(?:(?:\r?\n)|(?:\r))$/)) { buffer.push(''); } processBuffer(); done = true; }; // We want to capture the last line. stream.on('data', (d) => (data = dataToString(d, encoding))); stream.on('error', fnError); const rl = readline.createInterface({ input: stream, terminal: false, }); rl.on('close', fnComplete); rl.on('line', (text) => { buffer.push(text); processBuffer(); }); function registerPromise(resolve, reject) { pending.push({ resolve, reject }); processBuffer(); } function processBuffer() { if (error && pending.length && !buffer.length) { const p = pending.shift(); p === null || p === void 0 ? void 0 : p.reject(error); return; } while (pending.length && buffer.length) { const p = pending.shift(); const b = buffer.shift(); if (b !== undefined && p) { p.resolve({ done: false, value: b }); } } if (!done) { pending.length ? rl.resume() : rl.pause(); } if (done && pending.length && !buffer.length) { const p = pending.shift(); p === null || p === void 0 ? void 0 : p.resolve({ done, value: undefined }); } } const iter = { [Symbol.asyncIterator]: () => iter, next() { return new Promise(registerPromise); }, throw(e) { fnError(e); return new Promise(registerPromise); }, }; return iter; } exports.streamLineByLineAsync = streamLineByLineAsync; function dataToString(data, encoding = 'utf8') { if (typeof data === 'string') { return data; } return data.toString(encoding); } //# sourceMappingURL=fileReader.js.map