fileRead.ts
utils/fileRead.ts
103
Lines
3168
Bytes
5
Exports
2
Imports
10
Keywords
What this is
This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.
Beginner explanation
This file is one piece of the larger system. Its name, directory, imports, and exports show where it fits. Start by reading the exports and related files first.
How it is used
Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.
Expert explanation
Architecturally, this file intersects with file-tools. It contains 103 lines, 2 detected imports, and 5 detected exports.
Important relationships
Detected exports
LineEndingTypedetectEncodingForResolvedPathdetectLineEndingsForStringreadFileSyncWithMetadatareadFileSync
Keywords
contentfileresolvedpathbufferutf8encodingfilepathreadfilesyncbytesreaddetectlineendings
Detected imports
./debug.js./fsOperations.js
Source notes
This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.
Full source
/**
* Sync file-read path, extracted from file.ts.
*
* file.ts sits in the settings SCC via log.ts → types/logs.ts → types/message.ts →
* Tool.ts → commands.ts → … Anything that needs readFileSync from file.ts
* pulls in the whole chain. This leaf imports only fsOperations and debug,
* both of which terminate in Node builtins.
*
* detectFileEncoding/detectLineEndings stay in file.ts — they call logError
* (log.ts → SCC) on unexpected failures. The -ForResolvedPath/-ForString
* helpers here are the pure parts; callers who need the logging wrappers
* import from file.ts.
*/
import { logForDebugging } from './debug.js'
import { getFsImplementation, safeResolvePath } from './fsOperations.js'
export type LineEndingType = 'CRLF' | 'LF'
export function detectEncodingForResolvedPath(
resolvedPath: string,
): BufferEncoding {
const { buffer, bytesRead } = getFsImplementation().readSync(resolvedPath, {
length: 4096,
})
// Empty files should default to utf8, not ascii
// This fixes a bug where writing emojis/CJK to empty files caused corruption
if (bytesRead === 0) {
return 'utf8'
}
if (bytesRead >= 2) {
if (buffer[0] === 0xff && buffer[1] === 0xfe) return 'utf16le'
}
if (
bytesRead >= 3 &&
buffer[0] === 0xef &&
buffer[1] === 0xbb &&
buffer[2] === 0xbf
) {
return 'utf8'
}
// For non-empty files, default to utf8 since it's a superset of ascii
// and handles all Unicode characters properly
return 'utf8'
}
export function detectLineEndingsForString(content: string): LineEndingType {
let crlfCount = 0
let lfCount = 0
for (let i = 0; i < content.length; i++) {
if (content[i] === '\n') {
if (i > 0 && content[i - 1] === '\r') {
crlfCount++
} else {
lfCount++
}
}
}
return crlfCount > lfCount ? 'CRLF' : 'LF'
}
/**
* Like readFileSync but also returns the detected encoding and original line
* ending style in one filesystem pass. Callers writing the file back (e.g.
* FileEditTool) can reuse these instead of calling detectFileEncoding /
* detectLineEndings separately, which would each redo safeResolvePath +
* readSync(4KB).
*/
export function readFileSyncWithMetadata(filePath: string): {
content: string
encoding: BufferEncoding
lineEndings: LineEndingType
} {
const fs = getFsImplementation()
const { resolvedPath, isSymlink } = safeResolvePath(fs, filePath)
if (isSymlink) {
logForDebugging(`Reading through symlink: ${filePath} -> ${resolvedPath}`)
}
const encoding = detectEncodingForResolvedPath(resolvedPath)
const raw = fs.readFileSync(resolvedPath, { encoding })
// Detect line endings from the raw head before CRLF normalization erases
// the distinction. 4096 code units is ≥ detectLineEndings's 4096-byte
// readSync sample (line endings are ASCII, so the unit mismatch is moot).
const lineEndings = detectLineEndingsForString(raw.slice(0, 4096))
return {
content: raw.replaceAll('\r\n', '\n'),
encoding,
lineEndings,
}
}
export function readFileSync(filePath: string): string {
return readFileSyncWithMetadata(filePath).content
}