jsdiff

A JavaScript text differencing implementation. Try it out in the online demo.

Based on the algorithm proposed in "An O(ND) Difference Algorithm and its Variations" (Myers, 1986).

Installation

npm install diff --save

Getting started

Imports

In an environment where you can use imports, everything you need can be imported directly from diff. e.g.

ESM:

import {diffChars, createPatch} from 'diff';

CommonJS

const {diffChars, createPatch} = require('diff');

If you want to serve jsdiff to a web page without using a module system, you can use dist/diff.js or dist/diff.min.js. These create a global called Diff that contains the entire JsDiff API as its properties.

Usage

jsdiff's diff functions all take an old text and a new text and perform three steps:

  1. Split both texts into arrays of "tokens". What constitutes a token varies; in diffChars, each character is a token, while in diffLines, each line is a token.

  2. Find the smallest set of single-token insertions and deletions needed to transform the first array of tokens into the second.

    This step depends upon having some notion of a token from the old array being "equal" to one from the new array, and this notion of equality affects the results. Usually two tokens are equal if === considers them equal, but some of the diff functions use an alternative notion of equality or have options to configure it. For instance, by default diffChars("Foo", "FOOD") will require two deletions (o, o) and three insertions (O, O, D), but diffChars("Foo", "FOOD", {ignoreCase: true}) will require just one insertion (of a D), since ignoreCase causes o and O to be considered equal.

  3. Return an array representing the transformation computed in the previous step as a series of change objects. The array is ordered from the start of the input to the end, and each change object represents inserting one or more tokens, deleting one or more tokens, or keeping one or more tokens.

API

Universal options

Certain options can be provided in the options object of any method that calculates a diff (including diffChars, diffLines etc. as well as structuredPatch, createPatch, and createTwoFilesPatch):

Defining custom diffing behaviors

If you need behavior a little different to what any of the text diffing functions above offer, you can roll your own by customizing both the tokenization behavior used and the notion of equality used to determine if two tokens are equal.

The simplest way to customize tokenization behavior is to simply tokenize the texts you want to diff yourself, with your own code, then pass the arrays of tokens to diffArrays. For instance, if you wanted a semantically-aware diff of some code, you could try tokenizing it using a parser specific to the programming language the code is in, then passing the arrays of tokens to diffArrays.

To customize the notion of token equality used, use the comparator option to diffArrays.

For even more customisation of the diffing behavior, you can extend the Diff() class, override its castInput, tokenize, removeEmpty, equals, and join properties with your own functions, then call its diff(oldString, newString[, options]) method. The methods you can override are used as follows:

Change Objects

Many of the methods above return change objects. These objects consist of the following fields:

(Change objects where added and removed are both false represent content that is common to the old and new strings.)

Examples

Basic example in Node

require('colors');
const {diffChars} = require('diff');

const one = 'beep boop';
const other = 'beep boob blah';

const diff = diffChars(one, other);

diff.forEach((part) => {
  // green for additions, red for deletions
  let text = part.added ? part.value.bgGreen :
             part.removed ? part.value.bgRed :
                            part.value;
  process.stderr.write(text);
});

console.log();

Running the above program should yield

Node Example

Basic example in a web page

<pre id="display"></pre>
<script src="diff.js"></script>
<script>
const one = 'beep boop',
    other = 'beep boob blah',
    color = '';
    
let span = null;

const diff = Diff.diffChars(one, other),
    display = document.getElementById('display'),
    fragment = document.createDocumentFragment();

diff.forEach((part) => {
  // green for additions, red for deletions
  // grey for common parts
  const color = part.added ? 'green' :
    part.removed ? 'red' : 'grey';
  span = document.createElement('span');
  span.style.color = color;
  span.appendChild(document
    .createTextNode(part.value));
  fragment.appendChild(span);
});

display.appendChild(fragment);
</script>

Open the above .html file in a browser and you should see

Node Example

Example of generating a patch from Node

The code below is roughly equivalent to the Unix command diff -u file1.txt file2.txt > mydiff.patch:

const {createTwoFilesPatch} = require('diff');
const file1Contents = fs.readFileSync("file1.txt").toString();
const file2Contents = fs.readFileSync("file2.txt").toString();
const patch = createTwoFilesPatch("file1.txt", "file2.txt", file1Contents, file2Contents);
fs.writeFileSync("mydiff.patch", patch);

Examples of parsing and applying a patch from Node

Applying a patch to a specified file

The code below is roughly equivalent to the Unix command patch file1.txt mydiff.patch:

const {applyPatch} = require('diff');
const file1Contents = fs.readFileSync("file1.txt").toString();
const patch = fs.readFileSync("mydiff.patch").toString();
const patchedFile = applyPatch(file1Contents, patch);
fs.writeFileSync("file1.txt", patchedFile);
Applying a multi-file patch to the files specified by the patch file itself

The code below is roughly equivalent to the Unix command patch < mydiff.patch:

const {applyPatches} = require('diff');
const patch = fs.readFileSync("mydiff.patch").toString();
applyPatches(patch, {
    loadFile: (patch, callback) => {
        let fileContents;
        try {
            fileContents = fs.readFileSync(patch.oldFileName).toString();
        } catch (e) {
            callback(`No such file: ${patch.oldFileName}`);
            return;
        }
        callback(undefined, fileContents);
    },
    patched: (patch, patchedContent, callback) => {
        if (patchedContent === false) {
            callback(`Failed to apply patch to ${patch.oldFileName}`)
            return;
        }
        fs.writeFileSync(patch.oldFileName, patchedContent);
        callback();
    },
    complete: (err) => {
        if (err) {
            console.log("Failed with error:", err);
        }
    }
});

Compatibility

jsdiff should support all ES5 environments. If you find one that it doesn't support, please open an issue.

TypeScript

As of version 8, JsDiff ships with type definitions. From version 8 onwards, you should not depend on the @types/diff package.

One tricky pattern pervades the type definitions and is worth explaining here. Most diff-generating and patch-generating functions (diffChars, diffWords, structuredPatch, etc) can be run in async mode (by providing a callback option), in abortable mode (by passing a timeout or maxEditLength property), or both. This is awkward for typing, because these modes have different call signatures:

Our type definitions handle this as best they can by declaring different types for multiple overload signatures for each such function - and also by declaring different types for abortable and nonabortable options objects. For instance, an object of type DiffCharsOptionsAbortable is valid to pass as the options argument to diffChars and represents an abortable call (whose result may be undefined) since it necessarily contains either the timeout or maxEditLength property.

This approach, while probably the least bad way available to add types to JsDiff without radically refactoring the library's API, does not yield perfect results. As long as TypeScript is able to statically determine the type of your options, and therefore which overload signature is appropriate, everything should work fine. This should always be the case if you are passing an object literal as the options argument and inlining the definition of any callback function within that literal. But in cases where TypeScript cannot manage to do this - as may often be the case if you, say, define an options: any = {} object, build up your options programmatically, and then pass the result to a JsDiff function - then it is likely to fail to match the correct overload signature (probably defaulting to assuming you are calling the function in non-abortable, non-async mode), potentially causing type errors. You can either ignore (e.g. with @ts-expect-error) any such errors, or try to avoid them by refactoring your code so that TypeScript can always statically determine the type of the options you pass.

License

See LICENSE.

Deviations from the published Myers diff algorithm

jsdiff deviates from the published algorithm in a couple of ways that don't affect results but do affect performance: