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.
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.
npm install @ferrow/text-diff-patchimport { 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+}"Raw Myers O(ND) line-array diff. DiffOp = { type: "equal"|"delete"|"insert", value: string }.
Splits text into lines ("" → []).
Groups a DiffOp[] into unified-diff hunks with context lines of
surrounding unchanged text; adjacent changes within 2 * context lines
merge into one hunk.
Full unified-diff text. CreatePatchOptions = { context?: number; oldFile?: string; newFile?: string }.
Parses a unified-diff string back into Hunk[] ({ oldStart, oldLines, newStart, newLines, lines }).
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 }.
Splits a line into word/whitespace tokens, exactly reconstructible via join("").
Word-level Myers diff over tokenized lines.
Renders a word-diff as one string with insertions/deletions wrapped in
markers. HighlightOptions = { insertOpen?, insertClose?, deleteOpen?, deleteClose? }
(defaults: {+...+} / {-...-}).
- 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