Description
This is a stackoverflow question I copied over to github per the recommendation of a commenter that believes this is a bug.
This may be a duplicate of diffWords is not detecting a deleted space at the end of text. #402 but it's hard to say because it doesn't go into detail.That issue was closed with a comment suggesting these are unrelated issues.- I have tested and reproduced this with
diffChars
,diffWordsWithSpace
, anddiffLines
. - I've toggled every option I've seen documented, but it has no affect with respect to this issue.
- I am running this code in node v20.10.0 on Windows 10.
- I've tested and reproduced it in JavaScript and TypeScript.
- I've tested and reproduced it in jsdiff 5.1.0, 5.0.0, 4.0.2, and 4.0.1.
Question:
Why doesn't jsdiff show leading/trailing whitespace differences?
Details:
I found this popular npm library named jsdiff (github). I'm using it in a node.js environment and it provides this example on how to do so (note: I've modified commonjs requires
to esm import
s):
import 'colors';
import * as Diff from 'diff';
const one = 'beep boop';
const other = 'beep boob blah';
const diff = Diff.diffChars(one, other);
diff.forEach((part) => {
// green for additions, red for deletions
// grey for common parts
const color = part.added ? 'green' : part.removed ? 'red' : 'grey';
process.stderr.write(part.value[color]);
});
console.log();
Output:
But if I modify one
and/or other
to have trailing whitespace, it isn't printed as a difference between the strings:
import 'colors';
import * as Diff from 'diff';
const one = 'beep boop ';
const other = '\nbeep boob blah\t';
const diff = Diff.diffChars(one, other);
diff.forEach((part) => {
// green for additions, red for deletions
// grey for common parts
const color = part.added ? 'green' : part.removed ? 'red' : 'grey';
process.stderr.write(part.value[color]);
});
console.log();
Output:
The README doesn't say whether this is expected or unexpected behavior of Diff.diffChars
, but trailing whitespace is ignored when I change Diff.diffChars
to Diff.diffWordsWithSpace
:
The documentation for Diff.diffWordsWithSpace
is more clear on the matter (emphasis mine):
diffs two blocks of text, comparing word by word, treating whitespace as significant.
I thought it could have something to do with the colors modifying the font foreground instead of background; whitespace has no foreground, so it wouldn't show up. To test that hypothesis, I switched from colors
to chalk
(after reading many recommendations to do so) and changed the background instead of foreground:
import chalk from 'chalk';
import * as Diff from 'diff';
const one = 'beep boop ';
const other = '\nbeep boob blah\t';
const diff = Diff.diffChars(one, other); // note: I went back to diffChars here
diff.forEach((part) => {
// green for additions, red for deletions
// grey for common parts
const color = part.added
? chalk.bgGreen
: part.removed
? chalk.bgRed
: (x) => x; // identity function, return with normal color
const coloredText = color(part.value);
process.stderr.write(coloredText);
});
console.log();
But this didn't make a difference in output:
Why doesn't jsdiff print leading/trailing whitespace differences?
Notes:
You can find a working repo with this last example here: https://github.com/DanKaplanSES/jsdom-sandbox/tree/jsdiff-trailing-whitespace-so
Workaround:
This will print differences in leading and trailing whitespace:
import chalk from 'chalk';
import * as Diff from 'diff';
const one = 'beep boop ';
const other = '\nbeep boob blah\t';
const diff = Diff.diffChars(JSON.stringify(one), JSON.stringify( other));
diff.forEach((part, index) => {
// green for additions, red for deletions
// grey for common parts
const color = part.added
? chalk.bgGreen
: part.removed
? chalk.bgRed
: (x) => x; // identity function, return with normal color
const coloredText = color(part.value);
process.stderr.write(coloredText);
});
console.log();
Output:
I consider this to be a workaround, not a solution: JSON.stringify
converts whitespace into character escape codes (except for the space character) and that's the only reason this library prints a difference.
Better logs:
I modified the example to print better logs. This shows that the differences are recognized, they just aren't displayed:
import chalk from 'chalk';
import * as Diff from 'diff';
const one = 'beep boop ';
const other = '\nbeep boob blah\t';
const diff = Diff.diffChars(one, other);
let buffer = "";
diff.forEach((part, index) => {
// green for additions, red for deletions
// grey for common parts
const color = part.added
? chalk.bgGreen
: part.removed
? chalk.bgRed
: (x) => x; // identity function, return with normal color
const coloredText = color(part.value);
console.log(`JSON.stringify(part.value)`, JSON.stringify(part.value), `part.added`, part.added, `part.removed`, part.removed);
buffer += (coloredText);
});
console.log(buffer);
Output: