diff --git a/README.md b/README.md
index 9fe1c5e..f9c119d 100644
--- a/README.md
+++ b/README.md
@@ -149,6 +149,10 @@ The HTML output accepts a Javascript object with configuration. Possible options
- `outputFormat`: the format of the output data: `'line-by-line'` or `'side-by-side'`, default is `'line-by-line'`
- `drawFileList`: show a file list before the diff: `true` or `false`, default is `true`
- `diffStyle`: show differences level in each line: `word` or `char`, default is `word`
+- `diffMaxChanges`: number of changed lines after which a file diff is deemed as too big and not displayed, default is
+ `undefined`
+- `diffTooBigMessage`: function allowing to customize the message in case of file diff too big (if `diffMaxChanges` is
+ set)
- `matching`: matching level: `'lines'` for matching lines, `'words'` for matching lines and words or `'none'`, default
is `none`
- `matchWordsThreshold`: similarity threshold for word matching, default is `0.25`
diff --git a/src/__tests__/diff-parser-tests.ts b/src/__tests__/diff-parser-tests.ts
index 5768d8c..a3121f3 100644
--- a/src/__tests__/diff-parser-tests.ts
+++ b/src/__tests__/diff-parser-tests.ts
@@ -1975,5 +1975,231 @@ describe('DiffParser', () => {
]
`);
});
+
+ it('should work when `diffMaxChanges` is set and excedeed', () => {
+ const diff =
+ 'diff --git a/src/core/init.js b/src/core/init.js\n' +
+ 'index e49196a..50f310c 100644\n' +
+ '--- a/src/core/init.js\n' +
+ '+++ b/src/core/init.js\n' +
+ '@@ -101,7 +101,7 @@ var rootjQuery,\n' +
+ ' // HANDLE: $(function)\n' +
+ ' // Shortcut for document ready\n' +
+ ' } else if ( jQuery.isFunction( selector ) ) {\n' +
+ '- return typeof rootjQuery.ready !== "undefined" ?\n' +
+ '+ return rootjQuery.ready !== undefined ?\n' +
+ ' rootjQuery.ready( selector ) :\n' +
+ ' // Execute immediately if ready is not present\n' +
+ ' selector( jQuery );\n' +
+ 'diff --git a/src/event.js b/src/event.js\n' +
+ 'index 7336f4d..6183f70 100644\n' +
+ '--- a/src/event.js\n' +
+ '+++ b/src/event.js\n' +
+ '@@ -1,6 +1,5 @@\n' +
+ ' define([\n' +
+ ' "./core",\n' +
+ '- "./var/strundefined",\n' +
+ ' "./var/rnotwhite",\n' +
+ ' "./var/hasOwn",\n' +
+ ' "./var/slice",\n';
+ const result = parse(diff, { diffMaxChanges: 1 });
+ expect(result).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "addedLines": 0,
+ "blocks": Array [
+ Object {
+ "header": "Diff too big to be displayed",
+ "lines": Array [],
+ "newStartLine": 0,
+ "oldStartLine": 0,
+ "oldStartLine2": null,
+ },
+ ],
+ "checksumAfter": "50f310c",
+ "checksumBefore": "e49196a",
+ "deletedLines": 0,
+ "isCombined": false,
+ "isGitDiff": true,
+ "isTooBig": true,
+ "language": "js",
+ "mode": "100644",
+ "newName": "src/core/init.js",
+ "oldName": "src/core/init.js",
+ },
+ Object {
+ "addedLines": 0,
+ "blocks": Array [
+ Object {
+ "header": "@@ -1,6 +1,5 @@",
+ "lines": Array [
+ Object {
+ "content": " define([",
+ "newNumber": 1,
+ "oldNumber": 1,
+ "type": "context",
+ },
+ Object {
+ "content": " \\"./core\\",",
+ "newNumber": 2,
+ "oldNumber": 2,
+ "type": "context",
+ },
+ Object {
+ "content": "- \\"./var/strundefined\\",",
+ "newNumber": undefined,
+ "oldNumber": 3,
+ "type": "delete",
+ },
+ Object {
+ "content": " \\"./var/rnotwhite\\",",
+ "newNumber": 3,
+ "oldNumber": 4,
+ "type": "context",
+ },
+ Object {
+ "content": " \\"./var/hasOwn\\",",
+ "newNumber": 4,
+ "oldNumber": 5,
+ "type": "context",
+ },
+ Object {
+ "content": " \\"./var/slice\\",",
+ "newNumber": 5,
+ "oldNumber": 6,
+ "type": "context",
+ },
+ ],
+ "newStartLine": 1,
+ "oldStartLine": 1,
+ "oldStartLine2": null,
+ },
+ ],
+ "checksumAfter": "6183f70",
+ "checksumBefore": "7336f4d",
+ "deletedLines": 1,
+ "isCombined": false,
+ "isGitDiff": true,
+ "language": "js",
+ "mode": "100644",
+ "newName": "src/event.js",
+ "oldName": "src/event.js",
+ },
+ ]
+ `);
+ });
+
+ it('should work when `diffMaxChanges` is set and excedeed, and `diffTooBigMessage` is set', () => {
+ const diff =
+ 'diff --git a/src/core/init.js b/src/core/init.js\n' +
+ 'index e49196a..50f310c 100644\n' +
+ '--- a/src/core/init.js\n' +
+ '+++ b/src/core/init.js\n' +
+ '@@ -101,7 +101,7 @@ var rootjQuery,\n' +
+ ' // HANDLE: $(function)\n' +
+ ' // Shortcut for document ready\n' +
+ ' } else if ( jQuery.isFunction( selector ) ) {\n' +
+ '- return typeof rootjQuery.ready !== "undefined" ?\n' +
+ '+ return rootjQuery.ready !== undefined ?\n' +
+ ' rootjQuery.ready( selector ) :\n' +
+ ' // Execute immediately if ready is not present\n' +
+ ' selector( jQuery );\n' +
+ 'diff --git a/src/event.js b/src/event.js\n' +
+ 'index 7336f4d..6183f70 100644\n' +
+ '--- a/src/event.js\n' +
+ '+++ b/src/event.js\n' +
+ '@@ -1,6 +1,5 @@\n' +
+ ' define([\n' +
+ ' "./core",\n' +
+ '- "./var/strundefined",\n' +
+ ' "./var/rnotwhite",\n' +
+ ' "./var/hasOwn",\n' +
+ ' "./var/slice",\n';
+ const result = parse(diff, { diffMaxChanges: 1, diffTooBigMessage: (i: number) => `Custom ${i}` });
+ expect(result).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "addedLines": 0,
+ "blocks": Array [
+ Object {
+ "header": "Custom 0",
+ "lines": Array [],
+ "newStartLine": 0,
+ "oldStartLine": 0,
+ "oldStartLine2": null,
+ },
+ ],
+ "checksumAfter": "50f310c",
+ "checksumBefore": "e49196a",
+ "deletedLines": 0,
+ "isCombined": false,
+ "isGitDiff": true,
+ "isTooBig": true,
+ "language": "js",
+ "mode": "100644",
+ "newName": "src/core/init.js",
+ "oldName": "src/core/init.js",
+ },
+ Object {
+ "addedLines": 0,
+ "blocks": Array [
+ Object {
+ "header": "@@ -1,6 +1,5 @@",
+ "lines": Array [
+ Object {
+ "content": " define([",
+ "newNumber": 1,
+ "oldNumber": 1,
+ "type": "context",
+ },
+ Object {
+ "content": " \\"./core\\",",
+ "newNumber": 2,
+ "oldNumber": 2,
+ "type": "context",
+ },
+ Object {
+ "content": "- \\"./var/strundefined\\",",
+ "newNumber": undefined,
+ "oldNumber": 3,
+ "type": "delete",
+ },
+ Object {
+ "content": " \\"./var/rnotwhite\\",",
+ "newNumber": 3,
+ "oldNumber": 4,
+ "type": "context",
+ },
+ Object {
+ "content": " \\"./var/hasOwn\\",",
+ "newNumber": 4,
+ "oldNumber": 5,
+ "type": "context",
+ },
+ Object {
+ "content": " \\"./var/slice\\",",
+ "newNumber": 5,
+ "oldNumber": 6,
+ "type": "context",
+ },
+ ],
+ "newStartLine": 1,
+ "oldStartLine": 1,
+ "oldStartLine2": null,
+ },
+ ],
+ "checksumAfter": "6183f70",
+ "checksumBefore": "7336f4d",
+ "deletedLines": 1,
+ "isCombined": false,
+ "isGitDiff": true,
+ "language": "js",
+ "mode": "100644",
+ "newName": "src/event.js",
+ "oldName": "src/event.js",
+ },
+ ]
+ `);
+ });
});
});
diff --git a/src/__tests__/line-by-line-tests.ts b/src/__tests__/line-by-line-tests.ts
index ea7ad89..75f9aa7 100644
--- a/src/__tests__/line-by-line-tests.ts
+++ b/src/__tests__/line-by-line-tests.ts
@@ -505,6 +505,65 @@ describe('LineByLineRenderer', () => {
"
`);
});
+
+ it('should work for too big file diff', () => {
+ const exampleJson = [
+ {
+ blocks: [
+ {
+ header: 'Custom link to render',
+ lines: [],
+ newStartLine: 0,
+ oldStartLine: 0,
+ oldStartLine2: undefined,
+ },
+ ],
+ deletedLines: 0,
+ addedLines: 0,
+ oldName: 'sample',
+ language: 'js',
+ newName: 'sample',
+ isCombined: false,
+ isGitDiff: false,
+ isTooBig: true,
+ },
+ ];
+
+ const hoganUtils = new HoganJsUtils({});
+ const lineByLineRenderer = new LineByLineRenderer(hoganUtils);
+ const html = lineByLineRenderer.render(exampleJson);
+ expect(html).toMatchInlineSnapshot(`
+ "
"
+ `);
+ });
});
describe('_generateFileHtml', () => {
diff --git a/src/__tests__/side-by-side-printer-tests.ts b/src/__tests__/side-by-side-printer-tests.ts
index 6863a1a..2bbc59c 100644
--- a/src/__tests__/side-by-side-printer-tests.ts
+++ b/src/__tests__/side-by-side-printer-tests.ts
@@ -406,6 +406,81 @@ describe('SideBySideRenderer', () => {
"
`);
});
+
+ it('should work for too big file diff', () => {
+ const exampleJson = [
+ {
+ blocks: [
+ {
+ header: 'Custom link to render',
+ lines: [],
+ newStartLine: 0,
+ oldStartLine: 0,
+ oldStartLine2: undefined,
+ },
+ ],
+ deletedLines: 0,
+ addedLines: 0,
+ oldName: 'sample',
+ language: 'js',
+ newName: 'sample',
+ isCombined: false,
+ isGitDiff: false,
+ isTooBig: true,
+ },
+ ];
+
+ const hoganUtils = new HoganJsUtils({});
+ const sideBySideRenderer = new SideBySideRenderer(hoganUtils);
+ const html = sideBySideRenderer.render(exampleJson);
+ expect(html).toMatchInlineSnapshot(`
+ ""
+ `);
+ });
});
describe('processLines', () => {
diff --git a/src/diff-parser.ts b/src/diff-parser.ts
index 1c816a0..084acb1 100644
--- a/src/diff-parser.ts
+++ b/src/diff-parser.ts
@@ -4,6 +4,8 @@ import { escapeForRegExp } from './utils';
export interface DiffParserConfig {
srcPrefix?: string;
dstPrefix?: string;
+ diffMaxChanges?: number;
+ diffTooBigMessage?: (fileIndex: number) => string;
}
function getExtension(filename: string, language: string): string {
@@ -299,6 +301,30 @@ export function parse(diffInput: string, config: DiffParserConfig = {}): DiffFil
startFile();
}
+ // Ignore remaining diff for current file if marked as too big
+ if (currentFile?.isTooBig) {
+ return;
+ }
+
+ if (
+ currentFile &&
+ typeof config.diffMaxChanges === 'number' &&
+ currentFile.addedLines + currentFile.deletedLines > config.diffMaxChanges
+ ) {
+ currentFile.isTooBig = true;
+ currentFile.addedLines = 0;
+ currentFile.deletedLines = 0;
+ currentFile.blocks = [];
+ currentBlock = null;
+
+ const message =
+ typeof config.diffTooBigMessage === 'function'
+ ? config.diffTooBigMessage(files.length)
+ : 'Diff too big to be displayed';
+ startBlock(message);
+ return;
+ }
+
/*
* We need to make sure that we have the three lines of the header.
* This avoids cases like the ones described in:
diff --git a/src/line-by-line-renderer.ts b/src/line-by-line-renderer.ts
index 969782a..4258095 100644
--- a/src/line-by-line-renderer.ts
+++ b/src/line-by-line-renderer.ts
@@ -95,7 +95,7 @@ export default class LineByLineRenderer {
.map(block => {
let lines = this.hoganUtils.render(genericTemplatesPath, 'block-header', {
CSSLineClass: renderUtils.CSSLineClass,
- blockHeader: renderUtils.escapeForHtml(block.header),
+ blockHeader: file.isTooBig ? block.header : renderUtils.escapeForHtml(block.header),
lineClass: 'd2h-code-linenumber',
contentClass: 'd2h-code-line',
});
diff --git a/src/side-by-side-renderer.ts b/src/side-by-side-renderer.ts
index e38f7d0..9a0fb6b 100644
--- a/src/side-by-side-renderer.ts
+++ b/src/side-by-side-renderer.ts
@@ -97,7 +97,7 @@ export default class SideBySideRenderer {
return file.blocks
.map(block => {
const fileHtml = {
- left: this.makeHeaderHtml(block.header),
+ left: this.makeHeaderHtml(block.header, file),
right: this.makeHeaderHtml(''),
};
@@ -203,10 +203,10 @@ export default class SideBySideRenderer {
return doMatching ? matcher(oldLines, newLines) : [[oldLines, newLines]];
}
- makeHeaderHtml(blockHeader: string): string {
+ makeHeaderHtml(blockHeader: string, file?: DiffFile): string {
return this.hoganUtils.render(genericTemplatesPath, 'block-header', {
CSSLineClass: renderUtils.CSSLineClass,
- blockHeader: renderUtils.escapeForHtml(blockHeader),
+ blockHeader: file?.isTooBig ? blockHeader : renderUtils.escapeForHtml(blockHeader),
lineClass: 'd2h-code-side-linenumber',
contentClass: 'd2h-code-side-line',
});
diff --git a/src/types.ts b/src/types.ts
index d573ac9..ea28b49 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -62,6 +62,7 @@ export interface DiffFile extends DiffFileName {
isCopy?: boolean;
isRename?: boolean;
isBinary?: boolean;
+ isTooBig?: boolean;
unchangedPercentage?: number;
changedPercentage?: number;
checksumBefore?: string | string[];
diff --git a/website/templates/pages/demo/demo.ts b/website/templates/pages/demo/demo.ts
index 42eccae..62ba9e5 100644
--- a/website/templates/pages/demo/demo.ts
+++ b/website/templates/pages/demo/demo.ts
@@ -21,6 +21,7 @@ import './demo.css';
type URLParams = {
diff?: string;
+ diffTooBigMessage?: string;
[key: string]: string | boolean | number | undefined;
};
@@ -116,9 +117,9 @@ function prepareRequest(url: string): Request {
}
function getConfiguration(urlParams: URLParams): Diff2HtmlUIConfig {
- // Removing `diff` form `urlParams` to avoid being inserted
+ // Removing `diff` and `diffTooBigMessage` form `urlParams` to avoid being inserted
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- const { diff, ...urlParamsRest } = urlParams;
+ const { diff, diffTooBigMessage, ...urlParamsRest } = urlParams;
const config: URLParams = {
...defaultDiff2HtmlUIConfig,
...urlParamsRest,