Compare commits

...

715 commits

Author SHA1 Message Date
Rodrigo Fernandes
1350740c98
fix artifacts 2024-12-23 23:17:59 +00:00
Rodrigo Fernandes
04640cccbe
force release 2024-12-23 23:08:34 +00:00
Rodrigo Fernandes
babcdb0405
Merge pull request #551 from rtfpessoa/bump-dependencies
Bump dependencies
2024-12-23 22:55:42 +00:00
Rodrigo Fernandes
2f4f41a31d
bump node version 2024-12-23 22:52:07 +00:00
Rodrigo Fernandes
2f8c477f66
fix build 2024-12-23 22:49:18 +00:00
Rodrigo Fernandes
064659a70e
update tests 2024-12-23 22:41:43 +00:00
Rodrigo Fernandes
eef651f0d4
bump more versions 2024-12-23 22:34:58 +00:00
Rodrigo Fernandes
d67d80eb41
fix dark demo theme and bump deps 2024-07-20 22:46:00 +01:00
Rodrigo Fernandes
a88e4b3ba4
bump more versions 2024-07-09 18:35:22 +01:00
Rodrigo Fernandes
dbdd2e9c26
bump bulma 2024-07-09 18:30:45 +01:00
Rodrigo Fernandes
05fbd33a3a
fix output directories 2024-07-09 18:19:04 +01:00
Rodrigo Fernandes
b2e31b6083
bump more dependencies 2024-07-05 23:59:55 +01:00
Rodrigo Fernandes
00a12d11aa
bump dependencies 2024-07-05 23:52:51 +01:00
Rodrigo Fernandes
b517e40e5c
Merge pull request #531 from rtfpessoa/use-npm
use npm instead of yarn
2024-04-19 21:39:35 +01:00
Rodrigo Fernandes
dea1d300b3
use npm instead of yarn 2024-04-19 21:35:17 +01:00
Rodrigo Fernandes
4de87bf099
Merge pull request #523 from rtfpessoa/bump-dependencies
updates
2024-01-12 20:15:25 +00:00
Rodrigo Fernandes
aa4731953e
updates 2024-01-12 20:09:44 +00:00
Rodrigo Fernandes
363b619d87
Merge pull request #518 from JeroenVdb/support-shadow-dom
css: target both :root and :host to also cover usage in shadow DOM
2023-12-15 21:38:49 +00:00
Jeroen Van den Berghe
7005ced0b5 css: target both :root and :host to also cover usage in shadow DOM
Inside shadow DOM :root can't be targeted, so :host should be used here for those cases.

Solves issues #517
2023-10-17 13:26:41 +02:00
Rodrigo Fernandes
99b7757a3a
Merge pull request #515 from akwiatek/update-dark-mode-css
Amend malformed CSS.
2023-10-05 16:17:34 +01:00
Adam Kwiatek
dd4b774958 Amend malformed CSS. 2023-10-02 17:48:24 +02:00
Rodrigo Fernandes
b04a400329
Merge pull request #514 from JHWelch/add-dark-mode
Add Dark & Auto color schemes
2023-09-29 20:13:49 +01:00
Jordan Welch
7410d25ee4
Use d2h-light-color-scheme for light 2023-09-26 18:06:06 -05:00
Jordan Welch
987d8cb65c
update to GitHub dark colors 2023-09-26 17:47:59 -05:00
Jordan Welch
d425892b61
Remove duplicate "should"s 2023-09-20 08:02:40 -05:00
Jordan Welch
f7aacc4fc0
darken header 2023-09-19 20:54:19 -05:00
Jordan Welch
0de1e040b0
Create modified github theme for demo 2023-09-19 20:51:38 -05:00
Jordan Welch
4b42e4b0de
Toggle colorscheme on Demo body 2023-09-19 20:46:45 -05:00
Jordan Welch
863246eed0
remove redundant classes 2023-09-19 20:18:35 -05:00
Jordan Welch
3865a5f27e
Adjust colors 2023-09-19 19:54:40 -05:00
Jordan Welch
b26353c04d
Normalize remaining variables 2023-09-19 19:47:17 -05:00
Jordan Welch
8d9a8a8265
Fix --d2h-dark-moved-label-color references 2023-09-19 19:04:06 -05:00
Jordan Welch
1aa822da56
Update test text 2023-09-19 19:04:06 -05:00
Jordan Welch
786e5cc027
Fix remaining --d2h-dark-line-border-color 2023-09-19 19:04:06 -05:00
Jordan Welch
9806d677f3
Add file header 2023-09-19 19:04:05 -05:00
Jordan Welch
efd28ffb3a
Move dark variables and match pattern 2023-09-19 19:04:05 -05:00
Jordan Welch
93103e2c49
Convert light colors to variables 2023-09-19 19:04:05 -05:00
Jordan Welch
0029890ead
Add colorScheme information to README 2023-09-19 19:04:05 -05:00
Jordan Welch
79268800ff
Add colors for file list 2023-09-19 19:04:04 -05:00
Jordan Welch
9f8d6bd4ca
Pass config to file list renderer 2023-09-19 19:04:04 -05:00
Jordan Welch
669ee2bff8
Fix del/ins change colors 2023-09-19 19:04:03 -05:00
Jordan Welch
0dccfa26a5
Pass default from default render config 2023-09-19 19:04:03 -05:00
Jordan Welch
c244b0dd51
Add ColorSchemeType for file list 2023-09-19 19:04:03 -05:00
Jordan Welch
4e6bb49788
Update FileListRenderer to class 2023-09-19 19:04:02 -05:00
Jordan Welch
14989f1ddd
rename test to match Class & function 2023-09-19 19:04:02 -05:00
Jordan Welch
58c088977e
Fix color preference order 2023-09-19 19:04:02 -05:00
Jordan Welch
c22febdbd6
Adjust colors for line-by-line 2023-09-19 19:04:01 -05:00
Jordan Welch
1b0c1a87a2
swap highlight and base 2023-09-19 19:04:01 -05:00
Jordan Welch
84a323f0de
Add color scheme to demo page 2023-09-19 19:04:01 -05:00
Jordan Welch
0b76161d86
Use variables for dark mode 2023-09-19 19:04:00 -05:00
Jordan Welch
6ed6a5da88
Setup Auto color mode 2023-09-19 19:04:00 -05:00
Jordan Welch
47000723a9
Consolidate dark classes 2023-09-19 19:04:00 -05:00
Jordan Welch
6b420de752
est auto color scheme 2023-09-19 19:03:59 -05:00
Jordan Welch
bd4a84cd3e
Update colors 2023-09-19 19:03:59 -05:00
Jordan Welch
1aafcb201b
Add dark mode classes. 2023-09-19 19:03:59 -05:00
Jordan Welch
4d2505dac8
Normalize class names 2023-09-19 19:03:59 -05:00
Jordan Welch
ff3a86d393
Add colorscheme variable to wrapper 2023-09-19 19:03:58 -05:00
Jordan Welch
5dae945e95
Add initial dark mode css classes 2023-09-19 19:03:57 -05:00
Rodrigo Fernandes
8102d3cf61
Merge pull request #511 from JHWelch/organize-diff2html-tests
reorganize/rename blocks to match tested functions
2023-09-19 22:31:14 +01:00
Rodrigo Fernandes
2ffca85422
Merge pull request #512 from reustle/docs-typo-fix
Docs typo fix
2023-09-19 22:30:35 +01:00
Shane Reustle
299f7800c3
Docs typo fix 2023-09-12 19:55:53 -04:00
Jordan Welch
66912a3a54
reorganize/rename blocks to match tested functions 2023-09-11 22:48:23 -05:00
Rodrigo Fernandes
3732d59249
Merge pull request #508 from minami80630/fix-link
fix broken link
2023-09-01 23:59:19 +01:00
Rodrigo Fernandes
412928fc59
Merge pull request #507 from shoito/fix-start-website
fix 'yarn start:website' command
2023-09-01 23:58:52 +01:00
minami80630
61695e2896 fix broken link 2023-09-01 10:45:58 +09:00
shoito
93d0fbc6c3 fix 'yarn start:website' command 2023-08-30 21:38:18 +09:00
Rodrigo Fernandes
61d90c1c41
Merge pull request #506 from rtfpessoa/fix-error-unloaded-lang
fallback to plaintext when highlighting unloaded language
2023-08-27 21:40:19 +01:00
Rodrigo Fernandes
f6f05db2f6
fallback to plaintext when highlighting unloaded language 2023-08-27 21:37:39 +01:00
Rodrigo Fernandes
41a901694d
Merge pull request #503 from rtfpessoa/remove-import-helpers
Do not use import helpers
2023-08-07 10:23:40 +01:00
Rodrigo Fernandes
3516684c68
Do not use import helpers 2023-08-07 09:52:31 +01:00
Rodrigo Fernandes
638a4a286f
Merge pull request #500 from rtfpessoa/bump-deps
fix vuln
2023-08-06 19:52:13 +01:00
Rodrigo Fernandes
9d805bb021 fix vuln 2023-08-06 19:44:36 +01:00
Rodrigo Fernandes
8d34de633a
Merge pull request #499 from rtfpessoa/bump-deps
Bump deps
2023-08-06 19:32:04 +01:00
Rodrigo Fernandes
409b5e2f6b tweak tsconfig 2023-08-06 19:28:46 +01:00
Rodrigo Fernandes
657d48aac0 bump deps 2023-08-06 19:16:10 +01:00
Rodrigo Fernandes
611957e270
Merge pull request #498 from rtfpessoa/update-readme
fix badges
2023-08-06 19:09:58 +01:00
Rodrigo Fernandes
dfe2eec8df fix badges 2023-08-06 19:09:07 +01:00
Rodrigo Fernandes
40a9c0bdc8
Merge pull request #497 from rtfpessoa/update-readme
fix badges
2023-08-06 19:07:01 +01:00
Rodrigo Fernandes
a2d34e195c fix badges 2023-08-06 19:06:31 +01:00
Rodrigo Fernandes
e46184baa2
Merge pull request #488 from rtfpessoa/fix-vertical-align
fix vertical align on diff
2023-04-15 14:15:16 +01:00
Rodrigo Fernandes
7a3a4398d1 fix vertical align on diff 2023-04-15 14:12:08 +01:00
Rodrigo Fernandes
2c7e03d266
Merge pull request #486 from rtfpessoa/dependabot/npm_and_yarn/webpack-5.76.0
build(deps-dev): bump webpack from 5.74.0 to 5.76.0
2023-03-16 23:25:59 +00:00
dependabot[bot]
c76b24a1fd
build(deps-dev): bump webpack from 5.74.0 to 5.76.0
Bumps [webpack](https://github.com/webpack/webpack) from 5.74.0 to 5.76.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.74.0...v5.76.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-15 06:20:11 +00:00
Rodrigo Fernandes
68515376a1
Merge pull request #483 from phikes/hand-in-file-line-to-template
feat: hand in file/line to generic line template
2023-03-07 19:34:03 +00:00
Rodrigo Fernandes
551a0b407f
Merge pull request #482 from grimsteel/patch-1
Precise widths for `code-line` and `code-side-line`
2023-03-04 23:40:54 +00:00
Siddhant
32239a0b9d
Precise widths for code-line and code-side-line
The widths of these are now calculated based on:
100% minus the horizontal padding
2023-02-28 19:17:07 -06:00
Phillip Kessels
0d314aecd1
feat: hand in file/line to generic line template 2023-02-20 15:07:34 +00:00
Rodrigo Fernandes
09cbe87595
Merge pull request #478 from rtfpessoa/support-unix-diff-binaries
support unix diff binaires
2023-01-20 23:06:53 +00:00
Rodrigo Fernandes
a716739a18 support unix diff binaires 2023-01-20 23:00:34 +00:00
Rodrigo Fernandes
24ccfefa26
Merge pull request #477 from rtfpessoa/fix-firefox
fix-broken-header-ff
2023-01-20 22:32:05 +00:00
Rodrigo Fernandes
ca2397830c fix-broken-header-ff 2023-01-20 22:27:44 +00:00
Rodrigo Fernandes
66c305b854
Merge pull request #471 from rtfpessoa/tweak-css
fix home page link
2023-01-06 23:38:19 +00:00
Rodrigo Fernandes
be09a43715 fix home page link 2023-01-06 23:37:55 +00:00
Rodrigo Fernandes
d2e3705575
Merge pull request #470 from rtfpessoa/tweak-css
tweak css
2023-01-06 23:33:24 +00:00
Rodrigo Fernandes
d0ead3a14c tweak css 2023-01-06 23:32:58 +00:00
Rodrigo Fernandes
9247496115
Merge pull request #463 from FurcyPin/fpin/462
fix #462: Info lines shrunk in right panel of side-by-side diff
2023-01-06 23:29:07 +00:00
Rodrigo Fernandes
432901db33
Merge pull request #469 from rtfpessoa/test-actions
reorder
2023-01-06 23:24:18 +00:00
Rodrigo Fernandes
838bdf6095 reorder 2023-01-06 23:24:02 +00:00
Rodrigo Fernandes
7d173d5f4c
Merge pull request #468 from rtfpessoa/test-actions
fix env
2023-01-06 23:01:00 +00:00
Rodrigo Fernandes
897447d7d7 fix env 2023-01-06 23:00:41 +00:00
Rodrigo Fernandes
37caf65775
Merge pull request #467 from rtfpessoa/test-actions
Merge pull request #466 from rtfpessoa/test-actions
2023-01-06 22:45:47 +00:00
Rodrigo Fernandes
be4b1d0624 Merge pull request #466 from rtfpessoa/test-actions
fix s3 upload
2023-01-06 22:44:58 +00:00
Rodrigo Fernandes
1cc02ff4e1
Merge pull request #466 from rtfpessoa/test-actions
fix s3 upload
2023-01-06 22:38:28 +00:00
Rodrigo Fernandes
7e37ce307a fix s3 upload 2023-01-06 22:37:33 +00:00
Rodrigo Fernandes
81b0f67107
Merge pull request #465 from rtfpessoa/test-actions
fix s3 upload
2023-01-06 22:18:15 +00:00
Rodrigo Fernandes
51cd7854f9 fix s3 upload 2023-01-06 22:17:52 +00:00
Rodrigo Fernandes
88b06ef3e3
Merge pull request #464 from rtfpessoa/test-actions
Migrate to GitHub Actions
2023-01-06 21:58:30 +00:00
Rodrigo Fernandes
c89192a37f wip 2023-01-06 21:49:15 +00:00
fpin
df005c3f06 fix #462: Info lines shrunk in right panel of side-by-side diff 2023-01-02 16:17:16 +01:00
Rodrigo Fernandes
7cd6bb9aff
Merge pull request #459 from rtfpessoa/dependabot/npm_and_yarn/decode-uri-component-0.2.2
build(deps): bump decode-uri-component from 0.2.0 to 0.2.2
2023-01-01 22:26:46 +00:00
dependabot[bot]
1167be6add
build(deps): bump decode-uri-component from 0.2.0 to 0.2.2
Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
- [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
- [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2)

---
updated-dependencies:
- dependency-name: decode-uri-component
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-03 07:28:59 +00:00
Rodrigo Fernandes
7b6ee267af
Merge pull request #458 from ericcornelissen/check-hljs-once
Check if hljs is not null only once
2022-11-04 23:46:45 +00:00
Eric Cornelissen
3a480e4ab0 perf: check hljs is not null only once
Update the `highlightCode` method of `Diff2HtmlUI` to check if the hljs
reference is null only once (at the start). This address the "HACK"
comments by using a locally scoped variable for hljs. This way,
TypeScript is able to deduce that, after the initial null-check, hljs is
in fact not null.
2022-11-02 22:43:23 +01:00
Rodrigo Fernandes
6db4aae9e5
Merge pull request #456 from ericcornelissen/455-sticky-file-headers
Implement support for sticky file headers
2022-11-01 00:14:45 +00:00
Eric Cornelissen
4dae65d5c7 feat: enable sticky file headers by default
Update the stickyFileHeaders default value to `true` so that it is
enabled by default. Also correct the name of this option in the
documentation by adding the missing trailing "s".
2022-10-31 09:47:31 +01:00
Rodrigo Fernandes
35008fba4b
Merge pull request #457 from rtfpessoa/fix-side-by-side-margins-highlight-overrides
fix side by side margins and highlight usage of object instead of map
2022-10-30 20:34:38 +00:00
Rodrigo Fernandes
5ca9d22276 fix side by side margins and highlight usage of object instead of map 2022-10-30 20:31:33 +00:00
Eric Cornelissen
7d4a5dce6f feat: sticky file headers
Add support for sticky file headers by adding a `stickyFileHeaders`
option to the `Diff2HtmlUI`. By default this feature is disabled. Also
document this option in the README.

The feature is implemented through an optional CSS class on top of the
pre-existing `.d2h-file-header` class. The new class is added on all
file headers if the option is set to `true` (or the `stickyFileHeaders`
method is called).

This class, `.d2h-sticky-header`, has the minimum amount of styling to
get the desired effect. The `position` and `top` values make the headers
stick to the top as long as the wrapper is in the view. The `z-index`
value is needed to ensure the header is displayed over all other content
in the wrapper. In particular, from my testing in Firefox (106.0.2), the
line numbers would display over the header if the `z-index` value isn't
set.
2022-10-30 13:05:53 +01:00
Rodrigo Fernandes
59ff2956ab
Merge pull request #453 from rtfpessoa/fix-s3-website-release
fix website release
2022-10-23 18:42:22 +01:00
Rodrigo Fernandes
1a7612c0ff fix website release 2022-10-23 18:40:35 +01:00
Rodrigo Fernandes
86f43ba5ae
Merge pull request #452 from rtfpessoa/bump-deps
bump deps
2022-10-23 18:35:14 +01:00
Rodrigo Fernandes
f9d328e9b8
Merge pull request #451 from rtfpessoa/support-language-override
add support for language override
2022-10-23 18:35:04 +01:00
Rodrigo Fernandes
35e518fe4f bump deps 2022-10-23 18:31:07 +01:00
Rodrigo Fernandes
5373ae180b add support for language override 2022-10-23 18:26:19 +01:00
Rodrigo Fernandes
95e4c40a30
Merge pull request #450 from rtfpessoa/bump-deps
bump dependencies
2022-10-16 01:10:10 +01:00
Rodrigo Fernandes
a09d50a94f bump dependencies 2022-10-16 00:01:01 +01:00
Rodrigo Fernandes
26adbdb220
Merge pull request #447 from phaseOne/patch-1
Fix file collapse UI regression
2022-09-02 22:01:33 +01:00
Evan Bovie
a3a0385991 fix prettier complaint 2022-08-25 19:51:21 +00:00
Evan Bovie
a5e2fab2f1
Fix file collapse UI regression
Increase specificity of .d2h-d-none selector
2022-08-25 12:09:43 -07:00
Rodrigo Fernandes
0ed9e76a35
Merge pull request #444 from rtfpessoa/bump-deps
update dependencies
2022-07-24 18:57:20 +01:00
Rodrigo Fernandes
82a2da0033 update node versions 2022-07-24 18:49:57 +01:00
Rodrigo Fernandes
b164b511d8
Merge pull request #441 from m-masaki72/feature/set-parent-class-to-flex
Set display:flex to align inline-block elements
2022-07-24 18:34:43 +01:00
Rodrigo Fernandes
31a418601b force semver-regex update 2022-07-24 18:27:00 +01:00
Rodrigo Fernandes
93a53cdb4e force tercer update 2022-07-24 18:18:38 +01:00
Rodrigo Fernandes
1c59463034 update dependencies 2022-07-24 18:16:51 +01:00
森 雅希
41037b20f3 Set display:flex to align inline-block elements 2022-07-13 19:16:07 +09:00
Rodrigo Fernandes
e5c813949f
Merge pull request #432 from rtfpessoa/bump-deps
bump dependencies
2022-04-15 21:22:17 +01:00
Rodrigo Fernandes
4b9114afe5 bump node versions 2022-04-15 21:19:20 +01:00
Rodrigo Fernandes
7e3c867d74 bump dependencies 2022-04-15 21:05:09 +01:00
Rodrigo Fernandes
57ff821635
Merge pull request #431 from rtfpessoa/dependabot/npm_and_yarn/minimist-1.2.6
build(deps): bump minimist from 1.2.5 to 1.2.6
2022-04-15 20:27:05 +01:00
Rodrigo Fernandes
50806ba4e7
Merge pull request #429 from rtfpessoa/dependabot/npm_and_yarn/node-forge-1.3.0
build(deps): bump node-forge from 1.2.1 to 1.3.0
2022-04-15 20:26:52 +01:00
dependabot[bot]
1275853d62
build(deps): bump minimist from 1.2.5 to 1.2.6
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-10 09:38:43 +00:00
dependabot[bot]
95006629ec
build(deps): bump node-forge from 1.2.1 to 1.3.0
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 1.2.1 to 1.3.0.
- [Release notes](https://github.com/digitalbazaar/forge/releases)
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/v1.2.1...v1.3.0)

---
updated-dependencies:
- dependency-name: node-forge
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-23 13:15:06 +00:00
Rodrigo Fernandes
e08596da35
Merge pull request #426 from rtfpessoa/rtfpessoa-patch-1
Add git-tabular-diff atom plugin to website
2022-02-09 21:53:10 +00:00
Rodrigo Fernandes
1f61965110
Add git-tabular-diff atom plugin to website 2022-02-09 21:53:02 +00:00
Rodrigo Fernandes
4067ecdb92
Merge pull request #425 from rtfpessoa/dependabot/npm_and_yarn/node-fetch-2.6.7
build(deps): bump node-fetch from 2.6.5 to 2.6.7
2022-01-30 17:39:47 +00:00
Rodrigo Fernandes
e1f641748a fix markdown formatting 2022-01-30 17:35:12 +00:00
dependabot[bot]
f5e1437f34
build(deps): bump node-fetch from 2.6.5 to 2.6.7
Bumps [node-fetch](https://github.com/node-fetch/node-fetch) from 2.6.5 to 2.6.7.
- [Release notes](https://github.com/node-fetch/node-fetch/releases)
- [Commits](https://github.com/node-fetch/node-fetch/compare/v2.6.5...v2.6.7)

---
updated-dependencies:
- dependency-name: node-fetch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-30 17:30:06 +00:00
Rodrigo Fernandes
c541b98e14
Merge pull request #423 from rtfpessoa/dependabot/npm_and_yarn/nanoid-3.2.0
build(deps): bump nanoid from 3.1.23 to 3.2.0
2022-01-30 17:25:22 +00:00
Rodrigo Fernandes
bc6ca55e8d
Update README.md 2022-01-28 23:14:42 +00:00
dependabot[bot]
2466a74fec
build(deps): bump nanoid from 3.1.23 to 3.2.0
Bumps [nanoid](https://github.com/ai/nanoid) from 3.1.23 to 3.2.0.
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.1.23...3.2.0)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-22 01:08:50 +00:00
Rodrigo Fernandes
973842064c
Merge pull request #420 from rtfpessoa/dependabot/npm_and_yarn/node-forge-1.2.1
build(deps): bump node-forge from 0.10.0 to 1.2.1
2022-01-14 21:58:27 +00:00
dependabot[bot]
05c2915e59
build(deps): bump node-forge from 0.10.0 to 1.2.1
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 0.10.0 to 1.2.1.
- [Release notes](https://github.com/digitalbazaar/forge/releases)
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/0.10.0...v1.2.1)

---
updated-dependencies:
- dependency-name: node-forge
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-14 18:38:56 +00:00
Rodrigo Fernandes
b0c5a70216
Merge pull request #419 from aantn/master
Add Robusta to list of projects using diff2html
2021-12-29 12:04:36 +00:00
Natan Yellin
2f832aa610
Add Robusta to list of projects using diff2html 2021-12-28 22:27:59 +02:00
Rodrigo Fernandes
6b2b9de2c2
Merge pull request #416 from bmuskalla/patch-1
Fix broken link to documentation
2021-11-26 22:36:54 +00:00
Benjamin Muskalla
1d5c2b67f2
Fix broken link to documentation
Fixes duplicate closing tag (which made the link not usable at all) and linked readme section to jump directly to docs
2021-11-25 16:40:41 +01:00
Rodrigo Fernandes
68395e5562
Merge pull request #413 from rtfpessoa/update-contributors
Update contributors
2021-10-29 23:01:28 +02:00
Rodrigo Fernandes
d7fbb14ac6 Add @dependabot-preview[bot] as a contributor 2021-10-29 22:59:56 +02:00
Rodrigo Fernandes
206919b836 Add @campersau as a contributor 2021-10-29 22:59:45 +02:00
Rodrigo Fernandes
a77a8def37 Update @escitalopram as a contributor 2021-10-29 22:58:18 +02:00
Rodrigo Fernandes
ac47539d09 Update @dickeylth as a contributor 2021-10-29 22:57:36 +02:00
Rodrigo Fernandes
295d1fe156 Update @sss0791 as a contributor 2021-10-29 22:57:22 +02:00
Rodrigo Fernandes
182250152f Update @pubkey as a contributor 2021-10-29 22:57:11 +02:00
Rodrigo Fernandes
1b4420cadd Update @romellem as a contributor 2021-10-29 22:57:02 +02:00
Rodrigo Fernandes
b141f14762 Update @wesssel as a contributor 2021-10-29 22:56:53 +02:00
Rodrigo Fernandes
ad2314549a Update @dsabanin as a contributor 2021-10-29 22:56:44 +02:00
Rodrigo Fernandes
c04489d624 Update @starpit as a contributor 2021-10-29 22:56:26 +02:00
Rodrigo Fernandes
db07f271d6 Update @kaishuu0123 as a contributor 2021-10-29 22:55:42 +02:00
Rodrigo Fernandes
60b7eb80f5 Add @timgates42 as a contributor 2021-10-29 22:55:13 +02:00
Rodrigo Fernandes
31318145e7 Add @xlith as a contributor 2021-10-29 22:54:56 +02:00
Rodrigo Fernandes
7a042fdd6b Update @Pierrci as a contributor 2021-10-29 22:54:45 +02:00
Rodrigo Fernandes
abb9064e11 Add @Pierrci as a contributor 2021-10-29 22:54:32 +02:00
Rodrigo Fernandes
bba6114a86 Add @charguer as a contributor 2021-10-29 22:54:14 +02:00
Rodrigo Fernandes
c8901b5796 Add @domdomegg as a contributor 2021-10-29 22:53:55 +02:00
Rodrigo Fernandes
45e6f9f266 Update @mohd-akram as a contributor 2021-10-29 22:53:23 +02:00
Rodrigo Fernandes
b49cad2290 Add @pgrimaud as a contributor 2021-10-29 22:52:46 +02:00
Rodrigo Fernandes
561a7f9efa doc: add apeckham to contributors 2021-10-29 22:51:40 +02:00
Rodrigo Fernandes
d3859b8088
Merge pull request #412 from apeckham/patch-1
Fix indentation in Diff2HtmlUI example
2021-10-21 15:11:44 +01:00
Aaron
c6ccf02623
Fix indentation in Diff2HtmlUI example 2021-10-20 15:51:39 -07:00
Rodrigo Fernandes
7ef1a6f9a5
Merge pull request #411 from rtfpessoa/update-users
docs: Update user list
2021-10-16 00:12:26 +01:00
Rodrigo Fernandes
f8ea8a73b7
docs: Update user list 2021-10-16 00:11:10 +01:00
Rodrigo Fernandes
d262ff2176
Merge pull request #409 from rtfpessoa/dependabot/npm_and_yarn/nth-check-2.0.1
build(deps): bump nth-check from 2.0.0 to 2.0.1
2021-10-15 22:20:24 +01:00
dependabot[bot]
a9707cd9c3
build(deps): bump nth-check from 2.0.0 to 2.0.1
Bumps [nth-check](https://github.com/fb55/nth-check) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/fb55/nth-check/releases)
- [Commits](https://github.com/fb55/nth-check/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: nth-check
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-15 20:56:28 +00:00
Rodrigo Fernandes
1e0921c621
Merge pull request #408 from rtfpessoa/bump-dependencies
bump: Update all dependencies
2021-10-15 21:55:55 +01:00
Rodrigo Fernandes
72b0610d2c
bump: Update all dependencies 2021-10-15 21:29:07 +01:00
Rodrigo Fernandes
dbb27ed3b0
Merge pull request #407 from rtfpessoa/add-language-mappings
fix: Add language mappings for highlight.js
2021-10-15 21:02:25 +01:00
Rodrigo Fernandes
679e67b555
fix: Add language mappings for highlight.js 2021-10-15 20:48:14 +01:00
Rodrigo Fernandes
f788039ba0
Merge pull request #404 from rtfpessoa/dependabot/npm_and_yarn/tmpl-1.0.5
build(deps): bump tmpl from 1.0.4 to 1.0.5
2021-10-01 21:24:20 +01:00
dependabot[bot]
69987a8a7f
build(deps): bump tmpl from 1.0.4 to 1.0.5
Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5.
- [Release notes](https://github.com/daaku/nodejs-tmpl/releases)
- [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5)

---
updated-dependencies:
- dependency-name: tmpl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-21 16:01:45 +00:00
Rodrigo Fernandes
6ce9292c52
Merge pull request #402 from rtfpessoa/add-language-mappings
fix: Add language mappings for highlight.js
2021-09-03 23:32:54 +01:00
Rodrigo Fernandes
e44b4b1363
fix: Add language mappings for highlight.js 2021-09-03 23:26:31 +01:00
Rodrigo Fernandes
cfe56add8f
Merge pull request #401 from rtfpessoa/fix-sbs-file-toggle
fix: Remove display block to allow hidding file contents
2021-09-01 16:44:42 +01:00
Rodrigo Fernandes
7253dbb900 fix: Remove display block to allow hidding file contents 2021-09-01 15:39:34 +00:00
Rodrigo Fernandes
5f4b45c4b9
Merge pull request #399 from pgrimaud/master
docs: fix typos
2021-08-10 15:42:00 +01:00
Pierre Grimaud
73f7d4304b
docs: fix typos 2021-08-10 13:01:33 +02:00
Rodrigo Fernandes
04b598599a
Merge pull request #383 from rtfpessoa/bump-dependencies
Bump dependencies
2021-07-23 21:54:22 +01:00
Rodrigo Fernandes
6572b68bf2
bump: Update dependencies 2021-07-23 21:50:00 +01:00
Rodrigo Fernandes
b8594e1994
Merge pull request #396 from rtfpessoa/improve-git-diff-support
clean: Improve git diff support
2021-07-09 22:39:41 +01:00
Rodrigo Fernandes
6ad7f00be2
clean: Improve git diff support 2021-07-09 22:35:27 +01:00
Rodrigo Fernandes
9987e7c695
doc: Add docs about the input format 2021-07-09 22:27:06 +01:00
Rodrigo Fernandes
239d943fad
Merge pull request #395 from rtfpessoa/dependabot/npm_and_yarn/set-getter-0.1.1
build(deps): bump set-getter from 0.1.0 to 0.1.1
2021-07-06 21:08:57 +01:00
dependabot[bot]
6567ad423d
build(deps): bump set-getter from 0.1.0 to 0.1.1
Bumps [set-getter](https://github.com/doowb/set-getter) from 0.1.0 to 0.1.1.
- [Release notes](https://github.com/doowb/set-getter/releases)
- [Commits](https://github.com/doowb/set-getter/commits/0.1.1)

---
updated-dependencies:
- dependency-name: set-getter
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-21 21:21:01 +00:00
Rodrigo Fernandes
73ab5ba050
Merge pull request #389 from mohd-akram/remove-redundant-classes
Remove redundant HTML classes
2021-06-03 22:34:12 +01:00
Rodrigo Fernandes
be5d5ee1c7
Merge pull request #386 from rtfpessoa/dependabot/npm_and_yarn/postcss-8.2.10
build(deps-dev): [security] bump postcss from 8.2.9 to 8.2.10
2021-06-03 22:21:10 +01:00
Rodrigo Fernandes
110b90bc75
Merge pull request #388 from rtfpessoa/dependabot/npm_and_yarn/browserslist-4.16.6
build(deps): bump browserslist from 4.16.3 to 4.16.6
2021-06-03 22:21:02 +01:00
Rodrigo Fernandes
79d061f431
Merge pull request #390 from rtfpessoa/dependabot/npm_and_yarn/ws-7.4.6
build(deps): bump ws from 7.4.4 to 7.4.6
2021-06-03 22:20:52 +01:00
dependabot[bot]
1882e58796
build(deps): bump ws from 7.4.4 to 7.4.6
Bumps [ws](https://github.com/websockets/ws) from 7.4.4 to 7.4.6.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.4.4...7.4.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-29 22:18:46 +00:00
Mohamed Akram
ac9e2ce706 Remove redundant HTML classes 2021-05-29 02:08:47 +04:00
dependabot[bot]
8735dfccb6
build(deps): bump browserslist from 4.16.3 to 4.16.6
Bumps [browserslist](https://github.com/browserslist/browserslist) from 4.16.3 to 4.16.6.
- [Release notes](https://github.com/browserslist/browserslist/releases)
- [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/browserslist/browserslist/compare/4.16.3...4.16.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-25 03:41:11 +00:00
dependabot-preview[bot]
ea14e3d5ef
build(deps-dev): [security] bump postcss from 8.2.9 to 8.2.10
Bumps [postcss](https://github.com/postcss/postcss) from 8.2.9 to 8.2.10. **This update includes a security fix.**
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.2.9...8.2.10)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-05-10 15:54:18 +00:00
Rodrigo Fernandes
4f5db83d30
style: Fix format 2021-05-10 12:05:19 +01:00
Rodrigo Fernandes
97b245883c
Merge pull request #384 from domdomegg/update-documentation
docs: Update configuration reference
2021-05-10 12:04:18 +01:00
Rodrigo Fernandes
8c6ed5aa14
clean: Apply suggestions from code review 2021-05-10 11:56:14 +01:00
Adam Jones
78b9cb69ae
docs: Update configuration reference
- Add undocumented srcPrefix and dstPrefix options
- Better specify types and defaults
- Reorder options to match implementation
- Add example for rawTemplates
- Remove invalid 'inputFormat' option from examples
2021-05-10 11:26:35 +01:00
Rodrigo Fernandes
9ed73c7a66 fix: Use correct webpack typings 2021-04-08 14:55:58 +01:00
Rodrigo Fernandes
defff26b9a fix: Setup husky correctly 2021-04-08 13:14:00 +01:00
Rodrigo Fernandes
0475545a01
Merge pull request #363 from rtfpessoa/bump-dependencies
bump: Update dependencies
2021-04-04 13:17:06 +01:00
Rodrigo Fernandes
fb66c8f983
bump: Update dependencies 2021-04-04 13:11:30 +01:00
Rodrigo Fernandes
006d82c02b
clean: Improve README with suggestions 2021-04-04 12:29:44 +01:00
Rodrigo Fernandes
8fc491c499
Merge pull request #378 from charguer/update-readme-with-plain-html-example
Update readme with fully worked-out plain html example
2021-04-04 12:00:18 +01:00
charguer
794bdb2e1c Update readme with fully worked-out plain html example 2021-03-31 16:26:34 +02:00
Rodrigo Fernandes
642b000ad4
Merge pull request #374 from rtfpessoa/dependabot/npm_and_yarn/y18n-4.0.1
build(deps): [security] bump y18n from 4.0.0 to 4.0.1
2021-03-30 22:49:11 +01:00
dependabot-preview[bot]
ba257b53dc
build(deps): [security] bump y18n from 4.0.0 to 4.0.1
Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.1. **This update includes a security fix.**
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-29 17:21:08 +00:00
Rodrigo Fernandes
084eb408dd
Merge pull request #369 from huggingface/diff-max-line-length
feature: `diffMaxLineLength` to mark file diff as too big depending on diff line length
2021-03-16 09:26:26 +00:00
Pierric Cistac
bf7b52ad30
regroup handling of diffMaxLineLength and diffMaxChanges in parser 2021-03-11 18:17:33 -05:00
Pierric Cistac
a4e619efa4
feature: new diffMaxLineLength
Mark a file diff as too big if diff line length > threshold
2021-03-11 18:10:14 -05:00
Rodrigo Fernandes
51d19ebc2e
Merge pull request #367 from rtfpessoa/dependabot/npm_and_yarn/elliptic-6.5.4
build(deps): [security] bump elliptic from 6.5.3 to 6.5.4
2021-03-08 20:43:10 +00:00
dependabot-preview[bot]
32b7cfc5d1
build(deps): [security] bump elliptic from 6.5.3 to 6.5.4
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4. **This update includes a security fix.**
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-03-08 16:27:03 +00:00
Rodrigo Fernandes
38a9d8f93e
Merge pull request #365 from huggingface/diff-max-lines
feature: bypass parsing/rendering of file diff if specific threshold is crossed
2021-03-04 22:41:02 +00:00
Rodrigo Fernandes
e55d145036
Merge pull request #366 from huggingface/fix-viewed-checkbox
fix: only display "Viewed" checkbox if `fileContentToggle` is set
2021-03-04 22:40:24 +00:00
Pierric Cistac
7668889493
fix: only display "Viewed" checkbox if fileContentToggle is set 2021-03-01 17:17:08 -05:00
Pierric Cistac
a65a3b9562
update tests 2021-03-01 16:05:12 -05:00
Pierric Cistac
08f14e2db1
rm package-lock.json 2021-03-01 14:54:03 -05:00
Pierric Cistac
f220ca11e4
move everything inside parser 2021-03-01 14:48:12 -05:00
Pierric Cistac
b057c6f308
fix: URLParams typing in demo with new diffTooBigMessage option 2021-02-25 15:30:09 -05:00
Pierric Cistac
9aa2ea924e
doc: add diffMaxChanges and diffTooBigMessage in readme 2021-02-25 15:30:09 -05:00
Pierric Cistac
5915ecdaa1
feature: display message instead of diff if isTooBig is true
A default `Diff too big to be displayed` message is rendered for any file diff where `isTooBig` is `true`.

A new `diffTooBigMessage` option in render config allows to fully customize the message and receives the file index in the diff as an argument. It can be used to render a link to the raw file diff for example.
2021-02-25 14:08:18 -05:00
Pierric Cistac
ea9c1fee48
feature: new diffMaxChanges in parser config
If `diffMaxChanges` is set in parser config, and its value is exceeded for a given file diff, then parsing for this file is stopped, and `isTooBig` is set to `true` in the `DiffFile`
2021-02-25 14:02:43 -05:00
Rodrigo Fernandes
f046d183df
Merge pull request #360 from xlith/fix/space-in-side-by-side-display
fix: remove unnesesary height property in d2h-files-diff class
2021-02-20 11:48:43 +00:00
Civan Yavuzsen
9e831469a5 fix: remove unnesesary height property in d2h-files-diff class 2021-02-07 03:27:57 +03:00
Rodrigo Fernandes
c3d7df3bc5
Merge pull request #358 from mrfyda/feature/collapse-viewed
feature: Hide viewed files
2021-01-24 00:57:32 +00:00
Rafael Cortês
242fc5ae7b
feature: Add selected state style 2021-01-23 22:32:50 +00:00
Rafael Cortês
49bf898e0e
clean: Refactor font style 2021-01-23 22:18:03 +00:00
Rafael Cortês
8ee76bf44b
clean: Refactor function to toggle file contents 2021-01-23 22:16:25 +00:00
Rafael Cortês
316803df77
feature: Hide viewed files 2021-01-23 15:07:14 +00:00
Rodrigo Fernandes
c178c2e91e
Merge pull request #353 from rtfpessoa/dependabot/npm_and_yarn/node-notifier-8.0.1
build(deps): [security] bump node-notifier from 8.0.0 to 8.0.1
2020-12-22 09:06:25 +00:00
dependabot-preview[bot]
cc9d262086
build(deps): [security] bump node-notifier from 8.0.0 to 8.0.1
Bumps [node-notifier](https://github.com/mikaelbr/node-notifier) from 8.0.0 to 8.0.1. **This update includes a security fix.**
- [Release notes](https://github.com/mikaelbr/node-notifier/releases)
- [Changelog](https://github.com/mikaelbr/node-notifier/blob/v8.0.1/CHANGELOG.md)
- [Commits](https://github.com/mikaelbr/node-notifier/compare/v8.0.0...v8.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-21 16:56:56 +00:00
Rodrigo Fernandes
e56b2b0980
Merge pull request #352 from rtfpessoa/dependabot/npm_and_yarn/ini-1.3.7
build(deps): [security] bump ini from 1.3.5 to 1.3.7
2020-12-10 17:40:44 +00:00
dependabot-preview[bot]
73c87c1972
build(deps): [security] bump ini from 1.3.5 to 1.3.7
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7. **This update includes a security fix.**
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-10 17:34:12 +00:00
Rodrigo Fernandes
640935927e
Merge pull request #351 from rtfpessoa/dependabot/npm_and_yarn/highlight.js-10.4.1
build(deps): [security] bump highlight.js from 10.2.1 to 10.4.1
2020-12-08 17:52:29 +00:00
dependabot-preview[bot]
59474646cb
build(deps): [security] bump highlight.js from 10.2.1 to 10.4.1
Bumps [highlight.js](https://github.com/highlightjs/highlight.js) from 10.2.1 to 10.4.1. **This update includes a security fix.**
- [Release notes](https://github.com/highlightjs/highlight.js/releases)
- [Changelog](https://github.com/highlightjs/highlight.js/blob/master/CHANGES.md)
- [Commits](https://github.com/highlightjs/highlight.js/compare/10.2.1...10.4.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-04 16:56:29 +00:00
Rodrigo Fernandes
1f261e6bbf
Merge pull request #348 from rtfpessoa/only-allow-selection-line-content
fix: Only allow selection line content instead of parent element
2020-10-29 09:46:20 +00:00
Rodrigo Fernandes
33076dc13e
fix: Add review suggestions 2020-10-29 09:01:09 +00:00
Rodrigo Fernandes
2d4c293a27
fix: Only allow selection line content instead of parent element 2020-10-27 20:06:45 +00:00
Rodrigo Fernandes
e48259d990
Merge pull request #341 from rtfpessoa/bump-dependencies
Bump dependencies
2020-10-16 22:31:46 +01:00
Rodrigo Fernandes
98db156d30
bump: Update dependencies 2020-10-16 22:24:06 +01:00
Rodrigo Fernandes
908272a00f
fix: Remove cors proxy 2020-10-16 22:07:04 +01:00
Rodrigo Fernandes
275d09972f
Merge pull request #337 from rtfpessoa/bump-dependencies
bump: Update dependencies
2020-09-05 17:46:02 +01:00
Rodrigo Fernandes
27bfd3c987
bump: Update dependencies 2020-09-04 19:21:39 +01:00
Rodrigo Fernandes
739751a021
Merge pull request #334 from rtfpessoa/bump-dependencies
bump: Update dependencies
2020-08-15 15:20:58 +01:00
Rodrigo Fernandes
426d9ed2a5
bump: Update dependencies 2020-08-15 15:09:15 +01:00
Rodrigo Fernandes
ed506ef091
Merge pull request #332 from rtfpessoa/dependabot/npm_and_yarn/elliptic-6.5.3
build(deps): [security] bump elliptic from 6.5.2 to 6.5.3
2020-07-30 13:06:58 +01:00
dependabot-preview[bot]
235dbb153f
build(deps): [security] bump elliptic from 6.5.2 to 6.5.3
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.2 to 6.5.3. **This update includes a security fix.**
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.2...v6.5.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-29 21:10:02 +00:00
Rodrigo Fernandes
4162188f80
Merge pull request #329 from rtfpessoa/fix-escaping-long-lines
fix: Enforce escape for lines that are not highlighted
2020-07-26 14:01:11 +01:00
Rodrigo Fernandes
a61c7cfd43
fix: Enforce escape for lines that are not highlighted 2020-07-26 13:56:26 +01:00
Rodrigo Fernandes
c7c56bca03
Merge pull request #326 from rtfpessoa/dependabot/npm_and_yarn/lodash-4.17.19
build(deps): bump lodash from 4.17.15 to 4.17.19
2020-07-24 12:21:52 +01:00
dependabot[bot]
d410c1ac22
build(deps): bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-16 23:50:54 +00:00
Rodrigo Fernandes
8462e52b30
Merge pull request #325 from campersau/escape_header
escape diff headers for html
2020-06-30 18:57:29 +01:00
campersau
406be9f02a escape diff headers for html 2020-06-27 20:11:28 +02:00
Rodrigo Fernandes
12bf3068bd
Create codeql-analysis.yml 2020-06-25 10:23:43 +01:00
Rodrigo Fernandes
d2a143ef30
Merge pull request #323 from rtfpessoa/dependabot/npm_and_yarn/websocket-extensions-0.1.4
build(deps): [security] bump websocket-extensions from 0.1.3 to 0.1.4
2020-06-06 19:20:53 +01:00
dependabot-preview[bot]
964387c5b0
build(deps): [security] bump websocket-extensions from 0.1.3 to 0.1.4
Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4. **This update includes a security fix.**
- [Release notes](https://github.com/faye/websocket-extensions-node/releases)
- [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-05 20:04:16 +00:00
Rodrigo Fernandes
d7d08f0c36
Merge pull request #322 from rtfpessoa/update-codacy-coverage
Update codacy coverage and bump dependencies
2020-05-09 13:17:47 +01:00
Rodrigo Fernandes
1844fed147
bump: Update node versions 2020-05-09 13:12:15 +01:00
Rodrigo Fernandes
ae91dcedd0
bump: Update highlight.js 2020-05-09 12:36:23 +01:00
Rodrigo Fernandes
c37321b6fa
bump: Update dependencies 2020-05-09 11:27:49 +01:00
Rodrigo Fernandes
10687999f3
bump: Update codacy coverage reporter 2020-05-09 11:23:14 +01:00
Rodrigo Fernandes
c3a037bc65
Merge pull request #319 from timgates42/bugfix_typo_examples
docs: Fix simple typo, exemples -> examples
2020-04-17 19:45:34 +01:00
Tim Gates
ee43076fca
docs: Fix simple typo, exemples -> examples
There is a small typo in README.md.

Should read `examples` rather than `exemples`.
2020-04-17 07:38:27 +10:00
Rodrigo Fernandes
fd78929a3f
Merge pull request #316 from rtfpessoa/bump-dependencies
bump: Update dependencies specially autolinker
2020-03-14 21:19:09 +00:00
Rodrigo Fernandes
5918662d06
bump: Update dependencies specially autolinker 2020-03-14 21:14:33 +00:00
Rodrigo Fernandes
7a57dc9667
Merge pull request #315 from rtfpessoa/bump-dependencies
bump: Update dependencies specially acorn and minimist
2020-03-14 20:50:48 +00:00
Rodrigo Fernandes
ea5129b888
bump: Update dependencies specially acorn and minimist 2020-03-14 20:43:04 +00:00
Rodrigo Fernandes
3d5ebc7509
Merge pull request #314 from rtfpessoa/dependabot/npm_and_yarn/acorn-6.4.1
build(deps): [security] bump acorn from 6.4.0 to 6.4.1
2020-03-13 23:30:46 +00:00
dependabot-preview[bot]
953a5d64bf
build(deps): [security] bump acorn from 6.4.0 to 6.4.1
Bumps [acorn](https://github.com/acornjs/acorn) from 6.4.0 to 6.4.1. **This update includes a security fix.**
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/6.4.0...6.4.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-13 21:28:58 +00:00
Rodrigo Fernandes
fb778f9fa9
Merge pull request #312 from rtfpessoa/fix-usage-examples
doc: Add jsdelivr cdn examples and fix broken links
2020-03-10 20:41:19 +00:00
Rodrigo Fernandes
45632dd0e4
doc: Add jsdelivr cdn examples and fix broken links 2020-03-10 18:33:10 +00:00
Rodrigo Fernandes
559cdb420b
Merge pull request #311 from rtfpessoa/fix-file-list-toggle
fix: Add missing dot in class query selector
2020-03-10 18:32:20 +00:00
Rodrigo Fernandes
76fb5227af
fix: Add missing dot in class query selector 2020-03-10 18:16:04 +00:00
Rodrigo Fernandes
884b95be07
Merge pull request #307 from rtfpessoa/fix-selection-empty-lines-side
fix: Skip selection of structural lines in side diff
2020-02-09 19:23:33 +00:00
Rodrigo Fernandes
05c774139a
fix: Skip selection of structural lines in side diff 2020-02-09 19:06:44 +00:00
Rodrigo Fernandes
90d37608c8 Merge pull request #306 from rtfpessoa/fix-selection-empty-lines
fix: Allow selection of empty lines
2020-02-09 17:11:47 +00:00
Rodrigo Fernandes
7be6ef0f4d
fix: Allow selection of empty lines 2020-02-09 17:07:22 +00:00
Rodrigo Fernandes
4a3ed66135
Merge pull request #305 from rtfpessoa/fix-side-by-side-linenumber
Fix side by side linenumber
2020-02-09 16:30:26 +00:00
Rodrigo Fernandes
0cc9550fbf
Merge pull request #304 from rtfpessoa/implement-smart-selection-in-css
feature: Implement smart selection in CSS
2020-02-09 16:21:51 +00:00
Rodrigo Fernandes
bdd88f4b90
fix: Add missing padding to side by side line numbers 2020-02-09 16:21:06 +00:00
Rodrigo Fernandes
ee58e2d700
feature: Implement smart selection in CSS 2020-02-09 16:08:58 +00:00
Rodrigo Fernandes
4e28c5c327
fix: Git tag push 2020-02-05 21:27:25 +00:00
Rodrigo Fernandes
8731f97266
Merge pull request #297 from rtfpessoa/release-major-version
release: New major refactor of diff2html to Typescript
2020-02-05 20:33:14 +00:00
Rodrigo Fernandes
e53c7f0e21
bump: Update dependencies 2020-02-05 20:25:55 +00:00
Rodrigo Fernandes
845a631961
fix: Tag version 2020-02-05 20:23:33 +00:00
Rodrigo Fernandes
05695a7f4a
bump: Update dependencies 2020-01-26 00:31:04 +00:00
Rodrigo Fernandes
5b2c3ab3d3
fix: Only publish website on master 2020-01-26 00:01:14 +00:00
Rodrigo Fernandes
076e14400b
clean: Remove as type casts and use runtime typechecks with fallback when possible 2020-01-25 23:49:53 +00:00
Rodrigo Fernandes
fe8365bcc1
clean: Replace enums with string or type and object 2020-01-25 23:49:17 +00:00
Rodrigo Fernandes
e772b26d1c
doc: Code wrap default value in README 2020-01-19 18:24:12 +00:00
Rodrigo Fernandes
1cac91c05d
doc: Fix drawFileList default in README 2020-01-19 18:21:20 +00:00
Rodrigo Fernandes
fd1a13c1e2
release: New major refactor of diff2html to Typescript
:breaking:
2020-01-19 16:25:10 +00:00
Rodrigo Fernandes
df4481fdbf
Merge pull request #296 from rtfpessoa/add-toc
doc: Add table of contents in readme
2020-01-18 21:04:06 +00:00
Rodrigo Fernandes
e284b58f5e
doc: Add table of contents in readme 2020-01-18 20:32:44 +00:00
Rodrigo Fernandes
3be8155e8a
Merge pull request #295 from rtfpessoa/updates
Updates
2020-01-18 01:24:43 +00:00
Rodrigo Fernandes
499c67fea5
bump: Update versions 2020-01-18 01:20:33 +00:00
Rodrigo Fernandes
1c8a9eefc0
Add @JoshuaKGoldberg as a contributor 2020-01-18 01:16:57 +00:00
Rodrigo Fernandes
78f5c78ced
Merge pull request #294 from rtfpessoa/revert-to-circleci
feature: Release in CircleCI
2020-01-18 01:15:41 +00:00
Rodrigo Fernandes
eda3e66ce6
feature: Release in CircleCI 2020-01-18 01:11:05 +00:00
Rodrigo Fernandes
128204d3c9
Merge pull request #279 from rtfpessoa/JoshuaKGoldberg-type-shenanigans
Add typings for remaining highlight.js parts
2020-01-12 22:25:09 +00:00
Rodrigo Fernandes
1c36b2986a
Merge pull request #281 from rtfpessoa/fix-website
fix: Pass invalidation paths correctly
2020-01-12 02:49:21 +00:00
Rodrigo Fernandes
f89c3481a7
fix: Pass invalidation paths correctly 2020-01-12 02:48:55 +00:00
Rodrigo Fernandes
de3d404dfe
Merge pull request #280 from rtfpessoa/fix-website
clean: Tweak users section to avoid margin issues
2020-01-12 02:25:50 +00:00
Rodrigo Fernandes
42c8e26ff4
clean: Add typings for highlight.js languages 2020-01-12 02:24:16 +00:00
Rodrigo Fernandes
2498775e2f
clean: Tweak users section to avoid margin issues 2020-01-12 02:21:50 +00:00
Josh Goldberg
eca5b3cfd8 Point of comparison for export = hljs types 2020-01-11 00:14:41 -05:00
Rodrigo Fernandes
db48e96dca
Merge pull request #274 from rtfpessoa/improve-highlightjs-types
clean: Improve highlight.js types
2020-01-10 22:49:38 +00:00
Rodrigo Fernandes
594e6a42a8
Merge pull request #275 from rtfpessoa/dependabot/npm_and_yarn/eslint-plugin-jest-23.3.0
build(deps-dev): bump eslint-plugin-jest from 23.2.0 to 23.3.0
2020-01-10 22:48:58 +00:00
Rodrigo Fernandes
746d3e625e
Merge pull request #277 from rtfpessoa/improve-api-for-standalone-usage
clean: Improve diff2html-ui APIs for standalone usage
2020-01-08 22:45:57 +00:00
Rodrigo Fernandes
8b5111f8de
clean: Improve diff2html-ui APIs for standalone usage 2020-01-08 22:40:46 +00:00
dependabot-preview[bot]
f5a6dd3dd2
build(deps-dev): bump eslint-plugin-jest from 23.2.0 to 23.3.0
Bumps [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) from 23.2.0 to 23.3.0.
- [Release notes](https://github.com/jest-community/eslint-plugin-jest/releases)
- [Changelog](https://github.com/jest-community/eslint-plugin-jest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jest-community/eslint-plugin-jest/compare/v23.2.0...v23.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-06 07:44:10 +00:00
Rodrigo Fernandes
3c3f528815
clean: Improve highlight.js types 2020-01-05 23:46:32 +00:00
Rodrigo Fernandes
aa6dd18c37
Merge pull request #273 from rtfpessoa/update-project-usages
website: Add more projects usages
2020-01-05 16:50:56 +00:00
Rodrigo Fernandes
013aff2475
website: Add more projects usages 2020-01-05 16:48:01 +00:00
Rodrigo Fernandes
34808c6b77
Merge pull request #272 from rtfpessoa/update-project-usages
website: Update projects usages
2020-01-05 16:36:33 +00:00
Rodrigo Fernandes
28ad9f623c
website: Update projects usages 2020-01-05 16:24:31 +00:00
Rodrigo Fernandes
62f8c16c31
Merge pull request #270 from rtfpessoa/dependabot/npm_and_yarn/types/node-13.1.4
build(deps-dev): bump @types/node from 13.1.2 to 13.1.4
2020-01-05 15:27:44 +00:00
Rodrigo Fernandes
abd6196b11
Merge pull request #271 from rtfpessoa/replace-bootstrap-with-bulma
clean: Replace boostrap with bulma
2020-01-05 15:26:56 +00:00
Rodrigo Fernandes
8aa2a9eb5c
clean: Replace boostrap with bulma 2020-01-05 15:19:06 +00:00
dependabot-preview[bot]
3cee31e0ee
build(deps-dev): bump @types/node from 13.1.2 to 13.1.4
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 13.1.2 to 13.1.4.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-04 01:29:30 +00:00
Rodrigo Fernandes
64fcd31b1c
Merge pull request #269 from rtfpessoa/dependabot/npm_and_yarn/css-loader-3.4.1
build(deps-dev): bump css-loader from 3.4.0 to 3.4.1
2020-01-03 22:14:17 +00:00
dependabot-preview[bot]
90c1f5f59e
build(deps-dev): bump css-loader from 3.4.0 to 3.4.1
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v3.4.0...v3.4.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-03 21:44:47 +00:00
Rodrigo Fernandes
00446c5341
Merge pull request #267 from rtfpessoa/bumps
bump: Bump versions
2020-01-01 23:20:13 +00:00
Rodrigo Fernandes
404be7afd2
bump: Bump versions 2020-01-01 23:15:57 +00:00
Rodrigo Fernandes
18be919ab8
Merge pull request #261 from rtfpessoa/update-docs
Add @extend1994, dependabot[bot] and escitalopram as a contributors
2020-01-01 22:11:07 +00:00
Rodrigo Fernandes
dc85ddc735
Add @extend1994, @dependabot[bot] and @escitalopram as a contributors 2020-01-01 22:06:52 +00:00
Rodrigo Fernandes
6cebab0e7f
Merge pull request #260 from rtfpessoa/update-docs
docs: Update readme with configuration details
2020-01-01 21:44:50 +00:00
Rodrigo Fernandes
e0eee9e6fb
docs: Update readme with configuration details 2020-01-01 21:37:04 +00:00
Rodrigo Fernandes
060225cc7c
Merge pull request #259 from rtfpessoa/fix-ski-ci
fix: skip ci
2020-01-01 20:27:37 +00:00
Rodrigo Fernandes
b3a3fd8a30
fix: skip ci 2020-01-01 20:26:53 +00:00
Rodrigo Fernandes
37fb939e8e
Merge pull request #258 from rtfpessoa/fix-readme-highlight
doc: Fix readme highlight
2020-01-01 20:20:18 +00:00
Rodrigo Fernandes
a04aa652d9
feature: Allow to skip ci using commit message 2020-01-01 20:12:16 +00:00
Rodrigo Fernandes
88ce8e31ca
doc: Fix readme highlight 2020-01-01 20:04:54 +00:00
Rodrigo Fernandes
f1ff703e64
Merge pull request #257 from rtfpessoa/improve-build-configuration
clean: Improve build configurations
2020-01-01 19:58:10 +00:00
Rodrigo Fernandes
dc9c866041
clean: Improve build configurations 2020-01-01 19:51:36 +00:00
Rodrigo Fernandes
236347eaec
Merge pull request #256 from rtfpessoa/github-workflows
feature: Use GitHub workflows
2019-12-29 20:06:49 +00:00
Rodrigo Fernandes
3da01dda79
feature: Use GitHub workflows :breaking: 2019-12-29 19:35:52 +00:00
Rodrigo Fernandes
a4c43bd6ae
Merge pull request #254 from rtfpessoa/bump
bump: Update dependencies
2019-12-22 21:28:32 +00:00
Rodrigo Fernandes
aa4d674e36
bump: Update dependencies 2019-12-22 21:24:36 +00:00
Rodrigo Fernandes
00de8c88a2
Merge pull request #253 from rtfpessoa/use-jest-snapshots
test: Use Jest snapshots
2019-12-22 21:02:13 +00:00
Rodrigo Fernandes
c3c5f60c0d
test: Use Jest snapshots 2019-12-22 19:52:51 +00:00
Rodrigo Fernandes
e573663789
v3.0.0-beta.1 2019-12-22 18:57:06 +00:00
Rodrigo Fernandes
a2d0bad3b8
Merge pull request #238 from rtfpessoa/migrate-to-ts
Migrate codebase to Typescript
2019-12-22 18:55:22 +00:00
Rodrigo Fernandes
4a159277a2
clean: Prepare for pre release 2019-12-22 18:47:20 +00:00
Rodrigo Fernandes
8f1208eb01
refactor: Unify line-by-line and side-by-side 2019-12-22 18:35:05 +00:00
Rodrigo Fernandes
5c35de28eb
refactor: Separate matching in side-by-side algorithm 2019-12-22 18:04:52 +00:00
Rodrigo Fernandes
d8e0a99070
refactor: Separate matching in line-by-line algorithm 2019-12-22 18:01:01 +00:00
Rodrigo Fernandes
a25d06a8d7
refactor: Extract matching algorithm in line-by-line diff 2019-12-22 18:00:59 +00:00
Rodrigo Fernandes
0f08c85938
refactor: Unify escaping 2019-12-22 18:00:57 +00:00
Rodrigo Fernandes
f8f5c10c57
refactor: Generators 1 2019-12-22 18:00:55 +00:00
Rodrigo Fernandes
ef1ccb093e
refactor: Use lambda functions for map instead of anonymous functions 2019-12-22 18:00:53 +00:00
Rodrigo Fernandes
7b1727dc74
refactor: Refactor build to use webpack 2019-12-22 18:00:52 +00:00
Rodrigo Fernandes
4200bd7a3b
refactor: Move types and use enums 2019-12-22 18:00:50 +00:00
Rodrigo Fernandes
f72ee2ea46
wip: tweaks to website 2019-12-22 18:00:48 +00:00
Rodrigo Fernandes
4f607633dd
wip: Code and Tests working 2019-12-22 18:00:45 +00:00
Rodrigo Fernandes
ef08c53ba9
refactor: Migrate scripts to Typescript 2019-12-22 17:58:47 +00:00
Rodrigo Fernandes
a8b9b2b49a
refactor: Initial migration to typescript 2019-12-22 17:58:43 +00:00
Rodrigo Fernandes
5fe55be6f3
v2.12.2 2019-12-22 17:52:27 +00:00
Rodrigo Fernandes
fa3f7851d2
Merge pull request #252 from rtfpessoa/fix-side-diff-space
fix: Side by side line spacing
2019-12-22 17:51:34 +00:00
Rodrigo Fernandes
a047423f4e
fix: Side by side line spacing
Fixes https://github.com/rtfpessoa/diff2html/issues/251
2019-12-22 17:50:22 +00:00
Rodrigo Fernandes
67572dbea2
Merge pull request #240 from extend1994/add-cdnjs-badge
Add CDNJS version badge in README
2019-10-19 14:51:12 +01:00
Ann
f44b3e8bf4
Add CDNJS version badge in README 2019-10-18 20:52:50 +08:00
Rodrigo Fernandes
89573407e5
v2.12.1 2019-10-05 11:23:25 +01:00
Rodrigo Fernandes
d4cd37994a
doc: Update typings reference in readme 2019-10-05 11:23:03 +01:00
Rodrigo Fernandes
8fc91c921d
Merge pull request #237 from rtfpessoa/bundle-typings
bump: Bundle typings
2019-10-05 11:20:39 +01:00
Rodrigo Fernandes
a5c4b2d219
bump: Bundle typings 2019-10-05 11:18:17 +01:00
Rodrigo Fernandes
5a764deda2
v2.12.0 2019-10-05 10:53:07 +01:00
Rodrigo Fernandes
9a89f31b9c
Merge pull request #236 from rtfpessoa/fix-char-by-char-diff
fix: Bring char by char option back
2019-10-05 10:52:05 +01:00
Rodrigo Fernandes
53958687a6
fix: Bring char by char option back 2019-10-05 10:47:43 +01:00
Rodrigo Fernandes
50cb2bf51b
v2.11.3 2019-09-07 18:59:15 +01:00
Rodrigo Fernandes
994e9c7aa2
Merge pull request #232 from rtfpessoa/dependabot/npm_and_yarn/eslint-utils-1.4.2
Bump eslint-utils from 1.3.1 to 1.4.2
2019-08-27 09:40:39 +01:00
dependabot[bot]
a0ab25214d
Bump eslint-utils from 1.3.1 to 1.4.2
Bumps [eslint-utils](https://github.com/mysticatea/eslint-utils) from 1.3.1 to 1.4.2.
- [Release notes](https://github.com/mysticatea/eslint-utils/releases)
- [Commits](https://github.com/mysticatea/eslint-utils/compare/v1.3.1...v1.4.2)

Signed-off-by: dependabot[bot] <support@github.com>
2019-08-26 23:19:29 +00:00
Rodrigo Fernandes
f8d49b4370
Add all contributors to README 2019-08-22 23:25:44 +01:00
Rodrigo Fernandes
d952ad0f27
Release 2.11.2 2019-07-11 22:33:22 +01:00
Rodrigo Fernandes
39338293db
fix: Update lodash now for real 2019-07-11 22:31:55 +01:00
Rodrigo Fernandes
5ff6db6315
Release 2.11.1 2019-07-11 22:21:22 +01:00
Rodrigo Fernandes
e5d8e985ba
fix: Update lodash version 2019-07-11 22:20:54 +01:00
Rodrigo Fernandes
829a6dc814
Release 2.11.0 2019-07-11 22:12:05 +01:00
Rodrigo Fernandes
6d73e467b0
Merge pull request #229 from emarcotte/master
Expose `oldName` and `newName` to file list templates
2019-07-11 22:05:44 +01:00
Eugene Marcotte
f68f930dd2 Expose oldName and newName to file list templates 2019-07-09 14:28:52 -04:00
Rodrigo Fernandes
0697ac6d55
Release 2.10.1 2019-07-07 14:12:56 +01:00
Rodrigo Fernandes
4e6db583e3
Merge pull request #228 from kaishuu0123/feat/replace-space-to-nbsp
Replace space to &nbsp; for HTML Rendering.
2019-07-07 14:07:34 +01:00
Koki Oyatsu
201c6a9f5c Replace space to &nbsp; for HTML Rendering.(fix test) 2019-07-07 02:51:50 +09:00
Koki Oyatsu
5bf0575b46 Replace space to &nbsp; for HTML Rendering.
Reason:
Some javascript frameworks(ex. vue.js) manipulate HTML tags when drawing.

At that time, if there is a space-only DOM element, the space in the DOM
element is deleted.

Before:
`` `
<span> </span>
`` `

After (space deleted)
`` `
<span></span>
`` `

In order to avoid this, it has been modified to render with &nbsp; only when there is no diff.
2019-07-07 02:39:50 +09:00
Rodrigo Fernandes
faeb7b7e80
Merge pull request #226 from rtfpessoa/update-configurations
fix: Update typescript typings and default configuration options
2019-07-03 20:49:24 +01:00
Rodrigo Fernandes
d131860324
fix: Update typescript typings and default configuration options 2019-07-03 20:42:33 +01:00
Rodrigo Fernandes
defd8f399b
Release 2.10.0 2019-06-20 22:09:03 +01:00
Rodrigo Fernandes
0e1a908207
Merge pull request #222 from rtfpessoa/fix-oom
feature: Add `maxLineSizeInBlockForComparison` to avoid OOM
2019-06-20 22:07:31 +01:00
Rodrigo Fernandes
3c06b401b9
feature: Add maxLineSizeInBlockForComparison to avoid OOM
Fixes #220
2019-06-14 17:10:31 +01:00
Rodrigo Fernandes
3ad45ff86e
bump: Update dependencies 2019-06-14 16:15:59 +01:00
Rodrigo Fernandes
9eb33e8ab5 Update issue templates 2019-05-29 10:34:48 +01:00
Rodrigo Fernandes
86f884412e
Create CODE_OF_CONDUCT.md 2019-05-29 10:32:26 +01:00
Rodrigo Fernandes
c6624a3fcf
Create SECURITY.md 2019-05-23 20:56:33 +01:00
Rodrigo Fernandes
ce88d603fc
doc: Make more clear that stylesheets are always included in the end HTML 2019-05-23 20:43:12 +01:00
Rodrigo Fernandes
4c15a9ca93
Release 2.9.0 2019-05-08 23:45:42 +01:00
Rodrigo Fernandes
a8a7147f50
Merge pull request #217 from rtfpessoa/fix-merge-vuln
fix: Remove lodash.merge for merge
2019-05-08 23:45:05 +01:00
Rodrigo Fernandes
07f94aeb9e
fix: Remove lodash.merge for merge 2019-05-08 23:42:42 +01:00
Rodrigo Fernandes
7e9930bc7e
Release 2.8.0 2019-05-01 13:34:32 +01:00
Rodrigo Fernandes
1bb5a0bbdd
Merge pull request #215 from rtfpessoa/fix-linenumbers-on-scroll
Fix linenumbers on scroll
2019-05-01 13:34:02 +01:00
Rodrigo Fernandes
4418f5f00a
fix: Line numbers when scrolling 2019-05-01 13:31:21 +01:00
Rodrigo Fernandes
6a02f2dd22
clean: Update dependencies 2019-05-01 11:03:10 +01:00
Rodrigo Fernandes
cca1b34f0d
Merge pull request #209 from mohd-akram/preserve-tabs
Preserve tabs
2019-05-01 10:49:15 +01:00
Mohamed Akram
e4e0df62b9 Preserve tabs 2019-04-15 20:26:32 +04:00
Rodrigo Fernandes
35f86fe528
Merge pull request #203 from iliyaZelenko/patch-1
docs: Update example for Vue in README.md
2019-03-16 01:16:55 +00:00
Илья
06fda7c15a
Update README.md 2019-03-13 19:24:40 +02:00
Rodrigo Fernandes
8055f309da
Merge pull request #190 from brewern/render_nothing_when_empty
Add configuration for rendering nothing if the diff shows no changes were made.
2019-02-07 21:47:17 +00:00
Nick Brewer
9c30d10b97 Fix trailing comma lint issue. 2019-01-31 18:18:24 -06:00
Nick Brewer
3199a1ac7b Add new config option to the documentation. 2019-01-31 18:01:56 -06:00
Nick Brewer
b43cc1ae91 Add configuration option to disable the render of templates.
No template would render if the diff shows no difference in its comparison.
2019-01-31 17:58:48 -06:00
Rodrigo Fernandes
2512e72f32 Release 2.7.0 2019-01-04 23:32:42 +00:00
Rodrigo Fernandes
c3259937f1 Update yarn.lock 2019-01-04 23:32:27 +00:00
Rodrigo Fernandes
65f24c5674
Merge pull request #187 from starpit/lodash.merge
fix: trim lodash dependence down to lodash.merge
2019-01-04 23:30:03 +00:00
Rodrigo Fernandes
e60e6efb03 Release 2.6.0 2019-01-04 23:18:36 +00:00
Rodrigo Fernandes
37401d7c1c
Merge pull request #180 from rtfpessoa/improve-line-heigth
Improve line heigth
2019-01-04 23:17:28 +00:00
Nick Mitchell
cc3083710e fix: trim lodash dependence down to lodash.merge
Fixes #186
2019-01-02 20:45:28 -05:00
Rodrigo Fernandes
39f8d9b972
Merge pull request #183 from rtfpessoa/update-terraform
Update terraform
2018-11-25 11:18:06 +00:00
Rodrigo Fernandes
c76a421ff8
Update terraform 2018-11-25 10:59:08 +00:00
Rodrigo Fernandes
1906abd0e5
Merge pull request #181 from rtfpessoa/terraform
Initial terraform setup
2018-11-24 21:21:33 +00:00
Rodrigo Fernandes
7d41bd6afd
Initial terraform setup 2018-11-24 21:18:53 +00:00
Rodrigo Fernandes
7fbc8c238a
Improve line-height 2018-11-18 15:38:46 +00:00
Rodrigo Fernandes
a5835850c6
Update highlightjs 2018-11-14 23:55:16 +00:00
Rodrigo Fernandes
1cdfda0fc8
Merge pull request #167 from rtfpessoa/release-website-automatically
Release website on master deployments
2018-11-14 23:22:34 +00:00
Rodrigo Fernandes
4a22c4cb57
Release website on master deployments 2018-11-14 23:19:35 +00:00
Rodrigo Fernandes
cbfa0aa10d
Release 2.5.0 2018-11-14 23:07:09 +00:00
Rodrigo Fernandes
ed942a8f85
Remove bower support 2018-11-14 23:05:38 +00:00
Rodrigo Fernandes
bae73b15a2
Make highlight default in the demo 2018-11-14 23:03:32 +00:00
Rodrigo Fernandes
13a4880c59
Update dependencies and update yarn.lock 2018-11-14 22:56:48 +00:00
Rodrigo Fernandes
bed84b7853
Merge pull request #178 from stockmind/emptyplaceholder
Target and style blank lines in side-by-side view
2018-11-14 22:51:29 +00:00
Rodrigo Fernandes
9c2c1fe880
Update yarn.lock 2018-11-14 22:49:03 +00:00
Rodrigo Fernandes
030bcd5fe0
Merge pull request #179 from stockmind/patch-1
Add a Vue.js self-contained example
2018-11-14 22:41:47 +00:00
stockmind
ffb6fe227e
Remove trailing spaces 2018-11-14 23:19:40 +01:00
stockmind
84c1bd1d2a
Add a Vue.js self-contained example 2018-11-14 23:18:32 +01:00
stockmind
a222ff624c Target and style blank lines in side-by-side view 2018-11-14 22:18:39 +01:00
Rodrigo Fernandes
8116017763
Merge pull request #174 from dsabanin/patch-1
Add maxLineLengthHighlight to TypeScript definitions.
2018-09-02 12:03:12 +01:00
Dima Sabanin
22c48033ac
Add maxLineLengthHighlight to TypeScript definitions. 2018-09-01 17:09:40 -04:00
Rodrigo Fernandes
fe6fad4587
Fix Codacy badges 2018-08-09 10:20:37 +01:00
Rodrigo Fernandes
1d01d73e74
Add a browser example of usage
Thanks to @dxz6228 for the feedback
2018-06-24 14:41:59 +01:00
Rodrigo Fernandes
0f849d4581
Release version 2.4.0 2018-06-16 18:57:04 +01:00
Rodrigo Fernandes
97127c535d
Merge pull request #170 from rtfpessoa/updates
Update dependencies
2018-06-16 18:48:27 +01:00
Rodrigo Fernandes
5fdaa490c4
Update dependencies 2018-06-16 18:45:31 +01:00
Rodrigo Fernandes
98cb8655b3
Release version 2.3.3 2018-01-10 19:25:17 +00:00
Rodrigo Fernandes
60ee73bf8a
Merge pull request #159 from wesssel/master
load merge from lodash instead of whole module
2018-01-10 18:33:10 +00:00
wesssel
ffbb530f81 load merge from lodash instead of whole module 2018-01-10 18:07:46 +09:00
Rodrigo Fernandes
25631b3ed7
Merge pull request #149 from rtfpessoa/hellojukay-master
Add Angular usage example
2017-11-04 15:48:23 +00:00
lic
cab77729f6
add Angular4 usage 2017-11-04 15:40:34 +00:00
Rodrigo Fernandes
5b8ef31d76 Release version v2.3.2 2017-10-16 23:50:42 +01:00
Rodrigo Fernandes
b88c775ebe Merge pull request #145 from rtfpessoa/fix-stuck
Fix generation of diff with long lines
2017-10-16 23:49:12 +01:00
Rodrigo Fernandes
16d63a92ab
Fix diff generation for long lines 2017-10-16 23:44:00 +01:00
Rodrigo Fernandes
1b0af44179 Merge pull request #143 from romellem/fix-timestamp-strip
Fix regex to strip timestamp when timezone is behind GMT
2017-10-09 21:19:22 +01:00
romellem
5215321466 Fixes regex for timestamps to when behind GMT
Currently, a timezone that ends in say '+0200' will get stripped, but a
timezone _behind_ GMT will not (so a timezone in the United States of
say, '-0400' will **not** get stripped.

This regex change fixes that.
2017-10-09 09:34:55 -05:00
romellem
ea43094ed6 Adds failing test for timezones behind GMT 2017-10-09 09:34:44 -05:00
Rodrigo Fernandes
2059fd422a
Release version 2.3.1 2017-10-05 17:37:23 +01:00
Rodrigo Fernandes
0eaa02c9a2 Merge pull request #141 from rtfpessoa/update-dependencies
Update dependencies
2017-10-05 17:31:57 +01:00
Rodrigo Fernandes
7c3b7a1164
Update dependencies 2017-10-05 17:28:33 +01:00
Rodrigo Fernandes
564f44f053 Update README.md 2017-07-21 23:07:44 +01:00
Rodrigo Fernandes
9e3ba28811 Merge pull request #133 from pubkey/master
FIX typo diff with double-F
2017-06-20 12:00:50 +01:00
Daniel M
ab157d7152 FIX typo diff with double-F 2017-06-20 12:47:52 +02:00
Rodrigo Fernandes
94be9010f1 Merge pull request #130 from dickeylth/patch-1
Wrong tip order for api?
2017-06-15 19:41:01 +01:00
Rodrigo Fernandes
1fba984dd3 Merge pull request #131 from sss0791/master
Fix for typescript definition file
2017-06-15 19:40:10 +01:00
Sergey Semenov
23419f7568 Fixed type of return value for getJsonFromDiff 2017-06-14 14:02:04 +03:00
弘树@阿里
165007b636 Wrong tip order for api?
Order for json & html output tip is wrong?
2017-06-08 15:46:45 +08:00
Rodrigo Fernandes
e0f4a33c82 Fix typescript definition location 2017-05-07 10:38:17 +01:00
Rodrigo Fernandes
af6fd91521 Merge pull request #128 from jaminscript/typos
Fix typos
2017-04-28 11:29:23 +01:00
Ben Abbott
ebf085f3e2 Fix typos 2017-04-28 14:31:47 +12:00
Rodrigo Fernandes
6073d82124
Re-order build steps 2017-04-27 12:14:48 +01:00
Rodrigo Fernandes
ffb5d524a6 Release version 2.3.0 2017-03-18 00:48:04 +00:00
Rodrigo Fernandes
f31edcacba
Fix clean-css and postcss deps 2017-03-18 00:27:38 +00:00
Rodrigo Fernandes
ad69edd48c Merge pull request #124 from rtfpessoa/fix-escaping-in-headers
Fix escaping in header lines
2017-03-18 00:18:57 +00:00
Rodrigo Fernandes
267748f967
Fix escaping in header lines 2017-03-18 00:11:55 +00:00
Rodrigo Fernandes
01c0f1527c
Fix test script 2017-01-15 19:21:45 +00:00
Rodrigo Fernandes
21c4e720e1 Release version 2.0.12 2017-01-15 19:15:20 +00:00
Rodrigo Fernandes
523f2109fc
Expose synchronised scroll in the UI helper api 2017-01-15 18:36:12 +00:00
Rodrigo Fernandes
ba239b6f15
Update dependencies 2016-12-17 23:53:30 +00:00
Rodrigo Fernandes
0517e840b8
Improve website widths 2016-12-17 23:34:21 +00:00
Rodrigo Fernandes
22b9783d07
Update hljs to 9.8.0 2016-12-11 15:22:08 +00:00
Rodrigo Fernandes
63e4caf59c
Add issue template and update deps 2016-12-11 15:08:50 +00:00
Rodrigo Fernandes
36a1266012 Release version 2.0.11 2016-12-07 22:19:06 +00:00
Rodrigo Fernandes
817317c5af
Update deps and add troubleshooting tips 2016-12-01 16:58:05 +00:00
Rodrigo Fernandes
929d9f1312 Merge pull request #116 from rtfpessoa/parse-binary-patch-diff
Parse binary patch diffs
2016-12-01 16:45:14 +00:00
Rodrigo Fernandes
0f2d650436
Parse binary patch diffs 2016-12-01 16:39:01 +00:00
Rodrigo Fernandes
1b9200b201 Release version 2.0.10 2016-10-29 16:53:20 +01:00
Rodrigo Fernandes
bea194cee0
Skip bad release version 2016-10-29 16:52:55 +01:00
Rodrigo Fernandes
91a6c8a794 Merge pull request #114 from pbu88/remove_timestamps_off_the_headers_from_unified_diffs
Remove timestamps from filenames in unified diffs
2016-10-29 16:07:24 +01:00
Paulo Bu
a481fad7ca Remove timestamps from filenames in unified diffs 2016-10-25 13:48:00 +02:00
Rodrigo Fernandes
47b2e059b4 Merge pull request #113 from samiraguiar/patch-2
Add more details to syntax highlighting
2016-10-21 15:54:45 +01:00
Samir Aguiar
37b936eadf Add more details to syntax highlighting 2016-10-21 01:38:39 -02:00
Rodrigo Fernandes
2000c467a1
Improve search params on demo and allo configuration of all templates 2016-10-16 17:44:22 +01:00
Rodrigo Fernandes
0f7a45a142
Add Simple Git to projects using diff2html 2016-10-16 01:02:53 +01:00
Rodrigo Fernandes
477b858f9b
Update deps 2016-10-15 22:49:42 +01:00
Rodrigo Fernandes
cee09dbdf0 Release version 2.0.8 2016-10-15 22:21:16 +01:00
Rodrigo Fernandes
4ed847bf14
Improve widths 2016-10-15 22:18:16 +01:00
Rodrigo Fernandes
316ece4fa0 Merge pull request #112 from rtfpessoa/fix-mobile-view
Fix mobile view
2016-10-15 21:48:48 +01:00
Rodrigo Fernandes
a6d119ac81
Fix mobile view 2016-10-15 21:48:03 +01:00
Rodrigo Fernandes
c2c253d3e3 Merge pull request #106 from rtfpessoa/support-overriding-templates
Initial template override support
2016-10-15 13:31:33 +01:00
Rodrigo Fernandes
f3cadb9667
Allow uncompiled templates 2016-10-15 13:31:02 +01:00
Rodrigo Fernandes
2aaae31cc2 Initial template override support 2016-10-15 13:00:31 +01:00
Rodrigo Fernandes
d4bab74b1a
Fix typo 2016-10-15 12:58:19 +01:00
Rodrigo Fernandes
fb931d6189 Use crossorigin.me by default 2016-10-15 12:53:05 +01:00
Rodrigo Fernandes
149a76dfb9 Merge pull request #111 from rtfpessoa/fix-style-issues
Fix style issues
2016-10-15 12:43:48 +01:00
Rodrigo Fernandes
b3bf2006c5 Fix style issues 2016-10-15 12:42:55 +01:00
Rodrigo Fernandes
04b9b44e92 Merge pull request #110 from rtfpessoa/improve-history-rewrite
Improve history rewrite
2016-10-15 12:41:52 +01:00
Rodrigo Fernandes
2e2dac21cd Improve history rewrite 2016-10-15 12:41:31 +01:00
Rodrigo Fernandes
4e835fba42 Merge pull request #109 from rtfpessoa/use-apis-for-cors
Use apis where it is possible for the online diff
2016-10-15 12:17:02 +01:00
Rodrigo Fernandes
40f0548919
Use apis where it is possible for the online diff 2016-10-15 12:13:09 +01:00
Rodrigo Fernandes
269b1b6cfb Merge pull request #107 from rtfpessoa/github-online-diff
Online Diff Pretiffier
2016-10-13 00:12:03 +01:00
Rodrigo Fernandes
c225d665e8 Fix style in new url page 2016-10-12 23:59:18 +01:00
Rodrigo Fernandes
b81d9eb17e Use minified assets 2016-10-12 23:47:12 +01:00
Rodrigo Fernandes
801879fafd Add Light Review to project using diff2html and use octicons for ? 2016-10-12 23:33:50 +01:00
Rodrigo Fernandes
aab1891829 Improve usability of the options 2016-10-12 23:17:39 +01:00
Rodrigo Fernandes
259d3ef972 Add diff output options 2016-10-12 22:52:59 +01:00
Rodrigo Fernandes
ba13de46a0
Add support for online diff prettifier
* Initial support for GitHub, Bitbucket and GitLab urls
* Also support any Git or unified diffs urls
* Special thanks to @kevinsimper for the idea
2016-10-09 23:01:01 +01:00
Rodrigo Fernandes
744025efe2
Generic website page generation 2016-10-09 16:56:58 +01:00
Rodrigo Fernandes
1e22a3ec8f Release version 2.0.7 2016-10-08 16:55:55 +01:00
Rodrigo Fernandes
7d02e67f3b
Better test order and update deps 2016-10-08 16:31:39 +01:00
Rodrigo Fernandes
a4f7fa9bcd Merge pull request #105 from rtfpessoa/add-another-binary-pattern
Add another way to recognize git binary file names
2016-10-08 16:04:01 +01:00
Rodrigo Fernandes
2a18c91e70
Add another way to recognize binary file names 2016-10-08 15:55:30 +01:00
Rodrigo Fernandes
d3b053cae0
Force inputFormat on ui helper since it is always json 2016-09-18 22:06:29 +01:00
Rodrigo Fernandes
5b5feda59c Release version 2.0.6 2016-09-10 14:58:16 +01:00
Rodrigo Fernandes
7290650a08
Fix line height not matching between right and left side 2016-09-10 14:57:53 +01:00
Rodrigo Fernandes
0b3c3986c5
Fix eslint setup 2016-09-10 14:15:33 +01:00
Rodrigo Fernandes
29a9236a6e Release version 2.0.4 2016-09-10 13:53:51 +01:00
Rodrigo Fernandes
67655d674b
Update dev deps 2016-09-10 13:53:29 +01:00
Rodrigo Fernandes
639a13cf84 Clean website css 2016-09-10 13:32:03 +01:00
Rodrigo Fernandes
1f2ffdbe42 Merge pull request #98 from rtfpessoa/fix-nbsp-properly
Fix convert &nbps; to proper white spaces with white-space wrap
2016-09-10 13:31:35 +01:00
Rodrigo Fernandes
f43e03e785
Fix docs offline and fix css typo 2016-09-10 13:30:51 +01:00
Rodrigo Fernandes
6a47f8f3a5
Fix convert &nbps; to proper white spaces with white-space wrap 2016-09-05 22:34:18 +01:00
Rodrigo Fernandes
5303b2ff24 Release version 2.0.3 2016-09-02 17:52:43 +01:00
Rodrigo Fernandes
8cf734d016 Merge pull request #96 from rtfpessoa/fix-git-rename-with-hunk-header
Fix git diff with renames and hunk headers
2016-09-02 09:51:29 -07:00
Rodrigo Fernandes
ed8d33930d
Fix git diff with renames and hunk headers
When we have the file names we consider that the file is already past
the hunk header (@@) and since the renames were setting the names
before the diff was confused.
To fix this I kind of hacked a solution that only uses the rename
file paths if there is no hunk header comming.
We need to find a better way to decide when to start new hunk, finish
files etc
2016-09-02 17:45:35 +01:00
Rodrigo Fernandes
5ad783ae3d Add TypeScript test, use .eslintignore 2016-08-27 22:28:53 +01:00
Rodrigo Fernandes
3d0927a857 Create CNAME 2016-08-24 12:24:45 +01:00
Rodrigo Fernandes
06d506855e
Improve circle.yml 2016-08-24 12:02:03 +01:00
Rodrigo Fernandes
57a288a564
Migrate website to main branch 2016-08-23 18:33:49 +01:00
Rodrigo Fernandes
4b77d69d5a
Update diff2html website url 2016-08-23 17:33:54 +01:00
Rodrigo Fernandes
11c00bbd46 Release version 2.0.2 2016-08-23 17:25:18 +01:00
Rodrigo Fernandes
7821447330
Improve docs, lock npm versions, and update deps 2016-08-23 17:24:43 +01:00
Rodrigo Fernandes
0da16bbb4f
Update website link 2016-07-23 10:20:29 +01:00
Rodrigo Fernandes
3d7c48f17c Release version 2.0.1 2016-07-12 18:46:45 +01:00
Rodrigo Fernandes
e9e3d928ea
Only log failed hunk header parse if line starts with @@ 2016-07-12 18:44:09 +01:00
Rodrigo Fernandes
4bea8bb758 Release version 2.0.0 2016-07-12 18:28:29 +01:00
Rodrigo Fernandes
80d92ba181
Skip style tests in node 0.12 2016-07-12 18:05:14 +01:00
Rodrigo Fernandes
0f38cdeb79 Release version 2.0.0-rc.9 2016-07-12 16:19:22 +01:00
Rodrigo Fernandes
9bbc87ae89
Fix parsing of binary files 2016-07-12 16:18:42 +01:00
Rodrigo Fernandes
2f53bf777e
Use ESLint instead of JSCS 2016-07-12 13:47:29 +01:00
Rodrigo Fernandes
e0b8b4a53f
Update dependencies and add dependencyci badge 2016-07-12 13:13:44 +01:00
Rodrigo Fernandes
8fe49abaa4 2.0.0-rc.8 2016-06-28 22:19:44 +01:00
Rodrigo Fernandes
8679bc354d Merge pull request #88 from rtfpessoa/fix-parser
Fix parsing in cases where body lines can be confused with header lines
2016-06-28 22:17:30 +01:00
Rodrigo Fernandes
f2858f6c45 Fix parsing in cases where body lines can be confused with header lines 2016-06-28 22:15:03 +01:00
Rodrigo Fernandes
5e0ef645d1 2.0.0-rc.7 2016-06-27 21:21:07 +01:00
Rodrigo Fernandes
f500bb09ea Improve script for Ubuntu 2016-06-27 21:20:51 +01:00
Rodrigo Fernandes
7c79cc32db Fix parsing body lines starting with --- and +++ 2016-06-27 21:13:25 +01:00
Rodrigo Fernandes
730dccf069 Release version 2.0.0-rc.6 2016-05-26 16:33:51 +01:00
Rodrigo Fernandes
ae39ce97b6
Improve css prefixes 2016-05-26 16:33:27 +01:00
Rodrigo Fernandes
55a10d9f4a Merge pull request #83 from mrfyda/sync-horizontal-scroll
Add synchronised horizontal scroll
2016-05-21 23:29:48 +01:00
Rafael Cortês
90c9d08744
Add synchronised horizontal scroll 2016-05-21 23:24:52 +01:00
Rodrigo Fernandes
dbd8766966 Release version 2.0.0-rc.5 2016-05-21 02:13:08 +01:00
Rodrigo Fernandes
0e5c6cd892 Merge pull request #81 from rtfpessoa/improve-file-summary
Improve file summary
2016-05-21 02:12:21 +01:00
Rodrigo Fernandes
fa9bdebe4c
Update tests for new design improvements 2016-05-21 02:09:16 +01:00
Rodrigo Fernandes
f32c6c9f67
Migrate Side-by-Side diffs to hogan.js 2016-05-21 00:39:49 +01:00
Rodrigo Fernandes
5cba4efde9
Extract file path from diff wrapper 2016-05-20 23:59:37 +01:00
Rodrigo Fernandes
7c48e86a99 Add icons and tags to files indicating the type of change 2016-05-20 23:41:37 +01:00
Rodrigo Fernandes
024c37da67 Make file summary more appealing 2016-05-20 23:41:14 +01:00
Rodrigo Fernandes
15a2794ea3 Release version 2.0.0-rc.4 2016-05-20 23:34:13 +01:00
Rodrigo Fernandes
ae347be300 Merge pull request #80 from rtfpessoa/improve-paths
Improve paths
2016-05-20 23:33:14 +01:00
Rodrigo Fernandes
ce90977163
Use operator assignement and remove redundant else 2016-05-20 23:29:11 +01:00
Rodrigo Fernandes
146d951aca Merge pull request #79 from rtfpessoa/improve-removed-changed-line-color
Improve diff removed changed line background color
2016-05-20 23:21:52 +01:00
Rodrigo Fernandes
285c3fefc5
Improve rename paths
* Identify smallest different sub path of the changed files paths and
highlight the changed part
2016-05-20 23:17:03 +01:00
Rodrigo Fernandes
5cac9fd99f
Improve diff removed changed line background color 2016-05-19 18:11:54 +01:00
Rodrigo Fernandes
7ac83c9f34 Release version 2.0.0-rc.3 2016-05-14 13:47:17 +01:00
Rodrigo Fernandes
c5a5a57c04
Improve scripts to release npm and bower simultaneously 2016-05-14 13:46:38 +01:00
Rodrigo Fernandes
3a78226a6c 2.0.0-rc.2 2016-05-14 13:21:30 +01:00
Rodrigo Fernandes
07abb77208 Merge pull request #76 from rtfpessoa/improve-highlight
Improve highlight
2016-05-14 11:33:44 +01:00
Rodrigo Fernandes
978e54ab64
Extract highlight.js internal api code to its own file 2016-05-14 11:30:34 +01:00
Rodrigo Fernandes
93721e7e98
Add hljs class manually 2016-05-09 18:21:50 +01:00
Rodrigo Fernandes
73999bb782
Keep one state for old and another for the new diff 2016-05-09 11:15:57 +01:00
Rodrigo Fernandes
719b1cde71
Fix class names on highlighted elements 2016-05-09 10:50:46 +01:00
Rodrigo Fernandes
affd71eca0
Fix highlight for multiline elements
* Now highlight should be perfect except for cases where the diff
context is not enough to understant what is the code
2016-05-09 00:08:05 +01:00
Rodrigo Fernandes
1e242ded9a
Improve fonts and font sizes 2016-05-07 15:15:29 +01:00
Rodrigo Fernandes
aa2382436e
Format CSS with 2 spaces 2016-05-07 15:14:05 +01:00
Rodrigo Fernandes
9aa50909a7
Improve release scripts 2016-05-07 14:03:37 +01:00
Rodrigo Fernandes
ca2c52438d
Fix version identifiers 2016-05-07 10:49:27 +01:00
Rodrigo Fernandes
ef4a4e1b76
Prepare for release 2.0.0-beta18 2016-05-06 23:19:12 +01:00
Rodrigo Fernandes
2b05eaf0fa Merge pull request #73 from rtfpessoa/support-unified-diffs
Add support for parsing unified diffs
2016-05-06 23:16:47 +01:00
Rodrigo Fernandes
3cba36f959
Fix tests in node 0.12 2016-05-06 23:11:00 +01:00
Rodrigo Fernandes
cb145eddf6
Add support for parsing unified diffs
* Refactor code to separate parser parts
* Add test for unified example
2016-05-06 23:02:05 +01:00
Rodrigo Fernandes
8ead50d8b2
Prepare for 2.0.0-beta17 release 2016-05-01 20:52:25 +01:00
Rodrigo Fernandes
fc423e6124 Merge pull request #71 from lantian/separate-selection
Implemented separated column selection through javascript clipboard h…
2016-05-01 20:45:16 +01:00
Ivan Vorontsov
8c50a590b0 Quality gate fixes, part 2 2016-05-01 22:33:55 +03:00
Ivan Vorontsov
12b2ee9356 Test fixes. 2016-05-01 22:26:10 +03:00
Ivan Vorontsov
619b43ffa4 Implemented separated column selection through javascript clipboard hook. 2016-05-01 22:02:11 +03:00
Rodrigo Fernandes
354fa22444
Update README versions and browser dependencies 2016-04-25 19:41:00 +01:00
Rodrigo Fernandes
f08137f8ba
Improve branch coverage 2016-04-25 19:30:38 +01:00
Rodrigo Fernandes
95322eb87c
Update jscs, jquery and highlight.js 2016-04-25 18:25:39 +01:00
Rodrigo Fernandes
dd7b1e5820
Prepare for release 2.0.0-beta13 2016-04-25 17:54:39 +01:00
Rodrigo Fernandes
2463cb0270
Add more tests to hogan templates 2016-04-25 17:53:54 +01:00
Rodrigo Fernandes
67ba95e5df
Add more tests to utils 2016-04-25 17:33:43 +01:00
Rodrigo Fernandes
4785c032a5
Add more tests for the diff2html api 2016-04-25 17:12:27 +01:00
Rodrigo Fernandes
e35b80a478
Add more tests for line by line diff generation 2016-04-25 16:51:27 +01:00
Rodrigo Fernandes
3fd5eb86bb
Fix parsing of line numbers 2016-04-25 16:20:40 +01:00
Rodrigo Fernandes
0261b30a26
Fix tests 2016-04-25 15:52:20 +01:00
Rodrigo Fernandes
50bbca6cc2
Fix build on webpack 2016-04-25 15:45:47 +01:00
Rodrigo Fernandes
7a86a1b6cb
Prepare for 2.0.0 beta 11 release 2016-04-25 14:11:26 +01:00
Rodrigo Fernandes
9d342669a1 Merge pull request #69 from rtfpessoa/hogan.js
Use Hogan.js for templating engine
2016-04-25 13:36:44 +01:00
Rodrigo Fernandes
374265354e
Add more tests and fix index parsing 2016-04-25 12:17:04 +01:00
Rodrigo Fernandes
b1f1ba6008
Use pre-compiled templates in node 2016-04-16 17:46:25 +01:00
Rodrigo Fernandes
05a09ae2a7 Fix browserTemplates global usage in node 2016-04-16 12:07:23 +01:00
Rodrigo Fernandes
8c45320674 Fix wrong template path 2016-04-16 12:07:23 +01:00
Rodrigo Fernandes
8237c8da28 Use hogan.js as templating engine 2016-04-16 12:07:23 +01:00
Rodrigo Fernandes
4521361c4f
Add .editorconfig style 2016-04-16 12:07:16 +01:00
Rodrigo Fernandes
2e142900ef
Add HTML ressource dependency imports to the docs 2016-04-15 23:21:30 +01:00
Rodrigo Fernandes
7aeb70b9bc
Prepare for beta 10 release 2016-04-14 19:51:44 +01:00
Rodrigo Fernandes
0950d2b53f Merge pull request #68 from escitalopram/f-hanging-diff
Cap line matching comparisons
2016-04-14 19:42:13 +01:00
Wolfgang Illmeyer
ee494b88e5 Cap line matching comparisons 2016-04-14 17:28:06 +02:00
Rodrigo Fernandes
3929ca28fc Fix /dev/null filename on html generation 2016-03-21 00:23:50 +00:00
Rodrigo Fernandes
465dde689a Fix repeated variable name in same scope (thanks Codacy) 2016-02-24 00:13:43 +00:00
Rodrigo Fernandes
9205e5e0a1 Fix filename parsing on filenames with tabs
* Why would someone create files with tabs?
2016-02-23 21:40:56 +00:00
Rodrigo Fernandes
64c1e827b7 Prepare for release 2.0.0-beta2 2016-02-23 18:46:01 +00:00
Rodrigo Fernandes
e64ac60b98 Merge pull request #63 from rtfpessoa/fix-filenames-parsing
Fix filename parsing with special characters and prefixes
2016-02-23 18:43:03 +00:00
Rodrigo Fernandes
e44b2a2885 Fix filename parsing with special characters and prefixes
* Add support for diffs with prefixes through configuration
* Fix parsing of filenames with special chars
2016-02-23 18:21:34 +00:00
Rodrigo Fernandes
c0f7c7abd8 Merge pull request #62 from rtfpessoa/clean-css
Clean files summary and diff headers and line numbers
2016-02-21 17:11:19 +00:00
Rodrigo Fernandes
75c4c2da46 Clean files summary and diff headers and line numbers 2016-02-21 16:46:07 +00:00
Rodrigo Fernandes
208a98b88d Merge pull request #52 from rtfpessoa/pbu88-use_nunjucks_templates
Use nunjucks templates
2016-02-21 16:42:56 +00:00
Rodrigo Fernandes
d6a03668c4 Fix line wrapping 2016-02-21 16:40:22 +00:00
Rodrigo Fernandes
adc4b629c1 Prepare for nunjucks release 2016-02-21 14:45:50 +00:00
Rodrigo Fernandes
7638fbba52 Fix nunjucks in the browser 2016-02-21 11:16:36 +00:00
Paulo Bu
e15cde2d6f Adds test for whitespaces and indentation and adds template for the wrapper 2016-02-21 11:05:53 +00:00
Paulo Bu
da2cd63a75 Attempt to use nunjuck templates in Line By Line 2016-02-21 11:05:53 +00:00
Rodrigo Fernandes
82f0fb629e Merge pull request #60 from Jameskmonger/patch-1
Update david-dm.org dependency status badges
2016-02-13 00:40:43 +00:00
Jameskmonger
693c725866 Update david-dm.org dev dependency status badge 2016-02-13 00:35:54 +00:00
Jameskmonger
249fb07eae Update david-dm.org dependency status badge 2016-02-13 00:35:01 +00:00
Rodrigo Fernandes
7acee5186b Add url hashtag for file summary state override 2016-02-12 23:11:59 +00:00
Rodrigo Fernandes
71de8ea7f1 Gather file languages from the html 2016-02-09 21:26:22 +00:00
Rodrigo Fernandes
f0363ecefc Prepare for release and update packages 2016-02-07 14:41:06 +00:00
Rodrigo Fernandes
59914f1a10 Merge pull request #58 from rtfpessoa/fix-file-summary-switch
Fix file summary switch
2016-02-07 14:27:48 +00:00
Rodrigo Fernandes
43666f655a Fix file summary switch
Introduce diff2html-ui to perform any ui anymations or dynamic changes
like hightlight or animations.
2016-02-07 14:19:23 +00:00
Rodrigo Fernandes
4e9dfa76ba Merge pull request #59 from rtfpessoa/fix-windows-parsing
Fix parsing on Windows
2016-02-07 12:56:13 +00:00
Rodrigo Fernandes
93dd322382 Add more eol tests for old os x and mixed eols 2016-02-07 12:47:00 +00:00
Rodrigo Fernandes
269a6268c4 Fix parsing on Windows
Replace windows EOL by Unix EOL
2016-02-06 18:50:47 +00:00
Rodrigo Fernandes
6abfbdb6a8 Improve bower.json main field
* Add dist css and js to main field

Thanks to @fedescarpa
Closes #53
2016-02-01 09:39:07 +00:00
Rodrigo Fernandes
b00ca04832 Merge pull request #50 from mrfyda/master
Fix line count when EOF is added to file
2016-01-16 22:42:39 +00:00
Rafael Cortês
f7bc1552e5 Fix line count when EOF is added to file
Removes '\\ No newline at end of file' information from the diff lines.
The test case is based on Bitbucket's diff attached to issue #49.
2016-01-16 21:53:54 +00:00
Rodrigo Fernandes
cc6ed43f25 Remove extra white space 2016-01-10 18:49:16 +00:00
Rodrigo Fernandes
8d738452be Merge pull request #48 from pbu88/refactors_html_code_into_methods
Refactors html code into methods for SideBySidePrinter
2016-01-10 18:38:30 +00:00
Paulo Bu
2cdb67d938 Refactors HTML code into their own methods in SideBySidePrinter 2016-01-10 18:16:43 +01:00
Paulo Bu
f6a9ecf450 Add tests to generateSingleLineHtml for SideBySide tests 2015-12-30 22:01:41 +01:00
Rodrigo Fernandes
8955020e9e Merge pull request #47 from pbu88/refactors_html_code_into_methods
Refactors html code into methods for LineByLinePrinter
2015-12-27 16:39:06 +00:00
Paulo Bu
a37176cc26 Refactors HTML code into their own methods 2015-12-27 12:29:58 +01:00
Paulo Bu
97eafcd2f7 Add tests to generateLineHtml for LineByLine tests 2015-12-27 11:08:24 +01:00
Rodrigo Fernandes
f5c05aa91e Add more badges 2015-12-24 11:27:07 +00:00
Rodrigo Fernandes
219bcea51b Add contributing docs and help 2015-12-23 20:19:10 +00:00
Rodrigo Fernandes
8558bab3ca Merge pull request #46 from rtfpessoa/test-html-prefixes
Test html line prefixes
2015-12-23 19:54:17 +00:00
Rodrigo Fernandes
57369ae2e4 Add JSCS script and update dependencies 2015-12-23 19:47:09 +00:00
Rodrigo Fernandes
f3d2946aa4 Force JSCS style 2015-12-23 19:34:39 +00:00
Rodrigo Fernandes
7244f07e0a Test html line prefixes 2015-12-23 16:05:30 +00:00
Rodrigo Fernandes
246e459749 Merge pull request #45 from pbu88/side_by_side_prefix_bug
Fixes prefix bug on side by side diff
2015-12-23 15:05:36 +00:00
Paulo Bu
7f08de9c67 Fixes prefix bug on side by side diff 2015-12-23 15:34:44 +01:00
Rodrigo Fernandes
598c2d365c Merge pull request #44 from pbu88/add-mocha-tests
Adds simple tests for utils and diff2html
2015-12-23 14:18:27 +00:00
Paulo Bu
09d422be79 Adds simple tests for utils and diff2html 2015-12-23 14:14:29 +01:00
Rodrigo Fernandes
d184017bf2 Merge pull request #43 from rtfpessoa/add-mocha-tests
Start using mocha with istambul to test and setup circleci
2015-12-22 22:56:31 +00:00
Rodrigo Fernandes
9e550c2b65 Start using mocha with istambul to test and setup circleci 2015-12-22 15:48:33 +00:00
Rodrigo Fernandes
7920de8924 Merge pull request #42 from pbu88/adding_unit_tests
Adds unit tests using node-unit
2015-12-22 14:44:22 +00:00
Paulo Bu
678e5147d8 Adds unit tests using node-unit
First approach to include unit tests in the code using node-unit.

- Adds node unit dependency
- Adds 2 very basic tests for LineByLine and SideBySide printers
2015-12-22 12:37:39 +01:00
Rodrigo Fernandes
af9d00b56e Merge pull request #41 from pbu88/fixes_wrong_use_of_self
Use more object oriented style in SideBySidePrinter
2015-12-21 23:47:55 +00:00
Paulo Bu
c5f54d29f8 Passes config in the SideBySide constructor and uses it from within the class 2015-12-21 15:26:08 +01:00
Paulo Bu
a4cf385a7d Makes SideBySidePrinter more object oriented 2015-12-21 15:26:02 +01:00
Rodrigo Fernandes
a6920cef82 Add css folder to npm release 2015-12-21 00:17:19 +00:00
Rodrigo Fernandes
7fa8292da1 Fix some more CSS and JS style issues 2015-12-20 23:38:16 +00:00
Rodrigo Fernandes
c80a4c6415 Merge pull request #39 from rtfpessoa/improve-code-style
Update contributors list, add codacy badge and clean code
2015-12-20 23:01:05 +00:00
Rodrigo Fernandes
011e06d876 Remove conflicting CSS 2015-12-20 22:55:34 +00:00
Rodrigo Fernandes
8d86a15d69 Clean code and force code style
Since more people are contributing to the code Codacy will help
keep it consistent and easy to maintain, by suggesting improvements
2015-12-20 22:55:09 +00:00
Rodrigo Fernandes
4d3d752361 Update contributors list, add codacy badge 2015-12-20 22:51:31 +00:00
Rodrigo Fernandes
9dab24ed77 Merge pull request #40 from pbu88/fixes_wrong_use_of_self
Renames and correctly declares variable 'that' instead of 'self'
2015-12-20 22:51:16 +00:00
Paulo Bu
068f30c2ce Renames and correctly declares variable 'that' instead of 'self' 2015-12-20 23:42:37 +01:00
Rodrigo Fernandes
af013f1951 Merge pull request #38 from pbu88/more_object_oriented
A more object oriented approach
2015-12-20 19:10:23 +00:00
Paulo Bu
e3f572c075 Passes config in the LineByLine constructor and uses it from within the class 2015-12-20 15:56:53 +01:00
Paulo Bu
4a06df6039 Makes LineByLine more object oriented 2015-12-20 15:51:56 +01:00
Rodrigo Fernandes
ff8eca3a7b Enable line matching on side-by-side sample 2015-12-19 22:54:03 +00:00
Rodrigo Fernandes
1dce15ad7a Use line matching as default 2015-12-19 21:14:58 +00:00
Rodrigo Fernandes
20e078334b Prepare for version 1.2.0 release 2015-12-19 21:09:31 +00:00
Rodrigo Fernandes
d2e9f49a0f Merge pull request #36 from escitalopram/f-line-and-word-matching
Fix bug in change grouping algorithm
2015-12-19 21:08:09 +00:00
Wolfgang Illmeyer
e41ee3eae8 Fix change grouping algorithm 2015-12-19 21:52:34 +01:00
Rodrigo Fernandes
1ec5cbfc5d Merge pull request #34 from escitalopram/f-line-and-word-matching
Enhancements for highlighting
2015-12-14 08:57:50 +00:00
Wolfgang Illmeyer
94516c835b make matching configurable 2015-12-09 23:17:45 +01:00
Wolfgang Illmeyer
8c059d30da Add changetype constant, fix css color for changes 2015-12-06 22:45:58 +01:00
Wolfgang Illmeyer
7335c3bab3 Enhancements for highlighting
1. Fixed a bug that prevented highlighting when more lines were deleted
from a replacement block than added

2. Added similarity search feature to match similar lines for
highlighting.

3. Highlighting: Support for matching "ins" and "del" markers to allow
for a "change" marker

4. Bugfix: All lines are now split into prefix and content spans
2015-11-27 11:39:08 +01:00
Rodrigo Fernandes
8b9c2473b2 Merge pull request #32 from rtfpessoa/fix-changes-headers
fix changes summary on files headers
2015-10-31 21:58:45 +00:00
Rodrigo Fernandes
54b39158f5 fix changes summary on files headers
* since the alignment of the changes the file headers lost the style due to css changes
* update diff dependency and add dev dependencies
2015-10-31 21:55:15 +00:00
Rodrigo Fernandes
45e84c5bc7 Merge pull request #31 from rtfpessoa/fix-credits-typo
fix typo
2015-10-29 10:09:51 +00:00
Rodrigo Fernandes
8f9ff37a99 fix typo 2015-10-29 10:09:46 +00:00
Rodrigo Fernandes
0bdaef8513 Merge pull request #30 from rtfpessoa/credits
add contributors list
2015-10-29 10:08:57 +00:00
Rodrigo Fernandes
b021466486 add contributors list 2015-10-29 10:07:29 +00:00
Rodrigo Fernandes
63a004fd01 Merge pull request #29 from rtfpessoa/release-1.1.2
prepare release 1.1.2
2015-10-28 18:54:36 +00:00
Rodrigo Fernandes
2acee773fb prepare release 1.1.2 2015-10-28 18:54:16 +00:00
Rodrigo Fernandes
d88ff360df Merge pull request #28 from Rantanen/master
Align the file summary
2015-10-28 18:52:33 +00:00
Mikko Rantanen
26d349b736 Align the file summary
Use <table> element to lay out the file summary. The summary is tabular
data anyway so the use of <table> should be justified. First column
contains lines added, justified to right and the second column contains
lines removed, justified to the left. The last column has the file name.

Fixes #27
2015-10-28 19:43:06 +02:00
Rodrigo Fernandes
90b8892bb3 release version 1.1.1 2015-10-20 19:12:25 +01:00
Rodrigo Fernandes
cc858a24f9 Merge pull request #25 from pbu88/small_perf_improvement
Extracts variables out of a loop
2015-10-20 11:29:09 +01:00
Paulo Bu
c1ea2cb3b2 Extracts variables out of a loop 2015-10-20 10:55:22 +02:00
116 changed files with 31948 additions and 3603 deletions

342
.all-contributorsrc Normal file
View file

@ -0,0 +1,342 @@
{
"projectName": "diff2html",
"projectOwner": "rtfpessoa",
"repoType": "github",
"repoHost": "https://github.com",
"files": [
"README.md"
],
"imageSize": 100,
"commit": true,
"commitConvention": "none",
"contributors": [
{
"login": "rtfpessoa",
"name": "Rodrigo Fernandes",
"avatar_url": "https://avatars0.githubusercontent.com/u/902384?v=4",
"profile": "https://rtfpessoa.xyz",
"contributions": [
"code"
]
},
{
"login": "stockmind",
"name": "stockmind",
"avatar_url": "https://avatars3.githubusercontent.com/u/5653847?v=4",
"profile": "https://github.com/stockmind",
"contributions": [
"code"
]
},
{
"login": "lantian",
"name": "Ivan Vorontsov",
"avatar_url": "https://avatars3.githubusercontent.com/u/535545?v=4",
"profile": "https://github.com/lantian",
"contributions": [
"code"
]
},
{
"login": "brewern",
"name": "Nick Brewer",
"avatar_url": "https://avatars1.githubusercontent.com/u/129300?v=4",
"profile": "http://www.nick-brewer.com",
"contributions": [
"code"
]
},
{
"login": "romellem",
"name": "Matt Wade",
"avatar_url": "https://avatars0.githubusercontent.com/u/8504000?v=4",
"profile": "http://heyitsmattwade.com",
"contributions": [
"bug",
"code"
]
},
{
"login": "mrfyda",
"name": "Rafael Cortês",
"avatar_url": "https://avatars1.githubusercontent.com/u/593860?v=4",
"profile": "http://mrfyda.github.io",
"contributions": [
"code"
]
},
{
"login": "nmatpt",
"name": "Nuno Teixeira",
"avatar_url": "https://avatars2.githubusercontent.com/u/5034733?v=4",
"profile": "https://github.com/nmatpt",
"contributions": [
"code"
]
},
{
"login": "kaishuu0123",
"name": "Koki Oyatsu",
"avatar_url": "https://avatars0.githubusercontent.com/u/1567423?v=4",
"profile": "https://saino.me/",
"contributions": [
"bug",
"code"
]
},
{
"login": "Jameskmonger",
"name": "James Monger",
"avatar_url": "https://avatars2.githubusercontent.com/u/2037007?v=4",
"profile": "http://www.jamesmonger.com",
"contributions": [
"doc"
]
},
{
"login": "wesssel",
"name": "Wessel van der Pal",
"avatar_url": "https://avatars2.githubusercontent.com/u/7767299?v=4",
"profile": "http://wesssel.github.io/",
"contributions": [
"security",
"code"
]
},
{
"login": "jung-kim",
"name": "jk-kim",
"avatar_url": "https://avatars2.githubusercontent.com/u/5281068?v=4",
"profile": "https://jung-kim.github.io",
"contributions": [
"code"
]
},
{
"login": "sss0791",
"name": "Sergey Semenov",
"avatar_url": "https://avatars1.githubusercontent.com/u/1446970?v=4",
"profile": "https://github.com/sss0791",
"contributions": [
"bug",
"code"
]
},
{
"login": "starpit",
"name": "Nick Mitchell",
"avatar_url": "https://avatars3.githubusercontent.com/u/4741620?v=4",
"profile": "http://researcher.watson.ibm.com/researcher/view.php?person=us-nickm",
"contributions": [
"bug",
"code"
]
},
{
"login": "samiraguiar",
"name": "Samir Aguiar",
"avatar_url": "https://avatars0.githubusercontent.com/u/13439135?v=4",
"profile": "https://github.com/samiraguiar",
"contributions": [
"doc"
]
},
{
"login": "pubkey",
"name": "pubkey",
"avatar_url": "https://avatars3.githubusercontent.com/u/8926560?v=4",
"profile": "https://twitter.com/pubkeypubkey",
"contributions": [
"doc",
"code"
]
},
{
"login": "iliyaZelenko",
"name": "Илья",
"avatar_url": "https://avatars1.githubusercontent.com/u/13103045?v=4",
"profile": "https://github.com/iliyaZelenko",
"contributions": [
"doc"
]
},
{
"login": "mohd-akram",
"name": "Mohamed Akram",
"avatar_url": "https://avatars0.githubusercontent.com/u/1823771?v=4",
"profile": "https://akr.am",
"contributions": [
"bug",
"doc",
"code"
]
},
{
"login": "emarcotte",
"name": "Eugene Marcotte",
"avatar_url": "https://avatars0.githubusercontent.com/u/249390?v=4",
"profile": "https://github.com/emarcotte",
"contributions": [
"code"
]
},
{
"login": "dsabanin",
"name": "Dima Sabanin",
"avatar_url": "https://avatars0.githubusercontent.com/u/8316?v=4",
"profile": "http://twitter.com/dimasabanin",
"contributions": [
"maintenance",
"code"
]
},
{
"login": "benabbottnz",
"name": "Ben Abbott",
"avatar_url": "https://avatars2.githubusercontent.com/u/2616473?v=4",
"profile": "https://github.com/benabbottnz",
"contributions": [
"doc"
]
},
{
"login": "dickeylth",
"name": "弘树@阿里",
"avatar_url": "https://avatars1.githubusercontent.com/u/2196373?v=4",
"profile": "http://webminer.js.org",
"contributions": [
"bug",
"doc"
]
},
{
"login": "Rantanen",
"name": "Mikko Rantanen",
"avatar_url": "https://avatars0.githubusercontent.com/u/385385?v=4",
"profile": "https://github.com/Rantanen",
"contributions": [
"bug"
]
},
{
"login": "extend1994",
"name": "Ann",
"avatar_url": "https://avatars2.githubusercontent.com/u/13430892?v=4",
"profile": "https://github.com/extend1994",
"contributions": [
"doc"
]
},
{
"login": "escitalopram",
"name": "escitalopram",
"avatar_url": "https://avatars0.githubusercontent.com/u/1155220?v=4",
"profile": "https://github.com/escitalopram",
"contributions": [
"code"
]
},
{
"login": "dependabot[bot]",
"name": "dependabot[bot]",
"avatar_url": "https://avatars0.githubusercontent.com/in/29110?v=4",
"profile": "https://github.com/apps/dependabot",
"contributions": [
"security",
"maintenance"
]
},
{
"login": "JoshuaKGoldberg",
"name": "Josh Goldberg",
"avatar_url": "https://avatars1.githubusercontent.com/u/3335181?v=4",
"profile": "http://www.joshuakgoldberg.com",
"contributions": [
"code"
]
},
{
"login": "apeckham",
"name": "Aaron",
"avatar_url": "https://avatars.githubusercontent.com/u/14110?v=4",
"profile": "https://github.com/apeckham",
"contributions": [
"doc"
]
},
{
"login": "pgrimaud",
"name": "Pierre Grimaud",
"avatar_url": "https://avatars.githubusercontent.com/u/1866496?v=4",
"profile": "https://github.com/pgrimaud",
"contributions": [
"doc"
]
},
{
"login": "domdomegg",
"name": "Adam Jones",
"avatar_url": "https://avatars.githubusercontent.com/u/4953590?v=4",
"profile": "https://domdomegg.github.io/",
"contributions": [
"doc"
]
},
{
"login": "charguer",
"name": "Arthur Charguéraud",
"avatar_url": "https://avatars.githubusercontent.com/u/1830652?v=4",
"profile": "https://github.com/charguer",
"contributions": [
"doc"
]
},
{
"login": "Pierrci",
"name": "Pierric Cistac",
"avatar_url": "https://avatars.githubusercontent.com/u/5020707?v=4",
"profile": "https://twitter.com/pierrci",
"contributions": [
"doc",
"code"
]
},
{
"login": "xlith",
"name": "Civan Yavuzşen",
"avatar_url": "https://avatars.githubusercontent.com/u/510560?v=4",
"profile": "https://github.com/xlith",
"contributions": [
"code"
]
},
{
"login": "timgates42",
"name": "Tim Gates",
"avatar_url": "https://avatars.githubusercontent.com/u/47873678?v=4",
"profile": "https://github.com/timgates42",
"contributions": [
"doc"
]
},
{
"login": "campersau",
"name": "campersau",
"avatar_url": "https://avatars.githubusercontent.com/u/4009570?v=4",
"profile": "https://github.com/campersau",
"contributions": [
"code"
]
},
{
"login": "dependabot-preview[bot]",
"name": "dependabot-preview[bot]",
"avatar_url": "https://avatars.githubusercontent.com/in/2141?v=4",
"profile": "https://github.com/apps/dependabot-preview",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7
}

16
.editorconfig Normal file
View file

@ -0,0 +1,16 @@
# This file is for unifying the coding style for different editors and IDEs
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
max_line_length = 120
[Makefile]
indent_style = tab

44
.github/ISSUE_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,44 @@
### Step -1: Before filling an issue check out troubleshooting section
- Go to [README.md#Troubleshooting](https://github.com/rtfpessoa/diff2html#troubleshooting)
### Step 0: Describe your environment
- OS: **\_**
- diff2html version: **\_**
- Using diff2html directly or using diff2html-ui helper: **\_**
- Extra flags: **\_**
### Step 1: Describe the problem:
#### Steps to reproduce:
1. ***
2. ***
3. ***
#### diff example:
```diff
diff --git describe.c
index fabadb8,cc95eb0..4866510
--- a/describe.c
+++ b/describe.c
@@@ -98,20 -98,12 +98,20 @@@
return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
}
```
#### Observed Results:
- What happened? This could be a description, log output, etc.
#### Expected Results:
- What did you expect to happen?
#### Relevant Code:
```
// TODO(you): code here to reproduce the problem
```

23
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,23 @@
---
name: Bug report about: Create a report to help us improve title: "" labels: "" assignees: "" ---**Describe the bug** A
clear and concise description of what the bug is.
**To Reproduce** Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior** A clear and concise description of what you expected to happen.
**Screenshots** If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Windows, Linux, Mac]
- Browser [e.g. Firefox, Chrome, Safari]
- Version [e.g. 22]
**Additional context** Add any other context about the problem here.

View file

@ -0,0 +1,12 @@
---
name: Feature request about: Suggest an idea for this project title: "" labels: "" assignees: "" ---**Is your feature
request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always
frustrated when [...]
**Describe the solution you'd like** A clear and concise description of what you want to happen.
**Describe alternatives you've considered** A clear and concise description of any alternative solutions or features
you've considered.
**Additional context** Add any other context or screenshots about the feature request here.

12
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,12 @@
name: ci
on:
pull_request:
branches: [master]
jobs:
test-and-publish:
uses: ./.github/workflows/test-and-publish.yml
with:
environment: dev
secrets: inherit

35
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,35 @@
name: release
on:
push:
branches: [master]
jobs:
test-and-publish:
uses: ./.github/workflows/test-and-publish.yml
with:
environment: production
secrets: inherit
publish-website:
runs-on: ubuntu-latest
container:
image: amazon/aws-cli
needs: [test-and-publish]
environment: 'production'
steps:
- name: Download docs
uses: actions/download-artifact@v4
with:
name: docs
path: docs/
- name: Publish to S3
working-directory: docs
env:
AWS_CF_DISTRIBUTION_ID: ${{ secrets.AWS_CF_DISTRIBUTION_ID }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
run: |
aws s3 sync --region eu-west-1 --delete . s3://diff2html.xyz --metadata-directive REPLACE --cache-control max-age=31557600
aws cloudfront create-invalidation --region eu-west-1 --distribution-id $AWS_CF_DISTRIBUTION_ID --paths /index.html /demo.html /sitemap.xml /robots.txt

148
.github/workflows/test-and-publish.yml vendored Normal file
View file

@ -0,0 +1,148 @@
name: test-and-publish
on:
workflow_call:
inputs:
environment:
required: true
type: string
jobs:
version:
runs-on: ubuntu-latest
container:
image: codacy/git-version
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Fix tar access
run: apk add --update --no-progress tar
- name: Fix git access
run: |
git config --global --add safe.directory /__w/diff2html/diff2html
- name: Get next version
run: |
export NEXT_VERSION="$(/bin/git-version --folder=$PWD --release-branch=master)"
echo "Next version is ${NEXT_VERSION}"
echo "${NEXT_VERSION}" > next-version.txt
echo "version=${NEXT_VERSION}" >> $GITHUB_ENV
- name: Get next npm tag name
run: |
if [ "${GITHUB_REF_NAME}" = "master" ]; then
export PUBLISH_TAG="latest"
elif [ "${GITHUB_REF_NAME}" = "next" ]; then
export PUBLISH_TAG="next"
else
export PUBLISH_TAG="pr"
fi
echo "Next tag is ${PUBLISH_TAG}"
echo "${PUBLISH_TAG}" > publish-tag.txt
- name: Upload versions
uses: actions/upload-artifact@v4
with:
name: versions
if-no-files-found: error
path: |
next-version.txt
publish-tag.txt
build:
runs-on: ubuntu-latest
needs: [version]
strategy:
matrix:
node-version: [16.x, 18.x, 20.x, 22.x]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Log environment setup
run: |
node -v
npm -v
- name: Install dependencies
run: npm install
- name: Build templates
run: npm run build:templates
- name: Build library
run: npm run build
publish:
runs-on: ubuntu-latest
needs: [build]
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Download versions
uses: actions/download-artifact@v4
with:
name: versions
- name: Store version
run: echo "version=$(cat next-version.txt)" >> $GITHUB_ENV
- name: Configure Git
run: |
git config user.email "gh-actions@users.noreply.github.com"
git config user.name "GitHub Actions"
- name: Tag commit
uses: tvdias/github-tagger@v0.0.1
with:
repo-token: '${{ secrets.GITHUB_TOKEN }}'
tag: '${{ env.version }}'
- name: Install dependencies
run: npm install
- uses: actions/setup-node@v3
with:
registry-url: 'https://registry.npmjs.org'
node-version: '22.x'
- name: Configure NPM version
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
rm -f .npmrc
touch .npmrc
echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" >> .npmrc
echo "registry=https://registry.npmjs.org/" >> .npmrc
echo "access=public" >> .npmrc
echo "save-exact=true" >> .npmrc
- name: Version package
run: |
# Update version in packages to publish
npm version $(cat next-version.txt) -m "Release version %s"
- name: Publish to NPM
run: npm publish --tag $(cat publish-tag.txt)
- uses: actions/setup-node@v3
with:
node-version: '22.x'
registry-url: 'https://npm.pkg.github.com'
- name: Configure NPM version
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
rm -f .npmrc
touch .npmrc
echo "//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}" >> .npmrc
echo "@rtfpessoa:registry=https://npm.pkg.github.com/" >> .npmrc
echo "access=public" >> .npmrc
echo "save-exact=true" >> .npmrc
- name: Publish to GPR
run: |
# HACK: Override npm package name to be able to publish in GitHub
sed -i 's/^ "name":.*/ "name": "@rtfpessoa\/diff2html",/g' package.json
echo "Going to publish version $(cat next-version.txt) to GitHub"
npm publish --tag $(cat publish-tag.txt)
# HACK: Restore npm package name
sed -i 's/^ "name":.*/ "name": "diff2html",/g' package.json
- name: Upload docs
uses: actions/upload-artifact@v4
with:
name: docs
if-no-files-found: error
path: docs/

23
.gitignore vendored
View file

@ -18,6 +18,29 @@ target/
# Node
node_modules/
npm-debug.log
yarn-error.log
# Coverage
coverage/
# Bower
bower_components/
# Terraform
/terraform/.terraform
# Compiled templates
/src/diff2html-templates.*
# Compiled website
/docs/
# Bundles temporary typescript files compiled by webpack
/bundles-out/
# Web bundles
/bundles/
# CommonJS library
/lib/
# ESNext library
/lib-esm/

1
.husky/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
_

3
.husky/pre-commit Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
npm run lint:staged

16
.prettierrc.json Normal file
View file

@ -0,0 +1,16 @@
{
"arrowParens": "avoid",
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"jsxSingleQuote": false,
"printWidth": 120,
"proseWrap": "always",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"useTabs": false
}

3
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}

1
CNAME Normal file
View file

@ -0,0 +1 @@
diff2html.xyz

61
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,61 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making
participation in our project and our community a harassment-free experience for everyone, regardless of age, body size,
disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education,
socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take
appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits,
issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any
contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the
project or its community. Examples of representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed representative at an online or offline
event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at
rtfrodrigo [at] gmail [dot] com. All complaints will be reviewed and investigated and will result in a response that is
deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with
regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent
repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at
https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq

61
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,61 @@
## How to contribute to diff2html
### Main rules
- Before you open a ticket or send a pull request, [search](https://github.com/rtfpessoa/diff2html/issues) for previous
discussions about the same feature or issue. Add to the earlier ticket if you find one.
- If you're proposing a new feature, make sure you create an issue to let other contributors know what you are working
on.
- Before sending a pull request make sure your code is tested.
- Before sending a pull request for a feature, be sure to run tests with `npm run test`.
- Use the same coding style as the rest of the codebase, most of the check can be performed with `npm run run lint`.
- Use `git rebase` (not `git merge`) to sync your work from time to time with the master branch.
- After creating your pull request make sure the build is passing on
[CircleCI](https://circleci.com/gh/rtfpessoa/diff2html) and that [Codacy](https://www.codacy.com/app/Codacy/diff2html)
is also confident in the code quality.
- In your pull request, do not commit the `dist` or `build` folder if you needed to build the release files.
### Commit Style
Writing good commit logs is important. A commit log should describe what changed and why. Follow these guidelines when
writing one:
1. The first line should be 50 characters or less and contain a short description of the change prefixed with the name
of the changed subsystem (e.g. "net: add localAddress and localPort to Socket").
2. Keep the second line blank.
3. Wrap all other lines at 72 columns.
A good commit log can look something like this:
```
subsystem: explaining the commit in one line
Body of commit message is a few lines of text, explaining things
in more detail, possibly giving some background about the issue
being fixed, etc. etc.
The body of the commit message can be several paragraphs, and
please do proper word-wrap and keep columns shorter than about
72 characters or so. That way `git log` will show things
nicely even when it is indented.
```
### Developer's Certificate of Origin 1.0
By making a contribution to this project, I certify that:
- (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source
license indicated in the file; or
- (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate
open source license and I have the right under that license to submit that work with modifications, whether created in
whole or in part by me, under the same open source license (unless I am permitted to submit under a different
license), as indicated in the file; or
- (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not
modified it.

26
CREDITS.md Normal file
View file

@ -0,0 +1,26 @@
# Credits
This is the list of all the kind people that have contributed to the diff2html project. This list is ordered by first
contribution.
Thanks, @rtfpessoa
---
Rodrigo Fernandes, [@rtfpessoa](https://github.com/rtfpessoa)
JK Kim, [@codingtwinky](https://github.com/codingtwinky)
Paulo Bu, [@pbu88](https://github.com/pbu88)
Nuno Teixeira, [@nmatpt](https://github.com/nmatpt)
Mikko Rantanen, [@Rantanen](https://github.com/Rantanen)
Wolfgang Illmeyer, [@escitalopram](https://github.com/escitalopram)
Jameskmonger, [@Jameskmonger](https://github.com/Jameskmonger)
Rafael Cortês, [@mrfyda](https://github.com/mrfyda)
Ivan Vorontsov, [@lantian](https://github.com/lantian)

20
LICENSE
View file

@ -1,20 +0,0 @@
Copyright 2014 Rodrigo Fernandes https://rtfpessoa.github.io/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

14
LICENSE.md Normal file
View file

@ -0,0 +1,14 @@
Copyright 2014-2016 Rodrigo Fernandes https://rtfpessoa.github.io/
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

625
README.md
View file

@ -1,114 +1,613 @@
# Diff to Html by [rtfpessoa](https://github.com/rtfpessoa)
# diff2html
Diff to Html generates pretty HTML diffs from git diff output.
[![npm](https://img.shields.io/npm/v/diff2html)](https://www.npmjs.com/package/diff2html)
[![node](https://img.shields.io/node/v/diff2html)](https://www.npmjs.com/package/diff2html)
[![npm](https://img.shields.io/npm/l/diff2html)](https://www.npmjs.com/package/diff2html)
[![GitHub Actions](https://github.com/rtfpessoa/diff2html/actions/workflows/release.yml/badge.svg)](https://github.com/rtfpessoa/diff2html/actions/workflows/release.yml)
[![npm weekly downloads](https://img.shields.io/npm/dw/diff2html)](https://www.npmjs.com/package/diff2html)
[![npm monthly downloads](https://img.shields.io/npm/dm/diff2html)](https://www.npmjs.com/package/diff2html)
[![npm yearly downloads](https://img.shields.io/npm/dy/diff2html)](https://www.npmjs.com/package/diff2html)
[![npm downloads](https://img.shields.io/npm/dt/diff2html)](https://www.npmjs.com/package/diff2html)
[![jsdelivr CDN Downloads](https://data.jsdelivr.com/v1/package/npm/diff2html/badge)](https://www.jsdelivr.com/package/npm/diff2html)
[![All Contributors](https://img.shields.io/badge/all_contributors-22-orange)](#contributors)
diff2html generates pretty HTML diffs from git diff or unified diff output.
## Table of Contents
<!-- toc -->
- [Features](#features)
- [Online Example](#online-example)
- [Distributions](#distributions)
- [Usage](#usage)
- [Diff Text Input](#diff-text-input)
- [Diff2HtmlUI Usage](#diff2htmlui-usage)
- [Diff2HtmlUI API](#diff2htmlui-api)
- [Diff2HtmlUI Configuration](#diff2htmlui-configuration)
- [Diff2HtmlUI Browser](#diff2htmlui-browser)
- [Diff2HtmlUI Examples](#diff2htmlui-examples)
- [Diff2Html Usage](#diff2html-usage)
- [Diff2Html API](#diff2html-api)
- [Diff2Html Configuration](#diff2html-configuration)
- [Diff2Html Browser](#diff2html-browser)
- [Diff2Html NPM / Node.js Library](#diff2html-npm--nodejs-library)
- [Diff2Html Examples](#diff2html-examples)
- [Troubleshooting](#troubleshooting)
- [1. Out of memory or Slow execution](#1-out-of-memory-or-slow-execution)
- [Contribute](#contribute)
- [Contributors](#contributors)
- [License](#license)
- [Thanks](#thanks)
<!-- tocstop -->
## Features
* `line-by-line` and `side-by-side` diff
- Supports git and unified diffs
* new and old line numbers
- Line by line and Side by side diff
* inserted and removed lines
- New and old line numbers
* GitHub like style
- Inserted and removed lines
* Code syntax highlight
- GitHub like visual style
- Code syntax highlight
- Line similarity matching
- Easy code selection
## Online Example
> Go to [Diff2HTML](http://rtfpessoa.github.io/diff2html/)
> Go to [diff2html](https://diff2html.xyz/demo.html)
## Distributions
* [WebJar](http://www.webjars.org/)
- [jsdelivr CDN](https://www.jsdelivr.com/package/npm/diff2html)
- [WebJar](http://www.webjars.org/)
- [Node Library](https://www.npmjs.org/package/diff2html)
- [NPM CLI](https://www.npmjs.org/package/diff2html-cli)
- Manually use from jsdelivr or build the project:
- Browser / Bundle
- Parser and HTML Generator
- [bundles/js/diff2html.min.js](https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html.min.js) - includes the
diff parser and html generator
- Wrapper and helper adding syntax highlight, synchronized scroll, and other nice features
- [bundles/js/diff2html-ui.min.js](https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js) -
includes the wrapper of diff2html with highlight for all `highlight.js` supported languages
- [bundles/js/diff2html-ui-slim.min.js](https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui-slim.min.js) -
includes the wrapper of diff2html with "the most common" `highlight.js` supported languages
- [bundles/js/diff2html-ui-base.min.js](https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui-base.min.js) -
includes the wrapper of diff2html without including a `highlight.js` implementation. You can use it without
syntax highlight or by passing your own implementation with the languages you prefer
- NPM / Node.js library
- ES5
- [lib/diff2html.js](https://cdn.jsdelivr.net/npm/diff2html/lib/diff2html.js) - includes the diff parser and html
generator
- [lib/ui/js/diff2html-ui.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui.js) - includes the
wrapper of diff2html with highlight for all `highlight.js` supported languages
- [lib/ui/js/diff2html-ui-slim.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui-slim.js) -
includes the wrapper of diff2html with "the most common" `highlight.js` supported languages
- [lib/ui/js/diff2html-ui-base.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui-base.js)
- ES6
- [lib-esm/diff2html.js](https://cdn.jsdelivr.net/npm/diff2html/lib-esm/diff2html.js) - includes the diff parser
and html generator
- [lib/ui/js/diff2html-ui.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui.js) - includes the
wrapper of diff2html with highlight for all `highlight.js` supported languages
- [lib/ui/js/diff2html-ui-slim.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui-slim.js) -
includes the wrapper of diff2html with "the most common" `highlight.js` supported languages
- [lib/ui/js/diff2html-ui-base.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui-base.js) -
includes the wrapper of diff2html without including a `highlight.js` implementation. You can use it without
syntax highlight or by passing your own implementation with the languages you prefer
* [Node Module](https://www.npmjs.org/package/diff2html)
## Usage
* [Bower Package](http://bower.io/search/?q=diff2html)
Diff2Html can be used in various ways as listed in the [distributions](#distributions) section. The two main ways are:
* [Node CLI](https://www.npmjs.org/package/diff2html-cli)
- [Diff2HtmlUI](#diff2htmlui-usage): using this wrapper makes it easy to inject the html in the DOM and adds some nice
features to the diff, like syntax highlight.
- [Diff2Html](#diff2html-usage): using the parser and html generator directly from the library gives you complete
control about what you can do with the json or html generated.
* Manually download and import `dist/diff2html.min.js` into your page
Below you can find more details and examples about each option.
## How to use
## Diff Text Input
> Pretty HTML diff
diff2html accepts the text contents of a
[unified diff](https://www.gnu.org/software/diffutils/manual/html_node/Unified-Format.html) or the superset format git
diff (https://git-scm.com/docs/git-diff) (not combined or word diff). To provide multiples files as input just
concatenate the diffs (just like the output of git diff).
Diff2Html.getPrettyHtml(exInput, configuration)
## Diff2HtmlUI Usage
> Intermediate Json From Git Word Diff Output
> Simple wrapper to ease simple tasks in the browser such as: code highlight and js effects
Diff2Html.getJsonFromDiff(exInput)
- Invoke Diff2html
- Inject output in DOM element
- Enable collapsible file summary list
- Enable syntax highlight of the code in the diffs
> Check out the `index.html` for a complete example.
### Diff2HtmlUI API
## Configuration
The HTML output accepts a Javascript object with configuration. Possible options:
> Create a Diff2HtmlUI instance
- `inputFormat`: the format of the input data: `'diff'` or `'json'`, default is `'diff'`
- `outputFormat`: the format of the output data: `'line-by-line'` or `'side-by-side'`, default is `'line-by-line'`
- `showFiles`: show a file list before the diff: `true` or `false`, default is `false`
```ts
constructor(target: HTMLElement, diffInput?: string | DiffFile[]) // diff2html-ui, diff2html-ui-slim
constructor(target: HTMLElement, diffInput?: string | DiffFile[], config: Diff2HtmlUIConfig = {}, hljs?: HighlightJS) // diff2html-ui-base
```
> Generate and inject in the document the Pretty HTML representation of the diff
## Syntax Highlight
```ts
draw(): void
```
> Add the dependencies.
Choose one color scheme, and add the main highlight code.
If your favourite language is not included in the default package also add its javascript highlight file.
> Enable extra features
```ts
synchronisedScroll(): void
fileListToggle(startVisible: boolean): void
highlightCode(): void
stickyFileHeaders(): void
```
### Diff2HtmlUI Configuration
- `synchronisedScroll`: scroll both panes in side-by-side mode: `true` or `false`, default is `true`
- `highlight`: syntax highlight the code on the diff: `true` or `false`, default is `true`
- `fileListToggle`: allow the file summary list to be toggled: `true` or `false`, default is `true`
- `fileListStartVisible`: choose if the file summary list starts visible: `true` or `false`, default is `false`
- `fileContentToggle`: allow each file contents to be toggled: `true` or `false`, default is `true`
- `stickyFileHeaders`: make file headers sticky: `true` or `false`, default is `true`
- [All the options](#diff2html-configuration) from Diff2Html are also valid configurations in Diff2HtmlUI
### Diff2HtmlUI Browser
#### Mandatory HTML resource imports
```html
<!-- CSS -->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css" />
<!-- Javascripts -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
```
#### Init
```js
const targetElement = document.getElementById('destination-elem-id');
const configuration = { drawFileList: true, matching: 'lines' };
const diff2htmlUi = new Diff2HtmlUI(targetElement, diffString, configuration);
// or
const diff2htmlUi = new Diff2HtmlUI(targetElement, diffJson, configuration);
```
#### Draw
```js
diff2htmlUi.draw();
```
#### Syntax Highlight
**NOTE:** The highlight.js css should come before the diff2html css
```html
<!-- Stylesheet -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.6/styles/github.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github.min.css" />
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css" />
<!-- Javascripts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.6/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.6/languages/scala.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
```
> Invoke the highlightjs plugin
> Pass the option `highlight` with value true or invoke `diff2htmlUi.highlightCode()` after `diff2htmlUi.draw()`.
```js
document.addEventListener("DOMContentLoaded", function () {
// parse the diff to json
var diffJson = Diff2Html.getJsonFromDiff(lineDiffExample);
// collect all the file extensions in the json
var allFileLanguages = diffJson.map(function (line) {
return line.language;
});
// remove duplicated languages
var distinctLanguages = allFileLanguages.filter(function (v, i) {
return allFileLanguages.indexOf(v) == i;
});
// pass the languages to the highlightjs plugin
hljs.configure({languages: distinctLanguages});
// generate and inject the diff HTML into the desired place
document.getElementById("line-by-line").innerHTML = Diff2Html.getPrettyHtml(diffJson, { inputFormat: 'json' });
document.getElementById("side-by-side").innerHTML = Diff2Html.getPrettyHtml(diffJson, { inputFormat: 'json', outputFormat: 'side-by-side' });
// collect all the code lines and execute the highlight on them
var codeLines = document.getElementsByClassName("d2h-code-line-ctn");
[].forEach.call(codeLines, function (line) {
hljs.highlightBlock(line);
});
document.addEventListener('DOMContentLoaded', () => {
const diffString = `diff --git a/sample.js b/sample.js
index 0000001..0ddf2ba
--- a/sample.js
+++ b/sample.js
@@ -1 +1 @@
-console.log("Hello World!")
+console.log("Hello from Diff2Html!")`;
const targetElement = document.getElementById('myDiffElement');
const configuration = { drawFileList: true, matching: 'lines', highlight: true };
const diff2htmlUi = new Diff2HtmlUI(targetElement, diffString, configuration);
diff2htmlUi.draw();
diff2htmlUi.highlightCode();
});
```
## Contributions
When using the `auto` color scheme, you will need to specify both the light and dark themes for highlight.js to use.
All the contributions are welcome.
```html
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github.min.css"
media="screen and (prefers-color-scheme: light)"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/github-dark.min.css"
media="screen and (prefers-color-scheme: dark)"
/>
```
To contribute just send a pull request with your changes and I will review it asap.
#### Collapsable File Summary List
> Add the dependencies.
```html
<!-- Javascripts -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
```
> Invoke the Diff2HtmlUI helper Pass the option `fileListToggle` with value true or invoke
> `diff2htmlUi.fileListToggle()` after `diff2htmlUi.draw()`.
```js
document.addEventListener('DOMContentLoaded', () => {
const targetElement = document.getElementById('myDiffElement');
var diff2htmlUi = new Diff2HtmlUI(targetElement, lineDiffExample, { drawFileList: true, matching: 'lines' });
diff2htmlUi.draw();
diff2htmlUi.fileListToggle(false);
});
```
### Diff2HtmlUI Examples
#### Example with plain HTML+CSS+JS
```html
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8" />
<!-- Make sure to load the highlight.js CSS file before the Diff2Html CSS file -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.1/styles/github.min.css" />
<link
rel="stylesheet"
type="text/css"
href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css"
/>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
</head>
<script>
const diffString = `diff --git a/sample.js b/sample.js
index 0000001..0ddf2ba
--- a/sample.js
+++ b/sample.js
@@ -1 +1 @@
-console.log("Hello World!")
+console.log("Hello from Diff2Html!")`;
document.addEventListener('DOMContentLoaded', function () {
var targetElement = document.getElementById('myDiffElement');
var configuration = {
drawFileList: true,
fileListToggle: false,
fileListStartVisible: false,
fileContentToggle: false,
matching: 'lines',
outputFormat: 'side-by-side',
synchronisedScroll: true,
highlight: true,
renderNothingWhenEmpty: false,
};
var diff2htmlUi = new Diff2HtmlUI(targetElement, diffString, configuration);
diff2htmlUi.draw();
diff2htmlUi.highlightCode();
});
</script>
<body>
<div id="myDiffElement"></div>
</body>
</html>
```
#### StimulusJS with TypeScript
```ts
import { Controller } from '@hotwired/stimulus';
import { Diff2HtmlUI, Diff2HtmlUIConfig } from 'diff2html/lib/ui/js/diff2html-ui-slim.js';
// Requires `npm install highlight.js`
import 'highlight.js/styles/github.css';
import 'diff2html/bundles/css/diff2html.min.css';
export default class extends Controller {
connect(): void {
const diff2htmlUi = new Diff2HtmlUI(this.diffElement, this.unifiedDiff, this.diffConfiguration);
diff2htmlUi.draw();
}
get unifiedDiff(): string {
return this.data.get('unifiedDiff') || '';
}
get diffElement(): HTMLElement {
return this.element as HTMLElement;
}
get diffConfiguration(): Diff2HtmlUIConfig {
return {
drawFileList: true,
matching: 'lines',
};
}
}
```
## Diff2Html Usage
### Diff2Html API
> JSON representation of the diff
```ts
function parse(diffInput: string, configuration: Diff2HtmlConfig = {}): DiffFile[];
```
> Pretty HTML representation of the diff
```ts
function html(diffInput: string | DiffFile[], configuration: Diff2HtmlConfig = {}): string;
```
### Diff2Html Configuration
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`
- `srcPrefix`: add a prefix to all source (before changes) filepaths, default is `''`. Should match the prefix used when
[generating the diff](https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---src-prefixltprefixgt).
- `dstPrefix`: add a prefix to all destination (after changes) filepaths, default is `''`. Should match the prefix used
when [generating the diff](https://git-scm.com/docs/git-diff#Documentation/git-diff.txt---dst-prefixltprefixgt)
- `diffMaxChanges`: number of changed lines after which a file diff is deemed as too big and not displayed, default is
`undefined`
- `diffMaxLineLength`: number of characters in a diff line 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` or
`diffMaxLineLength` is set). Will be given a file index as a number and should return a string.
- `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`
- `maxLineLengthHighlight`: only perform diff changes highlight if lines are smaller than this, default is `10000`
- `diffStyle`: show differences level in each line: `'word'` or `'char'`, default is `'word'`
- `renderNothingWhenEmpty`: render nothing if the diff shows no change in its comparison: `true` or `false`, default is
`false`
- `matchingMaxComparisons`: perform at most this much comparisons for line matching a block of changes, default is
`2500`
- `maxLineSizeInBlockForComparison`: maximum number of characters of the bigger line in a block to apply comparison,
default is `200`
- `compiledTemplates`: object ([Hogan.js](https://github.com/twitter/hogan.js/) template values) with previously
compiled templates to replace parts of the html, default is `{}`. For example:
`{ "tag-file-changed": Hogan.compile("<span class="d2h-tag d2h-changed d2h-changed-tag">MODIFIED</span>") }`
- `rawTemplates`: object (string values) with raw not compiled templates to replace parts of the html, default is `{}`.
For example: `{ "tag-file-changed": "<span class="d2h-tag d2h-changed d2h-changed-tag">MODIFIED</span>" }`
> For more information regarding the possible templates look into
> [src/templates](https://github.com/rtfpessoa/diff2html/tree/master/src/templates)
- `highlightLanguages`: Map of extension to language name, used for highlighting. This overrides the default language
detection based on file extensions.
- `colorScheme`: color scheme to use for the diff, default is `light`. Possible values are `light`, `dark`, and `auto`
which will use the browser's preferred color scheme.
### Diff2Html Browser
Import the stylesheet and the library code.
To load correctly in the Browser you need to include the stylesheet in the final HTML.
```html
<!-- CSS -->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css" />
<!-- Javascripts -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html.min.js"></script>
```
It will now be available as a global variable named `Diff2Html`.
```js
document.addEventListener('DOMContentLoaded', () => {
var diffHtml = Diff2Html.html('<Unified Diff String>', {
drawFileList: true,
matching: 'lines',
outputFormat: 'side-by-side',
});
document.getElementById('destination-elem-id').innerHTML = diffHtml;
});
```
### Diff2Html NPM / Node.js Library
```js
const Diff2html = require('diff2html');
const diffJson = Diff2html.parse('<Unified Diff String>');
const diffHtml = Diff2html.html(diffJson, { drawFileList: true });
console.log(diffHtml);
```
### Diff2Html Examples
#### Example with Angular
- Typescript
```typescript
import * as Diff2Html from 'diff2html';
import { Component, OnInit } from '@angular/core';
export class AppDiffComponent implements OnInit {
outputHtml: string;
constructor() {
this.init();
}
ngOnInit() {}
init() {
let strInput =
'--- a/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n+++ b/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n@@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (\n \n // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n \n+func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {\n+\tr0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))\n+\tn = int(r0)\n+\tif e1 != 0 {\n+\t\terr = errnoErr(e1)\n+\t}\n+\treturn\n+}\n+\n+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n+\n func read(fd int, p []byte) (n int, err error) {\n \tvar _p0 unsafe.Pointer\n \tif len(p) > 0 {\n';
let outputHtml = Diff2Html.html(strInput, { drawFileList: true, matching: 'lines' });
this.outputHtml = outputHtml;
}
}
```
- HTML
```html
<!doctype html>
<html>
<head>
<title>diff2html</title>
</head>
<body>
<div [innerHtml]="outputHtml"></div>
</body>
</html>
```
- `.angular-cli.json` - Add styles
```json
"styles": [
"diff2html.min.css"
]
```
#### Example with Vue.js
```vue
<template>
<div v-html="prettyHtml" />
</template>
<script>
import * as Diff2Html from 'diff2html';
import 'diff2html/bundles/css/diff2html.min.css';
export default {
data() {
return {
diffs:
'--- a/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n+++ b/server/vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go\n@@ -1035,6 +1035,17 @@ func Prctl(option int, arg2 uintptr, arg3 uintptr, arg4 uintptr, arg5 uintptr) (\n \n // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n \n+func Pselect(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timespec, sigmask *Sigset_t) (n int, err error) {\n+\tr0, _, e1 := Syscall6(SYS_PSELECT6, uintptr(nfd), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)))\n+\tn = int(r0)\n+\tif e1 != 0 {\n+\t\terr = errnoErr(e1)\n+\t}\n+\treturn\n+}\n+\n+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n+\n func read(fd int, p []byte) (n int, err error) {\n \tvar _p0 unsafe.Pointer\n \tif len(p) > 0 {\n',
};
},
computed: {
prettyHtml() {
return Diff2Html.html(this.diffs, {
drawFileList: true,
matching: 'lines',
outputFormat: 'side-by-side',
});
},
},
};
</script>
```
## Troubleshooting
### 1. Out of memory or Slow execution
#### Causes:
- Big files
- Big lines
#### Fix:
- Disable the line matching algorithm, by setting the option `{"matching": "none"}` when invoking diff2html
## Contribute
This is a developer friendly project, all the contributions are welcome. To contribute just send a pull request with
your changes following the guidelines described in `CONTRIBUTING.md`. I will try to review them as soon as possible.
## Contributors
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://rtfpessoa.xyz"><img src="https://avatars0.githubusercontent.com/u/902384?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rodrigo Fernandes</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=rtfpessoa" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/stockmind"><img src="https://avatars3.githubusercontent.com/u/5653847?v=4?s=100" width="100px;" alt=""/><br /><sub><b>stockmind</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=stockmind" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/lantian"><img src="https://avatars3.githubusercontent.com/u/535545?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ivan Vorontsov</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=lantian" title="Code">💻</a></td>
<td align="center"><a href="http://www.nick-brewer.com"><img src="https://avatars1.githubusercontent.com/u/129300?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nick Brewer</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=brewern" title="Code">💻</a></td>
<td align="center"><a href="http://heyitsmattwade.com"><img src="https://avatars0.githubusercontent.com/u/8504000?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matt Wade</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/issues?q=author%3Aromellem" title="Bug reports">🐛</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=romellem" title="Code">💻</a></td>
<td align="center"><a href="http://mrfyda.github.io"><img src="https://avatars1.githubusercontent.com/u/593860?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rafael Cortês</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=mrfyda" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nmatpt"><img src="https://avatars2.githubusercontent.com/u/5034733?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nuno Teixeira</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=nmatpt" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://saino.me/"><img src="https://avatars0.githubusercontent.com/u/1567423?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Koki Oyatsu</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/issues?q=author%3Akaishuu0123" title="Bug reports">🐛</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=kaishuu0123" title="Code">💻</a></td>
<td align="center"><a href="http://www.jamesmonger.com"><img src="https://avatars2.githubusercontent.com/u/2037007?v=4?s=100" width="100px;" alt=""/><br /><sub><b>James Monger</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=Jameskmonger" title="Documentation">📖</a></td>
<td align="center"><a href="http://wesssel.github.io/"><img src="https://avatars2.githubusercontent.com/u/7767299?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wessel van der Pal</b></sub></a><br /><a href="#security-wesssel" title="Security">🛡️</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=wesssel" title="Code">💻</a></td>
<td align="center"><a href="https://jung-kim.github.io"><img src="https://avatars2.githubusercontent.com/u/5281068?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jk-kim</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=jung-kim" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/sss0791"><img src="https://avatars1.githubusercontent.com/u/1446970?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sergey Semenov</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/issues?q=author%3Asss0791" title="Bug reports">🐛</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=sss0791" title="Code">💻</a></td>
<td align="center"><a href="http://researcher.watson.ibm.com/researcher/view.php?person=us-nickm"><img src="https://avatars3.githubusercontent.com/u/4741620?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nick Mitchell</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/issues?q=author%3Astarpit" title="Bug reports">🐛</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=starpit" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/samiraguiar"><img src="https://avatars0.githubusercontent.com/u/13439135?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Samir Aguiar</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=samiraguiar" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://twitter.com/pubkeypubkey"><img src="https://avatars3.githubusercontent.com/u/8926560?v=4?s=100" width="100px;" alt=""/><br /><sub><b>pubkey</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=pubkey" title="Documentation">📖</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=pubkey" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/iliyaZelenko"><img src="https://avatars1.githubusercontent.com/u/13103045?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Илья</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=iliyaZelenko" title="Documentation">📖</a></td>
<td align="center"><a href="https://akr.am"><img src="https://avatars0.githubusercontent.com/u/1823771?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mohamed Akram</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/issues?q=author%3Amohd-akram" title="Bug reports">🐛</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=mohd-akram" title="Documentation">📖</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=mohd-akram" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/emarcotte"><img src="https://avatars0.githubusercontent.com/u/249390?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Eugene Marcotte</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=emarcotte" title="Code">💻</a></td>
<td align="center"><a href="http://twitter.com/dimasabanin"><img src="https://avatars0.githubusercontent.com/u/8316?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dima Sabanin</b></sub></a><br /><a href="#maintenance-dsabanin" title="Maintenance">🚧</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=dsabanin" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/benabbottnz"><img src="https://avatars2.githubusercontent.com/u/2616473?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ben Abbott</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=benabbottnz" title="Documentation">📖</a></td>
<td align="center"><a href="http://webminer.js.org"><img src="https://avatars1.githubusercontent.com/u/2196373?v=4?s=100" width="100px;" alt=""/><br /><sub><b>弘树@阿里</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/issues?q=author%3Adickeylth" title="Bug reports">🐛</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=dickeylth" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Rantanen"><img src="https://avatars0.githubusercontent.com/u/385385?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mikko Rantanen</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/issues?q=author%3ARantanen" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/extend1994"><img src="https://avatars2.githubusercontent.com/u/13430892?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ann</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=extend1994" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/escitalopram"><img src="https://avatars0.githubusercontent.com/u/1155220?v=4?s=100" width="100px;" alt=""/><br /><sub><b>escitalopram</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=escitalopram" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/apps/dependabot"><img src="https://avatars0.githubusercontent.com/in/29110?v=4?s=100" width="100px;" alt=""/><br /><sub><b>dependabot[bot]</b></sub></a><br /><a href="#security-dependabot[bot]" title="Security">🛡️</a> <a href="#maintenance-dependabot[bot]" title="Maintenance">🚧</a></td>
<td align="center"><a href="http://www.joshuakgoldberg.com"><img src="https://avatars1.githubusercontent.com/u/3335181?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Josh Goldberg</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=JoshuaKGoldberg" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/apeckham"><img src="https://avatars.githubusercontent.com/u/14110?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aaron</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=apeckham" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/pgrimaud"><img src="https://avatars.githubusercontent.com/u/1866496?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pierre Grimaud</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=pgrimaud" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://domdomegg.github.io/"><img src="https://avatars.githubusercontent.com/u/4953590?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Adam Jones</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=domdomegg" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/charguer"><img src="https://avatars.githubusercontent.com/u/1830652?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Arthur Charguéraud</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=charguer" title="Documentation">📖</a></td>
<td align="center"><a href="https://twitter.com/pierrci"><img src="https://avatars.githubusercontent.com/u/5020707?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pierric Cistac</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=Pierrci" title="Documentation">📖</a> <a href="https://github.com/rtfpessoa/diff2html/commits?author=Pierrci" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/xlith"><img src="https://avatars.githubusercontent.com/u/510560?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Civan Yavuzşen</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=xlith" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/timgates42"><img src="https://avatars.githubusercontent.com/u/47873678?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tim Gates</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=timgates42" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/campersau"><img src="https://avatars.githubusercontent.com/u/4009570?v=4?s=100" width="100px;" alt=""/><br /><sub><b>campersau</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=campersau" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/apps/dependabot-preview"><img src="https://avatars.githubusercontent.com/in/2141?v=4?s=100" width="100px;" alt=""/><br /><sub><b>dependabot-preview[bot]</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=dependabot-preview[bot]" title="Code">💻</a></td>
</tr>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification.
Contributions of any kind welcome!
## License
Copyright 2014 Rodrigo Fernandes. Released under the terms of the MIT license.
Copyright 2014-present Rodrigo Fernandes. Released under the terms of the MIT license.
## Thanks
This project is inspired in [pretty-diff](https://github.com/scottgonzalez/pretty-diff) by [Scott González](https://github.com/scottgonzalez).
This project is inspired in [pretty-diff](https://github.com/scottgonzalez/pretty-diff) by
[Scott González](https://github.com/scottgonzalez).
---

35
SECURITY.md Normal file
View file

@ -0,0 +1,35 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 2.9.x | :white_check_mark: |
| < 2.9 | :x: |
## Reporting a Vulnerability
We take all security bugs in `diff2html` seriously. Thank you for the help improving the security of `diff2html`. We
appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions.
Report security bugs by emailing the lead maintainer at `rtfrodrigo [at] gmail [dot] com`.
The lead maintainer will acknowledge your email within 48 hours, and will send a more detailed response within 48 hours
indicating the next steps in handling your report. After the initial reply to your report, the security team will
endeavor to keep you informed of the progress towards a fix and full announcement, and may ask for additional
information or guidance.
Report security bugs in third-party modules to the person or team maintaining the module.
## Disclosure Policy
When the security team receives a security bug report, they will assign it to a primary handler. This person will
coordinate the fix and release process, involving the following steps:
- Confirm the problem and determine the affected versions.
- Audit code to find any potential similar problems.
- Prepare fixes for all releases still under maintenance. These fixes will be released as fast as possible.
## Comments on this Policy
If you have suggestions on how this process could be improved please submit a pull request.

View file

@ -1,44 +0,0 @@
{
"name": "diff2html",
"version": "1.1.0",
"homepage": "http://rtfpessoa.github.io/diff2html/",
"description": "Fast Diff to colorized HTML",
"keywords": [
"git",
"diff",
"pretty",
"side",
"line",
"side-by-side",
"line-by-line",
"character",
"highlight",
"pretty",
"color",
"html",
"diff2html",
"difftohtml",
"colorized"
],
"authors": [
"Rodrigo Fernandes <rtfrodrigo@gmail.com>"
],
"repository": {
"type": "git",
"url": "git://github.com/rtfpessoa/diff2html.git"
},
"main": "./src/diff2html.js",
"license": "MIT",
"moduleType": [
"globals",
"node"
],
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"package.json",
"release.sh",
"config.jscs.json"
]
}

View file

@ -1,49 +0,0 @@
{
"disallowKeywords": [
"with"
],
"disallowKeywordsOnNewLine": [
"else"
],
"disallowMixedSpacesAndTabs": true,
"disallowMultipleVarDecl": "exceptUndefined",
"disallowNewlineBeforeBlockStatements": true,
"disallowQuotedKeysInObjects": true,
"disallowSpaceAfterObjectKeys": true,
"disallowSpaceAfterPrefixUnaryOperators": true,
"disallowSpacesInFunction": {
"beforeOpeningRoundBrace": true
},
"disallowSpacesInsideParentheses": true,
"disallowTrailingWhitespace": true,
"maximumLineLength": 120,
"requireCamelCaseOrUpperCaseIdentifiers": true,
"requireCapitalizedComments": true,
"requireCapitalizedConstructors": true,
"requireCurlyBraces": true,
"requireSpaceAfterKeywords": [
"if",
"else",
"for",
"while",
"do",
"switch",
"case",
"return",
"try",
"catch",
"typeof"
],
"requireSpaceAfterLineComment": true,
"requireSpaceAfterBinaryOperators": true,
"requireSpaceBeforeBinaryOperators": true,
"requireSpaceBeforeBlockStatements": true,
"requireSpaceBeforeObjectValues": true,
"requireSpacesInFunction": {
"beforeOpeningCurlyBrace": true
},
"requireTrailingComma": null,
"validateIndentation": 2,
"validateLineBreaks": "LF",
"validateQuoteMarks": "'"
}

View file

@ -1,267 +0,0 @@
/*
*
* Diff to HTML (diff2html.css)
* Author: rtfpessoa
*
*/
.d2h-wrapper {
display: block;
margin: 0 auto;
text-align: left;
width: 100%;
}
.d2h-file-wrapper {
border: 1px solid #ddd;
border-radius: 3px;
margin-bottom: 1em;
}
.d2h-file-header {
padding: 5px 10px;
border-bottom: 1px solid #d8d8d8;
background-color: #f7f7f7;
font: 13px Helvetica, arial, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol";
}
.d2h-file-stats {
display: inline;
font-size: 12px;
text-align: center;
max-width: 15%;
}
.d2h-lines-added {
background-color: #ceffce;
border: 1px solid #b4e2b4;
color: #399839;
border-radius: 5px 0 0 5px;
padding: 2px;
width: 25px;
}
.d2h-lines-deleted {
background-color: #f7c8c8;
border: 1px solid #e9aeae;
color: #c33;
border-radius: 0 5px 5px 0;
padding: 2px;
width: 25px;
}
.d2h-file-name {
display: inline;
height: 33px;
line-height: 33px;
max-width: 80%;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.d2h-diff-table {
border-collapse: collapse;
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 12px;
height: 18px;
line-height: 18px;
width: 100%;
}
.d2h-files-diff {
width: 100%;
}
.d2h-file-diff {
overflow-x: scroll;
overflow-y: hidden;
}
.d2h-file-side-diff {
display: inline-block;
overflow-x: scroll;
overflow-y: hidden;
width: 50%;
margin-right: -4px;
}
.d2h-code-line {
display: block;
white-space: pre;
padding: 0 10px;
height: 18px;
line-height: 18px;
margin-left: 80px;
/* Override HighlightJS */
color: inherit;
overflow-x: inherit;
background: none;
/* ******************** */
}
.d2h-code-side-line {
display: block;
white-space: pre;
padding: 0 10px;
height: 18px;
line-height: 18px;
margin-left: 50px;
/* Override HighlightJS */
color: inherit;
overflow-x: inherit;
background: none;
/* ******************** */
}
.d2h-code-line del,
.d2h-code-side-line del {
display: inline-block;
margin-top: -1px;
text-decoration: none;
background-color: #ffb6ba;
border-radius: 0.2em;
}
.d2h-code-line ins,
.d2h-code-side-line ins {
display: inline-block;
margin-top: -1px;
text-decoration: none;
background-color: #97f295;
border-radius: 0.2em;
}
.d2h-code-line-prefix {
float: left;
background: none;
padding: 0;
}
.d2h-code-line-ctn {
background: none;
padding: 0;
}
.line-num1 {
display: inline-block;
float: left;
width: 30px;
overflow: hidden;
text-overflow: ellipsis;
}
.line-num2 {
display: inline-block;
float: right;
width: 30px;
overflow: hidden;
text-overflow: ellipsis;
}
.d2h-code-linenumber {
position: absolute;
width: 2%;
min-width: 65px;
padding-left: 10px;
padding-right: 10px;
height: 18px;
line-height: 18px;
background-color: #fff;
color: rgba(0, 0, 0, 0.3);
text-align: right;
border: solid #eeeeee;
border-width: 0 1px 0 1px;
cursor: pointer;
}
.d2h-code-side-linenumber {
position: absolute;
width: 35px;
padding-left: 10px;
padding-right: 10px;
height: 18px;
line-height: 18px;
background-color: #fff;
color: rgba(0, 0, 0, 0.3);
text-align: right;
border: solid #eeeeee;
border-width: 0 1px 0 1px;
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
}
.d2h-del {
background-color: #fee8e9;
border-color: #e9aeae;
}
.d2h-ins {
background-color: #dfd;
border-color: #b4e2b4;
}
.d2h-info {
background-color: #f8fafd;
color: rgba(0, 0, 0, 0.3);
border-color: #d5e4f2;
}
.d2h-file-list-wrapper {
margin-bottom: 10px;
padding: 0 10px;
}
.d2h-file-list-wrapper a {
text-decoration: none;
color: #3572b0;
}
.d2h-file-list-wrapper a:visited {
color: #3572b0;
}
.d2h-file-list-header {
font-weight: bold;
margin-bottom: 5px;
text-align: left;
display: inline;
float:left;
}
.d2h-file-list-line {
text-align: left;
font: 13px Helvetica, arial, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol";
}
.d2h-file-list-line .d2h-file-name {
line-height: 21px;
}
.d2h-file-list {
display:none;
}
.d2h-clear {
display:block;
clear: both;
}
/* CSS only show/hide */
.d2h-show {
display: none;
float:left;
}
.d2h-hide {
float:left;
}
.d2h-hide:target + .d2h-show {
display: inline;
}
.d2h-hide:target {
display: none;
}
.d2h-hide:target ~ .d2h-file-list {
display:block;
}

267
dist/diff2html.css vendored
View file

@ -1,267 +0,0 @@
/*
*
* Diff to HTML (diff2html.css)
* Author: rtfpessoa
*
*/
.d2h-wrapper {
display: block;
margin: 0 auto;
text-align: left;
width: 100%;
}
.d2h-file-wrapper {
border: 1px solid #ddd;
border-radius: 3px;
margin-bottom: 1em;
}
.d2h-file-header {
padding: 5px 10px;
border-bottom: 1px solid #d8d8d8;
background-color: #f7f7f7;
font: 13px Helvetica, arial, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol";
}
.d2h-file-stats {
display: inline;
font-size: 12px;
text-align: center;
max-width: 15%;
}
.d2h-lines-added {
background-color: #ceffce;
border: 1px solid #b4e2b4;
color: #399839;
border-radius: 5px 0 0 5px;
padding: 2px;
width: 25px;
}
.d2h-lines-deleted {
background-color: #f7c8c8;
border: 1px solid #e9aeae;
color: #c33;
border-radius: 0 5px 5px 0;
padding: 2px;
width: 25px;
}
.d2h-file-name {
display: inline;
height: 33px;
line-height: 33px;
max-width: 80%;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.d2h-diff-table {
border-collapse: collapse;
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 12px;
height: 18px;
line-height: 18px;
width: 100%;
}
.d2h-files-diff {
width: 100%;
}
.d2h-file-diff {
overflow-x: scroll;
overflow-y: hidden;
}
.d2h-file-side-diff {
display: inline-block;
overflow-x: scroll;
overflow-y: hidden;
width: 50%;
margin-right: -4px;
}
.d2h-code-line {
display: block;
white-space: pre;
padding: 0 10px;
height: 18px;
line-height: 18px;
margin-left: 80px;
/* Override HighlightJS */
color: inherit;
overflow-x: inherit;
background: none;
/* ******************** */
}
.d2h-code-side-line {
display: block;
white-space: pre;
padding: 0 10px;
height: 18px;
line-height: 18px;
margin-left: 50px;
/* Override HighlightJS */
color: inherit;
overflow-x: inherit;
background: none;
/* ******************** */
}
.d2h-code-line del,
.d2h-code-side-line del {
display: inline-block;
margin-top: -1px;
text-decoration: none;
background-color: #ffb6ba;
border-radius: 0.2em;
}
.d2h-code-line ins,
.d2h-code-side-line ins {
display: inline-block;
margin-top: -1px;
text-decoration: none;
background-color: #97f295;
border-radius: 0.2em;
}
.d2h-code-line-prefix {
float: left;
background: none;
padding: 0;
}
.d2h-code-line-ctn {
background: none;
padding: 0;
}
.line-num1 {
display: inline-block;
float: left;
width: 30px;
overflow: hidden;
text-overflow: ellipsis;
}
.line-num2 {
display: inline-block;
float: right;
width: 30px;
overflow: hidden;
text-overflow: ellipsis;
}
.d2h-code-linenumber {
position: absolute;
width: 2%;
min-width: 65px;
padding-left: 10px;
padding-right: 10px;
height: 18px;
line-height: 18px;
background-color: #fff;
color: rgba(0, 0, 0, 0.3);
text-align: right;
border: solid #eeeeee;
border-width: 0 1px 0 1px;
cursor: pointer;
}
.d2h-code-side-linenumber {
position: absolute;
width: 35px;
padding-left: 10px;
padding-right: 10px;
height: 18px;
line-height: 18px;
background-color: #fff;
color: rgba(0, 0, 0, 0.3);
text-align: right;
border: solid #eeeeee;
border-width: 0 1px 0 1px;
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
}
.d2h-del {
background-color: #fee8e9;
border-color: #e9aeae;
}
.d2h-ins {
background-color: #dfd;
border-color: #b4e2b4;
}
.d2h-info {
background-color: #f8fafd;
color: rgba(0, 0, 0, 0.3);
border-color: #d5e4f2;
}
.d2h-file-list-wrapper {
margin-bottom: 10px;
padding: 0 10px;
}
.d2h-file-list-wrapper a {
text-decoration: none;
color: #3572b0;
}
.d2h-file-list-wrapper a:visited {
color: #3572b0;
}
.d2h-file-list-header {
font-weight: bold;
margin-bottom: 5px;
text-align: left;
display: inline;
float:left;
}
.d2h-file-list-line {
text-align: left;
font: 13px Helvetica, arial, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol";
}
.d2h-file-list-line .d2h-file-name {
line-height: 21px;
}
.d2h-file-list {
display:none;
}
.d2h-clear {
display:block;
clear: both;
}
/* CSS only show/hide */
.d2h-show {
display: none;
float:left;
}
.d2h-hide {
float:left;
}
.d2h-hide:target + .d2h-show {
display: inline;
}
.d2h-hide:target {
display: none;
}
.d2h-hide:target ~ .d2h-file-list {
display:block;
}

1642
dist/diff2html.js vendored

File diff suppressed because it is too large Load diff

View file

@ -1 +0,0 @@
.d2h-wrapper{display:block;margin:0 auto;text-align:left;width:100%}.d2h-file-wrapper{border:1px solid #ddd;border-radius:3px;margin-bottom:1em}.d2h-file-header{padding:5px 10px;border-bottom:1px solid #d8d8d8;background-color:#f7f7f7;font:13px Helvetica,arial,freesans,clean,sans-serif,"Segoe UI Emoji","Segoe UI Symbol"}.d2h-file-stats{display:inline;font-size:12px;text-align:center;max-width:15%}.d2h-lines-added{background-color:#ceffce;border:1px solid #b4e2b4;color:#399839;border-radius:5px 0 0 5px;padding:2px;width:25px}.d2h-lines-deleted{background-color:#f7c8c8;border:1px solid #e9aeae;color:#c33;border-radius:0 5px 5px 0;padding:2px;width:25px}.d2h-file-name{display:inline;height:33px;line-height:33px;max-width:80%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.d2h-diff-table{border-collapse:collapse;font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace;font-size:12px;height:18px;line-height:18px;width:100%}.d2h-files-diff{width:100%}.d2h-file-diff{overflow-x:scroll;overflow-y:hidden}.d2h-file-side-diff{display:inline-block;overflow-x:scroll;overflow-y:hidden;width:50%;margin-right:-4px}.d2h-code-line{display:block;white-space:pre;padding:0 10px;height:18px;line-height:18px;margin-left:80px;color:inherit;overflow-x:inherit;background:none}.d2h-code-side-line{display:block;white-space:pre;padding:0 10px;height:18px;line-height:18px;margin-left:50px;color:inherit;overflow-x:inherit;background:none}.d2h-code-line del,.d2h-code-side-line del{display:inline-block;margin-top:-1px;text-decoration:none;background-color:#ffb6ba;border-radius:.2em}.d2h-code-line ins,.d2h-code-side-line ins{display:inline-block;margin-top:-1px;text-decoration:none;background-color:#97f295;border-radius:.2em}.d2h-code-line-prefix{float:left;background:none;padding:0}.d2h-code-line-ctn{background:none;padding:0}.line-num1{display:inline-block;float:left;width:30px;overflow:hidden;text-overflow:ellipsis}.line-num2{display:inline-block;float:right;width:30px;overflow:hidden;text-overflow:ellipsis}.d2h-code-linenumber{position:absolute;width:2%;min-width:65px;padding-left:10px;padding-right:10px;height:18px;line-height:18px;background-color:#fff;color:rgba(0,0,0,0.3);text-align:right;border:solid #eeeeee;border-width:0 1px 0 1px;cursor:pointer}.d2h-code-side-linenumber{position:absolute;width:35px;padding-left:10px;padding-right:10px;height:18px;line-height:18px;background-color:#fff;color:rgba(0,0,0,0.3);text-align:right;border:solid #eeeeee;border-width:0 1px 0 1px;cursor:pointer;overflow:hidden;text-overflow:ellipsis}.d2h-del{background-color:#fee8e9;border-color:#e9aeae}.d2h-ins{background-color:#dfd;border-color:#b4e2b4}.d2h-info{background-color:#f8fafd;color:rgba(0,0,0,0.3);border-color:#d5e4f2}.d2h-file-list-wrapper{margin-bottom:10px;padding:0 10px}.d2h-file-list-wrapper a{text-decoration:none;color:#3572b0}.d2h-file-list-wrapper a:visited{color:#3572b0}.d2h-file-list-header{font-weight:bold;margin-bottom:5px;text-align:left;display:inline;float:left}.d2h-file-list-line{text-align:left;font:13px Helvetica,arial,freesans,clean,sans-serif,"Segoe UI Emoji","Segoe UI Symbol"}.d2h-file-list-line .d2h-file-name{line-height:21px}.d2h-file-list{display:none}.d2h-clear{display:block;clear:both}.d2h-show{display:none;float:left}.d2h-hide{float:left}.d2h-hide:target+.d2h-show{display:inline}.d2h-hide:target{display:none}.d2h-hide:target~.d2h-file-list{display:block}

File diff suppressed because one or more lines are too long

77
eslint.config.mjs Normal file
View file

@ -0,0 +1,77 @@
import globals from 'globals';
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import pluginJest from 'eslint-plugin-jest';
import json from '@eslint/json';
import pluginPromise from 'eslint-plugin-promise';
export default [
{ ...eslint.configs.recommended, files: ['src/**/*.{js,mjs,cjs,ts}'] },
...tseslint.configs.recommended,
// ...tseslint.configs.recommendedTypeChecked,
// ...tseslint.configs.strict,
// ...tseslint.configs.stylistic,
// ...tseslint.configs.strictTypeChecked,
// ...tseslint.configs.stylisticTypeChecked,
{
rules: {
'@typescript-eslint/no-unused-vars': [
'error',
{
args: 'all',
argsIgnorePattern: '^_',
caughtErrors: 'all',
caughtErrorsIgnorePattern: '^_',
destructuredArrayIgnorePattern: '^_',
varsIgnorePattern: '^_',
ignoreRestSiblings: true,
},
],
},
},
pluginPromise.configs['flat/recommended'],
{
plugins: {
json,
},
files: ['**/*.json'],
language: 'json/json',
rules: {
'json/no-duplicate-keys': 'error',
},
},
{
...pluginJest.configs['flat/recommended'],
...pluginJest.configs['flat/style'],
files: ['src/__tests__/**/*tests.ts'],
plugins: { jest: pluginJest },
languageOptions: {
globals: pluginJest.environments.globals.globals,
},
},
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
...globals.es2025,
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
document: 'readonly',
navigator: 'readonly',
window: 'readonly',
},
parserOptions: {
project: './tsconfig.eslint.json',
tsconfigRootDir: './',
},
},
},
{
ignores: ['src/diff2html-templates.*', 'coverage/', 'docs/', 'bundles-out/', 'bundles/', 'lib/', 'lib-esm/'],
},
{
...tseslint.configs.disableTypeChecked,
files: ['**/*.{js,mjs,cjs}'],
},
];

23
jest.config.js Normal file
View file

@ -0,0 +1,23 @@
module.exports = {
verbose: true,
preset: 'ts-jest',
testEnvironment: 'node',
coverageDirectory: './coverage',
coverageReporters: ['lcov', 'text', 'html', 'json', 'cobertura', 'clover'],
collectCoverageFrom: [
'src/**/*.ts',
'!src/ui/**',
'!src/diff2html-templates.ts',
'!src/__tests__/**',
'!node_modules/**',
],
coverageThreshold: {
global: {
statements: 93,
branches: 86,
functions: 98,
lines: 93,
},
},
prettierPath: require.resolve('prettier-2'),
};

18121
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
{
"name": "diff2html",
"version": "1.1.0",
"homepage": "http://rtfpessoa.github.io/diff2html/",
"version": "3.0.0-beta.1",
"homepage": "https://diff2html.xyz",
"description": "Fast Diff to colorized HTML",
"keywords": [
"git",
@ -26,25 +26,135 @@
},
"repository": {
"type": "git",
"url": "https://www.github.com/rtfpessoa/diff2html.git"
"url": "git://github.com/rtfpessoa/diff2html.git"
},
"bugs": {
"url": "https://www.github.com/rtfpessoa/diff2html/issues"
},
"engines": {
"node": ">=0.10"
"node": ">=12"
},
"preferGlobal": "true",
"scripts": {
"test": ""
"lint:staged": "lint-staged",
"lint:check": "eslint",
"lint:fix": "eslint --fix",
"prettier": "prettier --ignore-path .gitignore '**/*.+(js|jsx|ts|tsx|json|css|html|md|mdx)'",
"format:check": "npm run prettier --check",
"format:fix": "npm run prettier --write",
"build": "npm run build:css && npm run build:templates && npm run build:commonjs && npm run build:esm && npm run build:bundles && npm run build:website",
"build:commonjs": "rm -rf lib; tsc -p tsconfig.json -m CommonJS --outDir lib",
"build:esm": "rm -rf lib-esm; tsc -p tsconfig.json -m ESNext --outDir lib-esm",
"build:bundles": "rm -rf ./bundles/js; webpack --mode production --config webpack.bundles.ts",
"build:css": "rm -rf ./bundles/css; postcss --config ./postcss.config.js --no-map -o ./bundles/css/diff2html.min.css ./src/ui/css/diff2html.css",
"build:templates": "ts-node ./scripts/hulk.ts --wrapper ts --variable 'defaultTemplates' ./src/templates/*.mustache > ./src/diff2html-templates.ts",
"build:website": "rm -rf docs; webpack --mode production --config webpack.website.ts",
"gen": "npm run gen:toc",
"gen:toc-base": "markdown-toc --maxdepth 3 --bullets='-' -i",
"gen:toc": "npm run gen:toc-base README.md",
"test": "is-ci 'test:coverage' 'test:watch'",
"test:coverage": "jest --coverage",
"test:watch": "jest --watch",
"test:debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand --watch",
"coverage:open": "npm run test:coverage && open ./coverage/index.html",
"coverage:push": "curl -Ls https://coverage.codacy.com/get.sh | bash",
"validate": "npm run build:templates && npm run format:check && npm run lint:check && npm run build && npm run test:coverage",
"fix": "npm run format:fix && npm run lint:fix",
"start": "npm run start:website",
"start:website": "webpack serve --mode development --config webpack.website.ts",
"preversion": "npm run validate",
"version": "git add -A package.json",
"prepare": "husky"
},
"main": "./lib/diff2html.js",
"module": "./lib-esm/diff2html.js",
"types": "./lib/diff2html.d.ts",
"lint-staged": {
"**/*.+(js|jsx|ts|tsx|json)": [
"prettier --write",
"eslint --fix"
],
"**/*.+(css|html|md|mdx)": [
"prettier --write"
],
"README.md": [
"npm run gen:toc-base"
]
},
"main": "./src/diff2html.js",
"dependencies": {
"diff": "1.4.0"
"diff": "^7.0.0",
"hogan.js": "3.0.2"
},
"optionalDependencies": {
"highlight.js": "11.9.0"
},
"devDependencies": {
"prettier-2": "npm:prettier@^2",
"@eslint/js": "^9.17.0",
"@eslint/json": "^0.9.0",
"@types/diff": "^6.0.0",
"@types/hogan.js": "3.0.5",
"@types/jest": "^29.5.14",
"@types/node": "^22.10.2",
"@types/nopt": "3.0.32",
"all-contributors-cli": "^6.24.0",
"autoprefixer": "^10.4.20",
"bulma": "^1.0.2",
"clipboard": "2.0.11",
"copy-webpack-plugin": "^12.0.2",
"css-loader": "^7.1.2",
"cssnano": "^7.0.6",
"eslint": "^9.17.0",
"eslint-plugin-jest": "28.10.0",
"eslint-plugin-promise": "^7.2.1",
"file-loader": "6.2.0",
"globals": "^15.14.0",
"handlebars": "4.7.8",
"handlebars-loader": "1.7.3",
"html-webpack-plugin": "^5.6.3",
"husky": "^9.1.7",
"image-webpack-loader": "8.1.0",
"is-ci-cli": "2.2.0",
"jest": "29.7.0",
"lint-staged": "^15.2.11",
"markdown-toc": "^1.2.0",
"mini-css-extract-plugin": "^2.9.2",
"mkdirp": "3.0.1",
"nopt": "^8.0.0",
"postcss": "^8.4.49",
"postcss-cli": "11.0.0",
"postcss-import": "^16.1.0",
"postcss-loader": "^8.1.1",
"postcss-preset-env": "^10.1.2",
"prettier": "^3.4.2",
"ts-jest": "^29.2.5",
"ts-loader": "9.5.1",
"ts-node": "10.9.2",
"typescript": "^5.7.2",
"typescript-eslint": "^8.18.2",
"url-loader": "4.1.1",
"webpack": "^5.97.1",
"webpack-cli": "^6.0.1",
"webpack-dev-server": "^5.2.0",
"whatwg-fetch": "3.6.20"
},
"resolutions": {
"lodash": ">=4.17.20",
"minimist": ">=1.2.5",
"acorn": ">=7.4.0",
"autolinker": ">=3.14.1",
"bl": ">=2.2.1",
"decompress": ">=4.2.1",
"node-forge": ">=0.10.0",
"trim-newlines": ">=3.0.1",
"async": ">=2.6.4",
"terser": ">=5.14.2",
"semver-regex": ">=4.0.5",
"http-cache-semantics": ">=4.1.1"
},
"devDependencies": {},
"license": "MIT",
"files": [
"src"
"bundles",
"lib",
"lib-esm"
]
}

10
postcss.config.js Normal file
View file

@ -0,0 +1,10 @@
module.exports = {
sourceMap: false,
plugins: {
'postcss-import': {},
'postcss-preset-env': {
browsers: 'last 2 versions',
},
cssnano: {},
},
};

View file

@ -1,40 +0,0 @@
#!/bin/bash
#
# diff2html release script
# by rtfpessoa
#
OUTPUT_DIR=dist
OUTPUT_JS_FILE=${OUTPUT_DIR}/diff2html.js
OUTPUT_MIN_JS_FILE=${OUTPUT_DIR}/diff2html.min.js
OUTPUT_CSS_FILE=${OUTPUT_DIR}/diff2html.css
OUTPUT_MIN_CSS_FILE=${OUTPUT_DIR}/diff2html.min.css
echo "Creating diff2html release ..."
echo "Cleaning previous versions ..."
rm -rf ${OUTPUT_DIR}
mkdir -p ${OUTPUT_DIR}
echo "Preparing dependencies ..."
rm -rf node_modules
npm install
echo "Generating js aggregation file in ${OUTPUT_JS_FILE}"
webpack ./src/diff2html.js ${OUTPUT_JS_FILE}
echo "Minifying ${OUTPUT_JS_FILE} to ${OUTPUT_MIN_JS_FILE}"
uglifyjs ${OUTPUT_JS_FILE} -c -o ${OUTPUT_MIN_JS_FILE}
echo "Copying css file to ${OUTPUT_CSS_FILE}"
cp -f css/diff2html.css ${OUTPUT_CSS_FILE}
echo "Minifying ${OUTPUT_CSS_FILE} to ${OUTPUT_MIN_CSS_FILE}"
lessc -x ${OUTPUT_CSS_FILE} ${OUTPUT_MIN_CSS_FILE}
echo "diff2html release created successfully!"

View file

@ -1,273 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Diff to HTML by rtfpessoa</title>
<!--
Diff to HTML (index.html)
Author: rtfpessoa
-->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.6/styles/github.min.css">
<!-- -->
<link rel="stylesheet" type="text/css" href="../dist/diff2html.min.css">
<script type="text/javascript" src="../dist/diff2html.min.js"></script>
<!-- -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.6/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.6/languages/scala.min.js"></script>
<script>
var lineDiffExample =
'diff --git a/coverage.init b/coverage.init\n' +
'index fc56817..e8e7e49 100644\n' +
'--- a/coverage.init\n' +
'+++ b/coverage.init\n' +
'@@ -19,7 +19,7 @@\n' +
' -opt "\-nostart"\n' +
' \n' +
' # skip stopenv\n' +
'--do "runbvt,stopenv,getlogs,pullcoveragedata"\n' +
'+-do "runbvt,getlogs,pullcoveragedata"\n' +
' \n' +
' ##########################################\n' +
' # logs files to bring back to base\n' +
'diff --git a/src/attributes/attr.js b/src/attributes/attr.js\n' +
'index facdd41..b627fe8 100644\n' +
'--- a/src/attributes/attr.js\n' +
'+++ b/src/attributes/attr.js\n' +
'@@ -1,11 +1,10 @@\n' +
' define([\n' +
' "../core",\n' +
' "../var/rnotwhite",\n' +
'- "../var/strundefined",\n' +
' "../core/access",\n' +
' "./support",\n' +
' "../selector"\n' +
'-], function( jQuery, rnotwhite, strundefined, access, support ) {\n' +
'+], function( jQuery, rnotwhite, access, support ) {\n' +
' \n' +
' var nodeHook, boolHook,\n' +
' attrHandle = jQuery.expr.attrHandle;\n' +
'@@ -33,7 +32,7 @@ jQuery.extend({\n' +
' }\n' +
' \n' +
' // Fallback to prop when attributes are not supported\n' +
'- if ( typeof elem.getAttribute === strundefined ) {\n' +
'+ if ( !elem.getAttribute ) {\n' +
' return jQuery.prop( elem, name, value );\n' +
' }\n' +
' \n' +
'diff --git a/src/attributes/classes.js b/src/attributes/classes.js\n' +
'index c617824..c8d1393 100644\n' +
'--- a/src/attributes/classes.js\n' +
'+++ b/src/attributes/classes.js\n' +
'@@ -1,10 +1,9 @@\n' +
' define([\n' +
' "../core",\n' +
' "../var/rnotwhite",\n' +
'- "../var/strundefined",\n' +
' "../data/var/dataPriv",\n' +
' "../core/init"\n' +
'-], function( jQuery, rnotwhite, strundefined, dataPriv ) {\n' +
'+], function( jQuery, rnotwhite, dataPriv ) {\n' +
' \n' +
' var rclass = /[\\t\\r\\n\\f]/g;\n' +
' \n' +
'@@ -128,7 +127,7 @@ jQuery.fn.extend({\n' +
' }\n' +
' \n' +
' // Toggle whole class name\n' +
'- } else if ( type === strundefined || type === "boolean" ) {\n' +
'+ } else if ( value === undefined || type === "boolean" ) {\n' +
' if ( this.className ) {\n' +
' // store className if set\n' +
' dataPriv.set( this, "__className__", this.className );\n' +
'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' +
'@@ -10,7 +9,7 @@ define([\n' +
' "./core/init",\n' +
' "./data/accepts",\n' +
' "./selector"\n' +
'-], function( jQuery, strundefined, rnotwhite, hasOwn, slice, support, dataPriv ) {\n' +
'+], function( jQuery, rnotwhite, hasOwn, slice, support, dataPriv ) {\n' +
' \n' +
' var\n' +
' rkeyEvent = /^key/,\n' +
'@@ -72,7 +71,7 @@ jQuery.event = {\n' +
' eventHandle = elemData.handle = function( e ) {\n' +
' // Discard the second event of a jQuery.event.trigger() and\n' +
' // when an event is called after a page has unloaded\n' +
'- return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ?\n' +
'+ return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?\n' +
' jQuery.event.dispatch.apply( elem, arguments ) : undefined;\n' +
' };\n' +
' }\n' +
'diff --git a/src/exports/global.js b/src/exports/global.js\n' +
'index 6513287..1db4144 100644\n' +
'--- a/src/exports/global.js\n' +
'+++ b/src/exports/global.js\n' +
'@@ -1,7 +1,9 @@\n' +
' define([\n' +
'- "../core",\n' +
'- "../var/strundefined"\n' +
'-], function( jQuery, strundefined ) {\n' +
'+ "../core"\n' +
'+], function( jQuery ) {\n' +
'+\n' +
'+/* exported noGlobal */\n' +
'+/* global noGlobal: false */\n' +
' \n' +
' var\n' +
' // Map over jQuery in case of overwrite\n' +
'@@ -25,7 +27,7 @@ jQuery.noConflict = function( deep ) {\n' +
' // Expose jQuery and $ identifiers, even in AMD\n' +
' // (#7102#comment:10, https://github.com/jquery/jquery/pull/557)\n' +
' // and CommonJS for browser emulators (#13566)\n' +
'-if ( typeof noGlobal === strundefined ) {\n' +
'+if ( !noGlobal ) {\n' +
' window.jQuery = window.$ = jQuery;\n' +
' }\n' +
' \n' +
'diff --git a/src/offset.js b/src/offset.js\n' +
'index cc6ffb4..fa51f18 100644\n' +
'--- a/src/offset.js\n' +
'+++ b/src/offset.js\n' +
'@@ -1,6 +1,5 @@\n' +
' define([\n' +
' "./core",\n' +
'- "./var/strundefined",\n' +
' "./core/access",\n' +
' "./css/var/rnumnonpx",\n' +
' "./css/curCSS",\n' +
'@@ -10,7 +9,7 @@ define([\n' +
' "./core/init",\n' +
' "./css",\n' +
' "./selector" // contains\n' +
'-], function( jQuery, strundefined, access, rnumnonpx, curCSS, addGetHookIf, support ) {\n' +
'+], function( jQuery, access, rnumnonpx, curCSS, addGetHookIf, support ) {\n' +
' \n' +
' var docElem = window.document.documentElement;\n' +
' \n' +
'@@ -99,7 +98,7 @@ jQuery.fn.extend({\n' +
' \n' +
' // Support: BlackBerry 5, iOS 3 (original iPhone)\n' +
' // If we dont have gBCR, just use 0,0 rather than error\n' +
'- if ( typeof elem.getBoundingClientRect !== strundefined ) {\n' +
'+ if ( elem.getBoundingClientRect !== undefined ) {\n' +
' box = elem.getBoundingClientRect();\n' +
' }\n' +
' win = getWindow( doc );\n' +
'diff --git a/src/var/strundefined.js b/src/var/strundefined.js\n' +
'deleted file mode 100644\n' +
'index 04e16b0..0000000\n' +
'--- a/src/var/strundefined.js\n' +
'+++ /dev/null\n' +
'@@ -1,3 +0,0 @@\n' +
'-define(function() {\n' +
'- return typeof undefined;\n' +
'-});\n' +
'diff --git a/components/textdiff/textdiff.html b/components/textdiff/textdiff.html\n' +
'index a3484bf..82209af 100644\n' +
'--- a/components/textdiff/textdiff.html\n' +
'+++ b/components/textdiff/textdiff.html\n' +
'@@ -1,6 +1,8 @@\n' +
' <!-- ko if: isShowingDiffs -->\n' +
' <div>\n' +
'- <div data-bind="event: { load: setDom($element) }"></div>\n' +
'+ <!-- ko if: isParsed -->\n' +
'+ <div data-bind="template: {nodes: ko.utils.parseHtmlFragment(htmlSrc())}"></div>\n' +
'+ <!-- /ko -->\n' +
' <div class="btn-load-more" data-bind="visible: loadMoreCount() > 0">\n' +
' </div>\n' +
'diff --git a/test.js b/test.js\n' +
'new file mode 100644\n' +
'index 0000000..e1e22ec\n' +
'--- /dev/null\n' +
'+++ b/test.js\n' +
'@@ -0,0 +1,6 @@\n' +
"+var parser = require('./source/git-parser');\n" +
'+\n' +
"+var text = 'diff --git a/components/app/app.html b/components/app/app.html\\nindex ecb7a95..027bd9b 100644\\n--- a/components/app/app.html\\n+++ b/components/app/app.html\\n@@ -52,0 +53,3 @@\\n+\\n+\\n+\\n@@ -56,0 +60,3 @@\\n+\\n+\\n+\\n'\n" +
'+var patchLineList = [ false, false, false, false ];\n' +
'+\n' +
'+console.log(parser.parsePatchDiffResult(text, patchLineList));\n' +
"diff --git a/a.xml b/b.xml\n" +
"index e54317e..82a9a56 100644\n" +
"--- a/a.xml\n" +
"+++ b/b.xml\n" +
"@@ -242,4 +242,6 @@ need to create a new job for native server java api and move these tests to a ne\n" +
" </packages>\n" +
" </test>\n" +
" -->\n" +
"+\n" +
"+\n";
document.addEventListener("DOMContentLoaded", function() {
// parse the diff to json
var diffJson = Diff2Html.getJsonFromDiff(lineDiffExample);
// collect all the file extensions in the json
var allFileLanguages = diffJson.map(function(line) {
return line.language;
});
// remove duplicated languages
var distinctLanguages = allFileLanguages.filter(function(v, i) {
return allFileLanguages.indexOf(v) == i;
});
// pass the languages to the highlightjs plugin
hljs.configure({languages: distinctLanguages});
// generate and inject the diff HTML into the desired place
document.getElementById("line-by-line").innerHTML = Diff2Html.getPrettyHtml(diffJson, { inputFormat: 'json', showFiles: true });
document.getElementById("side-by-side").innerHTML = Diff2Html.getPrettyHtml(diffJson, { inputFormat: 'json', outputFormat: 'side-by-side' });
// collect all the code lines and execute the highlight on them
var codeLines = document.getElementsByClassName("d2h-code-line-ctn");
[].forEach.call(codeLines, function(line) {
hljs.highlightBlock(line);
});
});
</script>
</head>
<body style="text-align: center; font-family: 'Source Sans Pro',sans-serif;">
<h1>Diff to HTML by <a href="https://github.com/rtfpessoa">rtfpessoa</a></h1>
<h2>Line by Line</h2>
<div id="line-by-line" style="margin: 0 auto; max-width: 900px;">
</div>
<h2>Side by Side</h2>
<div id="side-by-side" style="margin: 0 auto;">
</div>
</body>
</html>

188
scripts/hulk.ts Executable file
View file

@ -0,0 +1,188 @@
/*
* Copyright 2011 Twitter, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as path from 'path';
import * as fs from 'fs';
import * as hogan from 'hogan.js';
import nopt from 'nopt';
import * as mkderp from 'mkdirp';
const options = nopt(
{
namespace: String,
outputdir: path,
variable: String,
wrapper: String,
version: true,
help: true,
},
{
n: ['--namespace'],
o: ['--outputdir'],
vn: ['--variable'],
w: ['--wrapper'],
h: ['--help'],
v: ['--version'],
},
);
const specials = ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'];
const specialsRegExp = new RegExp('(\\' + specials.join('|\\') + ')', 'g');
function escape(text: string): string {
return text.replace(specialsRegExp, '\\$1');
}
function cyan(text: string): string {
return '\x1B[36m' + text + '\x1B[39m';
}
function extractFiles(files: string[]): string[] {
const usage = `${cyan(
'USAGE:',
)} hulk [--wrapper wrapper] [--outputdir outputdir] [--namespace namespace] [--variable variable] FILES
${cyan('OPTIONS:')} [-w, --wrapper] :: wraps the template (i.e. amd)
[-o, --outputdir] :: outputs the templates as individual files to a directory
[-n, --namespace] :: prepend string to template names
[-vn, --variable] :: variable name for non-amd wrapper
${cyan('EXAMPLE:')} hulk --wrapper amd ./templates/*.mustache
${cyan('NOTE:')} hulk supports the "*" wildcard and allows you to target specific extensions too
`;
if (options.version) {
// eslint-disable-next-line @typescript-eslint/no-require-imports
console.log(require('../package.json').version);
process.exit(0);
}
if (!files.length || options.help) {
console.log(usage);
process.exit(0);
}
return files
.map((fileGlob: string) => {
if (/\*/.test(fileGlob)) {
const [fileGlobPrefix, fileGlobSuffix] = fileGlob.split('*');
return fs.readdirSync(fileGlobPrefix || '.').reduce<string[]>((previousFiles, relativeFilePath) => {
const file = path.join(fileGlobPrefix, relativeFilePath);
if (new RegExp(`${escape(fileGlobSuffix)}$`).test(relativeFilePath) && fs.statSync(file).isFile()) {
previousFiles.push(file);
}
return previousFiles;
}, []);
} else if (fs.statSync(fileGlob).isFile()) {
return [fileGlob];
} else {
return [];
}
})
.reduce((previous, current) => previous.concat(current), []);
}
// Remove utf-8 byte order mark, http://en.wikipedia.org/wiki/Byte_order_mark
function removeByteOrderMark(text: string): string {
if (text.charCodeAt(0) === 0xfeff) {
return text.substring(1);
}
return text;
}
// Wrap templates
function wrap(file: string, name: string, openedFile: string): string {
const hoganTemplateString = `new Hogan.Template(${hogan.compile(openedFile, { asString: true })})`;
const objectName = options.variable || 'templates';
const objectAccessor = `${objectName}["${name}"]`;
const objectStmt = `${objectAccessor} = ${hoganTemplateString};`;
switch (options.wrapper) {
case 'amd':
return `define(${
!options.outputdir ? `"${path.join(path.dirname(file), name)}", ` : ''
}["hogan.js"], function(Hogan) { return ${hoganTemplateString}; });`;
case 'node':
// If we have a template per file the export will expose the template directly
return options.outputdir ? `global.${objectStmt};\nmodule.exports = ${objectAccessor};` : `global.${objectStmt}`;
case 'ts':
return `// @ts-ignore\n${objectStmt}`;
default:
return objectStmt;
}
}
function prepareOutput(content: string): string {
const variableName = options.variable || 'templates';
switch (options.wrapper) {
case 'amd':
return content;
case 'node':
return `(function() {
if (!!!global.${variableName}) global.${variableName} = {};
var Hogan = require("hogan.js");
${content}
${!options.outputdir ? `module.exports = global.${variableName};\n` : ''})();`;
case 'ts':
return `import * as Hogan from "hogan.js";
type CompiledTemplates = { [name: string]: Hogan.Template };
export const ${variableName}: CompiledTemplates = {};
${content}`;
default:
return 'if (!!!' + variableName + ') var ' + variableName + ' = {};\n' + content;
}
}
// Write the directory
if (options.outputdir) {
mkderp.sync(options.outputdir);
}
// Prepend namespace to template name
function namespace(name: string): string {
return (options.namespace || '') + name;
}
// Write a template foreach file that matches template extension
const templates = extractFiles(options.argv.remain)
.map(file => {
const timmedFileContents = fs.readFileSync(file, 'utf8').trim();
if (!timmedFileContents) return;
const name = namespace(path.basename(file).replace(/\..*$/, ''));
const cleanFileContents = wrap(file, name, removeByteOrderMark(timmedFileContents));
if (!options.outputdir) return cleanFileContents;
const fileExtension = options.wrapper === 'ts' ? 'ts' : 'js';
return fs.writeFileSync(path.join(options.outputdir, `${name}.${fileExtension}`), prepareOutput(cleanFileContents));
})
.filter(templateContents => typeof templateContents !== 'undefined');
// Output templates
if (!templates.length || options.outputdir) process.exit(0);
console.log(prepareOutput(templates.join('\n')));

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,272 @@
import { FileListRenderer } from '../file-list-renderer';
import HoganJsUtils from '../hoganjs-utils';
import { ColorSchemeType } from '../types';
describe('FileListRenderer', () => {
describe('render', () => {
it('should expose old and new files to templates', () => {
const hoganUtils = new HoganJsUtils({
rawTemplates: {
'file-summary-wrapper': '{{{files}}}',
'file-summary-line': '{{oldName}}, {{newName}}, {{fileName}}',
},
});
const fileListRenderer = new FileListRenderer(hoganUtils);
const files = [
{
isCombined: false,
isGitDiff: false,
blocks: [],
addedLines: 12,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name.js',
newName: 'my/file/name.js',
},
{
isCombined: false,
isGitDiff: false,
blocks: [],
addedLines: 12,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name1.js',
newName: 'my/file/name2.js',
},
{
isCombined: false,
isGitDiff: false,
blocks: [],
addedLines: 12,
deletedLines: 0,
language: 'js',
oldName: 'dev/null',
newName: 'my/file/name.js',
isNew: true,
},
{
isCombined: false,
isGitDiff: false,
blocks: [],
addedLines: 0,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name.js',
newName: 'dev/null',
isDeleted: true,
},
];
const fileHtml = fileListRenderer.render(files);
expect(fileHtml).toMatchInlineSnapshot(`
"my/file/name.js, my/file/name.js, my/file/name.js
my/file/name1.js, my/file/name2.js, my/file/{name1.js name2.js}
dev/null, my/file/name.js, my/file/name.js
my/file/name.js, dev/null, my/file/name.js"
`);
});
it('should work for all kinds of files', () => {
const hoganUtils = new HoganJsUtils({});
const fileListRenderer = new FileListRenderer(hoganUtils);
const files = [
{
isCombined: false,
isGitDiff: false,
blocks: [],
addedLines: 12,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name.js',
newName: 'my/file/name.js',
},
{
isCombined: false,
isGitDiff: false,
blocks: [],
addedLines: 12,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name1.js',
newName: 'my/file/name2.js',
},
{
isCombined: false,
isGitDiff: false,
blocks: [],
addedLines: 12,
deletedLines: 0,
language: 'js',
oldName: 'dev/null',
newName: 'my/file/name.js',
isNew: true,
},
{
isCombined: false,
isGitDiff: false,
blocks: [],
addedLines: 0,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name.js',
newName: 'dev/null',
isDeleted: true,
},
];
const fileHtml = fileListRenderer.render(files);
expect(fileHtml).toMatchInlineSnapshot(`
"<div class="d2h-file-list-wrapper d2h-light-color-scheme">
<div class="d2h-file-list-header">
<span class="d2h-file-list-title">Files changed (4)</span>
<a class="d2h-file-switch d2h-hide">hide</a>
<a class="d2h-file-switch d2h-show">show</a>
</div>
<ol class="d2h-file-list">
<li class="d2h-file-list-line">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon d2h-changed" height="16" title="modified" version="1.1"
viewBox="0 0 14 16" width="14">
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM4 8c0-1.66 1.34-3 3-3s3 1.34 3 3-1.34 3-3 3-3-1.34-3-3z"></path>
</svg> <a href="#d2h-781444" class="d2h-file-name">my/file/name.js</a>
<span class="d2h-file-stats">
<span class="d2h-lines-added">+12</span>
<span class="d2h-lines-deleted">-41</span>
</span>
</span>
</li>
<li class="d2h-file-list-line">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon d2h-moved" height="16" title="renamed" version="1.1"
viewBox="0 0 14 16" width="14">
<path d="M6 9H3V7h3V4l5 4-5 4V9z m8-7v12c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h12c0.55 0 1 0.45 1 1z m-1 0H1v12h12V2z"></path>
</svg> <a href="#d2h-662683" class="d2h-file-name">my/file/{name1.js name2.js}</a>
<span class="d2h-file-stats">
<span class="d2h-lines-added">+12</span>
<span class="d2h-lines-deleted">-41</span>
</span>
</span>
</li>
<li class="d2h-file-list-line">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon d2h-added" height="16" title="added" version="1.1" viewBox="0 0 14 16"
width="14">
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM6 9H3V7h3V4h2v3h3v2H8v3H6V9z"></path>
</svg> <a href="#d2h-781444" class="d2h-file-name">my/file/name.js</a>
<span class="d2h-file-stats">
<span class="d2h-lines-added">+12</span>
<span class="d2h-lines-deleted">-0</span>
</span>
</span>
</li>
<li class="d2h-file-list-line">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon d2h-deleted" height="16" title="removed" version="1.1"
viewBox="0 0 14 16" width="14">
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM11 9H3V7h8v2z"></path>
</svg> <a href="#d2h-781444" class="d2h-file-name">my/file/name.js</a>
<span class="d2h-file-stats">
<span class="d2h-lines-added">+0</span>
<span class="d2h-lines-deleted">-41</span>
</span>
</span>
</li>
</ol>
</div>"
`);
});
describe('with dark colorScheme', () => {
it('should include dark colorScheme', () => {
const hoganUtils = new HoganJsUtils({});
const fileListRenderer = new FileListRenderer(hoganUtils, {
colorScheme: ColorSchemeType.DARK,
});
const files = [
{
isCombined: false,
isGitDiff: false,
blocks: [],
addedLines: 12,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name.js',
newName: 'my/file/name.js',
},
];
const fileHtml = fileListRenderer.render(files);
expect(fileHtml).toMatchInlineSnapshot(`
"<div class="d2h-file-list-wrapper d2h-dark-color-scheme">
<div class="d2h-file-list-header">
<span class="d2h-file-list-title">Files changed (1)</span>
<a class="d2h-file-switch d2h-hide">hide</a>
<a class="d2h-file-switch d2h-show">show</a>
</div>
<ol class="d2h-file-list">
<li class="d2h-file-list-line">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon d2h-changed" height="16" title="modified" version="1.1"
viewBox="0 0 14 16" width="14">
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM4 8c0-1.66 1.34-3 3-3s3 1.34 3 3-1.34 3-3 3-3-1.34-3-3z"></path>
</svg> <a href="#d2h-781444" class="d2h-file-name">my/file/name.js</a>
<span class="d2h-file-stats">
<span class="d2h-lines-added">+12</span>
<span class="d2h-lines-deleted">-41</span>
</span>
</span>
</li>
</ol>
</div>"
`);
});
});
describe('with auto colorScheme', () => {
it('should include auto colorScheme', () => {
const hoganUtils = new HoganJsUtils({});
const fileListRenderer = new FileListRenderer(hoganUtils, {
colorScheme: ColorSchemeType.AUTO,
});
const files = [
{
isCombined: false,
isGitDiff: false,
blocks: [],
addedLines: 12,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name.js',
newName: 'my/file/name.js',
},
];
const fileHtml = fileListRenderer.render(files);
expect(fileHtml).toMatchInlineSnapshot(`
"<div class="d2h-file-list-wrapper d2h-auto-color-scheme">
<div class="d2h-file-list-header">
<span class="d2h-file-list-title">Files changed (1)</span>
<a class="d2h-file-switch d2h-hide">hide</a>
<a class="d2h-file-switch d2h-show">show</a>
</div>
<ol class="d2h-file-list">
<li class="d2h-file-list-line">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon d2h-changed" height="16" title="modified" version="1.1"
viewBox="0 0 14 16" width="14">
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM4 8c0-1.66 1.34-3 3-3s3 1.34 3 3-1.34 3-3 3-3-1.34-3-3z"></path>
</svg> <a href="#d2h-781444" class="d2h-file-name">my/file/name.js</a>
<span class="d2h-file-stats">
<span class="d2h-lines-added">+12</span>
<span class="d2h-lines-deleted">-41</span>
</span>
</span>
</li>
</ol>
</div>"
`);
});
});
});
});

View file

@ -0,0 +1,56 @@
import HoganJsUtils from '../hoganjs-utils';
import { CSSLineClass } from '../render-utils';
describe('HoganJsUtils', () => {
describe('render', () => {
it('should render view', () => {
const hoganJsUtils = new HoganJsUtils({});
const result = hoganJsUtils.render('generic', 'empty-diff', {
contentClass: 'd2h-code-line',
CSSLineClass: CSSLineClass,
});
expect(result).toMatchInlineSnapshot(`
"<tr>
<td class="d2h-info">
<div class="d2h-code-line">
File without changes
</div>
</td>
</tr>"
`);
});
it('should throw exception if template is missing', () => {
const hoganJsUtils = new HoganJsUtils({});
expect(() => hoganJsUtils.render('generic', 'missing-template', {})).toThrow(Error);
});
it('should allow templates to be overridden with compiled templates', () => {
const emptyDiffTemplate = HoganJsUtils.compile('<p>{{myName}}</p>');
const hoganJsUtils = new HoganJsUtils({ compiledTemplates: { 'generic-empty-diff': emptyDiffTemplate } });
const result = hoganJsUtils.render('generic', 'empty-diff', { myName: 'Rodrigo Fernandes' });
expect(result).toMatchInlineSnapshot(`"<p>Rodrigo Fernandes</p>"`);
});
it('should allow templates to be overridden with uncompiled templates', () => {
const emptyDiffTemplate = '<p>{{myName}}</p>';
const hoganJsUtils = new HoganJsUtils({ rawTemplates: { 'generic-empty-diff': emptyDiffTemplate } });
const result = hoganJsUtils.render('generic', 'empty-diff', { myName: 'Rodrigo Fernandes' });
expect(result).toMatchInlineSnapshot(`"<p>Rodrigo Fernandes</p>"`);
});
it('should allow templates to be overridden giving priority to raw templates', () => {
const emptyDiffTemplate = HoganJsUtils.compile('<p>Not used!</p>');
const emptyDiffTemplateUncompiled = '<p>{{myName}}</p>';
const hoganJsUtils = new HoganJsUtils({
compiledTemplates: { 'generic-empty-diff': emptyDiffTemplate },
rawTemplates: { 'generic-empty-diff': emptyDiffTemplateUncompiled },
});
const result = hoganJsUtils.render('generic', 'empty-diff', { myName: 'Rodrigo Fernandes' });
expect(result).toMatchInlineSnapshot(`"<p>Rodrigo Fernandes</p>"`);
});
});
});

View file

@ -0,0 +1,725 @@
import LineByLineRenderer from '../line-by-line-renderer';
import HoganJsUtils from '../hoganjs-utils';
import { LineType, DiffFile, LineMatchingType } from '../types';
import { CSSLineClass } from '../render-utils';
describe('LineByLineRenderer', () => {
describe('_generateEmptyDiff', () => {
it('should return an empty diff', () => {
const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const fileHtml = lineByLineRenderer.generateEmptyDiff();
expect(fileHtml).toMatchInlineSnapshot(`
"<tr>
<td class="d2h-info">
<div class="d2h-code-line">
File without changes
</div>
</td>
</tr>"
`);
});
});
describe('makeLineHtml', () => {
it('should work for insertions', () => {
const file = {
addedLines: 12,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name.js',
newName: 'my/file/name.js',
isCombined: false,
isGitDiff: false,
blocks: [],
};
const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const fileHtml = lineByLineRenderer.generateSingleLineHtml(file, {
type: CSSLineClass.INSERTS,
prefix: '+',
content: 'test',
oldNumber: undefined,
newNumber: 30,
});
expect(fileHtml).toMatchInlineSnapshot(`
"<tr>
<td class="d2h-code-linenumber d2h-ins">
<div class="line-num1"></div>
<div class="line-num2">30</div>
</td>
<td class="d2h-ins">
<div class="d2h-code-line">
<span class="d2h-code-line-prefix">+</span>
<span class="d2h-code-line-ctn">test</span>
</div>
</td>
</tr>"
`);
});
it('should work for deletions', () => {
const file = {
addedLines: 12,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name.js',
newName: 'my/file/name.js',
isCombined: false,
isGitDiff: false,
blocks: [],
};
const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const fileHtml = lineByLineRenderer.generateSingleLineHtml(file, {
type: CSSLineClass.DELETES,
prefix: '-',
content: 'test',
oldNumber: 30,
newNumber: undefined,
});
expect(fileHtml).toMatchInlineSnapshot(`
"<tr>
<td class="d2h-code-linenumber d2h-del">
<div class="line-num1">30</div>
<div class="line-num2"></div>
</td>
<td class="d2h-del">
<div class="d2h-code-line">
<span class="d2h-code-line-prefix">-</span>
<span class="d2h-code-line-ctn">test</span>
</div>
</td>
</tr>"
`);
});
it('should convert indents into non breakin spaces (2 white spaces)', () => {
const file = {
addedLines: 12,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name.js',
newName: 'my/file/name.js',
isCombined: false,
isGitDiff: false,
blocks: [],
};
const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const fileHtml = lineByLineRenderer.generateSingleLineHtml(file, {
type: CSSLineClass.INSERTS,
prefix: '+',
content: ' test',
oldNumber: undefined,
newNumber: 30,
});
expect(fileHtml).toMatchInlineSnapshot(`
"<tr>
<td class="d2h-code-linenumber d2h-ins">
<div class="line-num1"></div>
<div class="line-num2">30</div>
</td>
<td class="d2h-ins">
<div class="d2h-code-line">
<span class="d2h-code-line-prefix">+</span>
<span class="d2h-code-line-ctn"> test</span>
</div>
</td>
</tr>"
`);
});
it('should convert indents into non breakin spaces (4 white spaces)', () => {
const file = {
addedLines: 12,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name.js',
newName: 'my/file/name.js',
isCombined: false,
isGitDiff: false,
blocks: [],
};
const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const fileHtml = lineByLineRenderer.generateSingleLineHtml(file, {
type: CSSLineClass.INSERTS,
prefix: '+',
content: ' test',
oldNumber: undefined,
newNumber: 30,
});
expect(fileHtml).toMatchInlineSnapshot(`
"<tr>
<td class="d2h-code-linenumber d2h-ins">
<div class="line-num1"></div>
<div class="line-num2">30</div>
</td>
<td class="d2h-ins">
<div class="d2h-code-line">
<span class="d2h-code-line-prefix">+</span>
<span class="d2h-code-line-ctn"> test</span>
</div>
</td>
</tr>"
`);
});
it('should preserve tabs', () => {
const file = {
addedLines: 12,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name.js',
newName: 'my/file/name.js',
isCombined: false,
isGitDiff: false,
blocks: [],
};
const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const fileHtml = lineByLineRenderer.generateSingleLineHtml(file, {
type: CSSLineClass.INSERTS,
prefix: '+',
content: '\ttest',
oldNumber: undefined,
newNumber: 30,
});
expect(fileHtml).toMatchInlineSnapshot(`
"<tr>
<td class="d2h-code-linenumber d2h-ins">
<div class="line-num1"></div>
<div class="line-num2">30</div>
</td>
<td class="d2h-ins">
<div class="d2h-code-line">
<span class="d2h-code-line-prefix">+</span>
<span class="d2h-code-line-ctn"> test</span>
</div>
</td>
</tr>"
`);
});
});
describe('makeFileDiffHtml', () => {
it('should work for simple file', () => {
const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const file = {
addedLines: 12,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name.js',
newName: 'my/file/name.js',
isCombined: false,
isGitDiff: false,
blocks: [],
};
const diffs = '<span>Random Html</span>';
const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs);
expect(fileHtml).toMatchInlineSnapshot(`
"<div id="d2h-781444" class="d2h-file-wrapper" data-lang="js">
<div class="d2h-file-header">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
</svg> <span class="d2h-file-name">my/file/name.js</span>
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></span>
<label class="d2h-file-collapse">
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
Viewed
</label>
</div>
<div class="d2h-file-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
<span>Random Html</span>
</tbody>
</table>
</div>
</div>
</div>"
`);
});
it('should work for simple added file', () => {
const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const file = {
addedLines: 12,
deletedLines: 0,
language: 'js',
oldName: 'dev/null',
newName: 'my/file/name.js',
isNew: true,
isCombined: false,
isGitDiff: false,
blocks: [],
};
const diffs = '<span>Random Html</span>';
const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs);
expect(fileHtml).toMatchInlineSnapshot(`
"<div id="d2h-781444" class="d2h-file-wrapper" data-lang="js">
<div class="d2h-file-header">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
</svg> <span class="d2h-file-name">my/file/name.js</span>
<span class="d2h-tag d2h-added d2h-added-tag">ADDED</span></span>
<label class="d2h-file-collapse">
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
Viewed
</label>
</div>
<div class="d2h-file-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
<span>Random Html</span>
</tbody>
</table>
</div>
</div>
</div>"
`);
});
it('should work for simple deleted file', () => {
const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const file = {
addedLines: 0,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name.js',
newName: 'dev/null',
isDeleted: true,
isCombined: false,
isGitDiff: false,
blocks: [],
};
const diffs = '<span>Random Html</span>';
const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs);
expect(fileHtml).toMatchInlineSnapshot(`
"<div id="d2h-781444" class="d2h-file-wrapper" data-lang="js">
<div class="d2h-file-header">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
</svg> <span class="d2h-file-name">my/file/name.js</span>
<span class="d2h-tag d2h-deleted d2h-deleted-tag">DELETED</span></span>
<label class="d2h-file-collapse">
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
Viewed
</label>
</div>
<div class="d2h-file-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
<span>Random Html</span>
</tbody>
</table>
</div>
</div>
</div>"
`);
});
it('should work for simple renamed file', () => {
const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const file = {
addedLines: 12,
deletedLines: 41,
language: 'js',
oldName: 'my/file/name1.js',
newName: 'my/file/name2.js',
isRename: true,
isCombined: false,
isGitDiff: false,
blocks: [],
};
const diffs = '<span>Random Html</span>';
const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs);
expect(fileHtml).toMatchInlineSnapshot(`
"<div id="d2h-662683" class="d2h-file-wrapper" data-lang="js">
<div class="d2h-file-header">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
</svg> <span class="d2h-file-name">my/file/{name1.js name2.js}</span>
<span class="d2h-tag d2h-moved d2h-moved-tag">RENAMED</span></span>
<label class="d2h-file-collapse">
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
Viewed
</label>
</div>
<div class="d2h-file-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
<span>Random Html</span>
</tbody>
</table>
</div>
</div>
</div>"
`);
});
it('should return empty when option renderNothingWhenEmpty is true and file blocks not present', () => {
const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {
renderNothingWhenEmpty: true,
});
const file = {
addedLines: 0,
deletedLines: 0,
language: 'js',
oldName: 'my/file/name1.js',
newName: 'my/file/name2.js',
isRename: true,
isCombined: false,
isGitDiff: false,
blocks: [],
};
const diffs = '<span>Random Html</span>';
const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs);
expect(fileHtml).toMatchInlineSnapshot(`""`);
});
});
describe('generateLineByLineJsonHtml', () => {
it('should work for list of files', () => {
const exampleJson: DiffFile[] = [
{
blocks: [
{
lines: [
{
content: '-test',
type: LineType.DELETE,
oldNumber: 1,
newNumber: undefined,
},
{
content: '+test1r',
type: LineType.INSERT,
oldNumber: undefined,
newNumber: 1,
},
],
oldStartLine: 1,
oldStartLine2: undefined,
newStartLine: 1,
header: '@@ -1 +1 @@',
},
],
deletedLines: 1,
addedLines: 1,
checksumBefore: '0000001',
checksumAfter: '0ddf2ba',
oldName: 'sample',
newName: 'sample',
language: 'txt',
isCombined: false,
isGitDiff: true,
},
];
const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {
matching: LineMatchingType.LINES,
});
const html = lineByLineRenderer.render(exampleJson);
expect(html).toMatchInlineSnapshot(`
"<div class="d2h-wrapper d2h-light-color-scheme">
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="txt">
<div class="d2h-file-header">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
</svg> <span class="d2h-file-name">sample</span>
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></span>
<label class="d2h-file-collapse">
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
Viewed
</label>
</div>
<div class="d2h-file-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
<tr>
<td class="d2h-code-linenumber d2h-info"></td>
<td class="d2h-info">
<div class="d2h-code-line">@@ -1 +1 @@</div>
</td>
</tr><tr>
<td class="d2h-code-linenumber d2h-del d2h-change">
<div class="line-num1">1</div>
<div class="line-num2"></div>
</td>
<td class="d2h-del d2h-change">
<div class="d2h-code-line">
<span class="d2h-code-line-prefix">-</span>
<span class="d2h-code-line-ctn"><del>test</del></span>
</div>
</td>
</tr><tr>
<td class="d2h-code-linenumber d2h-ins d2h-change">
<div class="line-num1"></div>
<div class="line-num2">1</div>
</td>
<td class="d2h-ins d2h-change">
<div class="d2h-code-line">
<span class="d2h-code-line-prefix">+</span>
<span class="d2h-code-line-ctn"><ins>test1r</ins></span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>"
`);
});
it('should work for empty blocks', () => {
const exampleJson = [
{
blocks: [],
deletedLines: 0,
addedLines: 0,
oldName: 'sample',
language: 'js',
newName: 'sample',
isCombined: false,
isGitDiff: false,
},
];
const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {
renderNothingWhenEmpty: false,
});
const html = lineByLineRenderer.render(exampleJson);
expect(html).toMatchInlineSnapshot(`
"<div class="d2h-wrapper d2h-light-color-scheme">
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="js">
<div class="d2h-file-header">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
</svg> <span class="d2h-file-name">sample</span>
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></span>
<label class="d2h-file-collapse">
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
Viewed
</label>
</div>
<div class="d2h-file-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
<tr>
<td class="d2h-info">
<div class="d2h-code-line">
File without changes
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>"
`);
});
it('should work for too big file diff', () => {
const exampleJson = [
{
blocks: [
{
header: '<a href="http://example.com">Custom link to render</a>',
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(`
"<div class="d2h-wrapper d2h-light-color-scheme">
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="js">
<div class="d2h-file-header">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
</svg> <span class="d2h-file-name">sample</span>
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></span>
<label class="d2h-file-collapse">
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
Viewed
</label>
</div>
<div class="d2h-file-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
<tr>
<td class="d2h-code-linenumber d2h-info"></td>
<td class="d2h-info">
<div class="d2h-code-line"><a href="http://example.com">Custom link to render</a></div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>"
`);
});
});
describe('_generateFileHtml', () => {
it('should work for simple file', () => {
const hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const file: DiffFile = {
blocks: [
{
lines: [
{
content: ' one context line',
type: LineType.CONTEXT,
oldNumber: 1,
newNumber: 1,
},
{
content: '-test',
type: LineType.DELETE,
oldNumber: 2,
newNumber: undefined,
},
{
content: '+test1r',
type: LineType.INSERT,
oldNumber: undefined,
newNumber: 2,
},
{
content: '+test2r',
type: LineType.INSERT,
oldNumber: undefined,
newNumber: 3,
},
],
oldStartLine: 1,
oldStartLine2: undefined,
newStartLine: 1,
header: '@@ -1 +1 @@',
},
],
deletedLines: 1,
addedLines: 1,
checksumBefore: '0000001',
checksumAfter: '0ddf2ba',
oldName: 'sample',
language: 'txt',
newName: 'sample',
isCombined: false,
isGitDiff: true,
};
const html = lineByLineRenderer.generateFileHtml(file);
expect(html).toMatchInlineSnapshot(`
"<tr>
<td class="d2h-code-linenumber d2h-info"></td>
<td class="d2h-info">
<div class="d2h-code-line">@@ -1 +1 @@</div>
</td>
</tr><tr>
<td class="d2h-code-linenumber d2h-cntx">
<div class="line-num1">1</div>
<div class="line-num2">1</div>
</td>
<td class="d2h-cntx">
<div class="d2h-code-line">
<span class="d2h-code-line-prefix">&nbsp;</span>
<span class="d2h-code-line-ctn">one context line</span>
</div>
</td>
</tr><tr>
<td class="d2h-code-linenumber d2h-del d2h-change">
<div class="line-num1">2</div>
<div class="line-num2"></div>
</td>
<td class="d2h-del d2h-change">
<div class="d2h-code-line">
<span class="d2h-code-line-prefix">-</span>
<span class="d2h-code-line-ctn"><del>test</del></span>
</div>
</td>
</tr><tr>
<td class="d2h-code-linenumber d2h-ins d2h-change">
<div class="line-num1"></div>
<div class="line-num2">2</div>
</td>
<td class="d2h-ins d2h-change">
<div class="d2h-code-line">
<span class="d2h-code-line-prefix">+</span>
<span class="d2h-code-line-ctn"><ins>test1r</ins></span>
</div>
</td>
</tr><tr>
<td class="d2h-code-linenumber d2h-ins">
<div class="line-num1"></div>
<div class="line-num2">3</div>
</td>
<td class="d2h-ins">
<div class="d2h-code-line">
<span class="d2h-code-line-prefix">+</span>
<span class="d2h-code-line-ctn">test2r</span>
</div>
</td>
</tr>"
`);
});
});
});

View file

@ -0,0 +1,152 @@
import { escapeForHtml, getHtmlId, filenameDiff, diffHighlight } from '../render-utils';
import { DiffStyleType, LineMatchingType } from '../types';
describe('Utils', () => {
describe('escapeForHtml', () => {
it('should escape & with &amp;', () => {
const result = escapeForHtml('&');
expect(result).toBe('&amp;');
});
it('should escape < with &lt;', () => {
const result = escapeForHtml('<');
expect(result).toBe('&lt;');
});
it('should escape > with &gt;', () => {
const result = escapeForHtml('>');
expect(result).toBe('&gt;');
});
it('should escape " with &quot;', () => {
const result = escapeForHtml('"');
expect(result).toBe('&quot;');
});
it("should escape ' with &#x27;", () => {
const result = escapeForHtml("'");
expect(result).toBe('&#x27;');
});
it('should escape / with &#x2F;', () => {
const result = escapeForHtml('/');
expect(result).toBe('&#x2F;');
});
it('should escape a string containing HTML code', () => {
const result = escapeForHtml(`<a href="/search?q=diff2html">Search 'Diff2Html'</a>`);
expect(result).toBe(
'&lt;a href=&quot;&#x2F;search?q=diff2html&quot;&gt;Search &#x27;Diff2Html&#x27;&lt;&#x2F;a&gt;',
);
});
});
describe('getHtmlId', () => {
it('should generate file unique id', () => {
const result = getHtmlId({
oldName: 'sample.js',
newName: 'sample.js',
});
expect(result).toBe('d2h-960013');
});
});
describe('getDiffName', () => {
it('should generate the file name for a changed file', () => {
const result = filenameDiff({
oldName: 'sample.js',
newName: 'sample.js',
});
expect(result).toBe('sample.js');
});
it('should generate the file name for a changed file and full rename', () => {
const result = filenameDiff({
oldName: 'sample1.js',
newName: 'sample2.js',
});
expect(result).toBe('sample1.js → sample2.js');
});
it('should generate the file name for a changed file and prefix rename', () => {
const result = filenameDiff({
oldName: 'src/path/sample.js',
newName: 'source/path/sample.js',
});
expect(result).toBe('{src → source}/path/sample.js');
});
it('should generate the file name for a changed file and suffix rename', () => {
const result = filenameDiff({
oldName: 'src/path/sample1.js',
newName: 'src/path/sample2.js',
});
expect(result).toBe('src/path/{sample1.js → sample2.js}');
});
it('should generate the file name for a changed file and middle rename', () => {
const result = filenameDiff({
oldName: 'src/really/big/path/sample.js',
newName: 'src/small/path/sample.js',
});
expect(result).toBe('src/{really/big → small}/path/sample.js');
});
it('should generate the file name for a deleted file', () => {
const result = filenameDiff({
oldName: 'src/my/file.js',
newName: '/dev/null',
});
expect(result).toBe('src/my/file.js');
});
it('should generate the file name for a new file', () => {
const result = filenameDiff({
oldName: '/dev/null',
newName: 'src/my/file.js',
});
expect(result).toBe('src/my/file.js');
});
});
describe('diffHighlight', () => {
it('should highlight two lines', () => {
const result = diffHighlight('-var myVar = 2;', '+var myVariable = 3;', false, {
matching: LineMatchingType.WORDS,
});
expect(result).toEqual({
oldLine: {
prefix: '-',
content: 'var <del>myVar</del> = <del>2</del>;',
},
newLine: {
prefix: '+',
content: 'var <ins>myVariable</ins> = <ins>3</ins>;',
},
});
});
it('should highlight two lines char by char', () => {
const result = diffHighlight('-var myVar = 2;', '+var myVariable = 3;', false, {
diffStyle: DiffStyleType.CHAR,
});
expect(result).toEqual({
oldLine: {
prefix: '-',
content: 'var myVar = <del>2</del>;',
},
newLine: {
prefix: '+',
content: 'var myVar<ins>iable</ins> = <ins>3</ins>;',
},
});
});
it('should highlight combined diff lines', () => {
const result = diffHighlight(' -var myVar = 2;', ' +var myVariable = 3;', true, {
diffStyle: DiffStyleType.WORD,
matching: LineMatchingType.WORDS,
matchWordsThreshold: 1.0,
});
expect(result).toEqual({
oldLine: {
prefix: ' -',
content: 'var <del class="d2h-change">myVar</del> = <del class="d2h-change">2</del>;',
},
newLine: {
prefix: ' +',
content: 'var <ins class="d2h-change">myVariable</ins> = <ins class="d2h-change">3</ins>;',
},
});
});
});
});

View file

@ -0,0 +1,538 @@
import SideBySideRenderer from '../side-by-side-renderer';
import HoganJsUtils from '../hoganjs-utils';
import { LineType, DiffLine, DiffFile, LineMatchingType } from '../types';
import { CSSLineClass } from '../render-utils';
describe('SideBySideRenderer', () => {
describe('generateEmptyDiff', () => {
it('should return an empty diff', () => {
const hoganUtils = new HoganJsUtils({});
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, {});
const fileHtml = sideBySideRenderer.generateEmptyDiff();
expect(fileHtml).toMatchInlineSnapshot(`
{
"left": "<tr>
<td class="d2h-info">
<div class="d2h-code-side-line">
File without changes
</div>
</td>
</tr>",
"right": "",
}
`);
});
});
describe('generateSideBySideFileHtml', () => {
it('should generate lines with the right prefixes', () => {
const hoganUtils = new HoganJsUtils({});
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, {});
const file: DiffFile = {
isGitDiff: true,
blocks: [
{
lines: [
{
content: ' context',
type: LineType.CONTEXT,
oldNumber: 19,
newNumber: 19,
},
{
content: '-removed',
type: LineType.DELETE,
oldNumber: 20,
newNumber: undefined,
},
{
content: '+added',
type: LineType.INSERT,
oldNumber: undefined,
newNumber: 20,
},
{
content: '+another added',
type: LineType.INSERT,
oldNumber: undefined,
newNumber: 21,
},
],
oldStartLine: 19,
newStartLine: 19,
header: '@@ -19,7 +19,7 @@',
},
],
deletedLines: 1,
addedLines: 2,
checksumBefore: 'fc56817',
checksumAfter: 'e8e7e49',
mode: '100644',
oldName: 'coverage.init',
language: 'init',
newName: 'coverage.init',
isCombined: false,
};
const fileHtml = sideBySideRenderer.generateFileHtml(file);
expect(fileHtml).toMatchInlineSnapshot(`
{
"left": "<tr>
<td class="d2h-code-side-linenumber d2h-info"></td>
<td class="d2h-info">
<div class="d2h-code-side-line">@@ -19,7 +19,7 @@</div>
</td>
</tr><tr>
<td class="d2h-code-side-linenumber d2h-cntx">
19
</td>
<td class="d2h-cntx">
<div class="d2h-code-side-line">
<span class="d2h-code-line-prefix">&nbsp;</span>
<span class="d2h-code-line-ctn">context</span>
</div>
</td>
</tr><tr>
<td class="d2h-code-side-linenumber d2h-del d2h-change">
20
</td>
<td class="d2h-del d2h-change">
<div class="d2h-code-side-line">
<span class="d2h-code-line-prefix">-</span>
<span class="d2h-code-line-ctn"><del>removed</del></span>
</div>
</td>
</tr><tr>
<td class="d2h-code-side-linenumber d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder">
</td>
<td class="d2h-cntx d2h-emptyplaceholder">
<div class="d2h-code-side-line d2h-code-side-emptyplaceholder">
<span class="d2h-code-line-prefix">&nbsp;</span>
<span class="d2h-code-line-ctn"><br></span>
</div>
</td>
</tr>",
"right": "<tr>
<td class="d2h-code-side-linenumber d2h-info"></td>
<td class="d2h-info">
<div class="d2h-code-side-line">&nbsp;</div>
</td>
</tr><tr>
<td class="d2h-code-side-linenumber d2h-cntx">
19
</td>
<td class="d2h-cntx">
<div class="d2h-code-side-line">
<span class="d2h-code-line-prefix">&nbsp;</span>
<span class="d2h-code-line-ctn">context</span>
</div>
</td>
</tr><tr>
<td class="d2h-code-side-linenumber d2h-ins d2h-change">
20
</td>
<td class="d2h-ins d2h-change">
<div class="d2h-code-side-line">
<span class="d2h-code-line-prefix">+</span>
<span class="d2h-code-line-ctn"><ins>added</ins></span>
</div>
</td>
</tr><tr>
<td class="d2h-code-side-linenumber d2h-ins">
21
</td>
<td class="d2h-ins">
<div class="d2h-code-side-line">
<span class="d2h-code-line-prefix">+</span>
<span class="d2h-code-line-ctn">another added</span>
</div>
</td>
</tr>",
}
`);
});
});
describe('generateSingleLineHtml', () => {
it('should work for insertions', () => {
const hoganUtils = new HoganJsUtils({});
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, {});
const fileHtml = sideBySideRenderer.generateLineHtml(undefined, {
type: CSSLineClass.INSERTS,
prefix: '+',
content: 'test',
number: 30,
});
expect(fileHtml).toMatchInlineSnapshot(`
{
"left": "<tr>
<td class="d2h-code-side-linenumber d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder">
</td>
<td class="d2h-cntx d2h-emptyplaceholder">
<div class="d2h-code-side-line d2h-code-side-emptyplaceholder">
<span class="d2h-code-line-prefix">&nbsp;</span>
<span class="d2h-code-line-ctn"><br></span>
</div>
</td>
</tr>",
"right": "<tr>
<td class="d2h-code-side-linenumber d2h-ins">
30
</td>
<td class="d2h-ins">
<div class="d2h-code-side-line">
<span class="d2h-code-line-prefix">+</span>
<span class="d2h-code-line-ctn">test</span>
</div>
</td>
</tr>",
}
`);
});
it('should work for deletions', () => {
const hoganUtils = new HoganJsUtils({});
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, {});
const fileHtml = sideBySideRenderer.generateLineHtml(
{
type: CSSLineClass.DELETES,
prefix: '-',
content: 'test',
number: 30,
},
undefined,
);
expect(fileHtml).toMatchInlineSnapshot(`
{
"left": "<tr>
<td class="d2h-code-side-linenumber d2h-del">
30
</td>
<td class="d2h-del">
<div class="d2h-code-side-line">
<span class="d2h-code-line-prefix">-</span>
<span class="d2h-code-line-ctn">test</span>
</div>
</td>
</tr>",
"right": "<tr>
<td class="d2h-code-side-linenumber d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder">
</td>
<td class="d2h-cntx d2h-emptyplaceholder">
<div class="d2h-code-side-line d2h-code-side-emptyplaceholder">
<span class="d2h-code-line-prefix">&nbsp;</span>
<span class="d2h-code-line-ctn"><br></span>
</div>
</td>
</tr>",
}
`);
});
});
describe('generateSideBySideJsonHtml', () => {
it('should work for list of files', () => {
const exampleJson: DiffFile[] = [
{
blocks: [
{
lines: [
{
content: '-test',
type: LineType.DELETE,
oldNumber: 1,
newNumber: undefined,
},
{
content: '+test1r',
type: LineType.INSERT,
oldNumber: undefined,
newNumber: 1,
},
],
oldStartLine: 1,
oldStartLine2: undefined,
newStartLine: 1,
header: '@@ -1 +1 @@',
},
],
deletedLines: 1,
addedLines: 1,
checksumBefore: '0000001',
checksumAfter: '0ddf2ba',
oldName: 'sample',
language: 'txt',
newName: 'sample',
isCombined: false,
isGitDiff: true,
},
];
const hoganUtils = new HoganJsUtils({});
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, { matching: LineMatchingType.LINES });
const html = sideBySideRenderer.render(exampleJson);
expect(html).toMatchInlineSnapshot(`
"<div class="d2h-wrapper d2h-light-color-scheme">
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="txt">
<div class="d2h-file-header">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
</svg> <span class="d2h-file-name">sample</span>
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></span>
<label class="d2h-file-collapse">
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
Viewed
</label>
</div>
<div class="d2h-files-diff">
<div class="d2h-file-side-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
<tr>
<td class="d2h-code-side-linenumber d2h-info"></td>
<td class="d2h-info">
<div class="d2h-code-side-line">@@ -1 +1 @@</div>
</td>
</tr><tr>
<td class="d2h-code-side-linenumber d2h-del d2h-change">
1
</td>
<td class="d2h-del d2h-change">
<div class="d2h-code-side-line">
<span class="d2h-code-line-prefix">-</span>
<span class="d2h-code-line-ctn"><del>test</del></span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="d2h-file-side-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
<tr>
<td class="d2h-code-side-linenumber d2h-info"></td>
<td class="d2h-info">
<div class="d2h-code-side-line">&nbsp;</div>
</td>
</tr><tr>
<td class="d2h-code-side-linenumber d2h-ins d2h-change">
1
</td>
<td class="d2h-ins d2h-change">
<div class="d2h-code-side-line">
<span class="d2h-code-line-prefix">+</span>
<span class="d2h-code-line-ctn"><ins>test1r</ins></span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>"
`);
});
it('should work for files without blocks', () => {
const exampleJson: DiffFile[] = [
{
blocks: [],
oldName: 'sample',
language: 'js',
newName: 'sample',
isCombined: false,
addedLines: 0,
deletedLines: 0,
isGitDiff: false,
},
];
const hoganUtils = new HoganJsUtils({});
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, {});
const html = sideBySideRenderer.render(exampleJson);
expect(html).toMatchInlineSnapshot(`
"<div class="d2h-wrapper d2h-light-color-scheme">
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="js">
<div class="d2h-file-header">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
</svg> <span class="d2h-file-name">sample</span>
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></span>
<label class="d2h-file-collapse">
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
Viewed
</label>
</div>
<div class="d2h-files-diff">
<div class="d2h-file-side-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
<tr>
<td class="d2h-info">
<div class="d2h-code-side-line">
File without changes
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="d2h-file-side-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>"
`);
});
it('should work for too big file diff', () => {
const exampleJson = [
{
blocks: [
{
header: '<a href="http://example.com">Custom link to render</a>',
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(`
"<div class="d2h-wrapper d2h-light-color-scheme">
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="js">
<div class="d2h-file-header">
<span class="d2h-file-name-wrapper">
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
</svg> <span class="d2h-file-name">sample</span>
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></span>
<label class="d2h-file-collapse">
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
Viewed
</label>
</div>
<div class="d2h-files-diff">
<div class="d2h-file-side-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
<tr>
<td class="d2h-code-side-linenumber d2h-info"></td>
<td class="d2h-info">
<div class="d2h-code-side-line"><a href="http://example.com">Custom link to render</a></div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="d2h-file-side-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
<tr>
<td class="d2h-code-side-linenumber d2h-info"></td>
<td class="d2h-info">
<div class="d2h-code-side-line">&nbsp;</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>"
`);
});
});
describe('processLines', () => {
it('should process file lines', () => {
const oldLines: DiffLine[] = [
{
content: '-test',
type: LineType.DELETE,
oldNumber: 1,
newNumber: undefined,
},
];
const newLines: DiffLine[] = [
{
content: '+test1r',
type: LineType.INSERT,
oldNumber: undefined,
newNumber: 1,
},
];
const hoganUtils = new HoganJsUtils({});
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, { matching: LineMatchingType.LINES });
const html = sideBySideRenderer.processChangedLines(false, oldLines, newLines);
expect(html).toMatchInlineSnapshot(`
{
"left": "<tr>
<td class="d2h-code-side-linenumber d2h-del d2h-change">
1
</td>
<td class="d2h-del d2h-change">
<div class="d2h-code-side-line">
<span class="d2h-code-line-prefix">-</span>
<span class="d2h-code-line-ctn"><del>test</del></span>
</div>
</td>
</tr>",
"right": "<tr>
<td class="d2h-code-side-linenumber d2h-ins d2h-change">
1
</td>
<td class="d2h-ins d2h-change">
<div class="d2h-code-side-line">
<span class="d2h-code-line-prefix">+</span>
<span class="d2h-code-line-ctn"><ins>test1r</ins></span>
</div>
</td>
</tr>",
}
`);
});
});
});

View file

@ -0,0 +1,31 @@
import { escapeForRegExp, unifyPath, hashCode } from '../utils';
describe('Utils', () => {
describe('escapeForRegExp', () => {
it('should escape markdown link text', () => {
const result = escapeForRegExp('[Link](https://diff2html.xyz)');
expect(result).toBe('\\[Link\\]\\(https:\\/\\/diff2html\\.xyz\\)');
});
it('should escape all dangerous characters', () => {
const result = escapeForRegExp('-[]/{}()*+?.\\^$|');
expect(result).toBe('\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|');
});
});
describe('unifyPath', () => {
it('should unify windows style path', () => {
const result = unifyPath('\\Users\\Downloads\\diff.html');
expect(result).toBe('/Users/Downloads/diff.html');
});
});
describe('hashCode', () => {
it('should create consistent hash for a text piece', () => {
const string = '/home/diff2html/diff.html';
expect(hashCode(string)).toEqual(hashCode(string));
});
it('should create different hash for different text pieces', () => {
expect(hashCode('/home/diff2html/diff1.html')).not.toEqual(hashCode('/home/diff2html/diff2.html'));
});
});
});

View file

@ -1,223 +0,0 @@
/*
*
* Diff Parser (diff-parser.js)
* Author: rtfpessoa
*
*/
(function(ctx, undefined) {
var utils = require('./utils.js').Utils;
var LINE_TYPE = {
INSERTS: 'd2h-ins',
DELETES: 'd2h-del',
CONTEXT: 'd2h-cntx',
INFO: 'd2h-info'
};
function DiffParser() {
}
DiffParser.prototype.LINE_TYPE = LINE_TYPE;
DiffParser.prototype.generateDiffJson = function(diffInput) {
var files = [];
var currentFile = null;
var currentBlock = null;
var oldLine = null;
var newLine = null;
var saveBlock = function() {
/* Add previous block(if exists) before start a new file */
if (currentBlock) {
currentFile.blocks.push(currentBlock);
currentBlock = null;
}
};
var saveFile = function() {
/*
* Add previous file(if exists) before start a new one
* if it has name (to avoid binary files errors)
*/
if (currentFile && currentFile.newName) {
files.push(currentFile);
currentFile = null;
}
};
var startFile = function() {
saveBlock();
saveFile();
/* Create file structure */
currentFile = {};
currentFile.blocks = [];
currentFile.deletedLines = 0;
currentFile.addedLines = 0;
};
var startBlock = function(line) {
saveBlock();
var values;
if (values = /^@@ -(\d+),\d+ \+(\d+),\d+ @@.*/.exec(line)) {
currentFile.isCombined = false;
} else if (values = /^@@@ -(\d+),\d+ -\d+,\d+ \+(\d+),\d+ @@@.*/.exec(line)) {
currentFile.isCombined = true;
} else {
values = [0, 0];
currentFile.isCombined = false;
}
oldLine = values[1];
newLine = values[2];
/* Create block metadata */
currentBlock = {};
currentBlock.lines = [];
currentBlock.oldStartLine = oldLine;
currentBlock.newStartLine = newLine;
currentBlock.header = line;
};
var createLine = function(line) {
var currentLine = {};
currentLine.content = line;
var newLinePrefixes = !currentFile.isCombined ? ['+'] : ['+', ' +'];
var delLinePrefixes = !currentFile.isCombined ? ['-'] : ['-', ' -'];
/* Fill the line data */
if (utils.startsWith(line, newLinePrefixes)) {
currentFile.addedLines++;
currentLine.type = LINE_TYPE.INSERTS;
currentLine.oldNumber = null;
currentLine.newNumber = newLine++;
currentBlock.lines.push(currentLine);
} else if (utils.startsWith(line, delLinePrefixes)) {
currentFile.deletedLines++;
currentLine.type = LINE_TYPE.DELETES;
currentLine.oldNumber = oldLine++;
currentLine.newNumber = null;
currentBlock.lines.push(currentLine);
} else {
currentLine.type = LINE_TYPE.CONTEXT;
currentLine.oldNumber = oldLine++;
currentLine.newNumber = newLine++;
currentBlock.lines.push(currentLine);
}
};
var diffLines = diffInput.split('\n');
diffLines.forEach(function(line) {
// Unmerged paths, and possibly other non-diffable files
// https://github.com/scottgonzalez/pretty-diff/issues/11
// Also, remove some useless lines
if (!line || utils.startsWith(line, '*')) {
return;
}
/* Diff */
var oldMode = /^old mode (\d{6})/;
var newMode = /^new mode (\d{6})/;
var deletedFileMode = /^deleted file mode (\d{6})/;
var newFileMode = /^new file mode (\d{6})/;
var copyFrom = /^copy from (.+)/;
var copyTo = /^copy to (.+)/;
var renameFrom = /^rename from (.+)/;
var renameTo = /^rename to (.+)/;
var similarityIndex = /^similarity index (\d+)%/;
var dissimilarityIndex = /^dissimilarity index (\d+)%/;
var index = /^index ([0-9a-z]+)..([0-9a-z]+) (\d{6})?/;
/* Combined Diff */
var combinedIndex = /^index ([0-9a-z]+),([0-9a-z]+)..([0-9a-z]+)/;
var combinedMode = /^mode (\d{6}),(\d{6})..(\d{6})/;
var combinedNewFile = /^new file mode (\d{6})/;
var combinedDeletedFile = /^deleted file mode (\d{6}),(\d{6})/;
var values = [];
if (utils.startsWith(line, 'diff')) {
startFile();
} else if (currentFile && !currentFile.oldName && (values = /^--- [aiwco]\/(.+)$/.exec(line))) {
currentFile.oldName = values[1];
currentFile.language = getExtension(currentFile.oldName, currentFile.language);
} else if (currentFile && !currentFile.newName && (values = /^\+\+\+ [biwco]?\/(.+)$/.exec(line))) {
currentFile.newName = values[1];
currentFile.language = getExtension(currentFile.newName, currentFile.language);
} else if (currentFile && utils.startsWith(line, '@@')) {
startBlock(line);
} else if ((values = oldMode.exec(line))) {
currentFile.oldMode = values[1];
} else if ((values = newMode.exec(line))) {
currentFile.newMode = values[1];
} else if ((values = deletedFileMode.exec(line))) {
currentFile.deletedFileMode = values[1];
} else if ((values = newFileMode.exec(line))) {
currentFile.newFileMode = values[1];
} else if ((values = copyFrom.exec(line))) {
currentFile.oldName = values[1];
currentFile.isCopy = true;
} else if ((values = copyTo.exec(line))) {
currentFile.newName = values[1];
currentFile.isCopy = true;
} else if ((values = renameFrom.exec(line))) {
currentFile.oldName = values[1];
currentFile.isRename = true;
} else if ((values = renameTo.exec(line))) {
currentFile.newName = values[1];
currentFile.isRename = true;
} else if ((values = similarityIndex.exec(line))) {
currentFile.unchangedPercentage = values[1];
} else if ((values = dissimilarityIndex.exec(line))) {
currentFile.changedPercentage = values[1];
} else if ((values = index.exec(line))) {
currentFile.checksumBefore = values[1];
currentFile.checksumAfter = values[2];
values[2] && (currentFile.mode = values[3]);
} else if ((values = combinedIndex.exec(line))) {
currentFile.checksumBefore = [values[2], values[3]];
currentFile.checksumAfter = values[1];
} else if ((values = combinedMode.exec(line))) {
currentFile.oldMode = [values[2], values[3]];
currentFile.newMode = values[1];
} else if ((values = combinedNewFile.exec(line))) {
currentFile.newFileMode = values[1];
} else if ((values = combinedDeletedFile.exec(line))) {
currentFile.deletedFileMode = values[1];
} else if (currentBlock) {
createLine(line);
}
});
saveBlock();
saveFile();
return files;
};
function getExtension(filename, language) {
var nameSplit = filename.split('.');
if (nameSplit.length > 1) {
return nameSplit[nameSplit.length - 1];
} else {
return language;
}
}
module.exports['DiffParser'] = new DiffParser();
})(this);

481
src/diff-parser.ts Normal file
View file

@ -0,0 +1,481 @@
import { DiffFile, DiffBlock, DiffLine, LineType } from './types';
import { escapeForRegExp } from './utils';
export interface DiffParserConfig {
srcPrefix?: string;
dstPrefix?: string;
diffMaxChanges?: number;
diffMaxLineLength?: number;
diffTooBigMessage?: (fileIndex: number) => string;
}
function getExtension(filename: string, language: string): string {
const filenameParts = filename.split('.');
return filenameParts.length > 1 ? filenameParts[filenameParts.length - 1] : language;
}
function startsWithAny(str: string, prefixes: string[]): boolean {
return prefixes.reduce<boolean>((startsWith, prefix) => startsWith || str.startsWith(prefix), false);
}
const baseDiffFilenamePrefixes = ['a/', 'b/', 'i/', 'w/', 'c/', 'o/'];
function getFilename(line: string, linePrefix?: string, extraPrefix?: string): string {
const prefixes = extraPrefix !== undefined ? [...baseDiffFilenamePrefixes, extraPrefix] : baseDiffFilenamePrefixes;
const FilenameRegExp = linePrefix
? new RegExp(`^${escapeForRegExp(linePrefix)} "?(.+?)"?$`)
: new RegExp('^"?(.+?)"?$');
const [, filename = ''] = FilenameRegExp.exec(line) || [];
const matchingPrefix = prefixes.find(p => filename.indexOf(p) === 0);
const fnameWithoutPrefix = matchingPrefix ? filename.slice(matchingPrefix.length) : filename;
// Cleanup timestamps generated by the unified diff (diff command) as specified in
// https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html
// Ie: 2016-10-25 11:37:14.000000000 +0200
return fnameWithoutPrefix.replace(/\s+\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)? [+-]\d{4}.*$/, '');
}
function getSrcFilename(line: string, srcPrefix?: string): string | undefined {
return getFilename(line, '---', srcPrefix);
}
function getDstFilename(line: string, dstPrefix?: string): string | undefined {
return getFilename(line, '+++', dstPrefix);
}
/**
*
* Docs:
* - Unified: https://www.gnu.org/software/diffutils/manual/html_node/Unified-Format.html
* - Git Diff: https://git-scm.com/docs/git-diff-tree#_raw_output_format
* - Git Combined Diff: https://git-scm.com/docs/git-diff-tree#_combined_diff_format
*
*/
export function parse(diffInput: string, config: DiffParserConfig = {}): DiffFile[] {
const files: DiffFile[] = [];
let currentFile: DiffFile | null = null;
let currentBlock: DiffBlock | null = null;
let oldLine: number | null = null;
let oldLine2: number | null = null; // Used for combined diff
let newLine: number | null = null;
let possibleOldName: string | null = null;
let possibleNewName: string | null = null;
/* Diff Header */
const oldFileNameHeader = '--- ';
const newFileNameHeader = '+++ ';
const hunkHeaderPrefix = '@@';
/* Diff */
const oldMode = /^old mode (\d{6})/;
const newMode = /^new mode (\d{6})/;
const deletedFileMode = /^deleted file mode (\d{6})/;
const newFileMode = /^new file mode (\d{6})/;
const copyFrom = /^copy from "?(.+)"?/;
const copyTo = /^copy to "?(.+)"?/;
const renameFrom = /^rename from "?(.+)"?/;
const renameTo = /^rename to "?(.+)"?/;
const similarityIndex = /^similarity index (\d+)%/;
const dissimilarityIndex = /^dissimilarity index (\d+)%/;
const index = /^index ([\da-z]+)\.\.([\da-z]+)\s*(\d{6})?/;
const binaryFiles = /^Binary files (.*) and (.*) differ/;
const binaryDiff = /^GIT binary patch/;
/* Combined Diff */
const combinedIndex = /^index ([\da-z]+),([\da-z]+)\.\.([\da-z]+)/;
const combinedMode = /^mode (\d{6}),(\d{6})\.\.(\d{6})/;
const combinedNewFile = /^new file mode (\d{6})/;
const combinedDeletedFile = /^deleted file mode (\d{6}),(\d{6})/;
const diffLines = diffInput
.replace(/\\ No newline at end of file/g, '')
.replace(/\r\n?/g, '\n')
.split('\n');
/* Add previous block(if exists) before start a new file */
function saveBlock(): void {
if (currentBlock !== null && currentFile !== null) {
currentFile.blocks.push(currentBlock);
currentBlock = null;
}
}
/*
* Add previous file(if exists) before start a new one
* if it has name (to avoid binary files errors)
*/
function saveFile(): void {
if (currentFile !== null) {
if (!currentFile.oldName && possibleOldName !== null) {
currentFile.oldName = possibleOldName;
}
if (!currentFile.newName && possibleNewName !== null) {
currentFile.newName = possibleNewName;
}
if (currentFile.newName) {
files.push(currentFile);
currentFile = null;
}
}
possibleOldName = null;
possibleNewName = null;
}
/* Create file structure */
function startFile(): void {
saveBlock();
saveFile();
// eslint-disable-next-line
// @ts-ignore
currentFile = {
blocks: [],
deletedLines: 0,
addedLines: 0,
};
}
function startBlock(line: string): void {
saveBlock();
let values;
/**
* From Range:
* -<start line>[,<number of lines>]
*
* To Range:
* +<start line>[,<number of lines>]
*
* @@ from-file-range to-file-range @@
*
* @@@ from-file-range from-file-range to-file-range @@@
*
* number of lines is optional, if omited consider 0
*/
if (currentFile !== null) {
if ((values = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@.*/.exec(line))) {
currentFile.isCombined = false;
oldLine = parseInt(values[1], 10);
newLine = parseInt(values[2], 10);
} else if ((values = /^@@@ -(\d+)(?:,\d+)? -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@@.*/.exec(line))) {
currentFile.isCombined = true;
oldLine = parseInt(values[1], 10);
oldLine2 = parseInt(values[2], 10);
newLine = parseInt(values[3], 10);
} else {
if (line.startsWith(hunkHeaderPrefix)) {
console.error('Failed to parse lines, starting in 0!');
}
oldLine = 0;
newLine = 0;
currentFile.isCombined = false;
}
}
/* Create block metadata */
currentBlock = {
lines: [],
// eslint-disable-next-line
// @ts-ignore
oldStartLine: oldLine,
// eslint-disable-next-line
// @ts-ignore
oldStartLine2: oldLine2,
// eslint-disable-next-line
// @ts-ignore
newStartLine: newLine,
header: line,
};
}
function createLine(line: string): void {
if (currentFile === null || currentBlock === null || oldLine === null || newLine === null) return;
// eslint-disable-next-line
// @ts-ignore
const currentLine: DiffLine = {
content: line,
};
const addedPrefixes = currentFile.isCombined ? ['+ ', ' +', '++'] : ['+'];
const deletedPrefixes = currentFile.isCombined ? ['- ', ' -', '--'] : ['-'];
if (startsWithAny(line, addedPrefixes)) {
currentFile.addedLines++;
currentLine.type = LineType.INSERT;
currentLine.oldNumber = undefined;
currentLine.newNumber = newLine++;
} else if (startsWithAny(line, deletedPrefixes)) {
currentFile.deletedLines++;
currentLine.type = LineType.DELETE;
currentLine.oldNumber = oldLine++;
currentLine.newNumber = undefined;
} else {
currentLine.type = LineType.CONTEXT;
currentLine.oldNumber = oldLine++;
currentLine.newNumber = newLine++;
}
currentBlock.lines.push(currentLine);
}
/*
* Checks if there is a hunk header coming before a new file starts
*
* Hunk header is a group of three lines started by ( `--- ` , `+++ ` , `@@` )
*/
function existHunkHeader(line: string, lineIdx: number): boolean {
let idx = lineIdx;
while (idx < diffLines.length - 3) {
if (line.startsWith('diff')) {
return false;
}
if (
diffLines[idx].startsWith(oldFileNameHeader) &&
diffLines[idx + 1].startsWith(newFileNameHeader) &&
diffLines[idx + 2].startsWith(hunkHeaderPrefix)
) {
return true;
}
idx++;
}
return false;
}
diffLines.forEach((line, lineIndex) => {
// Unmerged paths, and possibly other non-diffable files
// https://github.com/scottgonzalez/pretty-diff/issues/11
// Also, remove some useless lines
if (!line || line.startsWith('*')) {
return;
}
// Used to store regex capture groups
let values;
const prevLine = diffLines[lineIndex - 1];
const nxtLine = diffLines[lineIndex + 1];
const afterNxtLine = diffLines[lineIndex + 2];
if (line.startsWith('diff --git') || line.startsWith('diff --combined')) {
startFile();
// diff --git a/blocked_delta_results.png b/blocked_delta_results.png
const gitDiffStart = /^diff --git "?([a-ciow]\/.+)"? "?([a-ciow]\/.+)"?/;
if ((values = gitDiffStart.exec(line))) {
possibleOldName = getFilename(values[1], undefined, config.dstPrefix);
possibleNewName = getFilename(values[2], undefined, config.srcPrefix);
}
if (currentFile === null) {
throw new Error('Where is my file !!!');
}
currentFile.isGitDiff = true;
return;
}
if (line.startsWith('Binary files') && !currentFile?.isGitDiff) {
startFile();
const unixDiffBinaryStart = /^Binary files "?([a-ciow]\/.+)"? and "?([a-ciow]\/.+)"? differ/;
if ((values = unixDiffBinaryStart.exec(line))) {
possibleOldName = getFilename(values[1], undefined, config.dstPrefix);
possibleNewName = getFilename(values[2], undefined, config.srcPrefix);
}
if (currentFile === null) {
throw new Error('Where is my file !!!');
}
currentFile.isBinary = true;
return;
}
if (
!currentFile || // If we do not have a file yet, we should crete one
(!currentFile.isGitDiff &&
currentFile && // If we already have some file in progress and
line.startsWith(oldFileNameHeader) && // If we get to an old file path header line
// And is followed by the new file path header line and the hunk header line
nxtLine.startsWith(newFileNameHeader) &&
afterNxtLine.startsWith(hunkHeaderPrefix))
) {
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) ||
(typeof config.diffMaxLineLength === 'number' && line.length > config.diffMaxLineLength))
) {
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:
* - https://github.com/rtfpessoa/diff2html/issues/87
*/
if (
(line.startsWith(oldFileNameHeader) && nxtLine.startsWith(newFileNameHeader)) ||
(line.startsWith(newFileNameHeader) && prevLine.startsWith(oldFileNameHeader))
) {
/*
* --- Date Timestamp[FractionalSeconds] TimeZone
* --- 2002-02-21 23:30:39.942229878 -0800
*/
if (
currentFile &&
!currentFile.oldName &&
line.startsWith('--- ') &&
(values = getSrcFilename(line, config.srcPrefix))
) {
currentFile.oldName = values;
currentFile.language = getExtension(currentFile.oldName, currentFile.language);
return;
}
/*
* +++ Date Timestamp[FractionalSeconds] TimeZone
* +++ 2002-02-21 23:30:39.942229878 -0800
*/
if (
currentFile &&
!currentFile.newName &&
line.startsWith('+++ ') &&
(values = getDstFilename(line, config.dstPrefix))
) {
currentFile.newName = values;
currentFile.language = getExtension(currentFile.newName, currentFile.language);
return;
}
}
if (
currentFile &&
(line.startsWith(hunkHeaderPrefix) ||
(currentFile.isGitDiff && currentFile.oldName && currentFile.newName && !currentBlock))
) {
startBlock(line);
return;
}
/*
* There are three types of diff lines. These lines are defined by the way they start.
* 1. New line starts with: +
* 2. Old line starts with: -
* 3. Context line starts with: <SPACE>
*/
if (currentBlock && (line.startsWith('+') || line.startsWith('-') || line.startsWith(' '))) {
createLine(line);
return;
}
const doesNotExistHunkHeader = !existHunkHeader(line, lineIndex);
if (currentFile === null) {
throw new Error('Where is my file !!!');
}
/*
* Git diffs provide more information regarding files modes, renames, copies,
* commits between changes and similarity indexes
*/
if ((values = oldMode.exec(line))) {
currentFile.oldMode = values[1];
} else if ((values = newMode.exec(line))) {
currentFile.newMode = values[1];
} else if ((values = deletedFileMode.exec(line))) {
currentFile.deletedFileMode = values[1];
currentFile.isDeleted = true;
} else if ((values = newFileMode.exec(line))) {
currentFile.newFileMode = values[1];
currentFile.isNew = true;
} else if ((values = copyFrom.exec(line))) {
if (doesNotExistHunkHeader) {
currentFile.oldName = values[1];
}
currentFile.isCopy = true;
} else if ((values = copyTo.exec(line))) {
if (doesNotExistHunkHeader) {
currentFile.newName = values[1];
}
currentFile.isCopy = true;
} else if ((values = renameFrom.exec(line))) {
if (doesNotExistHunkHeader) {
currentFile.oldName = values[1];
}
currentFile.isRename = true;
} else if ((values = renameTo.exec(line))) {
if (doesNotExistHunkHeader) {
currentFile.newName = values[1];
}
currentFile.isRename = true;
} else if ((values = binaryFiles.exec(line))) {
currentFile.isBinary = true;
currentFile.oldName = getFilename(values[1], undefined, config.srcPrefix);
currentFile.newName = getFilename(values[2], undefined, config.dstPrefix);
startBlock('Binary file');
} else if (binaryDiff.test(line)) {
currentFile.isBinary = true;
startBlock(line);
} else if ((values = similarityIndex.exec(line))) {
currentFile.unchangedPercentage = parseInt(values[1], 10);
} else if ((values = dissimilarityIndex.exec(line))) {
currentFile.changedPercentage = parseInt(values[1], 10);
} else if ((values = index.exec(line))) {
currentFile.checksumBefore = values[1];
currentFile.checksumAfter = values[2];
if (values[3]) currentFile.mode = values[3];
} else if ((values = combinedIndex.exec(line))) {
currentFile.checksumBefore = [values[2], values[3]];
currentFile.checksumAfter = values[1];
} else if ((values = combinedMode.exec(line))) {
currentFile.oldMode = [values[2], values[3]];
currentFile.newMode = values[1];
} else if ((values = combinedNewFile.exec(line))) {
currentFile.newFileMode = values[1];
currentFile.isNew = true;
} else if ((values = combinedDeletedFile.exec(line))) {
currentFile.deletedFileMode = values[1];
currentFile.isDeleted = true;
}
});
saveBlock();
saveFile();
return files;
}

View file

@ -1,110 +0,0 @@
/*
*
* Diff to HTML (diff2html.js)
* Author: rtfpessoa
*
*/
(function(ctx, undefined) {
var diffParser = require('./diff-parser.js').DiffParser;
var fileLister = require('./file-list-printer.js').FileListPrinter;
var htmlPrinter = require('./html-printer.js').HtmlPrinter;
function Diff2Html() {
}
/*
* Line diff type configuration
var config = {
"wordByWord": true, // (default)
// OR
"charByChar": true
};
*/
/*
* Generates json object from string diff input
*/
Diff2Html.prototype.getJsonFromDiff = function(diffInput) {
return diffParser.generateDiffJson(diffInput);
};
/*
* Generates the html diff. The config parameter configures the output/input formats and other options
*/
Diff2Html.prototype.getPrettyHtml = function(diffInput, config) {
var configOrEmpty = config || {};
var diffJson = diffInput;
if(!configOrEmpty.inputFormat || configOrEmpty.inputFormat === 'diff') {
diffJson = diffParser.generateDiffJson(diffInput);
}
var fileList = "";
if(configOrEmpty.showFiles === true) {
fileList = fileLister.generateFileList(diffJson, configOrEmpty);
}
var diffOutput = "";
if(configOrEmpty.outputFormat === 'side-by-side') {
diffOutput = htmlPrinter.generateSideBySideJsonHtml(diffJson, configOrEmpty);
} else {
diffOutput = htmlPrinter.generateLineByLineJsonHtml(diffJson, configOrEmpty);
}
return fileList + diffOutput
};
/*
* Deprecated methods - The following methods exist only to maintain compatibility with previous versions
*/
/*
* Generates pretty html from string diff input
*/
Diff2Html.prototype.getPrettyHtmlFromDiff = function(diffInput, config) {
var configOrEmpty = config || {};
configOrEmpty['inputFormat'] = 'diff';
configOrEmpty['outputFormat'] = 'line-by-line';
return this.getPrettyHtml(diffInput, configOrEmpty)
};
/*
* Generates pretty html from a json object
*/
Diff2Html.prototype.getPrettyHtmlFromJson = function(diffJson, config) {
var configOrEmpty = config || {};
configOrEmpty['inputFormat'] = 'json';
configOrEmpty['outputFormat'] = 'line-by-line';
return this.getPrettyHtml(diffJson, configOrEmpty)
};
/*
* Generates pretty side by side html from string diff input
*/
Diff2Html.prototype.getPrettySideBySideHtmlFromDiff = function(diffInput, config) {
var configOrEmpty = config || {};
configOrEmpty['inputFormat'] = 'diff';
configOrEmpty['outputFormat'] = 'side-by-side';
return this.getPrettyHtml(diffInput, configOrEmpty)
};
/*
* Generates pretty side by side html from a json object
*/
Diff2Html.prototype.getPrettySideBySideHtmlFromJson = function(diffJson, config) {
var configOrEmpty = config || {};
configOrEmpty['inputFormat'] = 'json';
configOrEmpty['outputFormat'] = 'side-by-side';
return this.getPrettyHtml(diffJson, configOrEmpty)
};
var diffName = 'Diff2Html';
var diffObject = new Diff2Html();
module.exports[diffName] = diffObject;
// Expose diff2html in the browser
global[diffName] = diffObject;
})(this);

46
src/diff2html.ts Normal file
View file

@ -0,0 +1,46 @@
import * as DiffParser from './diff-parser';
import { FileListRenderer } from './file-list-renderer';
import LineByLineRenderer, { LineByLineRendererConfig, defaultLineByLineRendererConfig } from './line-by-line-renderer';
import SideBySideRenderer, { SideBySideRendererConfig, defaultSideBySideRendererConfig } from './side-by-side-renderer';
import { DiffFile, OutputFormatType } from './types';
import HoganJsUtils, { HoganJsUtilsConfig } from './hoganjs-utils';
export interface Diff2HtmlConfig
extends DiffParser.DiffParserConfig,
LineByLineRendererConfig,
SideBySideRendererConfig,
HoganJsUtilsConfig {
outputFormat?: OutputFormatType;
drawFileList?: boolean;
}
export const defaultDiff2HtmlConfig = {
...defaultLineByLineRendererConfig,
...defaultSideBySideRendererConfig,
outputFormat: OutputFormatType.LINE_BY_LINE,
drawFileList: true,
};
export function parse(diffInput: string, configuration: Diff2HtmlConfig = {}): DiffFile[] {
return DiffParser.parse(diffInput, { ...defaultDiff2HtmlConfig, ...configuration });
}
export function html(diffInput: string | DiffFile[], configuration: Diff2HtmlConfig = {}): string {
const config = { ...defaultDiff2HtmlConfig, ...configuration };
const diffJson = typeof diffInput === 'string' ? DiffParser.parse(diffInput, config) : diffInput;
const hoganUtils = new HoganJsUtils(config);
const { colorScheme } = config;
const fileListRendererConfig = { colorScheme };
const fileList = config.drawFileList ? new FileListRenderer(hoganUtils, fileListRendererConfig).render(diffJson) : '';
const diffOutput =
config.outputFormat === 'side-by-side'
? new SideBySideRenderer(hoganUtils, config).render(diffJson)
: new LineByLineRenderer(hoganUtils, config).render(diffJson);
return fileList + diffOutput;
}

View file

@ -1,41 +0,0 @@
/*
*
* FileListPrinter (file-list-printer.js)
* Author: nmatpt
*
*/
(function (ctx, undefined) {
var printerUtils = require('./printer-utils.js').PrinterUtils;
var utils = require('./utils.js').Utils;
function FileListPrinter() {
}
FileListPrinter.prototype.generateFileList = function (diffFiles) {
var hideId = utils.getRandomId("d2h-hide"); //necessary if there are 2 elements like this in the same page
var showId = utils.getRandomId("d2h-show");
return '<div class="d2h-file-list-wrapper">\n' +
' <div class="d2h-file-list-header">Files changed (' + diffFiles.length + ')&nbsp&nbsp</div>\n' +
' <a id="' + hideId + '" class="d2h-hide" href="#' + hideId + '">+</a>\n' +
' <a id="' + showId + 'd2h-show" class="d2h-show" href="#' + showId + '">-</a>\n' +
' <div class="d2h-clear"></div>\n' +
' <div class="d2h-file-list">\n' +
diffFiles.map(function (file) {
return ' <div class="d2h-file-list-line">\n' +
' <div class="d2h-file-stats">\n' +
' <span class="d2h-lines-added">+' + file.addedLines + '</span>\n' +
' <span class="d2h-lines-deleted">-' + file.deletedLines + '</span>\n' +
' </div>\n' +
' <div class="d2h-file-name"><a href="#' + printerUtils.getHtmlId(file) + '">&nbsp;' + printerUtils.getDiffName(file) + '</a></div>\n' +
' </div>\n'
}).join('\n') +
'</div></div>\n';
};
module.exports['FileListPrinter'] = new FileListPrinter();
})(this);

52
src/file-list-renderer.ts Normal file
View file

@ -0,0 +1,52 @@
import * as renderUtils from './render-utils';
import HoganJsUtils from './hoganjs-utils';
import { ColorSchemeType, DiffFile } from './types';
const baseTemplatesPath = 'file-summary';
const iconsBaseTemplatesPath = 'icon';
export interface FileListRendererConfig {
colorScheme?: ColorSchemeType;
}
export const defaultFileListRendererConfig = {
colorScheme: renderUtils.defaultRenderConfig.colorScheme,
};
export class FileListRenderer {
private readonly hoganUtils: HoganJsUtils;
private readonly config: typeof defaultFileListRendererConfig;
constructor(hoganUtils: HoganJsUtils, config: FileListRendererConfig = {}) {
this.hoganUtils = hoganUtils;
this.config = { ...defaultFileListRendererConfig, ...config };
}
render(diffFiles: DiffFile[]): string {
const files = diffFiles
.map(file =>
this.hoganUtils.render(
baseTemplatesPath,
'line',
{
fileHtmlId: renderUtils.getHtmlId(file),
oldName: file.oldName,
newName: file.newName,
fileName: renderUtils.filenameDiff(file),
deletedLines: '-' + file.deletedLines,
addedLines: '+' + file.addedLines,
},
{
fileIcon: this.hoganUtils.template(iconsBaseTemplatesPath, renderUtils.getFileIcon(file)),
},
),
)
.join('\n');
return this.hoganUtils.render(baseTemplatesPath, 'wrapper', {
colorScheme: renderUtils.colorSchemeToCss(this.config.colorScheme),
filesNumber: diffFiles.length,
files: files,
});
}
}

54
src/hoganjs-utils.ts Normal file
View file

@ -0,0 +1,54 @@
import * as Hogan from 'hogan.js';
import { defaultTemplates } from './diff2html-templates';
export interface RawTemplates {
[name: string]: string;
}
export interface CompiledTemplates {
[name: string]: Hogan.Template;
}
export interface HoganJsUtilsConfig {
compiledTemplates?: CompiledTemplates;
rawTemplates?: RawTemplates;
}
export default class HoganJsUtils {
private preCompiledTemplates: CompiledTemplates;
constructor({ compiledTemplates = {}, rawTemplates = {} }: HoganJsUtilsConfig) {
const compiledRawTemplates = Object.entries(rawTemplates).reduce<CompiledTemplates>(
(previousTemplates, [name, templateString]) => {
const compiledTemplate: Hogan.Template = Hogan.compile(templateString, { asString: false });
return { ...previousTemplates, [name]: compiledTemplate };
},
{},
);
this.preCompiledTemplates = { ...defaultTemplates, ...compiledTemplates, ...compiledRawTemplates };
}
static compile(templateString: string): Hogan.Template {
return Hogan.compile(templateString, { asString: false });
}
render(namespace: string, view: string, params: Hogan.Context, partials?: Hogan.Partials, indent?: string): string {
const templateKey = this.templateKey(namespace, view);
try {
const template = this.preCompiledTemplates[templateKey];
return template.render(params, partials, indent);
} catch (_e) {
throw new Error(`Could not find template to render '${templateKey}'`);
}
}
template(namespace: string, view: string): Hogan.Template {
return this.preCompiledTemplates[this.templateKey(namespace, view)];
}
private templateKey(namespace: string, view: string): string {
return `${namespace}-${view}`;
}
}

View file

@ -1,22 +0,0 @@
/*
*
* HtmlPrinter (html-printer.js)
* Author: rtfpessoa
*
*/
(function(ctx, undefined) {
var lineByLinePrinter = require('./line-by-line-printer.js').LineByLinePrinter;
var sideBySidePrinter = require('./side-by-side-printer.js').SideBySidePrinter;
function HtmlPrinter() {
}
HtmlPrinter.prototype.generateLineByLineJsonHtml = lineByLinePrinter.generateLineByLineJsonHtml;
HtmlPrinter.prototype.generateSideBySideJsonHtml = sideBySidePrinter.generateSideBySideJsonHtml;
module.exports['HtmlPrinter'] = new HtmlPrinter();
})(this);

View file

@ -1,168 +0,0 @@
/*
*
* LineByLinePrinter (line-by-line-printer.js)
* Author: rtfpessoa
*
*/
(function(ctx, undefined) {
var diffParser = require('./diff-parser.js').DiffParser;
var printerUtils = require('./printer-utils.js').PrinterUtils;
var utils = require('./utils.js').Utils;
function LineByLinePrinter() {
}
LineByLinePrinter.prototype.generateLineByLineJsonHtml = function(diffFiles, config) {
return '<div class="d2h-wrapper">\n' +
diffFiles.map(function(file) {
var diffs;
if (file.blocks.length) {
diffs = generateFileHtml(file, config);
} else {
diffs = generateEmptyDiff();
}
return '<div id="' + printerUtils.getHtmlId(file) + '" class="d2h-file-wrapper" data-lang="' + file.language + '">\n' +
' <div class="d2h-file-header">\n' +
' <div class="d2h-file-stats">\n' +
' <span class="d2h-lines-added">+' + file.addedLines + '</span>\n' +
' <span class="d2h-lines-deleted">-' + file.deletedLines + '</span>\n' +
' </div>\n' +
' <div class="d2h-file-name">' + printerUtils.getDiffName(file) + '</div>\n' +
' </div>\n' +
' <div class="d2h-file-diff">\n' +
' <div class="d2h-code-wrapper">\n' +
' <table class="d2h-diff-table">\n' +
' <tbody class="d2h-diff-tbody">\n' +
' ' + diffs +
' </tbody>\n' +
' </table>\n' +
' </div>\n' +
' </div>\n' +
' </div>\n';
}).join('\n') +
'</div>\n';
};
function generateFileHtml(file, config) {
return file.blocks.map(function(block) {
var lines = '<tr>\n' +
' <td class="d2h-code-linenumber ' + diffParser.LINE_TYPE.INFO + '"></td>\n' +
' <td class="' + diffParser.LINE_TYPE.INFO + '">' +
' <div class="d2h-code-line ' + diffParser.LINE_TYPE.INFO + '">' + utils.escape(block.header) + '</div>' +
' </td>\n' +
'</tr>\n';
var oldLines = [];
var newLines = [];
var processedOldLines = [];
var processedNewLines = [];
for (var i = 0; i < block.lines.length; i++) {
var line = block.lines[i];
var escapedLine = utils.escape(line.content);
if (line.type == diffParser.LINE_TYPE.CONTEXT && !oldLines.length && !newLines.length) {
lines += generateLineHtml(line.type, line.oldNumber, line.newNumber, escapedLine);
} else if (line.type == diffParser.LINE_TYPE.INSERTS && !oldLines.length && !newLines.length) {
lines += generateLineHtml(line.type, line.oldNumber, line.newNumber, escapedLine);
} else if (line.type == diffParser.LINE_TYPE.DELETES && !newLines.length) {
oldLines.push(line);
} else if (line.type == diffParser.LINE_TYPE.INSERTS && oldLines.length > newLines.length) {
newLines.push(line);
} else {
var j = 0;
var oldLine, newLine;
if (oldLines.length === newLines.length) {
for (j = 0; j < oldLines.length; j++) {
oldLine = oldLines[j];
newLine = newLines[j];
config.isCombined = file.isCombined;
var diff = printerUtils.diffHighlight(oldLine.content, newLine.content, config);
processedOldLines +=
generateLineHtml(oldLine.type, oldLine.oldNumber, oldLine.newNumber,
diff.first.line, diff.first.prefix);
processedNewLines +=
generateLineHtml(newLine.type, newLine.oldNumber, newLine.newNumber,
diff.second.line, diff.second.prefix);
}
lines += processedOldLines + processedNewLines;
} else {
lines += processLines(oldLines, newLines);
}
oldLines = [];
newLines = [];
processedOldLines = [];
processedNewLines = [];
i--;
}
}
lines += processLines(oldLines, newLines);
return lines;
}).join('\n');
}
function processLines(oldLines, newLines) {
var lines = '';
for (j = 0; j < oldLines.length; j++) {
var oldLine = oldLines[j];
var oldEscapedLine = utils.escape(oldLine.content);
lines += generateLineHtml(oldLine.type, oldLine.oldNumber, oldLine.newNumber, oldEscapedLine);
}
for (j = 0; j < newLines.length; j++) {
var newLine = newLines[j];
var newEscapedLine = utils.escape(newLine.content);
lines += generateLineHtml(newLine.type, newLine.oldNumber, newLine.newNumber, newEscapedLine);
}
return lines;
}
function generateLineHtml(type, oldNumber, newNumber, content, prefix) {
var htmlPrefix = '';
if (prefix) {
htmlPrefix = '<span class="d2h-code-line-prefix">' + prefix + '</span>';
}
var htmlContent = '';
if (content) {
htmlContent = '<span class="d2h-code-line-ctn">' + content + '</span>';
}
return '<tr>\n' +
' <td class="d2h-code-linenumber ' + type + '">' +
' <div class="line-num1">' + utils.valueOrEmpty(oldNumber) + '</div>' +
' <div class="line-num2">' + utils.valueOrEmpty(newNumber) + '</div>' +
' </td>\n' +
' <td class="' + type + '">' +
' <div class="d2h-code-line ' + type + '">' + htmlPrefix + htmlContent + '</div>' +
' </td>\n' +
'</tr>\n';
}
function generateEmptyDiff() {
return '<tr>\n' +
' <td class="' + diffParser.LINE_TYPE.INFO + '">' +
' <div class="d2h-code-line ' + diffParser.LINE_TYPE.INFO + '">' +
'File without changes' +
' </div>' +
' </td>\n' +
'</tr>\n';
}
module.exports['LineByLinePrinter'] = new LineByLinePrinter();
})(this);

View file

@ -0,0 +1,299 @@
import HoganJsUtils from './hoganjs-utils';
import * as Rematch from './rematch';
import * as renderUtils from './render-utils';
import {
DiffFile,
DiffLine,
LineType,
DiffBlock,
DiffLineDeleted,
DiffLineContent,
DiffLineContext,
DiffLineInserted,
} from './types';
export interface LineByLineRendererConfig extends renderUtils.RenderConfig {
renderNothingWhenEmpty?: boolean;
matchingMaxComparisons?: number;
maxLineSizeInBlockForComparison?: number;
}
export const defaultLineByLineRendererConfig = {
...renderUtils.defaultRenderConfig,
renderNothingWhenEmpty: false,
matchingMaxComparisons: 2500,
maxLineSizeInBlockForComparison: 200,
};
const genericTemplatesPath = 'generic';
const baseTemplatesPath = 'line-by-line';
const iconsBaseTemplatesPath = 'icon';
const tagsBaseTemplatesPath = 'tag';
export default class LineByLineRenderer {
private readonly hoganUtils: HoganJsUtils;
private readonly config: typeof defaultLineByLineRendererConfig;
constructor(hoganUtils: HoganJsUtils, config: LineByLineRendererConfig = {}) {
this.hoganUtils = hoganUtils;
this.config = { ...defaultLineByLineRendererConfig, ...config };
}
render(diffFiles: DiffFile[]): string {
const diffsHtml = diffFiles
.map(file => {
let diffs;
if (file.blocks.length) {
diffs = this.generateFileHtml(file);
} else {
diffs = this.generateEmptyDiff();
}
return this.makeFileDiffHtml(file, diffs);
})
.join('\n');
return this.hoganUtils.render(genericTemplatesPath, 'wrapper', {
colorScheme: renderUtils.colorSchemeToCss(this.config.colorScheme),
content: diffsHtml,
});
}
makeFileDiffHtml(file: DiffFile, diffs: string): string {
if (this.config.renderNothingWhenEmpty && Array.isArray(file.blocks) && file.blocks.length === 0) return '';
const fileDiffTemplate = this.hoganUtils.template(baseTemplatesPath, 'file-diff');
const filePathTemplate = this.hoganUtils.template(genericTemplatesPath, 'file-path');
const fileIconTemplate = this.hoganUtils.template(iconsBaseTemplatesPath, 'file');
const fileTagTemplate = this.hoganUtils.template(tagsBaseTemplatesPath, renderUtils.getFileIcon(file));
return fileDiffTemplate.render({
file: file,
fileHtmlId: renderUtils.getHtmlId(file),
diffs: diffs,
filePath: filePathTemplate.render(
{
fileDiffName: renderUtils.filenameDiff(file),
},
{
fileIcon: fileIconTemplate,
fileTag: fileTagTemplate,
},
),
});
}
generateEmptyDiff(): string {
return this.hoganUtils.render(genericTemplatesPath, 'empty-diff', {
contentClass: 'd2h-code-line',
CSSLineClass: renderUtils.CSSLineClass,
});
}
generateFileHtml(file: DiffFile): string {
const matcher = Rematch.newMatcherFn(
Rematch.newDistanceFn((e: DiffLine) => renderUtils.deconstructLine(e.content, file.isCombined).content),
);
return file.blocks
.map(block => {
let lines = this.hoganUtils.render(genericTemplatesPath, 'block-header', {
CSSLineClass: renderUtils.CSSLineClass,
blockHeader: file.isTooBig ? block.header : renderUtils.escapeForHtml(block.header),
lineClass: 'd2h-code-linenumber',
contentClass: 'd2h-code-line',
});
this.applyLineGroupping(block).forEach(([contextLines, oldLines, newLines]) => {
if (oldLines.length && newLines.length && !contextLines.length) {
this.applyRematchMatching(oldLines, newLines, matcher).map(([oldLines, newLines]) => {
const { left, right } = this.processChangedLines(file, file.isCombined, oldLines, newLines);
lines += left;
lines += right;
});
} else if (contextLines.length) {
contextLines.forEach(line => {
const { prefix, content } = renderUtils.deconstructLine(line.content, file.isCombined);
lines += this.generateSingleLineHtml(file, {
type: renderUtils.CSSLineClass.CONTEXT,
prefix: prefix,
content: content,
oldNumber: line.oldNumber,
newNumber: line.newNumber,
});
});
} else if (oldLines.length || newLines.length) {
const { left, right } = this.processChangedLines(file, file.isCombined, oldLines, newLines);
lines += left;
lines += right;
} else {
console.error('Unknown state reached while processing groups of lines', contextLines, oldLines, newLines);
}
});
return lines;
})
.join('\n');
}
applyLineGroupping(block: DiffBlock): DiffLineGroups {
const blockLinesGroups: DiffLineGroups = [];
let oldLines: (DiffLineDeleted & DiffLineContent)[] = [];
let newLines: (DiffLineInserted & DiffLineContent)[] = [];
for (let i = 0; i < block.lines.length; i++) {
const diffLine = block.lines[i];
if (
(diffLine.type !== LineType.INSERT && newLines.length) ||
(diffLine.type === LineType.CONTEXT && oldLines.length > 0)
) {
blockLinesGroups.push([[], oldLines, newLines]);
oldLines = [];
newLines = [];
}
if (diffLine.type === LineType.CONTEXT) {
blockLinesGroups.push([[diffLine], [], []]);
} else if (diffLine.type === LineType.INSERT && oldLines.length === 0) {
blockLinesGroups.push([[], [], [diffLine]]);
} else if (diffLine.type === LineType.INSERT && oldLines.length > 0) {
newLines.push(diffLine);
} else if (diffLine.type === LineType.DELETE) {
oldLines.push(diffLine);
}
}
if (oldLines.length || newLines.length) {
blockLinesGroups.push([[], oldLines, newLines]);
oldLines = [];
newLines = [];
}
return blockLinesGroups;
}
applyRematchMatching(
oldLines: DiffLine[],
newLines: DiffLine[],
matcher: Rematch.MatcherFn<DiffLine>,
): DiffLine[][][] {
const comparisons = oldLines.length * newLines.length;
const maxLineSizeInBlock = Math.max.apply(
null,
[0].concat(oldLines.concat(newLines).map(elem => elem.content.length)),
);
const doMatching =
comparisons < this.config.matchingMaxComparisons &&
maxLineSizeInBlock < this.config.maxLineSizeInBlockForComparison &&
(this.config.matching === 'lines' || this.config.matching === 'words');
return doMatching ? matcher(oldLines, newLines) : [[oldLines, newLines]];
}
processChangedLines(file: DiffFile, isCombined: boolean, oldLines: DiffLine[], newLines: DiffLine[]): FileHtml {
const fileHtml = {
right: '',
left: '',
};
const maxLinesNumber = Math.max(oldLines.length, newLines.length);
for (let i = 0; i < maxLinesNumber; i++) {
const oldLine = oldLines[i];
const newLine = newLines[i];
const diff =
oldLine !== undefined && newLine !== undefined
? renderUtils.diffHighlight(oldLine.content, newLine.content, isCombined, this.config)
: undefined;
const preparedOldLine =
oldLine !== undefined && oldLine.oldNumber !== undefined
? {
...(diff !== undefined
? {
prefix: diff.oldLine.prefix,
content: diff.oldLine.content,
type: renderUtils.CSSLineClass.DELETE_CHANGES,
}
: {
...renderUtils.deconstructLine(oldLine.content, isCombined),
type: renderUtils.toCSSClass(oldLine.type),
}),
oldNumber: oldLine.oldNumber,
newNumber: oldLine.newNumber,
}
: undefined;
const preparedNewLine =
newLine !== undefined && newLine.newNumber !== undefined
? {
...(diff !== undefined
? {
prefix: diff.newLine.prefix,
content: diff.newLine.content,
type: renderUtils.CSSLineClass.INSERT_CHANGES,
}
: {
...renderUtils.deconstructLine(newLine.content, isCombined),
type: renderUtils.toCSSClass(newLine.type),
}),
oldNumber: newLine.oldNumber,
newNumber: newLine.newNumber,
}
: undefined;
const { left, right } = this.generateLineHtml(file, preparedOldLine, preparedNewLine);
fileHtml.left += left;
fileHtml.right += right;
}
return fileHtml;
}
generateLineHtml(file: DiffFile, oldLine?: DiffPreparedLine, newLine?: DiffPreparedLine): FileHtml {
return {
left: this.generateSingleLineHtml(file, oldLine),
right: this.generateSingleLineHtml(file, newLine),
};
}
generateSingleLineHtml(file: DiffFile, line?: DiffPreparedLine): string {
if (line === undefined) return '';
const lineNumberHtml = this.hoganUtils.render(baseTemplatesPath, 'numbers', {
oldNumber: line.oldNumber || '',
newNumber: line.newNumber || '',
});
return this.hoganUtils.render(genericTemplatesPath, 'line', {
type: line.type,
lineClass: 'd2h-code-linenumber',
contentClass: 'd2h-code-line',
prefix: line.prefix === ' ' ? '&nbsp;' : line.prefix,
content: line.content,
lineNumber: lineNumberHtml,
line,
file,
});
}
}
type DiffLineGroups = [
(DiffLineContext & DiffLineContent)[],
(DiffLineDeleted & DiffLineContent)[],
(DiffLineInserted & DiffLineContent)[],
][];
type DiffPreparedLine = {
type: renderUtils.CSSLineClass;
prefix: string;
content: string;
oldNumber?: number;
newNumber?: number;
};
type FileHtml = {
left: string;
right: string;
};

View file

@ -1,109 +0,0 @@
/*
*
* PrinterUtils (printer-utils.js)
* Author: rtfpessoa
*
*/
(function(ctx, undefined) {
var jsDiff = require('diff');
var utils = require('./utils.js').Utils;
function PrinterUtils() {
}
PrinterUtils.prototype.getHtmlId = function(file) {
var hashCode = function(text) {
var hash = 0, i, chr, len;
if (text.length == 0) return hash;
for (i = 0, len = text.length; i < len; i++) {
chr = text.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
};
return "d2h-" + hashCode(this.getDiffName(file)).toString().slice(-6);
};
PrinterUtils.prototype.getDiffName = function(file) {
var oldFilename = file.oldName;
var newFilename = file.newName;
if (oldFilename && newFilename
&& oldFilename !== newFilename
&& !isDeletedName(newFilename)) {
return oldFilename + ' -> ' + newFilename;
} else if (newFilename && !isDeletedName(newFilename)) {
return newFilename;
} else if (oldFilename) {
return oldFilename;
} else {
return 'Unknown filename';
}
};
PrinterUtils.prototype.diffHighlight = function(diffLine1, diffLine2, config) {
var lineStart1, lineStart2;
var prefixSize = 1;
if (config.isCombined) {
prefixSize = 2;
}
lineStart1 = diffLine1.substr(0, prefixSize);
lineStart2 = diffLine2.substr(0, prefixSize);
diffLine1 = diffLine1.substr(prefixSize);
diffLine2 = diffLine2.substr(prefixSize);
var diff;
if (config.charByChar) {
diff = jsDiff.diffChars(diffLine1, diffLine2);
} else {
diff = jsDiff.diffWordsWithSpace(diffLine1, diffLine2);
}
var highlightedLine = '';
diff.forEach(function(part) {
var elemType = part.added ? 'ins' : part.removed ? 'del' : null;
var escapedValue = utils.escape(part.value);
if (elemType !== null) {
highlightedLine += '<' + elemType + '>' + escapedValue + '</' + elemType + '>';
} else {
highlightedLine += escapedValue;
}
});
return {
first: {
prefix: lineStart1,
line: removeIns(highlightedLine)
},
second: {
prefix: lineStart2,
line: removeDel(highlightedLine)
}
}
};
function isDeletedName(name) {
return name === 'dev/null';
}
function removeIns(line) {
return line.replace(/(<ins>((.|\n)*?)<\/ins>)/g, '');
}
function removeDel(line) {
return line.replace(/(<del>((.|\n)*?)<\/del>)/g, '');
}
module.exports['PrinterUtils'] = new PrinterUtils();
})(this);

135
src/rematch.ts Normal file
View file

@ -0,0 +1,135 @@
/*
* Matching two sequences of objects by similarity
* Author: W. Illmeyer, Nexxar GmbH
*/
export type BestMatch = {
indexA: number;
indexB: number;
score: number;
};
/*
Copyright (c) 2011 Andrei Mackenzie
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
export function levenshtein(a: string, b: string): number {
if (a.length === 0) {
return b.length;
}
if (b.length === 0) {
return a.length;
}
const matrix = [];
// Increment along the first column of each row
let i;
for (i = 0; i <= b.length; i++) {
matrix[i] = [i];
}
// Increment each column in the first row
let j;
for (j = 0; j <= a.length; j++) {
matrix[0][j] = j;
}
// Fill in the rest of the matrix
for (i = 1; i <= b.length; i++) {
for (j = 1; j <= a.length; j++) {
if (b.charAt(i - 1) === a.charAt(j - 1)) {
matrix[i][j] = matrix[i - 1][j - 1];
} else {
matrix[i][j] = Math.min(
matrix[i - 1][j - 1] + 1, // Substitution
Math.min(
matrix[i][j - 1] + 1, // Insertion
matrix[i - 1][j] + 1,
),
); // Deletion
}
}
}
return matrix[b.length][a.length];
}
export type DistanceFn<T> = (x: T, y: T) => number;
export function newDistanceFn<T>(str: (value: T) => string): DistanceFn<T> {
return (x: T, y: T): number => {
const xValue = str(x).trim();
const yValue = str(y).trim();
const lev = levenshtein(xValue, yValue);
return lev / (xValue.length + yValue.length);
};
}
export type MatcherFn<T> = (a: T[], b: T[], level?: number, cache?: Map<string, number>) => T[][][];
export function newMatcherFn<T>(distance: (x: T, y: T) => number): MatcherFn<T> {
function findBestMatch(a: T[], b: T[], cache: Map<string, number> = new Map()): BestMatch | undefined {
let bestMatchDist = Infinity;
let bestMatch;
for (let i = 0; i < a.length; ++i) {
for (let j = 0; j < b.length; ++j) {
const cacheKey = JSON.stringify([a[i], b[j]]);
let md;
if (!(cache.has(cacheKey) && (md = cache.get(cacheKey)))) {
md = distance(a[i], b[j]);
cache.set(cacheKey, md);
}
if (md < bestMatchDist) {
bestMatchDist = md;
bestMatch = { indexA: i, indexB: j, score: bestMatchDist };
}
}
}
return bestMatch;
}
function group(a: T[], b: T[], level = 0, cache: Map<string, number> = new Map()): T[][][] {
const bm = findBestMatch(a, b, cache);
if (!bm || a.length + b.length < 3) {
return [[a, b]];
}
const a1 = a.slice(0, bm.indexA);
const b1 = b.slice(0, bm.indexB);
const aMatch = [a[bm.indexA]];
const bMatch = [b[bm.indexB]];
const tailA = bm.indexA + 1;
const tailB = bm.indexB + 1;
const a2 = a.slice(tailA);
const b2 = b.slice(tailB);
const group1 = group(a1, b1, level + 1, cache);
const groupMatch = group(aMatch, bMatch, level + 1, cache);
const group2 = group(a2, b2, level + 1, cache);
let result = groupMatch;
if (bm.indexA > 0 || bm.indexB > 0) {
result = group1.concat(result);
}
if (a.length > tailA || b.length > tailB) {
result = result.concat(group2);
}
return result;
}
return group;
}

304
src/render-utils.ts Normal file
View file

@ -0,0 +1,304 @@
import * as jsDiff from 'diff';
import { unifyPath, hashCode } from './utils';
import * as rematch from './rematch';
import {
ColorSchemeType,
DiffFile,
DiffFileName,
DiffLineParts,
DiffStyleType,
LineMatchingType,
LineType,
} from './types';
export type CSSLineClass =
| 'd2h-ins'
| 'd2h-del'
| 'd2h-cntx'
| 'd2h-info'
| 'd2h-ins d2h-change'
| 'd2h-del d2h-change';
export const CSSLineClass: { [_: string]: CSSLineClass } = {
INSERTS: 'd2h-ins',
DELETES: 'd2h-del',
CONTEXT: 'd2h-cntx',
INFO: 'd2h-info',
INSERT_CHANGES: 'd2h-ins d2h-change',
DELETE_CHANGES: 'd2h-del d2h-change',
};
export type HighlightedLines = {
oldLine: {
prefix: string;
content: string;
};
newLine: {
prefix: string;
content: string;
};
};
export interface RenderConfig {
matching?: LineMatchingType;
matchWordsThreshold?: number;
maxLineLengthHighlight?: number;
diffStyle?: DiffStyleType;
colorScheme?: ColorSchemeType;
}
export const defaultRenderConfig = {
matching: LineMatchingType.NONE,
matchWordsThreshold: 0.25,
maxLineLengthHighlight: 10000,
diffStyle: DiffStyleType.WORD,
colorScheme: ColorSchemeType.LIGHT,
};
const separator = '/';
const distance = rematch.newDistanceFn((change: jsDiff.Change) => change.value);
const matcher = rematch.newMatcherFn(distance);
function isDevNullName(name: string): boolean {
return name.indexOf('dev/null') !== -1;
}
function removeInsElements(line: string): string {
return line.replace(/(<ins[^>]*>((.|\n)*?)<\/ins>)/g, '');
}
function removeDelElements(line: string): string {
return line.replace(/(<del[^>]*>((.|\n)*?)<\/del>)/g, '');
}
/**
* Convert from LineType to CSSLineClass
*/
export function toCSSClass(lineType: LineType): CSSLineClass {
switch (lineType) {
case LineType.CONTEXT:
return CSSLineClass.CONTEXT;
case LineType.INSERT:
return CSSLineClass.INSERTS;
case LineType.DELETE:
return CSSLineClass.DELETES;
}
}
export function colorSchemeToCss(colorScheme: ColorSchemeType): string {
switch (colorScheme) {
case ColorSchemeType.DARK:
return 'd2h-dark-color-scheme';
case ColorSchemeType.AUTO:
return 'd2h-auto-color-scheme';
case ColorSchemeType.LIGHT:
default:
return 'd2h-light-color-scheme';
}
}
/**
* Prefix length of the hunk lines in the diff
*/
function prefixLength(isCombined: boolean): number {
return isCombined ? 2 : 1;
}
/**
* Escapes all required characters for safe HTML rendering
*/
// TODO: Test this method inside deconstructLine since it should not be used anywhere else
export function escapeForHtml(str: string): string {
return str
.slice(0)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#x27;')
.replace(/\//g, '&#x2F;');
}
/**
* Deconstructs diff @line by separating the content from the prefix type
*/
export function deconstructLine(line: string, isCombined: boolean, escape = true): DiffLineParts {
const indexToSplit = prefixLength(isCombined);
return {
prefix: line.substring(0, indexToSplit),
content: escape ? escapeForHtml(line.substring(indexToSplit)) : line.substring(indexToSplit),
};
}
/**
* Generates pretty filename diffs
*
* e.g.:
* 1. file = { oldName: "my/path/to/file.js", newName: "my/path/to/new-file.js" }
* returns "my/path/to/{file.js → new-file.js}"
* 2. file = { oldName: "my/path/to/file.js", newName: "very/new/path/to/new-file.js" }
* returns "my/path/to/file.js → very/new/path/to/new-file.js"
* 3. file = { oldName: "my/path/to/file.js", newName: "my/path/for/file.js" }
* returns "my/path/{to → for}/file.js"
*/
export function filenameDiff(file: DiffFileName): string {
// TODO: Move unify path to parsing
const oldFilename = unifyPath(file.oldName);
const newFilename = unifyPath(file.newName);
if (oldFilename !== newFilename && !isDevNullName(oldFilename) && !isDevNullName(newFilename)) {
const prefixPaths = [];
const suffixPaths = [];
const oldFilenameParts = oldFilename.split(separator);
const newFilenameParts = newFilename.split(separator);
const oldFilenamePartsSize = oldFilenameParts.length;
const newFilenamePartsSize = newFilenameParts.length;
let i = 0;
let j = oldFilenamePartsSize - 1;
let k = newFilenamePartsSize - 1;
while (i < j && i < k) {
if (oldFilenameParts[i] === newFilenameParts[i]) {
prefixPaths.push(newFilenameParts[i]);
i += 1;
} else {
break;
}
}
while (j > i && k > i) {
if (oldFilenameParts[j] === newFilenameParts[k]) {
suffixPaths.unshift(newFilenameParts[k]);
j -= 1;
k -= 1;
} else {
break;
}
}
const finalPrefix = prefixPaths.join(separator);
const finalSuffix = suffixPaths.join(separator);
const oldRemainingPath = oldFilenameParts.slice(i, j + 1).join(separator);
const newRemainingPath = newFilenameParts.slice(i, k + 1).join(separator);
if (finalPrefix.length && finalSuffix.length) {
return (
finalPrefix + separator + '{' + oldRemainingPath + ' → ' + newRemainingPath + '}' + separator + finalSuffix
);
} else if (finalPrefix.length) {
return finalPrefix + separator + '{' + oldRemainingPath + ' → ' + newRemainingPath + '}';
} else if (finalSuffix.length) {
return '{' + oldRemainingPath + ' → ' + newRemainingPath + '}' + separator + finalSuffix;
}
return oldFilename + ' → ' + newFilename;
} else if (!isDevNullName(newFilename)) {
return newFilename;
} else {
return oldFilename;
}
}
/**
* Generates a unique string numerical identifier based on the names of the file diff
*/
export function getHtmlId(file: DiffFileName): string {
return `d2h-${hashCode(filenameDiff(file)).toString().slice(-6)}`;
}
/**
* Selects the correct icon name for the file
*/
export function getFileIcon(file: DiffFile): string {
let templateName = 'file-changed';
if (file.isRename) {
templateName = 'file-renamed';
} else if (file.isCopy) {
templateName = 'file-renamed';
} else if (file.isNew) {
templateName = 'file-added';
} else if (file.isDeleted) {
templateName = 'file-deleted';
} else if (file.newName !== file.oldName) {
// If file is not Added, not Deleted and the names changed it must be a rename :)
templateName = 'file-renamed';
}
return templateName;
}
/**
* Highlight differences between @diffLine1 and @diffLine2 using <ins> and <del> tags
*/
export function diffHighlight(
diffLine1: string,
diffLine2: string,
isCombined: boolean,
config: RenderConfig = {},
): HighlightedLines {
const { matching, maxLineLengthHighlight, matchWordsThreshold, diffStyle } = { ...defaultRenderConfig, ...config };
const line1 = deconstructLine(diffLine1, isCombined, false);
const line2 = deconstructLine(diffLine2, isCombined, false);
if (line1.content.length > maxLineLengthHighlight || line2.content.length > maxLineLengthHighlight) {
return {
oldLine: {
prefix: line1.prefix,
content: escapeForHtml(line1.content),
},
newLine: {
prefix: line2.prefix,
content: escapeForHtml(line2.content),
},
};
}
const diff =
diffStyle === 'char'
? jsDiff.diffChars(line1.content, line2.content)
: jsDiff.diffWordsWithSpace(line1.content, line2.content);
const changedWords: jsDiff.Change[] = [];
if (diffStyle === 'word' && matching === 'words') {
const removed = diff.filter(element => element.removed);
const added = diff.filter(element => element.added);
const chunks = matcher(added, removed);
chunks.forEach(chunk => {
if (chunk[0].length === 1 && chunk[1].length === 1) {
const dist = distance(chunk[0][0], chunk[1][0]);
if (dist < matchWordsThreshold) {
changedWords.push(chunk[0][0]);
changedWords.push(chunk[1][0]);
}
}
});
}
const highlightedLine = diff.reduce((highlightedLine, part) => {
const elemType = part.added ? 'ins' : part.removed ? 'del' : null;
const addClass = changedWords.indexOf(part) > -1 ? ' class="d2h-change"' : '';
const escapedValue = escapeForHtml(part.value);
return elemType !== null
? `${highlightedLine}<${elemType}${addClass}>${escapedValue}</${elemType}>`
: `${highlightedLine}${escapedValue}`;
}, '');
return {
oldLine: {
prefix: line1.prefix,
content: removeInsElements(highlightedLine),
},
newLine: {
prefix: line2.prefix,
content: removeDelElements(highlightedLine),
},
};
}

View file

@ -1,205 +0,0 @@
/*
*
* HtmlPrinter (html-printer.js)
* Author: rtfpessoa
*
*/
(function(ctx, undefined) {
var diffParser = require('./diff-parser.js').DiffParser;
var printerUtils = require('./printer-utils.js').PrinterUtils;
var utils = require('./utils.js').Utils;
function SideBySidePrinter() {
}
SideBySidePrinter.prototype.generateSideBySideJsonHtml = function(diffFiles, config) {
return '<div class="d2h-wrapper">\n' +
diffFiles.map(function(file) {
var diffs;
if (file.blocks.length) {
diffs = generateSideBySideFileHtml(file, config);
} else {
diffs = generateEmptyDiff();
}
return '<div id="' + printerUtils.getHtmlId(file) + '" class="d2h-file-wrapper" data-lang="' + file.language + '">\n' +
' <div class="d2h-file-header">\n' +
' <div class="d2h-file-stats">\n' +
' <span class="d2h-lines-added">+' + file.addedLines + '</span>\n' +
' <span class="d2h-lines-deleted">-' + file.deletedLines + '</span>\n' +
' </div>\n' +
' <div class="d2h-file-name">' + printerUtils.getDiffName(file) + '</div>\n' +
' </div>\n' +
' <div class="d2h-files-diff">\n' +
' <div class="d2h-file-side-diff">\n' +
' <div class="d2h-code-wrapper">\n' +
' <table class="d2h-diff-table">\n' +
' <tbody class="d2h-diff-tbody">\n' +
' ' + diffs.left +
' </tbody>\n' +
' </table>\n' +
' </div>\n' +
' </div>\n' +
' <div class="d2h-file-side-diff">\n' +
' <div class="d2h-code-wrapper">\n' +
' <table class="d2h-diff-table">\n' +
' <tbody class="d2h-diff-tbody">\n' +
' ' + diffs.right +
' </tbody>\n' +
' </table>\n' +
' </div>\n' +
' </div>\n' +
' </div>\n' +
' </div>\n';
}).join('\n') +
'</div>\n';
};
function generateSideBySideFileHtml(file, config) {
var fileHtml = {};
fileHtml.left = '';
fileHtml.right = '';
file.blocks.forEach(function(block) {
fileHtml.left += '<tr>\n' +
' <td class="d2h-code-side-linenumber ' + diffParser.LINE_TYPE.INFO + '"></td>\n' +
' <td class="' + diffParser.LINE_TYPE.INFO + '">' +
' <div class="d2h-code-side-line ' + diffParser.LINE_TYPE.INFO + '">' +
' ' + utils.escape(block.header) +
' </div>' +
' </td>\n' +
'</tr>\n';
fileHtml.right += '<tr>\n' +
' <td class="d2h-code-side-linenumber ' + diffParser.LINE_TYPE.INFO + '"></td>\n' +
' <td class="' + diffParser.LINE_TYPE.INFO + '">' +
' <div class="d2h-code-side-line ' + diffParser.LINE_TYPE.INFO + '"></div>' +
' </td>\n' +
'</tr>\n';
var oldLines = [];
var newLines = [];
var tmpHtml = '';
for (var i = 0; i < block.lines.length; i++) {
var line = block.lines[i];
var escapedLine = utils.escape(line.content);
if (line.type == diffParser.LINE_TYPE.CONTEXT && !oldLines.length && !newLines.length) {
fileHtml.left += generateSingleLineHtml(line.type, line.oldNumber, escapedLine);
fileHtml.right += generateSingleLineHtml(line.type, line.newNumber, escapedLine);
} else if (line.type == diffParser.LINE_TYPE.INSERTS && !oldLines.length && !newLines.length) {
fileHtml.left += generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
fileHtml.right += generateSingleLineHtml(line.type, line.newNumber, escapedLine);
} else if (line.type == diffParser.LINE_TYPE.DELETES && !newLines.length) {
oldLines.push(line);
} else if (line.type == diffParser.LINE_TYPE.INSERTS && oldLines.length > newLines.length) {
newLines.push(line);
} else {
var j = 0;
var oldLine, newLine;
if (oldLines.length === newLines.length) {
for (j = 0; j < oldLines.length; j++) {
oldLine = oldLines[j];
newLine = newLines[j];
config.isCombined = file.isCombined;
var diff = printerUtils.diffHighlight(oldLine.content, newLine.content, config);
fileHtml.left +=
generateSingleLineHtml(oldLine.type, oldLine.oldNumber,
diff.first.line, diff.first.prefix);
fileHtml.right +=
generateSingleLineHtml(newLine.type, newLine.newNumber,
diff.second.line, diff.second.prefix);
}
} else {
tmpHtml = processLines(oldLines, newLines);
fileHtml.left += tmpHtml.left;
fileHtml.right += tmpHtml.right;
}
oldLines = [];
newLines = [];
i--;
}
}
tmpHtml = processLines(oldLines, newLines);
fileHtml.left += tmpHtml.left;
fileHtml.right += tmpHtml.right;
});
return fileHtml;
}
function processLines(oldLines, newLines) {
var fileHtml = {};
fileHtml.left = '';
fileHtml.right = '';
var maxLinesNumber = Math.max(oldLines.length, newLines.length);
for (j = 0; j < maxLinesNumber; j++) {
var oldLine = oldLines[j];
var newLine = newLines[j];
if (oldLine && newLine) {
fileHtml.left += generateSingleLineHtml(oldLine.type, oldLine.oldNumber, utils.escape(oldLine.content));
fileHtml.right += generateSingleLineHtml(newLine.type, newLine.newNumber, utils.escape(newLine.content));
} else if (oldLine) {
fileHtml.left += generateSingleLineHtml(oldLine.type, oldLine.oldNumber, utils.escape(oldLine.content));
fileHtml.right += generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
} else if (newLine) {
fileHtml.left += generateSingleLineHtml(diffParser.LINE_TYPE.CONTEXT, '', '', '');
fileHtml.right += generateSingleLineHtml(newLine.type, newLine.newNumber, utils.escape(newLine.content));
} else {
console.error('How did it get here?');
}
}
return fileHtml;
}
function generateSingleLineHtml(type, number, content, prefix) {
var htmlPrefix = '';
if (prefix) {
htmlPrefix = '<span class="d2h-code-line-prefix">' + prefix + '</span>';
}
var htmlContent = '';
if (content) {
htmlContent = '<span class="d2h-code-line-ctn">' + content + '</span>';
}
return '<tr>\n' +
' <td class="d2h-code-side-linenumber ' + type + '">' + number + '</td>\n' +
' <td class="' + type + '">' +
' <div class="d2h-code-side-line ' + type + '">' + htmlPrefix + htmlContent + '</div>' +
' </td>\n' +
' </tr>\n';
}
function generateEmptyDiff() {
var fileHtml = {};
fileHtml.right = '';
fileHtml.left = '<tr>\n' +
' <td class="' + diffParser.LINE_TYPE.INFO + '">' +
' <div class="d2h-code-side-line ' + diffParser.LINE_TYPE.INFO + '">' +
'File without changes' +
' </div>' +
' </td>\n' +
'</tr>\n';
return fileHtml;
}
module.exports['SideBySidePrinter'] = new SideBySidePrinter();
})(this);

View file

@ -0,0 +1,314 @@
import HoganJsUtils from './hoganjs-utils';
import * as Rematch from './rematch';
import * as renderUtils from './render-utils';
import {
DiffLine,
LineType,
DiffFile,
DiffBlock,
DiffLineContext,
DiffLineDeleted,
DiffLineInserted,
DiffLineContent,
} from './types';
export interface SideBySideRendererConfig extends renderUtils.RenderConfig {
renderNothingWhenEmpty?: boolean;
matchingMaxComparisons?: number;
maxLineSizeInBlockForComparison?: number;
}
export const defaultSideBySideRendererConfig = {
...renderUtils.defaultRenderConfig,
renderNothingWhenEmpty: false,
matchingMaxComparisons: 2500,
maxLineSizeInBlockForComparison: 200,
};
const genericTemplatesPath = 'generic';
const baseTemplatesPath = 'side-by-side';
const iconsBaseTemplatesPath = 'icon';
const tagsBaseTemplatesPath = 'tag';
export default class SideBySideRenderer {
private readonly hoganUtils: HoganJsUtils;
private readonly config: typeof defaultSideBySideRendererConfig;
constructor(hoganUtils: HoganJsUtils, config: SideBySideRendererConfig = {}) {
this.hoganUtils = hoganUtils;
this.config = { ...defaultSideBySideRendererConfig, ...config };
}
render(diffFiles: DiffFile[]): string {
const diffsHtml = diffFiles
.map(file => {
let diffs;
if (file.blocks.length) {
diffs = this.generateFileHtml(file);
} else {
diffs = this.generateEmptyDiff();
}
return this.makeFileDiffHtml(file, diffs);
})
.join('\n');
return this.hoganUtils.render(genericTemplatesPath, 'wrapper', {
colorScheme: renderUtils.colorSchemeToCss(this.config.colorScheme),
content: diffsHtml,
});
}
makeFileDiffHtml(file: DiffFile, diffs: FileHtml): string {
if (this.config.renderNothingWhenEmpty && Array.isArray(file.blocks) && file.blocks.length === 0) return '';
const fileDiffTemplate = this.hoganUtils.template(baseTemplatesPath, 'file-diff');
const filePathTemplate = this.hoganUtils.template(genericTemplatesPath, 'file-path');
const fileIconTemplate = this.hoganUtils.template(iconsBaseTemplatesPath, 'file');
const fileTagTemplate = this.hoganUtils.template(tagsBaseTemplatesPath, renderUtils.getFileIcon(file));
return fileDiffTemplate.render({
file: file,
fileHtmlId: renderUtils.getHtmlId(file),
diffs: diffs,
filePath: filePathTemplate.render(
{
fileDiffName: renderUtils.filenameDiff(file),
},
{
fileIcon: fileIconTemplate,
fileTag: fileTagTemplate,
},
),
});
}
generateEmptyDiff(): FileHtml {
return {
right: '',
left: this.hoganUtils.render(genericTemplatesPath, 'empty-diff', {
contentClass: 'd2h-code-side-line',
CSSLineClass: renderUtils.CSSLineClass,
}),
};
}
generateFileHtml(file: DiffFile): FileHtml {
const matcher = Rematch.newMatcherFn(
Rematch.newDistanceFn((e: DiffLine) => renderUtils.deconstructLine(e.content, file.isCombined).content),
);
return file.blocks
.map(block => {
const fileHtml = {
left: this.makeHeaderHtml(block.header, file),
right: this.makeHeaderHtml(''),
};
this.applyLineGroupping(block).forEach(([contextLines, oldLines, newLines]) => {
if (oldLines.length && newLines.length && !contextLines.length) {
this.applyRematchMatching(oldLines, newLines, matcher).map(([oldLines, newLines]) => {
const { left, right } = this.processChangedLines(file.isCombined, oldLines, newLines);
fileHtml.left += left;
fileHtml.right += right;
});
} else if (contextLines.length) {
contextLines.forEach(line => {
const { prefix, content } = renderUtils.deconstructLine(line.content, file.isCombined);
const { left, right } = this.generateLineHtml(
{
type: renderUtils.CSSLineClass.CONTEXT,
prefix: prefix,
content: content,
number: line.oldNumber,
},
{
type: renderUtils.CSSLineClass.CONTEXT,
prefix: prefix,
content: content,
number: line.newNumber,
},
);
fileHtml.left += left;
fileHtml.right += right;
});
} else if (oldLines.length || newLines.length) {
const { left, right } = this.processChangedLines(file.isCombined, oldLines, newLines);
fileHtml.left += left;
fileHtml.right += right;
} else {
console.error('Unknown state reached while processing groups of lines', contextLines, oldLines, newLines);
}
});
return fileHtml;
})
.reduce(
(accomulated, html) => {
return { left: accomulated.left + html.left, right: accomulated.right + html.right };
},
{ left: '', right: '' },
);
}
applyLineGroupping(block: DiffBlock): DiffLineGroups {
const blockLinesGroups: DiffLineGroups = [];
let oldLines: (DiffLineDeleted & DiffLineContent)[] = [];
let newLines: (DiffLineInserted & DiffLineContent)[] = [];
for (let i = 0; i < block.lines.length; i++) {
const diffLine = block.lines[i];
if (
(diffLine.type !== LineType.INSERT && newLines.length) ||
(diffLine.type === LineType.CONTEXT && oldLines.length > 0)
) {
blockLinesGroups.push([[], oldLines, newLines]);
oldLines = [];
newLines = [];
}
if (diffLine.type === LineType.CONTEXT) {
blockLinesGroups.push([[diffLine], [], []]);
} else if (diffLine.type === LineType.INSERT && oldLines.length === 0) {
blockLinesGroups.push([[], [], [diffLine]]);
} else if (diffLine.type === LineType.INSERT && oldLines.length > 0) {
newLines.push(diffLine);
} else if (diffLine.type === LineType.DELETE) {
oldLines.push(diffLine);
}
}
if (oldLines.length || newLines.length) {
blockLinesGroups.push([[], oldLines, newLines]);
oldLines = [];
newLines = [];
}
return blockLinesGroups;
}
applyRematchMatching(
oldLines: DiffLine[],
newLines: DiffLine[],
matcher: Rematch.MatcherFn<DiffLine>,
): DiffLine[][][] {
const comparisons = oldLines.length * newLines.length;
const maxLineSizeInBlock = Math.max.apply(
null,
[0].concat(oldLines.concat(newLines).map(elem => elem.content.length)),
);
const doMatching =
comparisons < this.config.matchingMaxComparisons &&
maxLineSizeInBlock < this.config.maxLineSizeInBlockForComparison &&
(this.config.matching === 'lines' || this.config.matching === 'words');
return doMatching ? matcher(oldLines, newLines) : [[oldLines, newLines]];
}
makeHeaderHtml(blockHeader: string, file?: DiffFile): string {
return this.hoganUtils.render(genericTemplatesPath, 'block-header', {
CSSLineClass: renderUtils.CSSLineClass,
blockHeader: file?.isTooBig ? blockHeader : renderUtils.escapeForHtml(blockHeader),
lineClass: 'd2h-code-side-linenumber',
contentClass: 'd2h-code-side-line',
});
}
processChangedLines(isCombined: boolean, oldLines: DiffLine[], newLines: DiffLine[]): FileHtml {
const fileHtml = {
right: '',
left: '',
};
const maxLinesNumber = Math.max(oldLines.length, newLines.length);
for (let i = 0; i < maxLinesNumber; i++) {
const oldLine = oldLines[i];
const newLine = newLines[i];
const diff =
oldLine !== undefined && newLine !== undefined
? renderUtils.diffHighlight(oldLine.content, newLine.content, isCombined, this.config)
: undefined;
const preparedOldLine =
oldLine !== undefined && oldLine.oldNumber !== undefined
? {
...(diff !== undefined
? {
prefix: diff.oldLine.prefix,
content: diff.oldLine.content,
type: renderUtils.CSSLineClass.DELETE_CHANGES,
}
: {
...renderUtils.deconstructLine(oldLine.content, isCombined),
type: renderUtils.toCSSClass(oldLine.type),
}),
number: oldLine.oldNumber,
}
: undefined;
const preparedNewLine =
newLine !== undefined && newLine.newNumber !== undefined
? {
...(diff !== undefined
? {
prefix: diff.newLine.prefix,
content: diff.newLine.content,
type: renderUtils.CSSLineClass.INSERT_CHANGES,
}
: {
...renderUtils.deconstructLine(newLine.content, isCombined),
type: renderUtils.toCSSClass(newLine.type),
}),
number: newLine.newNumber,
}
: undefined;
const { left, right } = this.generateLineHtml(preparedOldLine, preparedNewLine);
fileHtml.left += left;
fileHtml.right += right;
}
return fileHtml;
}
generateLineHtml(oldLine?: DiffPreparedLine, newLine?: DiffPreparedLine): FileHtml {
return {
left: this.generateSingleHtml(oldLine),
right: this.generateSingleHtml(newLine),
};
}
generateSingleHtml(line?: DiffPreparedLine): string {
const lineClass = 'd2h-code-side-linenumber';
const contentClass = 'd2h-code-side-line';
return this.hoganUtils.render(genericTemplatesPath, 'line', {
type: line?.type || `${renderUtils.CSSLineClass.CONTEXT} d2h-emptyplaceholder`,
lineClass: line !== undefined ? lineClass : `${lineClass} d2h-code-side-emptyplaceholder`,
contentClass: line !== undefined ? contentClass : `${contentClass} d2h-code-side-emptyplaceholder`,
prefix: line?.prefix === ' ' ? '&nbsp;' : line?.prefix,
content: line?.content,
lineNumber: line?.number,
});
}
}
type DiffLineGroups = [
(DiffLineContext & DiffLineContent)[],
(DiffLineDeleted & DiffLineContent)[],
(DiffLineInserted & DiffLineContent)[],
][];
type DiffPreparedLine = {
type: renderUtils.CSSLineClass;
prefix: string;
content: string;
number: number;
};
type FileHtml = {
left: string;
right: string;
};

View file

@ -0,0 +1,10 @@
<li class="d2h-file-list-line">
<span class="d2h-file-name-wrapper">
{{>fileIcon}}
<a href="#{{fileHtmlId}}" class="d2h-file-name">{{fileName}}</a>
<span class="d2h-file-stats">
<span class="d2h-lines-added">{{addedLines}}</span>
<span class="d2h-lines-deleted">{{deletedLines}}</span>
</span>
</span>
</li>

View file

@ -0,0 +1,10 @@
<div class="d2h-file-list-wrapper {{colorScheme}}">
<div class="d2h-file-list-header">
<span class="d2h-file-list-title">Files changed ({{filesNumber}})</span>
<a class="d2h-file-switch d2h-hide">hide</a>
<a class="d2h-file-switch d2h-show">show</a>
</div>
<ol class="d2h-file-list">
{{{files}}}
</ol>
</div>

View file

@ -0,0 +1,6 @@
<tr>
<td class="{{lineClass}} {{CSSLineClass.INFO}}"></td>
<td class="{{CSSLineClass.INFO}}">
<div class="{{contentClass}}">{{#blockHeader}}{{{blockHeader}}}{{/blockHeader}}{{^blockHeader}}&nbsp;{{/blockHeader}}</div>
</td>
</tr>

View file

@ -0,0 +1,7 @@
<tr>
<td class="{{CSSLineClass.INFO}}">
<div class="{{contentClass}}">
File without changes
</div>
</td>
</tr>

View file

@ -0,0 +1,9 @@
<span class="d2h-file-name-wrapper">
{{>fileIcon}}
<span class="d2h-file-name">{{fileDiffName}}</span>
{{>fileTag}}
</span>
<label class="d2h-file-collapse">
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
Viewed
</label>

View file

@ -0,0 +1,21 @@
<tr>
<td class="{{lineClass}} {{type}}">
{{{lineNumber}}}
</td>
<td class="{{type}}">
<div class="{{contentClass}}">
{{#prefix}}
<span class="d2h-code-line-prefix">{{{prefix}}}</span>
{{/prefix}}
{{^prefix}}
<span class="d2h-code-line-prefix">&nbsp;</span>
{{/prefix}}
{{#content}}
<span class="d2h-code-line-ctn">{{{content}}}</span>
{{/content}}
{{^content}}
<span class="d2h-code-line-ctn"><br></span>
{{/content}}
</div>
</td>
</tr>

View file

@ -0,0 +1,3 @@
<div class="d2h-wrapper {{colorScheme}}">
{{{content}}}
</div>

View file

@ -0,0 +1,4 @@
<svg aria-hidden="true" class="d2h-icon d2h-added" height="16" title="added" version="1.1" viewBox="0 0 14 16"
width="14">
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM6 9H3V7h3V4h2v3h3v2H8v3H6V9z"></path>
</svg>

After

Width:  |  Height:  |  Size: 292 B

View file

@ -0,0 +1,4 @@
<svg aria-hidden="true" class="d2h-icon d2h-changed" height="16" title="modified" version="1.1"
viewBox="0 0 14 16" width="14">
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM4 8c0-1.66 1.34-3 3-3s3 1.34 3 3-1.34 3-3 3-3-1.34-3-3z"></path>
</svg>

After

Width:  |  Height:  |  Size: 324 B

View file

@ -0,0 +1,4 @@
<svg aria-hidden="true" class="d2h-icon d2h-deleted" height="16" title="removed" version="1.1"
viewBox="0 0 14 16" width="14">
<path d="M13 1H1C0.45 1 0 1.45 0 2v12c0 0.55 0.45 1 1 1h12c0.55 0 1-0.45 1-1V2c0-0.55-0.45-1-1-1z m0 13H1V2h12v12zM11 9H3V7h8v2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 281 B

View file

@ -0,0 +1,4 @@
<svg aria-hidden="true" class="d2h-icon d2h-moved" height="16" title="renamed" version="1.1"
viewBox="0 0 14 16" width="14">
<path d="M6 9H3V7h3V4l5 4-5 4V9z m8-7v12c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h12c0.55 0 1 0.45 1 1z m-1 0H1v12h12V2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 289 B

View file

@ -0,0 +1,3 @@
<svg aria-hidden="true" class="d2h-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12">
<path d="M6 5H2v-1h4v1zM2 8h7v-1H2v1z m0 2h7v-1H2v1z m0 2h7v-1H2v1z m10-7.5v9.5c0 0.55-0.45 1-1 1H1c-0.55 0-1-0.45-1-1V2c0-0.55 0.45-1 1-1h7.5l3.5 3.5z m-1 0.5L8 2H1v12h10V5z"></path>
</svg>

After

Width:  |  Height:  |  Size: 294 B

View file

@ -0,0 +1,14 @@
<div id="{{fileHtmlId}}" class="d2h-file-wrapper" data-lang="{{file.language}}">
<div class="d2h-file-header">
{{{filePath}}}
</div>
<div class="d2h-file-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
{{{diffs}}}
</tbody>
</table>
</div>
</div>
</div>

View file

@ -0,0 +1,2 @@
<div class="line-num1">{{oldNumber}}</div>
<div class="line-num2">{{newNumber}}</div>

View file

@ -0,0 +1,25 @@
<div id="{{fileHtmlId}}" class="d2h-file-wrapper" data-lang="{{file.language}}">
<div class="d2h-file-header">
{{{filePath}}}
</div>
<div class="d2h-files-diff">
<div class="d2h-file-side-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
{{{diffs.left}}}
</tbody>
</table>
</div>
</div>
<div class="d2h-file-side-diff">
<div class="d2h-code-wrapper">
<table class="d2h-diff-table">
<tbody class="d2h-diff-tbody">
{{{diffs.right}}}
</tbody>
</table>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1 @@
<span class="d2h-tag d2h-added d2h-added-tag">ADDED</span>

View file

@ -0,0 +1 @@
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span>

View file

@ -0,0 +1 @@
<span class="d2h-tag d2h-deleted d2h-deleted-tag">DELETED</span>

View file

@ -0,0 +1 @@
<span class="d2h-tag d2h-moved d2h-moved-tag">RENAMED</span>

99
src/types.ts Normal file
View file

@ -0,0 +1,99 @@
export type DiffLineParts = {
prefix: string;
content: string;
};
export enum LineType {
INSERT = 'insert',
DELETE = 'delete',
CONTEXT = 'context',
}
export interface DiffLineDeleted {
type: LineType.DELETE;
oldNumber: number;
newNumber: undefined;
}
export interface DiffLineInserted {
type: LineType.INSERT;
oldNumber: undefined;
newNumber: number;
}
export interface DiffLineContext {
type: LineType.CONTEXT;
oldNumber: number;
newNumber: number;
}
export type DiffLineContent = {
content: string;
};
export type DiffLine = (DiffLineDeleted | DiffLineInserted | DiffLineContext) & DiffLineContent;
export interface DiffBlock {
oldStartLine: number;
oldStartLine2?: number;
newStartLine: number;
header: string;
lines: DiffLine[];
}
export interface DiffFileName {
oldName: string;
newName: string;
}
export interface DiffFile extends DiffFileName {
addedLines: number;
deletedLines: number;
isCombined: boolean;
isGitDiff: boolean;
language: string;
blocks: DiffBlock[];
oldMode?: string | string[];
newMode?: string;
deletedFileMode?: string;
newFileMode?: string;
isDeleted?: boolean;
isNew?: boolean;
isCopy?: boolean;
isRename?: boolean;
isBinary?: boolean;
isTooBig?: boolean;
unchangedPercentage?: number;
changedPercentage?: number;
checksumBefore?: string | string[];
checksumAfter?: string;
mode?: string;
}
export type OutputFormatType = 'line-by-line' | 'side-by-side';
export const OutputFormatType: { [_: string]: OutputFormatType } = {
LINE_BY_LINE: 'line-by-line',
SIDE_BY_SIDE: 'side-by-side',
};
export type LineMatchingType = 'lines' | 'words' | 'none';
export const LineMatchingType: { [_: string]: LineMatchingType } = {
LINES: 'lines',
WORDS: 'words',
NONE: 'none',
};
export type DiffStyleType = 'word' | 'char';
export const DiffStyleType: { [_: string]: DiffStyleType } = {
WORD: 'word',
CHAR: 'char',
};
export enum ColorSchemeType {
AUTO = 'auto',
DARK = 'dark',
LIGHT = 'light',
}

741
src/ui/css/diff2html.css Normal file
View file

@ -0,0 +1,741 @@
/*
*
* Diff to HTML (diff2html.css)
* Author: rtfpessoa
*
*/
:root,
:host {
--d2h-bg-color: #fff;
--d2h-border-color: #ddd;
--d2h-dim-color: rgba(0, 0, 0, 0.3);
--d2h-line-border-color: #eeeeee;
--d2h-file-header-bg-color: #f7f7f7;
--d2h-file-header-border-color: #d8d8d8;
--d2h-empty-placeholder-bg-color: #f1f1f1;
--d2h-empty-placeholder-border-color: #e1e1e1;
--d2h-selected-color: #c8e1ff;
--d2h-ins-bg-color: #dfd;
--d2h-ins-border-color: #b4e2b4;
--d2h-ins-highlight-bg-color: #97f295;
--d2h-ins-label-color: #399839;
--d2h-del-bg-color: #fee8e9;
--d2h-del-border-color: #e9aeae;
--d2h-del-highlight-bg-color: #ffb6ba;
--d2h-del-label-color: #c33;
--d2h-change-del-color: #fdf2d0;
--d2h-change-ins-color: #ded;
--d2h-info-bg-color: #f8fafd;
--d2h-info-border-color: #d5e4f2;
--d2h-change-label-color: #d0b44c;
--d2h-moved-label-color: #3572b0;
/**
* Dark Color Scheme
*/
--d2h-dark-color: rgb(230, 237, 243);
--d2h-dark-bg-color: rgb(13, 17, 23);
--d2h-dark-border-color: rgb(48, 54, 61);
--d2h-dark-dim-color: rgb(110, 118, 129);
--d2h-dark-line-border-color: rgb(33, 38, 45);
--d2h-dark-file-header-bg-color: rgb(22, 27, 34);
--d2h-dark-file-header-border-color: rgb(48, 54, 61);
--d2h-dark-empty-placeholder-bg-color: rgba(110, 118, 129, 0.1);
--d2h-dark-empty-placeholder-border-color: rgb(48, 54, 61);
--d2h-dark-selected-color: rgba(56, 139, 253, 0.1);
--d2h-dark-ins-bg-color: rgba(46, 160, 67, 0.15);
--d2h-dark-ins-border-color: rgba(46, 160, 67, 0.4);
--d2h-dark-ins-highlight-bg-color: rgba(46, 160, 67, 0.4);
--d2h-dark-ins-label-color: rgb(63, 185, 80);
--d2h-dark-del-bg-color: rgba(248, 81, 73, 0.1);
--d2h-dark-del-border-color: rgba(248, 81, 73, 0.4);
--d2h-dark-del-highlight-bg-color: rgba(248, 81, 73, 0.4);
--d2h-dark-del-label-color: rgb(248, 81, 73);
--d2h-dark-change-del-color: rgba(210, 153, 34, 0.2);
--d2h-dark-change-ins-color: rgba(46, 160, 67, 0.25);
--d2h-dark-info-bg-color: rgba(56, 139, 253, 0.1);
--d2h-dark-info-border-color: rgba(56, 139, 253, 0.4);
--d2h-dark-change-label-color: rgb(210, 153, 34);
--d2h-dark-moved-label-color: #3572b0;
}
.d2h-wrapper {
text-align: left;
}
.d2h-file-header {
display: flex;
height: 35px;
padding: 5px 10px;
border-bottom: 1px solid var(--d2h-file-header-border-color);
background-color: var(--d2h-file-header-bg-color);
font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
.d2h-file-header.d2h-sticky-header {
position: sticky;
top: 0;
z-index: 1;
}
.d2h-file-stats {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
margin-left: auto;
font-size: 14px;
}
.d2h-lines-added {
text-align: right;
border: 1px solid var(--d2h-ins-border-color);
border-radius: 5px 0 0 5px;
color: var(--d2h-ins-label-color);
padding: 2px;
vertical-align: middle;
}
.d2h-lines-deleted {
text-align: left;
border: 1px solid var(--d2h-del-border-color);
border-radius: 0 5px 5px 0;
color: var(--d2h-del-label-color);
padding: 2px;
vertical-align: middle;
margin-left: 1px;
}
.d2h-file-name-wrapper {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
width: 100%;
font-size: 15px;
}
.d2h-file-name {
white-space: nowrap;
text-overflow: ellipsis;
overflow-x: hidden;
}
.d2h-file-wrapper {
border: 1px solid var(--d2h-border-color);
border-radius: 3px;
margin-bottom: 1em;
}
.d2h-file-collapse {
justify-content: flex-end;
display: none;
cursor: pointer;
font-size: 12px;
align-items: center;
border-radius: 3px;
border: 1px solid var(--d2h-border-color);
padding: 4px 8px;
}
.d2h-file-collapse.d2h-selected {
background-color: var(--d2h-selected-color);
}
.d2h-file-collapse-input {
margin: 0 4px 0 0;
}
.d2h-diff-table {
width: 100%;
border-collapse: collapse;
font-family: 'Menlo', 'Consolas', monospace;
font-size: 13px;
}
.d2h-files-diff {
display: flex;
width: 100%;
}
.d2h-file-diff {
overflow-y: hidden;
}
.d2h-files-diff.d2h-d-none,
.d2h-file-diff.d2h-d-none {
display: none;
}
.d2h-file-side-diff {
display: inline-block;
overflow-x: scroll;
overflow-y: hidden;
width: 50%;
}
.d2h-code-line {
display: inline-block;
white-space: nowrap;
user-select: none;
width: calc(100% - 16em);
/* Compensate for the absolute positioning of the line numbers */
padding: 0 8em;
}
.d2h-code-side-line {
display: inline-block;
white-space: nowrap;
user-select: none;
width: calc(100% - 9em);
/* Compensate for the absolute positioning of the line numbers */
padding: 0 4.5em;
}
.d2h-code-line-ctn {
display: inline-block;
background: none;
padding: 0;
word-wrap: normal;
white-space: pre;
user-select: text;
width: 100%;
vertical-align: middle;
}
.d2h-code-line del,
.d2h-code-side-line del {
display: inline-block;
margin-top: -1px;
text-decoration: none;
background-color: var(--d2h-del-highlight-bg-color);
border-radius: 0.2em;
}
.d2h-code-line ins,
.d2h-code-side-line ins {
display: inline-block;
margin-top: -1px;
text-decoration: none;
background-color: var(--d2h-ins-highlight-bg-color);
border-radius: 0.2em;
text-align: left;
}
.d2h-code-line-prefix {
display: inline;
background: none;
padding: 0;
word-wrap: normal;
white-space: pre;
}
.line-num1 {
box-sizing: border-box;
float: left;
width: 3.5em;
overflow: hidden;
text-overflow: ellipsis;
padding: 0 0.5em 0 0.5em;
}
.line-num2 {
box-sizing: border-box;
float: right;
width: 3.5em;
overflow: hidden;
text-overflow: ellipsis;
padding: 0 0.5em 0 0.5em;
}
.d2h-code-linenumber {
box-sizing: border-box;
width: 7.5em;
/* Keep the numbers fixed on line contents scroll */
position: absolute;
display: inline-block;
background-color: var(--d2h-bg-color);
color: var(--d2h-dim-color);
text-align: right;
border: solid var(--d2h-line-border-color);
border-width: 0 1px 0 1px;
cursor: pointer;
}
.d2h-code-linenumber:after {
content: '\200b';
}
.d2h-code-side-linenumber {
/* Keep the numbers fixed on line contents scroll */
position: absolute;
display: inline-block;
box-sizing: border-box;
width: 4em;
background-color: var(--d2h-bg-color);
color: var(--d2h-dim-color);
text-align: right;
border: solid var(--d2h-line-border-color);
border-width: 0 1px 0 1px;
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
padding: 0 0.5em 0 0.5em;
}
.d2h-code-side-linenumber:after {
content: '\200b';
}
.d2h-code-side-emptyplaceholder,
.d2h-emptyplaceholder {
background-color: var(--d2h-empty-placeholder-bg-color);
border-color: var(--d2h-empty-placeholder-border-color);
}
.d2h-code-linenumber,
.d2h-code-side-linenumber,
.d2h-code-line-prefix,
.d2h-emptyplaceholder {
user-select: none;
}
.d2h-code-linenumber,
.d2h-code-side-linenumber {
direction: rtl;
}
/*
* Changes Highlight
*/
.d2h-del {
background-color: var(--d2h-del-bg-color);
border-color: var(--d2h-del-border-color);
}
.d2h-ins {
background-color: var(--d2h-ins-bg-color);
border-color: var(--d2h-ins-border-color);
}
.d2h-info {
background-color: var(--d2h-info-bg-color);
color: var(--d2h-dim-color);
border-color: var(--d2h-info-border-color);
}
.d2h-file-diff .d2h-del.d2h-change {
background-color: var(--d2h-change-del-color);
}
.d2h-file-diff .d2h-ins.d2h-change {
background-color: var(--d2h-change-ins-color);
}
/*
* File Summary List
*/
.d2h-file-list-wrapper {
margin-bottom: 10px;
}
.d2h-file-list-wrapper a {
text-decoration: none;
color: var(--d2h-moved-label-color);
}
.d2h-file-list-wrapper a:visited {
color: var(--d2h-moved-label-color);
}
.d2h-file-list-header {
text-align: left;
}
.d2h-file-list-title {
font-weight: bold;
}
.d2h-file-list-line {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
text-align: left;
}
.d2h-file-list {
display: block;
list-style: none;
padding: 0;
margin: 0;
}
.d2h-file-list > li {
border-bottom: var(--d2h-border-color) solid 1px;
padding: 5px 10px;
margin: 0;
}
.d2h-file-list > li:last-child {
border-bottom: none;
}
.d2h-file-switch {
display: none;
font-size: 10px;
cursor: pointer;
}
.d2h-icon {
vertical-align: middle;
margin-right: 10px;
fill: currentColor;
}
.d2h-deleted {
color: var(--d2h-del-label-color);
}
.d2h-added {
color: var(--d2h-ins-label-color);
}
.d2h-changed {
color: var(--d2h-change-label-color);
}
.d2h-moved {
color: var(--d2h-moved-label-color);
}
.d2h-tag {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
font-size: 10px;
margin-left: 5px;
padding: 0 2px;
background-color: var(--d2h-bg-color);
}
.d2h-deleted-tag {
border: var(--d2h-del-label-color) 1px solid;
}
.d2h-added-tag {
border: var(--d2h-ins-label-color) 1px solid;
}
.d2h-changed-tag {
border: var(--d2h-change-label-color) 1px solid;
}
.d2h-moved-tag {
border: var(--d2h-moved-label-color) 1px solid;
}
/**
* Dark Mode Colors
*/
.d2h-dark-color-scheme {
color: var(--d2h-dark-color);
background-color: var(--d2h-dark-bg-color);
}
.d2h-dark-color-scheme .d2h-file-header {
background-color: var(--d2h-dark-file-header-bg-color);
border-bottom: var(--d2h-dark-file-header-border-color);
}
.d2h-dark-color-scheme .d2h-lines-added {
border: 1px solid var(--d2h-dark-ins-border-color);
color: var(--d2h-dark-ins-label-color);
}
.d2h-dark-color-scheme .d2h-lines-deleted {
border: 1px solid var(--d2h-dark-del-border-color);
color: var(--d2h-dark-del-label-color);
}
.d2h-dark-color-scheme .d2h-code-line del,
.d2h-dark-color-scheme .d2h-code-side-line del {
background-color: var(--d2h-dark-del-highlight-bg-color);
}
.d2h-dark-color-scheme .d2h-code-line ins,
.d2h-dark-color-scheme .d2h-code-side-line ins {
background-color: var(--d2h-dark-ins-highlight-bg-color);
}
.d2h-dark-color-scheme .d2h-diff-tbody {
border-color: var(--d2h-dark-border-color);
}
.d2h-dark-color-scheme .d2h-code-side-linenumber {
background-color: var(--d2h-dark-bg-color);
color: var(--d2h-dark-dim-color);
border-color: var(--d2h-dark-line-border-color);
}
.d2h-dark-color-scheme .d2h-files-diff .d2h-code-side-emptyplaceholder,
.d2h-dark-color-scheme .d2h-files-diff .d2h-emptyplaceholder {
background-color: var(--d2h-dark-empty-placeholder-bg-color);
border-color: var(--d2h-dark-empty-placeholder-border-color);
}
.d2h-dark-color-scheme .d2h-code-linenumber {
background-color: var(--d2h-dark-bg-color);
color: var(--d2h-dark-dim-color);
border-color: var(--d2h-dark-line-border-color);
}
.d2h-dark-color-scheme .d2h-del {
background-color: var(--d2h-dark-del-bg-color);
border-color: var(--d2h-dark-del-border-color);
}
.d2h-dark-color-scheme .d2h-ins {
background-color: var(--d2h-dark-ins-bg-color);
border-color: var(--d2h-dark-ins-border-color);
}
.d2h-dark-color-scheme .d2h-info {
background-color: var(--d2h-dark-info-bg-color);
color: var(--d2h-dark-dim-color);
border-color: var(--d2h-dark-info-border-color);
}
.d2h-dark-color-scheme .d2h-file-diff .d2h-del.d2h-change {
background-color: var(--d2h-dark-change-del-color);
}
.d2h-dark-color-scheme .d2h-file-diff .d2h-ins.d2h-change {
background-color: var(--d2h-dark-change-ins-color);
}
.d2h-dark-color-scheme .d2h-file-wrapper {
border: 1px solid var(--d2h-dark-border-color);
}
.d2h-dark-color-scheme .d2h-file-collapse {
border: 1px solid var(--d2h-dark-bg-color);
}
.d2h-dark-color-scheme .d2h-file-collapse.d2h-selected {
background-color: var(--d2h-dark-selected-color);
}
.d2h-dark-color-scheme .d2h-file-list-wrapper a {
color: var(--d2h-dark-moved-label-color);
}
.d2h-dark-color-scheme .d2h-file-list-wrapper a:visited {
color: var(--d2h-dark-moved-label-color);
}
.d2h-dark-color-scheme .d2h-file-list > li {
border-bottom: var(--d2h-dark-bg-color) solid 1px;
}
.d2h-dark-color-scheme .d2h-deleted {
color: var(--d2h-dark-del-label-color);
}
.d2h-dark-color-scheme .d2h-added {
color: var(--d2h-dark-ins-label-color);
}
.d2h-dark-color-scheme .d2h-changed {
color: var(--d2h-dark-change-label-color);
}
.d2h-dark-color-scheme .d2h-moved {
color: var(--d2h-dark-moved-label-color);
}
.d2h-dark-color-scheme .d2h-tag {
background-color: var(--d2h-dark-bg-color);
}
.d2h-dark-color-scheme .d2h-deleted-tag {
border: var(--d2h-dark-del-label-color) 1px solid;
}
.d2h-dark-color-scheme .d2h-added-tag {
border: var(--d2h-dark-ins-label-color) 1px solid;
}
.d2h-dark-color-scheme .d2h-changed-tag {
border: var(--d2h-dark-change-label-color) 1px solid;
}
.d2h-dark-color-scheme .d2h-moved-tag {
border: var(--d2h-dark-moved-label-color) 1px solid;
}
/**
* Auto Mode Colors
*/
@media (prefers-color-scheme: dark) {
.d2h-auto-color-scheme {
background-color: var(--d2h-dark-bg-color);
color: var(--d2h-dark-color);
}
.d2h-auto-color-scheme .d2h-file-header {
background-color: var(--d2h-dark-file-header-bg-color);
border-bottom: var(--d2h-dark-file-header-border-color);
}
.d2h-auto-color-scheme .d2h-lines-added {
border: 1px solid var(--d2h-dark-ins-border-color);
color: var(--d2h-dark-ins-label-color);
}
.d2h-auto-color-scheme .d2h-lines-deleted {
border: 1px solid var(--d2h-dark-del-border-color);
color: var(--d2h-dark-del-label-color);
}
.d2h-auto-color-scheme .d2h-code-line del,
.d2h-auto-color-scheme .d2h-code-side-line del {
background-color: var(--d2h-dark-del-highlight-bg-color);
}
.d2h-auto-color-scheme .d2h-code-line ins,
.d2h-auto-color-scheme .d2h-code-side-line ins {
background-color: var(--d2h-dark-ins-highlight-bg-color);
}
.d2h-auto-color-scheme .d2h-diff-tbody {
border-color: var(--d2h-dark-border-color);
}
.d2h-auto-color-scheme .d2h-code-side-linenumber {
background-color: var(--d2h-dark-bg-color);
color: var(--d2h-dark-dim-color);
border-color: var(--d2h-dark-line-border-color);
}
.d2h-auto-color-scheme .d2h-files-diff .d2h-code-side-emptyplaceholder,
.d2h-auto-color-scheme .d2h-files-diff .d2h-emptyplaceholder {
background-color: var(--d2h-dark-empty-placeholder-bg-color);
border-color: var(--d2h-dark-empty-placeholder-border-color);
}
.d2h-auto-color-scheme .d2h-code-linenumber {
background-color: var(--d2h-dark-bg-color);
color: var(--d2h-dark-dim-color);
border-color: var(--d2h-dark-line-border-color);
}
.d2h-auto-color-scheme .d2h-del {
background-color: var(--d2h-dark-del-bg-color);
border-color: var(--d2h-dark-del-border-color);
}
.d2h-auto-color-scheme .d2h-ins {
background-color: var(--d2h-dark-ins-bg-color);
border-color: var(--d2h-dark-ins-border-color);
}
.d2h-auto-color-scheme .d2h-info {
background-color: var(--d2h-dark-info-bg-color);
color: var(--d2h-dark-dim-color);
border-color: var(--d2h-dark-info-border-color);
}
.d2h-auto-color-scheme .d2h-file-diff .d2h-del.d2h-change {
background-color: var(--d2h-dark-change-del-color);
}
.d2h-auto-color-scheme .d2h-file-diff .d2h-ins.d2h-change {
background-color: var(--d2h-dark-change-ins-color);
}
.d2h-auto-color-scheme .d2h-file-wrapper {
border: 1px solid var(--d2h-dark-border-color);
}
.d2h-auto-color-scheme .d2h-file-collapse {
border: 1px solid var(--d2h-dark-bg-color);
}
.d2h-auto-color-scheme .d2h-file-collapse.d2h-selected {
background-color: var(--d2h-dark-selected-color);
}
.d2h-auto-color-scheme .d2h-file-list-wrapper a {
color: var(--d2h-dark-moved-label-color);
}
.d2h-auto-color-scheme .d2h-file-list-wrapper a:visited {
color: var(--d2h-dark-moved-label-color);
}
.d2h-auto-color-scheme .d2h-file-list > li {
border-bottom: var(--d2h-dark-bg-color) solid 1px;
}
.d2h-dark-color-scheme .d2h-deleted {
color: var(--d2h-dark-del-label-color);
}
.d2h-auto-color-scheme .d2h-added {
color: var(--d2h-dark-ins-label-color);
}
.d2h-auto-color-scheme .d2h-changed {
color: var(--d2h-dark-change-label-color);
}
.d2h-auto-color-scheme .d2h-moved {
color: var(--d2h-dark-moved-label-color);
}
.d2h-auto-color-scheme .d2h-tag {
background-color: var(--d2h-dark-bg-color);
}
.d2h-auto-color-scheme .d2h-deleted-tag {
border: var(--d2h-dark-del-label-color) 1px solid;
}
.d2h-auto-color-scheme .d2h-added-tag {
border: var(--d2h-dark-ins-label-color) 1px solid;
}
.d2h-auto-color-scheme .d2h-changed-tag {
border: var(--d2h-dark-change-label-color) 1px solid;
}
.d2h-auto-color-scheme .d2h-moved-tag {
border: var(--d2h-dark-moved-label-color) 1px solid;
}
}

View file

@ -0,0 +1,225 @@
import { closeTags, nodeStream, mergeStreams, getLanguage } from './highlight.js-helpers';
import { html, Diff2HtmlConfig, defaultDiff2HtmlConfig } from '../../diff2html';
import { DiffFile } from '../../types';
import { HighlightResult, HLJSApi } from 'highlight.js';
export interface Diff2HtmlUIConfig extends Diff2HtmlConfig {
synchronisedScroll?: boolean;
highlight?: boolean;
fileListToggle?: boolean;
fileListStartVisible?: boolean;
highlightLanguages?: Map<string, string>;
/**
* @deprecated since version 3.1.0
* Smart selection is now enabled by default with vanilla CSS
*/
smartSelection?: boolean;
fileContentToggle?: boolean;
stickyFileHeaders?: boolean;
}
export const defaultDiff2HtmlUIConfig = {
...defaultDiff2HtmlConfig,
synchronisedScroll: true,
highlight: true,
fileListToggle: true,
fileListStartVisible: false,
highlightLanguages: new Map<string, string>(),
/**
* @deprecated since version 3.1.0
* Smart selection is now enabled by default with vanilla CSS
*/
smartSelection: true,
fileContentToggle: true,
stickyFileHeaders: true,
};
export class Diff2HtmlUI {
readonly config: typeof defaultDiff2HtmlUIConfig;
readonly diffHtml: string;
readonly targetElement: HTMLElement;
readonly hljs: HLJSApi | null = null;
currentSelectionColumnId = -1;
constructor(target: HTMLElement, diffInput?: string | DiffFile[], config: Diff2HtmlUIConfig = {}, hljs?: HLJSApi) {
this.config = { ...defaultDiff2HtmlUIConfig, ...config };
this.diffHtml = diffInput !== undefined ? html(diffInput, this.config) : target.innerHTML;
this.targetElement = target;
if (hljs !== undefined) this.hljs = hljs;
}
draw(): void {
this.targetElement.innerHTML = this.diffHtml;
if (this.config.synchronisedScroll) this.synchronisedScroll();
if (this.config.highlight) this.highlightCode();
if (this.config.fileListToggle) this.fileListToggle(this.config.fileListStartVisible);
if (this.config.fileContentToggle) this.fileContentToggle();
if (this.config.stickyFileHeaders) this.stickyFileHeaders();
}
synchronisedScroll(): void {
this.targetElement.querySelectorAll('.d2h-file-wrapper').forEach(wrapper => {
const [left, right] = Array<Element>().slice.call(wrapper.querySelectorAll('.d2h-file-side-diff'));
if (left === undefined || right === undefined) return;
const onScroll = (event: Event): void => {
if (event === null || event.target === null) return;
if (event.target === left) {
right.scrollTop = left.scrollTop;
right.scrollLeft = left.scrollLeft;
} else {
left.scrollTop = right.scrollTop;
left.scrollLeft = right.scrollLeft;
}
};
left.addEventListener('scroll', onScroll);
right.addEventListener('scroll', onScroll);
});
}
fileListToggle(startVisible: boolean): void {
const showBtn: HTMLElement | null = this.targetElement.querySelector('.d2h-show');
const hideBtn: HTMLElement | null = this.targetElement.querySelector('.d2h-hide');
const fileList: HTMLElement | null = this.targetElement.querySelector('.d2h-file-list');
if (showBtn === null || hideBtn === null || fileList === null) return;
const show: () => void = () => {
showBtn.style.display = 'none';
hideBtn.style.display = 'inline';
fileList.style.display = 'block';
};
const hide: () => void = () => {
showBtn.style.display = 'inline';
hideBtn.style.display = 'none';
fileList.style.display = 'none';
};
showBtn.addEventListener('click', () => show());
hideBtn.addEventListener('click', () => hide());
const hashTag = this.getHashTag();
if (hashTag === 'files-summary-show') show();
else if (hashTag === 'files-summary-hide') hide();
else if (startVisible) show();
else hide();
}
fileContentToggle(): void {
this.targetElement.querySelectorAll<HTMLElement>('.d2h-file-collapse').forEach(fileContentToggleBtn => {
fileContentToggleBtn.style.display = 'flex';
const toggleFileContents: (selector: string) => void = selector => {
const fileContents: HTMLElement | null | undefined = fileContentToggleBtn
.closest('.d2h-file-wrapper')
?.querySelector(selector);
if (fileContents !== null && fileContents !== undefined) {
fileContentToggleBtn.classList.toggle('d2h-selected');
fileContents.classList.toggle('d2h-d-none');
}
};
const toggleHandler: (e: Event) => void = e => {
if (fileContentToggleBtn === e.target) return;
toggleFileContents('.d2h-file-diff');
toggleFileContents('.d2h-files-diff');
};
fileContentToggleBtn.addEventListener('click', e => toggleHandler(e));
});
}
highlightCode(): void {
const hljs = this.hljs;
if (hljs === null) {
throw new Error('Missing a `highlight.js` implementation. Please provide one when instantiating Diff2HtmlUI.');
}
// Collect all the diff files and execute the highlight on their lines
const files = this.targetElement.querySelectorAll('.d2h-file-wrapper');
files.forEach(file => {
const language = file.getAttribute('data-lang');
if (!(this.config.highlightLanguages instanceof Map)) {
this.config.highlightLanguages = new Map(Object.entries(this.config.highlightLanguages));
}
let hljsLanguage =
language && this.config.highlightLanguages.has(language)
? this.config.highlightLanguages.get(language)!
: language
? getLanguage(language)
: 'plaintext';
// Fallback to plaintext in case language is not loaded
if (hljs.getLanguage(hljsLanguage) === undefined) {
hljsLanguage = 'plaintext';
}
// Collect all the code lines and execute the highlight on them
const codeLines = file.querySelectorAll('.d2h-code-line-ctn');
codeLines.forEach(line => {
const text = line.textContent;
const lineParent = line.parentNode;
if (text === null || lineParent === null || !this.isElement(lineParent)) return;
const result: HighlightResult = closeTags(
hljs.highlight(text, {
language: hljsLanguage,
ignoreIllegals: true,
}),
);
const originalStream = nodeStream(line);
if (originalStream.length) {
const resultNode = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
resultNode.innerHTML = result.value;
result.value = mergeStreams(originalStream, nodeStream(resultNode), text);
}
line.classList.add('hljs');
if (result.language) {
line.classList.add(result.language);
}
line.innerHTML = result.value;
});
});
}
stickyFileHeaders(): void {
this.targetElement.querySelectorAll('.d2h-file-header').forEach(header => {
header.classList.add('d2h-sticky-header');
});
}
/**
* @deprecated since version 3.1.0
*/
smartSelection(): void {
console.warn('Smart selection is now enabled by default with CSS. No need to call this method anymore.');
}
private getHashTag(): string | null {
const docUrl = document.URL;
const hashTagIndex = docUrl.indexOf('#');
let hashTag = null;
if (hashTagIndex !== -1) {
hashTag = docUrl.substr(hashTagIndex + 1);
}
return hashTag;
}
private isElement(arg?: unknown): arg is Element {
return arg !== null && (arg as Element)?.classList !== undefined;
}
}

View file

@ -0,0 +1,13 @@
import { hljs } from './highlight.js-slim';
import { DiffFile } from '../../types';
import { Diff2HtmlUI as Diff2HtmlUIBase, Diff2HtmlUIConfig, defaultDiff2HtmlUIConfig } from './diff2html-ui-base';
export class Diff2HtmlUI extends Diff2HtmlUIBase {
constructor(target: HTMLElement, diffInput?: string | DiffFile[], config: Diff2HtmlUIConfig = {}) {
super(target, diffInput, config, hljs);
}
}
export { defaultDiff2HtmlUIConfig };
export type { Diff2HtmlUIConfig };

13
src/ui/js/diff2html-ui.ts Normal file
View file

@ -0,0 +1,13 @@
import hljs from 'highlight.js';
import { DiffFile } from '../../types';
import { Diff2HtmlUI as Diff2HtmlUIBase, Diff2HtmlUIConfig, defaultDiff2HtmlUIConfig } from './diff2html-ui-base';
export class Diff2HtmlUI extends Diff2HtmlUIBase {
constructor(target: HTMLElement, diffInput?: string | DiffFile[], config: Diff2HtmlUIConfig = {}) {
super(target, diffInput, config, hljs);
}
}
export { defaultDiff2HtmlUIConfig };
export type { Diff2HtmlUIConfig };

View file

@ -0,0 +1,655 @@
/*
* Adapted Highlight.js Internal APIs
* Used to highlight selected html elements using context
*/
import { HighlightResult } from 'highlight.js';
/* Utility functions */
function escapeHTML(value: string): string {
return value.replace(/&/gm, '&amp;').replace(/</gm, '&lt;').replace(/>/gm, '&gt;');
}
function tag(node: Node): string {
return node.nodeName.toLowerCase();
}
/* Stream merging */
type NodeEvent = {
event: 'start' | 'stop';
offset: number;
node: Node;
};
export function nodeStream(node: Node): NodeEvent[] {
const result: NodeEvent[] = [];
const nodeStream = (node: Node, offset: number): number => {
for (let child = node.firstChild; child; child = child.nextSibling) {
if (child.nodeType === 3 && child.nodeValue !== null) {
offset += child.nodeValue.length;
} else if (child.nodeType === 1) {
result.push({
event: 'start',
offset: offset,
node: child,
});
offset = nodeStream(child, offset);
// Prevent void elements from having an end tag that would actually
// double them in the output. There are more void elements in HTML
// but we list only those realistically expected in code display.
if (!tag(child).match(/br|hr|img|input/)) {
result.push({
event: 'stop',
offset: offset,
node: child,
});
}
}
}
return offset;
};
nodeStream(node, 0);
return result;
}
export function mergeStreams(original: NodeEvent[], highlighted: NodeEvent[], value: string): string {
let processed = 0;
let result = '';
const nodeStack = [];
function isElement(arg?: unknown): arg is Element {
return arg !== null && (arg as Element)?.attributes !== undefined;
}
function selectStream(): NodeEvent[] {
if (!original.length || !highlighted.length) {
return original.length ? original : highlighted;
}
if (original[0].offset !== highlighted[0].offset) {
return original[0].offset < highlighted[0].offset ? original : highlighted;
}
/*
To avoid starting the stream just before it should stop the order is
ensured that original always starts first and closes last:
if (event1 == 'start' && event2 == 'start')
return original;
if (event1 == 'start' && event2 == 'stop')
return highlighted;
if (event1 == 'stop' && event2 == 'start')
return original;
if (event1 == 'stop' && event2 == 'stop')
return highlighted;
... which is collapsed to:
*/
return highlighted[0].event === 'start' ? original : highlighted;
}
function open(node: Node): void {
if (!isElement(node)) {
throw new Error('Node is not an Element');
}
result += `<${tag(node)} ${Array<Attr>()
.map.call(node.attributes, attr => `${attr.nodeName}="${escapeHTML(attr.value).replace(/"/g, '&quot;')}"`)
.join(' ')}>`;
}
function close(node: Node): void {
result += '</' + tag(node) + '>';
}
function render(event: NodeEvent): void {
(event.event === 'start' ? open : close)(event.node);
}
while (original.length || highlighted.length) {
let stream = selectStream();
result += escapeHTML(value.substring(processed, stream[0].offset));
processed = stream[0].offset;
if (stream === original) {
/*
On any opening or closing tag of the original markup we first close
the entire highlighted node stack, then render the original tag along
with all the following original tags at the same offset and then
reopen all the tags on the highlighted stack.
*/
nodeStack.reverse().forEach(close);
do {
render(stream.splice(0, 1)[0]);
stream = selectStream();
} while (stream === original && stream.length && stream[0].offset === processed);
nodeStack.reverse().forEach(open);
} else {
if (stream[0].event === 'start') {
nodeStack.push(stream[0].node);
} else {
nodeStack.pop();
}
render(stream.splice(0, 1)[0]);
}
}
return result + escapeHTML(value.substr(processed));
}
// https://github.com/hexojs/hexo-util/blob/979873b63a725377c2bd6ad834d790023496130d/lib/highlight.js#L123
export function closeTags(res: HighlightResult): HighlightResult {
const tokenStack = new Array<string>();
res.value = res.value
.split('\n')
.map(line => {
const prepend = tokenStack.map(token => `<span class="${token}">`).join('');
const matches = line.matchAll(/(<span class="(.*?)">|<\/span>)/g);
Array.from(matches).forEach(match => {
if (match[0] === '</span>') tokenStack.shift();
else tokenStack.unshift(match[2]);
});
const append = '</span>'.repeat(tokenStack.length);
return prepend + line + append;
})
.join('\n');
return res;
}
// Sourced from https://github.com/highlightjs/highlight.js/blob/main/SUPPORTED_LANGUAGES.md and
// https://github.com/exercism/v2-website/blob/main/config/initializers/prism.rb#L187-L315
const languagesToExt: { [_: string]: string } = {
'1c': '1c',
abnf: 'abnf',
accesslog: 'accesslog',
as: 'actionscript',
adb: 'ada',
ada: 'ada',
ads: 'ada',
angelscript: 'angelscript',
// asc: 'angelscript',
apache: 'apache',
applescript: 'applescript',
scpt: 'applescript',
arcade: 'arcade',
cpp: 'cpp',
hpp: 'cpp',
arduino: 'arduino',
ino: 'arduino',
armasm: 'armasm',
arm: 'armasm',
xml: 'xml',
html: 'xml',
xhtml: 'xml',
rss: 'xml',
atom: 'xml',
xjb: 'xml',
xsd: 'xml',
xsl: 'xml',
plist: 'xml',
svg: 'xml',
asciidoc: 'asciidoc',
adoc: 'asciidoc',
asc: 'asciidoc',
aspectj: 'aspectj',
ahk: 'autohotkey',
ahkl: 'autohotkey',
au3: 'autoit',
avrasm: 'avrasm',
awk: 'awk',
axapta: 'axapta',
'x++': 'axapta',
bash: 'bash',
sh: 'bash',
zsh: 'bash',
b: 'basic',
bnf: 'bnf',
bf: 'brainfuck',
c: 'c',
h: 'c',
cats: 'c',
idc: 'c',
cal: 'cal',
capnproto: 'capnproto',
capnp: 'capnproto',
ceylon: 'ceylon',
clean: 'clean',
clj: 'clojure',
boot: 'clojure',
cl2: 'clojure',
cljc: 'clojure',
cljs: 'clojure',
'cljs.hl': 'clojure',
cljscm: 'clojure',
cljx: 'clojure',
hic: 'clojure',
'clojure-repl': 'clojure-repl',
cmake: 'cmake',
'cmake.in': 'cmake',
coffee: 'coffeescript',
_coffee: 'coffeescript',
cake: 'coffeescript',
cjsx: 'coffeescript',
iced: 'coffeescript',
cson: 'coffeescript',
coq: 'coq',
cos: 'cos',
cls: 'cos',
crmsh: 'crmsh',
crm: 'crmsh',
pcmk: 'crmsh',
cr: 'crystal',
cs: 'csharp',
csx: 'csharp',
csp: 'csp',
css: 'css',
d: 'd',
di: 'd',
md: 'markdown',
markdown: 'markdown',
mdown: 'markdown',
mdwn: 'markdown',
mkd: 'markdown',
mkdn: 'markdown',
mkdown: 'markdown',
ronn: 'markdown',
workbook: 'markdown',
dart: 'dart',
dpr: 'delphi',
dfm: 'delphi',
pas: 'delphi',
pascal: 'delphi',
diff: 'diff',
patch: 'diff',
django: 'django',
jinja: 'django',
dns: 'dns',
zone: 'dns',
bind: 'dns',
dockerfile: 'dockerfile',
docker: 'dockerfile',
dos: 'dos',
bat: 'dos',
cmd: 'dos',
dsconfig: 'dsconfig',
dts: 'dts',
dust: 'dust',
dst: 'dust',
ebnf: 'ebnf',
ex: 'elixir',
exs: 'elixir',
elm: 'elm',
rb: 'ruby',
builder: 'ruby',
eye: 'ruby',
gemspec: 'ruby',
god: 'ruby',
jbuilder: 'ruby',
mspec: 'ruby',
pluginspec: 'ruby',
podspec: 'ruby',
rabl: 'ruby',
rake: 'ruby',
rbuild: 'ruby',
rbw: 'ruby',
rbx: 'ruby',
ru: 'ruby',
ruby: 'ruby',
spec: 'ruby',
thor: 'ruby',
watchr: 'ruby',
erb: 'erb',
'erlang-repl': 'erlang-repl',
erl: 'erlang',
'app.src': 'erlang',
escript: 'erlang',
hrl: 'erlang',
xrl: 'erlang',
yrl: 'erlang',
excel: 'excel',
xls: 'excel',
xlsx: 'excel',
fix: 'fix',
flix: 'flix',
f90: 'fortran',
f: 'fortran',
f03: 'fortran',
f08: 'fortran',
f77: 'fortran',
f95: 'fortran',
for: 'fortran',
fpp: 'fortran',
fs: 'fsharp',
fsx: 'fsharp',
gams: 'gams',
gms: 'gams',
gauss: 'gauss',
gss: 'gauss',
gcode: 'gcode',
nc: 'gcode',
gherkin: 'gherkin',
glsl: 'glsl',
fp: 'glsl',
frag: 'glsl',
frg: 'glsl',
fsh: 'glsl',
fshader: 'glsl',
geo: 'glsl',
geom: 'glsl',
glslv: 'glsl',
gshader: 'glsl',
shader: 'glsl',
tesc: 'glsl',
tese: 'glsl',
vert: 'glsl',
vrx: 'glsl',
vsh: 'glsl',
vshader: 'glsl',
gml: 'gml',
go: 'go',
bal: 'go',
golo: 'golo',
gololang: 'golo',
gradle: 'gradle',
groovy: 'groovy',
grt: 'groovy',
gtpl: 'groovy',
gvy: 'groovy',
haml: 'haml',
'haml.deface': 'haml',
handlebars: 'handlebars',
hbs: 'handlebars',
'html.hbs': 'handlebars',
'html.handlebars': 'handlebars',
hs: 'haskell',
hsc: 'haskell',
idr: 'haskell',
purs: 'haskell',
hx: 'haxe',
hxsl: 'haxe',
hsp: 'hsp',
htmlbars: 'htmlbars',
http: 'http',
https: 'http',
hy: 'hy',
inform7: 'inform7',
i7: 'inform7',
ini: 'ini',
toml: 'ini',
cfg: 'ini',
prefs: 'ini',
// properties: 'ini',
irpf90: 'irpf90',
isbl: 'isbl',
java: 'java',
jsp: 'java',
js: 'javascript',
jsx: 'javascript',
_js: 'javascript',
bones: 'javascript',
es: 'javascript',
es6: 'javascript',
gs: 'javascript',
jake: 'javascript',
jsb: 'javascript',
jscad: 'javascript',
jsfl: 'javascript',
jsm: 'javascript',
jss: 'javascript',
mjs: 'javascript',
njs: 'javascript',
pac: 'javascript',
sjs: 'javascript',
ssjs: 'javascript',
xsjs: 'javascript',
xsjslib: 'javascript',
cfc: 'javascript',
'jboss-cli': 'jboss-cli',
json: 'json',
avsc: 'json',
geojson: 'json',
gltf: 'json',
'JSON-tmLanguage': 'json',
jsonl: 'json',
tfstate: 'json',
'tfstate.backup': 'json',
topojson: 'json',
webapp: 'json',
webmanifest: 'json',
jl: 'julia',
'julia-repl': 'julia-repl',
kt: 'kotlin',
ktm: 'kotlin',
kts: 'kotlin',
lasso: 'lasso',
// ls: 'lasso',
lassoscript: 'lasso',
tex: 'latex',
ldif: 'ldif',
leaf: 'leaf',
less: 'less',
lisp: 'lisp',
factor: 'lisp',
livecodeserver: 'livecodeserver',
ls: 'livescript',
_ls: 'livescript',
llvm: 'llvm',
lsl: 'lsl',
lua: 'lua',
nse: 'lua',
p8: 'lua',
pd_lua: 'lua',
rbxs: 'lua',
wlua: 'lua',
mak: 'makefile',
make: 'makefile',
mk: 'makefile',
mkfile: 'makefile',
mathematica: 'mathematica',
mma: 'mathematica',
wl: 'mathematica',
matlab: 'matlab',
maxima: 'maxima',
mel: 'mel',
mercury: 'mercury',
mipsasm: 'mipsasm',
miz: 'mizar',
voc: 'mizar',
al: 'perl',
cgi: 'perl',
fcgi: 'perl',
perl: 'perl',
ph: 'perl',
plx: 'perl',
pl: 'perl',
pm: 'perl',
psgi: 'perl',
t: 'perl',
mojolicious: 'mojolicious',
monkey: 'monkey',
monkey2: 'monkey',
moonscript: 'moonscript',
moon: 'moonscript',
n1ql: 'n1ql',
nginxconf: 'nginx',
nim: 'nim',
nimrod: 'nim',
nix: 'nix',
nsi: 'nsis',
nsh: 'nsis',
m: 'objectivec',
objc: 'objectivec',
mm: 'objectivec',
'obj-c': 'objectivec',
'obj-c++': 'objectivec',
'objective-c++': 'objectivec',
fun: 'ocaml',
sig: 'ocaml',
// sml: 'ocaml',
ml: 'ocaml',
mli: 'ocaml',
eliom: 'ocaml',
eliomi: 'ocaml',
ml4: 'ocaml',
mll: 'ocaml',
mly: 'ocaml',
openscad: 'openscad',
oxygene: 'oxygene',
parser3: 'parser3',
pf: 'pf',
'pf.conf': 'pf',
pgsql: 'pgsql',
postgres: 'pgsql',
postgresql: 'pgsql',
php: 'php',
aw: 'php',
ctp: 'php',
inc: 'php',
php3: 'php',
php4: 'php',
php5: 'php',
phps: 'php',
phpt: 'php',
'php-template': 'php-template',
plaintext: 'plaintext',
txt: 'plaintext',
text: 'plaintext',
pony: 'pony',
ps: 'powershell',
ps1: 'powershell',
psd1: 'powershell',
psm1: 'powershell',
pde: 'processing',
profile: 'profile',
pro: 'prolog',
prolog: 'prolog',
yap: 'prolog',
properties: 'properties',
proto: 'protobuf',
puppet: 'puppet',
pp: 'puppet',
purebasic: 'purebasic',
py: 'python',
bzl: 'python',
gyp: 'python',
gypi: 'python',
lmi: 'python',
py3: 'python',
pyde: 'python',
pyi: 'python',
pyp: 'python',
pyt: 'python',
pyw: 'python',
rpy: 'python',
tac: 'python',
wsgi: 'python',
xpy: 'python',
'python-repl': 'python-repl',
pycon: 'python-repl',
q: 'q',
k: 'q',
kdb: 'q',
qml: 'qml',
r: 'r',
rd: 'r',
rsx: 'r',
reasonml: 'reasonml',
re: 'reasonml',
rib: 'rib',
roboconf: 'roboconf',
graph: 'roboconf',
instances: 'roboconf',
routeros: 'routeros',
rsl: 'rsl',
ruleslanguage: 'ruleslanguage',
rs: 'rust',
'rs.in': 'rust',
sas: 'sas',
// pony: 'scala',
scala: 'scala',
kojo: 'scala',
sbt: 'scala',
sc: 'scala',
scm: 'scheme',
sch: 'scheme',
sld: 'scheme',
sls: 'scheme',
sps: 'scheme',
ss: 'scheme',
rkt: 'scheme',
scilab: 'scilab',
scss: 'scss',
shell: 'shell',
smali: 'smali',
st: 'smalltalk',
sml: 'sml',
sqf: 'sqf',
sql: 'sql',
cql: 'sql',
ddl: 'sql',
mysql: 'sql',
prc: 'sql',
tab: 'sql',
udf: 'sql',
viw: 'sql',
stan: 'stan',
stanfuncs: 'stan',
stata: 'stata',
step21: 'step21',
step: 'step21',
stp: 'step21',
styl: 'stylus',
subunit: 'subunit',
swift: 'swift',
taggerscript: 'taggerscript',
yml: 'yaml',
mir: 'yaml',
reek: 'yaml',
rviz: 'yaml',
'sublime-syntax': 'yaml',
syntax: 'yaml',
yaml: 'yaml',
'yaml-tmlanguage': 'yaml',
'yml.mysql': 'yaml',
tap: 'tap',
tcl: 'tcl',
adp: 'tcl',
tm: 'tcl',
thrift: 'thrift',
tp: 'tp',
twig: 'twig',
craftcms: 'twig',
ts: 'typescript',
tsx: 'typescript',
vala: 'vala',
vbnet: 'vbnet',
vb: 'vbnet',
vbscript: 'vbscript',
vbs: 'vbscript',
'vbscript-html': 'vbscript-html',
v: 'verilog',
veo: 'verilog',
vhdl: 'vhdl',
vhd: 'vhdl',
vhf: 'vhdl',
vhi: 'vhdl',
vho: 'vhdl',
vhs: 'vhdl',
vht: 'vhdl',
vhw: 'vhdl',
vim: 'vim',
x86asm: 'x86asm',
xl: 'xl',
xquery: 'xquery',
xpath: 'xquery',
xq: 'xquery',
zephir: 'zephir',
zep: 'zephir',
};
export function getLanguage(fileExtension: string): string {
return languagesToExt[fileExtension] ?? 'plaintext';
}

View file

@ -0,0 +1,391 @@
// Require the highlight.js library without languages
import highlightJS from 'highlight.js/lib/core';
// Convert to imports
// ^hljs\.registerLanguage\('(.+)', require\('\./languages\/(.+)'\)\);$
// import $1 from 'highlight.js/lib/languages/$2';
// import _1c from 'highlight.js/lib/languages/1c';
// import abnf from 'highlight.js/lib/languages/abnf';
// import accesslog from 'highlight.js/lib/languages/accesslog';
// import actionscript from 'highlight.js/lib/languages/actionscript';
// import ada from 'highlight.js/lib/languages/ada';
// import angelscript from 'highlight.js/lib/languages/angelscript';
// import apache from 'highlight.js/lib/languages/apache';
// import applescript from 'highlight.js/lib/languages/applescript';
// import arcade from 'highlight.js/lib/languages/arcade';
import cpp from 'highlight.js/lib/languages/cpp';
// import arduino from 'highlight.js/lib/languages/arduino';
// import armasm from 'highlight.js/lib/languages/armasm';
import xml from 'highlight.js/lib/languages/xml';
// import asciidoc from 'highlight.js/lib/languages/asciidoc';
// import aspectj from 'highlight.js/lib/languages/aspectj';
// import autohotkey from 'highlight.js/lib/languages/autohotkey';
// import autoit from 'highlight.js/lib/languages/autoit';
// import avrasm from 'highlight.js/lib/languages/avrasm';
import awk from 'highlight.js/lib/languages/awk';
// import axapta from 'highlight.js/lib/languages/axapta';
import bash from 'highlight.js/lib/languages/bash';
// import basic from 'highlight.js/lib/languages/basic';
// import bnf from 'highlight.js/lib/languages/bnf';
// import brainfuck from 'highlight.js/lib/languages/brainfuck';
import c from 'highlight.js/lib/languages/c';
// import cal from 'highlight.js/lib/languages/cal';
// import capnproto from 'highlight.js/lib/languages/capnproto';
// import ceylon from 'highlight.js/lib/languages/ceylon';
// import clean from 'highlight.js/lib/languages/clean';
import clojure from 'highlight.js/lib/languages/clojure';
// import clojureRepl from 'highlight.js/lib/languages/clojure-repl';
// import cmake from 'highlight.js/lib/languages/cmake';
// import coffeescript from 'highlight.js/lib/languages/coffeescript';
// import coq from 'highlight.js/lib/languages/coq';
// import cos from 'highlight.js/lib/languages/cos';
// import crmsh from 'highlight.js/lib/languages/crmsh';
import crystal from 'highlight.js/lib/languages/crystal';
import csharp from 'highlight.js/lib/languages/csharp';
import csp from 'highlight.js/lib/languages/csp';
import css from 'highlight.js/lib/languages/css';
// import d from 'highlight.js/lib/languages/d';
import markdown from 'highlight.js/lib/languages/markdown';
import dart from 'highlight.js/lib/languages/dart';
// import delphi from 'highlight.js/lib/languages/delphi';
import diff from 'highlight.js/lib/languages/diff';
// import django from 'highlight.js/lib/languages/django';
// import dns from 'highlight.js/lib/languages/dns';
import dockerfile from 'highlight.js/lib/languages/dockerfile';
// import dos from 'highlight.js/lib/languages/dos';
// import dsconfig from 'highlight.js/lib/languages/dsconfig';
// import dts from 'highlight.js/lib/languages/dts';
// import dust from 'highlight.js/lib/languages/dust';
// import ebnf from 'highlight.js/lib/languages/ebnf';
import elixir from 'highlight.js/lib/languages/elixir';
import elm from 'highlight.js/lib/languages/elm';
import ruby from 'highlight.js/lib/languages/ruby';
// import erb from 'highlight.js/lib/languages/erb';
// import erlangRepl from 'highlight.js/lib/languages/erlang-repl';
import erlang from 'highlight.js/lib/languages/erlang';
// import excel from 'highlight.js/lib/languages/excel';
// import fix from 'highlight.js/lib/languages/fix';
// import flix from 'highlight.js/lib/languages/flix';
// import fortran from 'highlight.js/lib/languages/fortran';
import fsharp from 'highlight.js/lib/languages/fsharp';
// import gams from 'highlight.js/lib/languages/gams';
// import gauss from 'highlight.js/lib/languages/gauss';
// import gcode from 'highlight.js/lib/languages/gcode';
// import gherkin from 'highlight.js/lib/languages/gherkin';
// import glsl from 'highlight.js/lib/languages/glsl';
// import gml from 'highlight.js/lib/languages/gml';
import go from 'highlight.js/lib/languages/go';
// import golo from 'highlight.js/lib/languages/golo';
import gradle from 'highlight.js/lib/languages/gradle';
import groovy from 'highlight.js/lib/languages/groovy';
// import haml from 'highlight.js/lib/languages/haml';
import handlebars from 'highlight.js/lib/languages/handlebars';
import haskell from 'highlight.js/lib/languages/haskell';
// import haxe from 'highlight.js/lib/languages/haxe';
// import hsp from 'highlight.js/lib/languages/hsp';
// import htmlbars from 'highlight.js/lib/languages/htmlbars';
// import http from 'highlight.js/lib/languages/http';
// import hy from 'highlight.js/lib/languages/hy';
// import inform7 from 'highlight.js/lib/languages/inform7';
import ini from 'highlight.js/lib/languages/ini';
// import irpf90 from 'highlight.js/lib/languages/irpf90';
// import isbl from 'highlight.js/lib/languages/isbl';
import java from 'highlight.js/lib/languages/java';
import javascript from 'highlight.js/lib/languages/javascript';
// import jbossCli from 'highlight.js/lib/languages/jboss-cli';
import json from 'highlight.js/lib/languages/json';
// import julia from 'highlight.js/lib/languages/julia';
// import juliaRepl from 'highlight.js/lib/languages/julia-repl';
import kotlin from 'highlight.js/lib/languages/kotlin';
// import lasso from 'highlight.js/lib/languages/lasso';
// import latex from 'highlight.js/lib/languages/latex';
// import ldif from 'highlight.js/lib/languages/ldif';
// import leaf from 'highlight.js/lib/languages/leaf';
import less from 'highlight.js/lib/languages/less';
import lisp from 'highlight.js/lib/languages/lisp';
// import livecodeserver from 'highlight.js/lib/languages/livecodeserver';
// import livescript from 'highlight.js/lib/languages/livescript';
// import llvm from 'highlight.js/lib/languages/llvm';
// import lsl from 'highlight.js/lib/languages/lsl';
import lua from 'highlight.js/lib/languages/lua';
import makefile from 'highlight.js/lib/languages/makefile';
// import mathematica from 'highlight.js/lib/languages/mathematica';
// import matlab from 'highlight.js/lib/languages/matlab';
// import maxima from 'highlight.js/lib/languages/maxima';
// import mel from 'highlight.js/lib/languages/mel';
// import mercury from 'highlight.js/lib/languages/mercury';
// import mipsasm from 'highlight.js/lib/languages/mipsasm';
// import mizar from 'highlight.js/lib/languages/mizar';
import perl from 'highlight.js/lib/languages/perl';
// import mojolicious from 'highlight.js/lib/languages/mojolicious';
// import monkey from 'highlight.js/lib/languages/monkey';
// import moonscript from 'highlight.js/lib/languages/moonscript';
// import n1ql from 'highlight.js/lib/languages/n1ql';
import nginx from 'highlight.js/lib/languages/nginx';
// import nim from 'highlight.js/lib/languages/nim';
// import nix from 'highlight.js/lib/languages/nix';
// import nsis from 'highlight.js/lib/languages/nsis';
import objectivec from 'highlight.js/lib/languages/objectivec';
// import ocaml from 'highlight.js/lib/languages/ocaml';
// import openscad from 'highlight.js/lib/languages/openscad';
// import oxygene from 'highlight.js/lib/languages/oxygene';
// import parser3 from 'highlight.js/lib/languages/parser3';
// import pf from 'highlight.js/lib/languages/pf';
import pgsql from 'highlight.js/lib/languages/pgsql';
import php from 'highlight.js/lib/languages/php';
// import phpTemplate from 'highlight.js/lib/languages/php-template';
import plaintext from 'highlight.js/lib/languages/plaintext';
// import pony from 'highlight.js/lib/languages/pony';
import powershell from 'highlight.js/lib/languages/powershell';
// import processing from 'highlight.js/lib/languages/processing';
// import profile from 'highlight.js/lib/languages/profile';
// import prolog from 'highlight.js/lib/languages/prolog';
import properties from 'highlight.js/lib/languages/properties';
import protobuf from 'highlight.js/lib/languages/protobuf';
// import puppet from 'highlight.js/lib/languages/puppet';
// import purebasic from 'highlight.js/lib/languages/purebasic';
import python from 'highlight.js/lib/languages/python';
// import pythonRepl from 'highlight.js/lib/languages/python-repl';
// import q from 'highlight.js/lib/languages/q';
// import qml from 'highlight.js/lib/languages/qml';
// import r from 'highlight.js/lib/languages/r';
// import reasonml from 'highlight.js/lib/languages/reasonml';
// import rib from 'highlight.js/lib/languages/rib';
// import roboconf from 'highlight.js/lib/languages/roboconf';
// import routeros from 'highlight.js/lib/languages/routeros';
// import rsl from 'highlight.js/lib/languages/rsl';
// import ruleslanguage from 'highlight.js/lib/languages/ruleslanguage';
import rust from 'highlight.js/lib/languages/rust';
// import sas from 'highlight.js/lib/languages/sas';
import scala from 'highlight.js/lib/languages/scala';
// import scheme from 'highlight.js/lib/languages/scheme';
// import scilab from 'highlight.js/lib/languages/scilab';
import scss from 'highlight.js/lib/languages/scss';
import shell from 'highlight.js/lib/languages/shell';
// import smali from 'highlight.js/lib/languages/smali';
// import smalltalk from 'highlight.js/lib/languages/smalltalk';
// import sml from 'highlight.js/lib/languages/sml';
// import sqf from 'highlight.js/lib/languages/sqf';
import sql from 'highlight.js/lib/languages/sql';
// import stan from 'highlight.js/lib/languages/stan';
// import stata from 'highlight.js/lib/languages/stata';
// import step21 from 'highlight.js/lib/languages/step21';
// import stylus from 'highlight.js/lib/languages/stylus';
// import subunit from 'highlight.js/lib/languages/subunit';
import swift from 'highlight.js/lib/languages/swift';
// import taggerscript from 'highlight.js/lib/languages/taggerscript';
import yaml from 'highlight.js/lib/languages/yaml';
// import tap from 'highlight.js/lib/languages/tap';
// import tcl from 'highlight.js/lib/languages/tcl';
// import thrift from 'highlight.js/lib/languages/thrift';
// import tp from 'highlight.js/lib/languages/tp';
// import twig from 'highlight.js/lib/languages/twig';
import typescript from 'highlight.js/lib/languages/typescript';
// import vala from 'highlight.js/lib/languages/vala';
// import vbnet from 'highlight.js/lib/languages/vbnet';
// import vbscript from 'highlight.js/lib/languages/vbscript';
// import vbscriptHtml from 'highlight.js/lib/languages/vbscript-html';
// import verilog from 'highlight.js/lib/languages/verilog';
// import vhdl from 'highlight.js/lib/languages/vhdl';
// import vim from 'highlight.js/lib/languages/vim';
// import x86asm from 'highlight.js/lib/languages/x86asm';
// import xl from 'highlight.js/lib/languages/xl';
// import xquery from 'highlight.js/lib/languages/xquery';
// import zephir from 'highlight.js/lib/languages/zephir';
// Convert to registerLanguage
// ^hljs\.registerLanguage\('(.+)', require\('\./languages\/(.+)'\)\);$
// highlightJS.registerLanguage('$1', $1);
// Separately require languages
// highlightJS.registerLanguage('1c', _1c);
// highlightJS.registerLanguage('abnf', abnf);
// highlightJS.registerLanguage('accesslog', accesslog);
// highlightJS.registerLanguage('actionscript', actionscript);
// highlightJS.registerLanguage('ada', ada);
// highlightJS.registerLanguage('angelscript', angelscript);
// highlightJS.registerLanguage('apache', apache);
// highlightJS.registerLanguage('applescript', applescript);
// highlightJS.registerLanguage('arcade', arcade);
highlightJS.registerLanguage('cpp', cpp);
// highlightJS.registerLanguage('arduino', arduino);
// highlightJS.registerLanguage('armasm', armasm);
highlightJS.registerLanguage('xml', xml);
// highlightJS.registerLanguage('asciidoc', asciidoc);
// highlightJS.registerLanguage('aspectj', aspectj);
// highlightJS.registerLanguage('autohotkey', autohotkey);
// highlightJS.registerLanguage('autoit', autoit);
// highlightJS.registerLanguage('avrasm', avrasm);
highlightJS.registerLanguage('awk', awk);
// highlightJS.registerLanguage('axapta', axapta);
highlightJS.registerLanguage('bash', bash);
// highlightJS.registerLanguage('basic', basic);
// highlightJS.registerLanguage('bnf', bnf);
// highlightJS.registerLanguage('brainfuck', brainfuck);
highlightJS.registerLanguage('c', c);
// highlightJS.registerLanguage('cal', cal);
// highlightJS.registerLanguage('capnproto', capnproto);
// highlightJS.registerLanguage('ceylon', ceylon);
// highlightJS.registerLanguage('clean', clean);
highlightJS.registerLanguage('clojure', clojure);
// highlightJS.registerLanguage('clojure-repl', clojureRepl);
// highlightJS.registerLanguage('cmake', cmake);
// highlightJS.registerLanguage('coffeescript', coffeescript);
// highlightJS.registerLanguage('coq', coq);
// highlightJS.registerLanguage('cos', cos);
// highlightJS.registerLanguage('crmsh', crmsh);
highlightJS.registerLanguage('crystal', crystal);
highlightJS.registerLanguage('csharp', csharp);
highlightJS.registerLanguage('csp', csp);
highlightJS.registerLanguage('css', css);
// highlightJS.registerLanguage('d', d);
highlightJS.registerLanguage('markdown', markdown);
highlightJS.registerLanguage('dart', dart);
// highlightJS.registerLanguage('delphi', delphi);
highlightJS.registerLanguage('diff', diff);
// highlightJS.registerLanguage('django', django);
// highlightJS.registerLanguage('dns', dns);
highlightJS.registerLanguage('dockerfile', dockerfile);
// highlightJS.registerLanguage('dos', dos);
// highlightJS.registerLanguage('dsconfig', dsconfig);
// highlightJS.registerLanguage('dts', dts);
// highlightJS.registerLanguage('dust', dust);
// highlightJS.registerLanguage('ebnf', ebnf);
highlightJS.registerLanguage('elixir', elixir);
highlightJS.registerLanguage('elm', elm);
highlightJS.registerLanguage('ruby', ruby);
// highlightJS.registerLanguage('erb', erb);
// highlightJS.registerLanguage('erlang-repl', erlangRepl);
highlightJS.registerLanguage('erlang', erlang);
// highlightJS.registerLanguage('excel', excel);
// highlightJS.registerLanguage('fix', fix);
// highlightJS.registerLanguage('flix', flix);
// highlightJS.registerLanguage('fortran', fortran);
highlightJS.registerLanguage('fsharp', fsharp);
// highlightJS.registerLanguage('gams', gams);
// highlightJS.registerLanguage('gauss', gauss);
// highlightJS.registerLanguage('gcode', gcode);
// highlightJS.registerLanguage('gherkin', gherkin);
// highlightJS.registerLanguage('glsl', glsl);
// highlightJS.registerLanguage('gml', gml);
highlightJS.registerLanguage('go', go);
// highlightJS.registerLanguage('golo', golo);
highlightJS.registerLanguage('gradle', gradle);
highlightJS.registerLanguage('groovy', groovy);
// highlightJS.registerLanguage('haml', haml);
highlightJS.registerLanguage('handlebars', handlebars);
highlightJS.registerLanguage('haskell', haskell);
// highlightJS.registerLanguage('haxe', haxe);
// highlightJS.registerLanguage('hsp', hsp);
// highlightJS.registerLanguage('htmlbars', htmlbars);
// highlightJS.registerLanguage('http', http);
// highlightJS.registerLanguage('hy', hy);
// highlightJS.registerLanguage('inform7', inform7);
highlightJS.registerLanguage('ini', ini);
// highlightJS.registerLanguage('irpf90', irpf90);
// highlightJS.registerLanguage('isbl', isbl);
highlightJS.registerLanguage('java', java);
highlightJS.registerLanguage('javascript', javascript);
// highlightJS.registerLanguage('jboss-cli', jbossCli);
highlightJS.registerLanguage('json', json);
// highlightJS.registerLanguage('julia', julia);
// highlightJS.registerLanguage('julia-repl', juliaRepl);
highlightJS.registerLanguage('kotlin', kotlin);
// highlightJS.registerLanguage('lasso', lasso);
// highlightJS.registerLanguage('latex', latex);
// highlightJS.registerLanguage('ldif', ldif);
// highlightJS.registerLanguage('leaf', leaf);
highlightJS.registerLanguage('less', less);
highlightJS.registerLanguage('lisp', lisp);
// highlightJS.registerLanguage('livecodeserver', livecodeserver);
// highlightJS.registerLanguage('livescript', livescript);
// highlightJS.registerLanguage('llvm', llvm);
// highlightJS.registerLanguage('lsl', lsl);
highlightJS.registerLanguage('lua', lua);
highlightJS.registerLanguage('makefile', makefile);
// highlightJS.registerLanguage('mathematica', mathematica);
// highlightJS.registerLanguage('matlab', matlab);
// highlightJS.registerLanguage('maxima', maxima);
// highlightJS.registerLanguage('mel', mel);
// highlightJS.registerLanguage('mercury', mercury);
// highlightJS.registerLanguage('mipsasm', mipsasm);
// highlightJS.registerLanguage('mizar', mizar);
highlightJS.registerLanguage('perl', perl);
// highlightJS.registerLanguage('mojolicious', mojolicious);
// highlightJS.registerLanguage('monkey', monkey);
// highlightJS.registerLanguage('moonscript', moonscript);
// highlightJS.registerLanguage('n1ql', n1ql);
highlightJS.registerLanguage('nginx', nginx);
// highlightJS.registerLanguage('nim', nim);
// highlightJS.registerLanguage('nix', nix);
// highlightJS.registerLanguage('nsis', nsis);
highlightJS.registerLanguage('objectivec', objectivec);
// highlightJS.registerLanguage('ocaml', ocaml);
// highlightJS.registerLanguage('openscad', openscad);
// highlightJS.registerLanguage('oxygene', oxygene);
// highlightJS.registerLanguage('parser3', parser3);
// highlightJS.registerLanguage('pf', pf);
highlightJS.registerLanguage('pgsql', pgsql);
highlightJS.registerLanguage('php', php);
// highlightJS.registerLanguage('php-template', phpTemplate);
highlightJS.registerLanguage('plaintext', plaintext);
// highlightJS.registerLanguage('pony', pony);
highlightJS.registerLanguage('powershell', powershell);
// highlightJS.registerLanguage('processing', processing);
// highlightJS.registerLanguage('profile', profile);
// highlightJS.registerLanguage('prolog', prolog);
highlightJS.registerLanguage('properties', properties);
highlightJS.registerLanguage('protobuf', protobuf);
// highlightJS.registerLanguage('puppet', puppet);
// highlightJS.registerLanguage('purebasic', purebasic);
highlightJS.registerLanguage('python', python);
// highlightJS.registerLanguage('python-repl', pythonRepl);
// highlightJS.registerLanguage('q', q);
// highlightJS.registerLanguage('qml', qml);
// highlightJS.registerLanguage('r', r);
// highlightJS.registerLanguage('reasonml', reasonml);
// highlightJS.registerLanguage('rib', rib);
// highlightJS.registerLanguage('roboconf', roboconf);
// highlightJS.registerLanguage('routeros', routeros);
// highlightJS.registerLanguage('rsl', rsl);
// highlightJS.registerLanguage('ruleslanguage', ruleslanguage);
highlightJS.registerLanguage('rust', rust);
// highlightJS.registerLanguage('sas', sas);
highlightJS.registerLanguage('scala', scala);
// highlightJS.registerLanguage('scheme', scheme);
// highlightJS.registerLanguage('scilab', scilab);
highlightJS.registerLanguage('scss', scss);
highlightJS.registerLanguage('shell', shell);
// highlightJS.registerLanguage('smali', smali);
// highlightJS.registerLanguage('smalltalk', smalltalk);
// highlightJS.registerLanguage('sml', sml);
// highlightJS.registerLanguage('sqf', sqf);
highlightJS.registerLanguage('sql', sql);
// highlightJS.registerLanguage('stan', stan);
// highlightJS.registerLanguage('stata', stata);
// highlightJS.registerLanguage('step21', step21);
// highlightJS.registerLanguage('stylus', stylus);
// highlightJS.registerLanguage('subunit', subunit);
highlightJS.registerLanguage('swift', swift);
// highlightJS.registerLanguage('taggerscript', taggerscript);
highlightJS.registerLanguage('yaml', yaml);
// highlightJS.registerLanguage('tap', tap);
// highlightJS.registerLanguage('tcl', tcl);
// highlightJS.registerLanguage('thrift', thrift);
// highlightJS.registerLanguage('tp', tp);
// highlightJS.registerLanguage('twig', twig);
highlightJS.registerLanguage('typescript', typescript);
// highlightJS.registerLanguage('vala', vala);
// highlightJS.registerLanguage('vbnet', vbnet);
// highlightJS.registerLanguage('vbscript', vbscript);
// highlightJS.registerLanguage('vbscript-html', vbscriptHtml);
// highlightJS.registerLanguage('verilog', verilog);
// highlightJS.registerLanguage('vhdl', vhdl);
// highlightJS.registerLanguage('vim', vim);
// highlightJS.registerLanguage('x86asm', x86asm);
// highlightJS.registerLanguage('xl', xl);
// highlightJS.registerLanguage('xquery', xquery);
// highlightJS.registerLanguage('zephir', zephir);
export const hljs = highlightJS;

View file

@ -1,46 +0,0 @@
/*
*
* Utils (utils.js)
* Author: rtfpessoa
*
*/
(function(ctx, undefined) {
function Utils() {
}
Utils.prototype.escape = function(str) {
return str.slice(0)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\t/g, ' ');
};
Utils.prototype.getRandomId = function(prefix) {
return prefix + "-" + Math.random().toString(36).slice(-3);
};
Utils.prototype.startsWith = function(str, start) {
if (typeof start === 'object') {
var result = false;
start.forEach(function(s) {
if (str.indexOf(s) === 0) {
result = true;
}
});
return result;
}
return str.indexOf(start) === 0;
};
Utils.prototype.valueOrEmpty = function(value) {
return value ? value : '';
};
module.exports['Utils'] = new Utils();
})(this);

54
src/utils.ts Normal file
View file

@ -0,0 +1,54 @@
const specials = [
// Order matters for these
'-',
'[',
']',
// Order doesn't matter for any of these
'/',
'{',
'}',
'(',
')',
'*',
'+',
'?',
'.',
'\\',
'^',
'$',
'|',
];
// All characters will be escaped with '\'
// even though only some strictly require it when inside of []
const regex = RegExp('[' + specials.join('\\') + ']', 'g');
/**
* Escapes all required characters for safe usage inside a RegExp
*/
export function escapeForRegExp(str: string): string {
return str.replace(regex, '\\$&');
}
/**
* Converts all '\' in @path to unix style '/'
*/
export function unifyPath(path: string): string {
return path ? path.replace(/\\/g, '/') : path;
}
/**
* Create unique number identifier for @text
*/
export function hashCode(text: string): number {
let i, chr, len;
let hash = 0;
for (i = 0, len = text.length; i < len; i++) {
chr = text.charCodeAt(i);
hash = (hash << 5) - hash + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
}

217
terraform/main.tf Normal file
View file

@ -0,0 +1,217 @@
# Inspired by https://gist.github.com/danihodovic/a51eb0d9d4b29649c2d094f4251827dd
provider "aws" {
profile = "${var.aws_profile}"
region = "${var.aws_region}"
}
provider "aws" {
alias = "nvirginia"
profile = "${var.aws_profile}"
region = "us-east-1"
}
terraform {
backend "s3" {
region = "us-east-1"
encrypt = true
bucket = "terraform-state-bucket.rtfpessoa.xyz"
dynamodb_table = "terraform-state-table"
key = "diff2html.xyz"
}
}
resource "aws_acm_certificate" "cert" {
provider = "aws.nvirginia"
domain_name = "${var.domain}"
subject_alternative_names = ["*.${var.domain}"]
validation_method = "DNS"
lifecycle {
create_before_destroy = true
}
}
resource "aws_route53_record" "root_domain" {
zone_id = "${var.hosted_zone_id}"
name = "${var.domain}"
type = "A"
alias {
name = "${aws_cloudfront_distribution.cdn.domain_name}"
zone_id = "${aws_cloudfront_distribution.cdn.hosted_zone_id}"
evaluate_target_health = false
}
}
resource "aws_route53_record" "www_domain" {
zone_id = "${var.hosted_zone_id}"
name = "${local.www_domain}"
type = "A"
alias {
name = "${aws_cloudfront_distribution.www_cdn.domain_name}"
zone_id = "${aws_cloudfront_distribution.www_cdn.hosted_zone_id}"
evaluate_target_health = false
}
}
resource "aws_route53_record" "cert_validation" {
zone_id = "${var.hosted_zone_id}"
name = "${aws_acm_certificate.cert.domain_validation_options.0.resource_record_name}"
type = "${aws_acm_certificate.cert.domain_validation_options.0.resource_record_type}"
records = ["${aws_acm_certificate.cert.domain_validation_options.0.resource_record_value}"]
ttl = 60
}
resource "aws_acm_certificate_validation" "cert" {
provider = "aws.nvirginia"
certificate_arn = "${aws_acm_certificate.cert.arn}"
validation_record_fqdns = ["${aws_route53_record.cert_validation.fqdn}"]
}
resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
comment = "${var.domain} origin access identity"
}
locals {
s3_origin_id = "S3-${var.domain}"
s3_www_origin_id = "S3-www-${var.domain}"
www_domain = "www.${var.domain}"
}
resource "aws_s3_bucket" "site" {
bucket = "${var.domain}"
acl = "private"
policy = <<EOF
{
"Version": "2008-10-17",
"Statement": [{
"Sid": "AllowCloudFrontRead",
"Effect": "Allow",
"Principal": { "AWS": "${aws_cloudfront_origin_access_identity.origin_access_identity.iam_arn}" },
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::${var.domain}/*"
}]
}
EOF
}
resource "aws_cloudfront_distribution" "cdn" {
origin {
domain_name = "${aws_s3_bucket.site.bucket_regional_domain_name}"
origin_id = "${local.s3_origin_id}"
s3_origin_config {
origin_access_identity = "${aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path}"
}
}
# If using route53 aliases for DNS we need to declare it here too, otherwise we'll get 403s.
aliases = ["${var.domain}"]
enabled = true
is_ipv6_enabled = true
default_root_object = "index.html"
default_cache_behavior {
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "${local.s3_origin_id}"
forwarded_values {
query_string = true
cookies {
forward = "none"
}
}
min_ttl = 0
default_ttl = 86400
max_ttl = 31536000
compress = true
viewer_protocol_policy = "redirect-to-https"
}
price_class = "PriceClass_All"
restrictions {
geo_restriction {
restriction_type = "none"
locations = []
}
}
viewer_certificate {
acm_certificate_arn = "${aws_acm_certificate_validation.cert.certificate_arn}"
minimum_protocol_version = "TLSv1.1_2016"
ssl_support_method = "sni-only"
}
}
resource "aws_s3_bucket" "www_site" {
bucket = "${local.www_domain}"
acl = "public-read"
website {
redirect_all_requests_to = "https://${var.domain}"
}
}
resource "aws_cloudfront_distribution" "www_cdn" {
origin {
origin_id = "${local.s3_www_origin_id}"
domain_name = "${aws_s3_bucket.www_site.website_endpoint}"
custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "http-only"
origin_ssl_protocols = ["TLSv1.1", "TLSv1.2"]
}
}
# If using route53 aliases for DNS we need to declare it here too, otherwise we'll get 403s.
aliases = ["${local.www_domain}"]
enabled = true
is_ipv6_enabled = true
default_cache_behavior {
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "${local.s3_www_origin_id}"
forwarded_values {
query_string = true
cookies {
forward = "none"
}
}
min_ttl = 0
default_ttl = 86400
max_ttl = 31536000
compress = true
viewer_protocol_policy = "redirect-to-https"
}
price_class = "PriceClass_All"
restrictions {
geo_restriction {
restriction_type = "none"
locations = []
}
}
viewer_certificate {
acm_certificate_arn = "${aws_acm_certificate_validation.cert.certificate_arn}"
minimum_protocol_version = "TLSv1.1_2016"
ssl_support_method = "sni-only"
}
}

7
terraform/outputs.tf Normal file
View file

@ -0,0 +1,7 @@
output "route53_domain" {
value = "${aws_route53_record.root_domain.fqdn}"
}
output "cdn_domain" {
value = "${aws_cloudfront_distribution.cdn.domain_name}"
}

19
terraform/variables.tf Normal file
View file

@ -0,0 +1,19 @@
variable "aws_region" {
description = "The aws region to deploy"
default = "eu-west-1"
}
variable "aws_profile" {
description = "The aws profile to use"
default = "personal"
}
variable "domain" {
description = "The domain to deploy this page"
default = "diff2html.xyz"
}
variable "hosted_zone_id" {
description = "The hosted zone id where the domain will be created"
default = "Z2T76N7UKY0XQI"
}

5
tsconfig.eslint.json Normal file
View file

@ -0,0 +1,5 @@
{
"extends": "./tsconfig.json",
"exclude": ["node_modules"],
"include": ["./**/*"]
}

37
tsconfig.json Normal file
View file

@ -0,0 +1,37 @@
{
"include": ["src/**/*", "typings/**/*"],
"exclude": ["node_modules", "src/__tests__/**"],
"compilerOptions": {
"outDir": "bundles-out",
"module": "CommonJS",
"moduleResolution": "Node",
"target": "ES6",
"lib": ["ES6", "DOM"],
"jsx": "preserve",
"allowJs": false,
"checkJs": false,
"importHelpers": false,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"noEmit": false,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"incremental": false,
"strictNullChecks": true,
"removeComments": true,
"preserveConstEnums": true,
"alwaysStrict": true,
"noImplicitAny": true,
"noImplicitThis": true
}
}

3
typings/merge.d.ts vendored Normal file
View file

@ -0,0 +1,3 @@
declare module 'merge' {
export function recursive(clone: boolean, ...items: object[]): object;
}

61
webpack.bundles.ts Normal file
View file

@ -0,0 +1,61 @@
import path from 'path';
import webpack from 'webpack';
const diff2htmlBrowserConfig: webpack.Configuration = {
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.jsx', '.js'],
},
entry: './src/diff2html.ts',
output: {
path: path.resolve(__dirname, 'bundles/js'),
libraryTarget: 'umd',
globalObject: 'this',
library: 'Diff2Html',
filename: 'diff2html.min.js',
umdNamedDefine: true,
},
};
function diff2htmlUIBrowserConfig(entrypointName: string): webpack.Configuration {
return {
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.jsx', '.js'],
},
entry: `./src/ui/js/${entrypointName}.ts`,
output: {
path: path.resolve(__dirname, 'bundles/js'),
libraryTarget: 'umd',
globalObject: 'this',
filename: `${entrypointName}.min.js`,
umdNamedDefine: true,
},
};
}
const config: webpack.Configuration[] = [
diff2htmlBrowserConfig,
diff2htmlUIBrowserConfig('diff2html-ui'),
diff2htmlUIBrowserConfig('diff2html-ui-slim'),
diff2htmlUIBrowserConfig('diff2html-ui-base'),
];
export default config;

150
webpack.website.ts Normal file
View file

@ -0,0 +1,150 @@
import path from 'path';
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import CopyPlugin from 'copy-webpack-plugin';
const pages = ['index', 'demo'];
type Plugin = ((this: webpack.Compiler, compiler: webpack.Compiler) => void) | webpack.WebpackPluginInstance;
function plugins(page: string): Plugin[] {
return [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
}),
new HtmlWebpackPlugin({
hash: true,
inject: true,
title: `${page} page`,
filename: `${page}.html`,
template: `./website/templates/pages/${page}/${page}.handlebars`,
minify: {
html5: true,
collapseWhitespace: true,
caseSensitive: true,
removeEmptyElements: false,
removeComments: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
new CopyPlugin({
patterns: [
{ from: 'website/favicon.ico', to: 'favicon.ico' },
{ from: 'website/robots.txt', to: 'robots.txt' },
{ from: 'website/sitemap.xml', to: 'sitemap.xml' },
],
}),
];
}
const config: webpack.Configuration[] = pages.map(page => {
return {
entry: {
[page]: `./website/templates/pages/${page}/${page}.ts`,
},
output: {
path: path.resolve(__dirname, './docs'),
},
resolve: {
extensions: ['.tsx', '.ts', '.jsx', '.js'],
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.(html)$/,
use: {
loader: 'html-loader',
options: {
attrs: ['img:src'],
},
},
},
{
test: /\.handlebars$/,
loader: 'handlebars-loader',
options: {
inlineRequires: '/images/',
precompileOptions: {
knownHelpersOnly: false,
},
helperDirs: [path.join(__dirname, 'website/templates/helpers')],
partialDirs: [path.join(__dirname, 'website/templates')],
},
},
{
test: /\.(gif|png|jpe?g|webp)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]',
outputPath: 'images',
esModule: false,
},
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 65,
},
optipng: {
enabled: true,
},
pngquant: {
quality: [0.65, 0.9],
speed: 4,
},
gifsicle: {
interlaced: false,
},
webp: {
quality: 75,
},
},
},
],
},
{
test: /\.(css)$/,
use: [MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 1 } }, 'postcss-loader'],
},
{
test: /\.woff(2)?(\?v=\d\.\d\.\d)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 1000,
mimetype: 'application/font-woff',
},
},
],
},
{
test: /\.(ttf|eot|svg)(\?v=\d\.\d\.\d)?$/,
loader: 'file-loader',
},
],
},
plugins: plugins(page),
};
});
export default config;

BIN
website/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

22
website/main.css Normal file
View file

@ -0,0 +1,22 @@
.hero-green {
color: #26a65b;
}
.hero-black {
color: #353535;
}
.hero-red {
color: #cb2c37;
}
.button.is-green {
color: #fff;
background: #26a65b;
font-weight: 400;
}
.button.is-green:hover {
color: #fff;
background: #5dbe5d;
}

13
website/main.ts Normal file
View file

@ -0,0 +1,13 @@
import 'bulma/css/bulma.css';
document.addEventListener('DOMContentLoaded', () => {
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
$navbarBurgers.forEach((el: HTMLElement) => {
el.addEventListener('click', () => {
el.classList.toggle('is-active');
if (el.dataset.target !== undefined) {
document.getElementById(el.dataset.target)?.classList.toggle('is-active');
}
});
});
});

1
website/robots.txt Normal file
View file

@ -0,0 +1 @@
User-agent: *

Some files were not shown because too many files have changed in this diff Show more