export * as Bom from "./bom.js" import { Effect } from "effect" import type { FSUtil } from "./fs-util.js" const code = 0xfeff const value = String.fromCharCode(code) export function split(text: string) { const stripped = text.replace(/^\uFEFF+/, "") return { bom: stripped.length !== text.length, text: stripped } } export function join(text: string, bom: boolean) { const stripped = split(text).text return bom ? value + stripped : stripped } export function has(content: Uint8Array) { return content[0] === 0xef && content[1] === 0xbb && content[2] === 0xbf } export function decodeBytes(content: Uint8Array) { return split(decode(content)) } export function syncBytes(content: Uint8Array, bom: boolean) { const decoded = decode(content) const current = split(decoded) const canonical = bom ? value + current.text : current.text return { text: current.text, bytes: decoded === canonical ? undefined : new TextEncoder().encode(canonical) } } export const readFile = Effect.fn("Bom.readFile")(function* (fs: FSUtil.Interface, filepath: string) { return decodeBytes(yield* fs.readFile(filepath)) }) export const syncFile = Effect.fn("Bom.syncFile")(function* (fs: FSUtil.Interface, filepath: string, bom: boolean) { const synced = syncBytes(yield* fs.readFile(filepath), bom) if (synced.bytes) yield* fs.writeWithDirs(filepath, synced.bytes) return synced.text }) function decode(content: Uint8Array) { return new TextDecoder("utf-8", { ignoreBOM: true }).decode(content) }