import fs from 'fs'; import { PatchDetail } from '../patch/patch-detail'; import path from 'path'; import { getHash, withFileLock } from '../utils'; import { TsModule } from './ts-module'; /* ****************************************************************************************************************** */ // region: Config /* ****************************************************************************************************************** */ const SHORT_CHUNK_SIZE = 1024; const LONG_CHUNK_SIZE = 64_536; // endregion /* ****************************************************************************************************************** */ // region: Types /* ****************************************************************************************************************** */ export interface ModuleFile { moduleName: string patchDetail?: PatchDetail filePath: string contentFilePath: string get content(): string getHash(): string } // endregion /* ****************************************************************************************************************** */ // region: Helpers /* ****************************************************************************************************************** */ function readFile(filePath: string, headersOnly?: boolean) { return withFileLock(filePath, () => { let CHUNK_SIZE = headersOnly ? SHORT_CHUNK_SIZE : LONG_CHUNK_SIZE; let result = ''; let doneReadingHeaders = false; let bytesRead; let buffer = Buffer.alloc(CHUNK_SIZE); const headerLines: string[] = []; let isLastHeaderIncomplete = false; const fd = fs.openSync(filePath, 'r'); try { readFileLoop: while ((bytesRead = fs.readSync(fd, buffer, 0, CHUNK_SIZE, null)) > 0) { const chunkString = buffer.toString('utf-8', 0, bytesRead); /* Handle Header */ if (!doneReadingHeaders) { const lines = chunkString.split('\n'); lineLoop: for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (i === 0 && isLastHeaderIncomplete) { headerLines[headerLines.length - 1] += line; } else { if (line.startsWith('///')) { headerLines.push(line); } else { doneReadingHeaders = true; if (!headersOnly) { result += lines.slice(i).join('\n'); CHUNK_SIZE = LONG_CHUNK_SIZE; buffer = Buffer.alloc(CHUNK_SIZE); break lineLoop; } else { break readFileLoop; } } } } if (!doneReadingHeaders) isLastHeaderIncomplete = !chunkString.endsWith('\n'); } else { /* Handle content */ result += chunkString; } } return { headerLines, content: headersOnly ? undefined : result }; } finally { fs.closeSync(fd); } }); } // endregion /* ****************************************************************************************************************** */ // region: Utils /* ****************************************************************************************************************** */ export function getModuleFile(filePath: string, loadFullContent?: boolean): ModuleFile { /* Determine shim redirect file - see: https://github.com/nonara/ts-patch/issues/174 */ const moduleName = path.basename(filePath); const contentFilePath = TsModule.getContentFilePathForModulePath(filePath); /* Get PatchDetail */ let { headerLines, content } = readFile(contentFilePath, !loadFullContent); const patchDetail = PatchDetail.fromHeader(headerLines); return { moduleName, filePath, contentFilePath, patchDetail, get content() { if (content == null) content = readFile(this.contentFilePath, false).content; return content!; }, getHash(): string { return getHash(this.content); } }; } // endregion