read-last-lines
read-last-lines copied to clipboard
Want a More Modern Version of Your Code?
I stole your source code, and then revamped it to use modern JS (eg. async
/await
). I also changed your snake case variables to camel case, factored a few inner functions outside, etc. (I also eliminated the encoding arg since I didn't need it, but it could easily be restored and I left a note about it in the comments.)
Now I completely understand that when someone says "hey I took your great source code and screwed it all up", the original author runs screaming in the opposite :) So feel free to close this ticket without even looking at it ... but just in case you wanted to see my take, I thought I'd share.
Either way though, thanks for making this (small but useful) library :)
const readPreviousChar = async (stat, file, currentCharacterCount) => {
const position = stat.size - 1 - currentCharacterCount;
const { bytesRead, buffer } = await fs.read(
file, // file descriptor
Buffer.alloc(1), // buffer to write data to
0, // offset
1, // # of bytes to read
position // position in data to read
);
return String.fromCharCode(buffer[0]);
};
const NEW_LINE_CHARACTERS = ['\n'];
const doWhileLoop = async function (
self,
lines,
chars,
lineCount,
maxLineCount,
resolve
) {
if (lines.length > self.stat.size) {
lines = lines.substring(lines.length - self.stat.size);
}
if (lines.length >= self.stat.size || lineCount >= maxLineCount) {
if (NEW_LINE_CHARACTERS.includes(lines.substring(0, 1))) {
lines = lines.substring(1);
}
fs.close(self.file);
// I didn't have to worry about other encoding types;
// if you want to keep supporting them this function would need an encoding arg
// if (encoding === 'buffer') {
// return resolve(Buffer.from(lines, 'binary'));
// }
// return resolve(Buffer.from(lines, 'binary').toString(encoding));
return resolve(Buffer.from(lines, 'binary').toString('utf8'));
}
const nextCharacter = await readPreviousChar(self.stat, self.file, chars);
lines = nextCharacter + lines;
if (NEW_LINE_CHARACTERS.includes(nextCharacter) && lines.length > 1) {
lineCount++;
}
chars++;
await doWhileLoop(self, lines, chars, lineCount, maxLineCount, resolve);
};
/**
* Read in the last `n` lines of a file
* @param {string} inputFilePath - file (direct or relative path to file.)
* @param {int} maxLineCount - max number of lines to read in.
* @param {encoding} encoding - specifies the character encoding to be used, or 'buffer'. defaults to 'utf8'.
*
* @return {promise} a promise resolved with the lines or rejected with an error.
*/
const readLast = async (inputFilePath, maxLineCount) => {
return new Promise(async (resolve, reject) => {
let self = { stat: null, file: null };
try {
// @see https://nodejs.org/api/fs.html#fsexistspath-callback
// "Using fs.exists() to check for the existence of a file before calling
// fs.open(), fs.readFile() or fs.writeFile() is not recommended"
// if (!(await fs.exists(inputFilePath))) {
// throw new Error(`file does not exist: ${inputFilePath}`);
// }
await Promise.all([
// Load file Stats.
fs.stat(inputFilePath).then((stat) => (self.stat = stat)),
// Open file for reading.
fs.open(inputFilePath, 'r').then((file) => (self.file = file)),
]);
let chars = 0;
let lines = '';
return doWhileLoop(self, lines, chars, 0, maxLineCount, resolve);
} catch (reason) {
if (self.file !== null) {
try {
fs.close(self.file);
} catch {
// We might get here if the encoding is invalid.
// Since we are already rejecting, let's ignore this error.
}
}
return reject(reason);
}
});
};
Check here: https://github.com/tjx666/scripting-listener/blob/master/src/LogViewer/readLastLines.ts