Built and signed on GitHub ActionsBuilt and signed on GitHub Actions
Modern JS/TS and Unicode-friendly version of diff-match-patch.
Diff-Match-Patch Unicode 
Modern JS/TS and Unicode-friendly version of Neil Fraserβs diff-match-patch. Currently only supports diffing; matching and patching may be added in the future, depending on need.
Usage
diff
diff(before: string, after: string, options?: Partial<DiffOptions>): Diff[]
Diff two strings. Fully Unicode-aware by default.
Pass a segmenter option to customize the units of calculation for the diff (char, line, word, grapheme, sentence, etc).
Example
import { Differ, segmenters } from '@clearlylocal/diff-match-patch-unicode' const differ = new Differ() const str1 = 'Hello, world! π«' const str2 = 'Goodbye, world! π©' // default behavior: UTF-8 char diff assertDiffsEqual( differ.diff(str1, str2), [[-1, 'Hell'], [1, 'G'], [0, 'o'], [1, 'odbye'], [0, ', world! '], [-1, 'π«'], [1, 'π©']], ) // word diff with `Intl.Segmenter` assertDiffsEqual( differ.diff(str1, str2, { segmenter: segmenters.word }), [[-1, 'Hello'], [1, 'Goodbye'], [0, ', world! '], [-1, 'π«'], [1, 'π©']], ) // pass in a custom `Intl.Segmenter` instance assertDiffsEqual( differ.diff('δΈ€εͺε°θθ', 'δΈ€εͺθθ', { segmenter: new Intl.Segmenter('zh-CN', { granularity: 'word' }) }), [[0, 'δΈ€εͺ'], [-1, 'ε°θθ'], [1, 'θθ']], ) // line diff assertDiffsEqual( differ.diff(str1, str2, { segmenter: segmenters.line }), [[-1, 'Hello, world! π«'], [1, 'Goodbye, world! π©']], ) // custom UTF-16 code-unit diff (equivalent to using `diffCodeUnits` directly... but less performant) assertDiffsEqual( differ.diff(str1, str2, { segmenter: (str) => str.split('') }), [[-1, 'Hell'], [1, 'G'], [0, 'o'], [1, 'odbye'], [0, ', world! \ud83d'], [-1, '\udcab'], [1, '\udca9']], )
Limitations
The maximum number of unique segments (chars, lines, words, graphemes, sentences, code units, etc) is capped at 65535 (0xFFFF), the maximum codepoint in the BMP. In addition, the maximum number of unique segments in the first string is capped at two thirds of that total (43690 or 0xAAAA). This is due to the original algorithm working with JS native UTF-16 strings and using non-UTF-8-aware methods (String.fromCharCode, charAt, substring, indexOf etc.) extensively.
If working with diffs larger than this limit, the last segment of each string will contain all of its remaining text until the end of the input.
Add Package
deno add jsr:@clearlylocal/diff-match-patch-unicode
Import symbol
import * as diff_match_patch_unicode from "@clearlylocal/diff-match-patch-unicode";
Import directly with a jsr specifier
import * as diff_match_patch_unicode from "jsr:@clearlylocal/diff-match-patch-unicode";
Add Package
pnpm i jsr:@clearlylocal/diff-match-patch-unicode
pnpm dlx jsr add @clearlylocal/diff-match-patch-unicode
Import symbol
import * as diff_match_patch_unicode from "@clearlylocal/diff-match-patch-unicode";
Add Package
yarn add jsr:@clearlylocal/diff-match-patch-unicode
yarn dlx jsr add @clearlylocal/diff-match-patch-unicode
Import symbol
import * as diff_match_patch_unicode from "@clearlylocal/diff-match-patch-unicode";
Add Package
vlt install jsr:@clearlylocal/diff-match-patch-unicode
Import symbol
import * as diff_match_patch_unicode from "@clearlylocal/diff-match-patch-unicode";
Add Package
npx jsr add @clearlylocal/diff-match-patch-unicode
Import symbol
import * as diff_match_patch_unicode from "@clearlylocal/diff-match-patch-unicode";
Add Package
bunx jsr add @clearlylocal/diff-match-patch-unicode
Import symbol
import * as diff_match_patch_unicode from "@clearlylocal/diff-match-patch-unicode";