You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
4.5 KiB
140 lines
4.5 KiB
"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
|