feat(core): add V2 formatter runtime (#39564)

This commit is contained in:
James Long 2026-07-29 17:23:04 -04:00 committed by GitHub
commit 94ee274aeb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 916 additions and 59 deletions

38
packages/util/src/bom.ts Normal file
View file

@ -0,0 +1,38 @@
export * as Bom from "./bom.js"
import { Effect } from "effect"
import { 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 const readFile = Effect.fn("Bom.readFile")(function* (fs: FSUtil.Interface, filepath: string) {
return split(decode(yield* fs.readFile(filepath)))
})
export const syncFile = Effect.fn("Bom.syncFile")(function* (fs: FSUtil.Interface, filepath: string, bom: boolean) {
const decoded = decode(yield* fs.readFile(filepath))
const current = split(decoded)
const canonical = join(current.text, bom)
if (decoded === canonical) return current.text
yield* fs.writeWithDirs(filepath, canonical)
return current.text
})
function decode(content: Uint8Array) {
return new TextDecoder("utf-8", { ignoreBOM: true }).decode(content)
}

View file

@ -1,6 +1,7 @@
export * as Patch from "./patch.js"
import { Result, Schema } from "effect"
import { Bom } from "./bom.js"
export class BoundaryError extends Schema.TaggedErrorClass<BoundaryError>()("Patch.BoundaryError", {
boundary: Schema.Literals(["first", "last"]),
@ -125,20 +126,19 @@ export function parse(patchText: string): Result.Result<ReadonlyArray<Hunk>, Par
}
export function derive(path: string, chunks: ReadonlyArray<UpdateFileChunk>, original: string): FileUpdate {
const source = splitBom(original)
const source = Bom.split(original)
const lines = source.text.split("\n")
if (lines.at(-1) === "") lines.pop()
const replacements = computeReplacements(lines, path, chunks)
const updated = [...lines]
for (const [start, remove, insert] of replacements.toReversed()) updated.splice(start, remove, ...insert)
if (updated.at(-1) !== "") updated.push("")
const next = splitBom(updated.join("\n"))
const next = Bom.split(updated.join("\n"))
return { content: next.text, bom: source.bom || next.bom }
}
export function joinBom(text: string, bom: boolean) {
const stripped = splitBom(text).text
return bom ? `\uFEFF${stripped}` : stripped
return Bom.join(text, bom)
}
function parseAdd(
@ -379,6 +379,4 @@ const normalize = (value: string) =>
.replace(/[“”„‟]/g, '"')
.replace(/[‐‑‒–—―−]/g, "-")
.replace(/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g, " ")
const splitBom = (text: string) =>
text.startsWith("\uFEFF") ? { bom: true, text: text.slice(1) } : { bom: false, text }
const stripHeredoc = (input: string) => input.match(/^(?:cat\s+)?<<(['"]?)(\w+)\1\s*\n([\s\S]*?)\n\2\s*$/)?.[3] ?? input