(filePath: string)
| 60 | * @returns Promise that resolves to true if the file appears to be binary. |
| 61 | */ |
| 62 | export async function isBinaryFile(filePath: string): Promise<boolean> { |
| 63 | let fileHandle: fs.promises.FileHandle | undefined; |
| 64 | try { |
| 65 | fileHandle = await fs.promises.open(filePath, 'r'); |
| 66 | |
| 67 | // Read up to 4KB or file size, whichever is smaller |
| 68 | const stats = await fileHandle.stat(); |
| 69 | const fileSize = stats.size; |
| 70 | if (fileSize === 0) { |
| 71 | // Empty file is not considered binary for content checking |
| 72 | return false; |
| 73 | } |
| 74 | const bufferSize = Math.min(4096, fileSize); |
| 75 | const buffer = Buffer.alloc(bufferSize); |
| 76 | const result = await fileHandle.read(buffer, 0, buffer.length, 0); |
| 77 | const bytesRead = result.bytesRead; |
| 78 | |
| 79 | if (bytesRead === 0) return false; |
| 80 | |
| 81 | let nonPrintableCount = 0; |
| 82 | for (let i = 0; i < bytesRead; i++) { |
| 83 | if (buffer[i] === 0) return true; // Null byte is a strong indicator |
| 84 | if (buffer[i] < 9 || (buffer[i] > 13 && buffer[i] < 32)) { |
| 85 | nonPrintableCount++; |
| 86 | } |
| 87 | } |
| 88 | // If >30% non-printable characters, consider it binary |
| 89 | return nonPrintableCount / bytesRead > 0.3; |
| 90 | } catch (error) { |
| 91 | // Log error for debugging while maintaining existing behavior |
| 92 | console.warn( |
| 93 | `Failed to check if file is binary: ${filePath}`, |
| 94 | error instanceof Error ? error.message : String(error), |
| 95 | ); |
| 96 | // If any error occurs (e.g. file not found, permissions), |
| 97 | // treat as not binary here; let higher-level functions handle existence/access errors. |
| 98 | return false; |
| 99 | } finally { |
| 100 | // Safely close the file handle if it was successfully opened |
| 101 | if (fileHandle) { |
| 102 | try { |
| 103 | await fileHandle.close(); |
| 104 | } catch (closeError) { |
| 105 | // Log close errors for debugging while continuing with cleanup |
| 106 | console.warn( |
| 107 | `Failed to close file handle for: ${filePath}`, |
| 108 | closeError instanceof Error ? closeError.message : String(closeError), |
| 109 | ); |
| 110 | // The important thing is that we attempted to clean up |
| 111 | } |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | /** |
| 117 | * Detects the type of file based on extension and content. |
no test coverage detected