(filePath: string, numLines: number)
| 283 | |
| 284 | // Memory-efficient implementation to get the last N lines of a file |
| 285 | export async function tailFile(filePath: string, numLines: number): Promise<string> { |
| 286 | const CHUNK_SIZE = 1024; // Read 1KB at a time |
| 287 | const stats = await fs.stat(filePath); |
| 288 | const fileSize = stats.size; |
| 289 | |
| 290 | if (fileSize === 0) return ''; |
| 291 | |
| 292 | // Open file for reading |
| 293 | const fileHandle = await fs.open(filePath, 'r'); |
| 294 | try { |
| 295 | const lines: string[] = []; |
| 296 | let position = fileSize; |
| 297 | let chunk = Buffer.alloc(CHUNK_SIZE); |
| 298 | let linesFound = 0; |
| 299 | let remainingText = ''; |
| 300 | |
| 301 | // Read chunks from the end of the file until we have enough lines |
| 302 | while (position > 0 && linesFound < numLines) { |
| 303 | const size = Math.min(CHUNK_SIZE, position); |
| 304 | position -= size; |
| 305 | |
| 306 | const { bytesRead } = await fileHandle.read(chunk, 0, size, position); |
| 307 | if (!bytesRead) break; |
| 308 | |
| 309 | // Get the chunk as a string and prepend any remaining text from previous iteration |
| 310 | const readData = chunk.slice(0, bytesRead).toString('utf-8'); |
| 311 | const chunkText = readData + remainingText; |
| 312 | |
| 313 | // Split by newlines and count |
| 314 | const chunkLines = normalizeLineEndings(chunkText).split('\n'); |
| 315 | |
| 316 | // If this isn't the end of the file, the first line is likely incomplete |
| 317 | // Save it to prepend to the next chunk |
| 318 | if (position > 0) { |
| 319 | remainingText = chunkLines[0]; |
| 320 | chunkLines.shift(); // Remove the first (incomplete) line |
| 321 | } |
| 322 | |
| 323 | // Add lines to our result (up to the number we need) |
| 324 | for (let i = chunkLines.length - 1; i >= 0 && linesFound < numLines; i--) { |
| 325 | lines.unshift(chunkLines[i]); |
| 326 | linesFound++; |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | return lines.join('\n'); |
| 331 | } finally { |
| 332 | await fileHandle.close(); |
| 333 | } |
| 334 | } |
| 335 | |
| 336 | // New function to get the first N lines of a file |
| 337 | export async function headFile(filePath: string, numLines: number): Promise<string> { |
no test coverage detected