@ferrow/text-diff-patch
v1.0.0
Published
Zero-dependency line-based Myers diff (O(ND)) producing unified-diff hunks, with fuzzy patch application, reject reporting, and word-level intra-line diff highlighting.
Maintainers
Readme
text-diff-patch
Zero-dependency line-based diff and patch for TypeScript/JavaScript: Myers O(ND) diff producing unified-diff-style hunks, fuzzy patch application with reject reporting, and a word-level intra-line diff highlighter.
Why
Most diff libraries either only diff or only apply, and few report why a
hunk failed to apply against a shifted file. This package does both
directions with zero runtime dependencies, strict TypeScript, and an
explicit fuzz tolerance for applying patches against slightly-moved text —
the case agents hit constantly when re-applying an edit to a file that
changed underneath them.
Install
npm install text-diff-patchQuickstart
import { createPatch, applyPatch, highlightWordDiff } from "text-diff-patch";
const patch = createPatch(oldText, newText, { oldFile: "a", newFile: "b" });
console.log(patch); // unified-diff-style string
const { result, applied, rejects } = applyPatch(oldText, patch, { fuzz: 3 });
// applied: number of hunks applied; rejects: hunks that couldn't be placed
highlightWordDiff("hello world", "hello there");
// "hello {-world-}{+there+}"API
myersDiff(a: string[], b: string[]): DiffOp[]
Raw Myers O(ND) line-array diff. DiffOp = { type: "equal"|"delete"|"insert", value: string }.
toLines(text: string): string[]
Splits text into lines ("" → []).
buildHunks(ops: DiffOp[], context = 3): Hunk[]
Groups a DiffOp[] into unified-diff hunks with context lines of
surrounding unchanged text; adjacent changes within 2 * context lines
merge into one hunk.
createPatch(oldText: string, newText: string, options?: CreatePatchOptions): string
Full unified-diff text. CreatePatchOptions = { context?: number; oldFile?: string; newFile?: string }.
parsePatch(patchText: string): Hunk[]
Parses a unified-diff string back into Hunk[] ({ oldStart, oldLines, newStart, newLines, lines }).
applyPatch(text: string, patch: string | Hunk[], options?: ApplyOptions): ApplyResult
Applies hunks to text. ApplyOptions = { fuzz?: number } — with fuzz > 0,
a hunk whose exact recorded line is off is retried at nearby offsets
(±1..±fuzz) before being rejected.
ApplyResult = { result: string; applied: number; rejects: RejectedHunk[] },
RejectedHunk = { hunk: Hunk; reason: string }.
tokenizeWords(line: string): string[]
Splits a line into word/whitespace tokens, exactly reconstructible via join("").
diffWords(a: string, b: string): DiffOp[]
Word-level Myers diff over tokenized lines.
highlightWordDiff(a: string, b: string, options?: HighlightOptions): string
Renders a word-diff as one string with insertions/deletions wrapped in
markers. HighlightOptions = { insertOpen?, insertClose?, deleteOpen?, deleteClose? }
(defaults: {+...+} / {-...-}).
Limits
- Line-based only —
myersDiff/createPatchoperate on\n-split lines, not characters (usediffWordsfor intra-line detail). applyPatch's fuzz search is a bounded ±N-line offset retry per hunk, not a general approximate-matching algorithm — very large shifts or heavily edited context will still reject.- No binary-file support; input is always treated as text.
parsePatchexpects hunk bodies to use exactly one leading' '/'+'/'-'per line (standard unified-diff shape) — it does not tolerate tab-indented or CRLF-mixed patches.
Part of the ferrow-toolkit collection · Sponsored by Ferrow
