Compare commits

..

1 commit

Author SHA1 Message Date
rtfpessoa
52837add44 Release version 3.0.0-master.62f8c16 2020-01-05 15:30:31 +00:00
60 changed files with 13056 additions and 24404 deletions

View file

@ -52,8 +52,7 @@
"avatar_url": "https://avatars0.githubusercontent.com/u/8504000?v=4", "avatar_url": "https://avatars0.githubusercontent.com/u/8504000?v=4",
"profile": "http://heyitsmattwade.com", "profile": "http://heyitsmattwade.com",
"contributions": [ "contributions": [
"bug", "bug"
"code"
] ]
}, },
{ {
@ -80,8 +79,7 @@
"avatar_url": "https://avatars0.githubusercontent.com/u/1567423?v=4", "avatar_url": "https://avatars0.githubusercontent.com/u/1567423?v=4",
"profile": "https://saino.me/", "profile": "https://saino.me/",
"contributions": [ "contributions": [
"bug", "bug"
"code"
] ]
}, },
{ {
@ -99,8 +97,7 @@
"avatar_url": "https://avatars2.githubusercontent.com/u/7767299?v=4", "avatar_url": "https://avatars2.githubusercontent.com/u/7767299?v=4",
"profile": "http://wesssel.github.io/", "profile": "http://wesssel.github.io/",
"contributions": [ "contributions": [
"security", "security"
"code"
] ]
}, },
{ {
@ -118,8 +115,7 @@
"avatar_url": "https://avatars1.githubusercontent.com/u/1446970?v=4", "avatar_url": "https://avatars1.githubusercontent.com/u/1446970?v=4",
"profile": "https://github.com/sss0791", "profile": "https://github.com/sss0791",
"contributions": [ "contributions": [
"bug", "bug"
"code"
] ]
}, },
{ {
@ -128,8 +124,7 @@
"avatar_url": "https://avatars3.githubusercontent.com/u/4741620?v=4", "avatar_url": "https://avatars3.githubusercontent.com/u/4741620?v=4",
"profile": "http://researcher.watson.ibm.com/researcher/view.php?person=us-nickm", "profile": "http://researcher.watson.ibm.com/researcher/view.php?person=us-nickm",
"contributions": [ "contributions": [
"bug", "bug"
"code"
] ]
}, },
{ {
@ -147,8 +142,7 @@
"avatar_url": "https://avatars3.githubusercontent.com/u/8926560?v=4", "avatar_url": "https://avatars3.githubusercontent.com/u/8926560?v=4",
"profile": "https://twitter.com/pubkeypubkey", "profile": "https://twitter.com/pubkeypubkey",
"contributions": [ "contributions": [
"doc", "doc"
"code"
] ]
}, },
{ {
@ -166,9 +160,7 @@
"avatar_url": "https://avatars0.githubusercontent.com/u/1823771?v=4", "avatar_url": "https://avatars0.githubusercontent.com/u/1823771?v=4",
"profile": "https://akr.am", "profile": "https://akr.am",
"contributions": [ "contributions": [
"bug", "bug"
"doc",
"code"
] ]
}, },
{ {
@ -186,8 +178,7 @@
"avatar_url": "https://avatars0.githubusercontent.com/u/8316?v=4", "avatar_url": "https://avatars0.githubusercontent.com/u/8316?v=4",
"profile": "http://twitter.com/dimasabanin", "profile": "http://twitter.com/dimasabanin",
"contributions": [ "contributions": [
"maintenance", "maintenance"
"code"
] ]
}, },
{ {
@ -205,8 +196,7 @@
"avatar_url": "https://avatars1.githubusercontent.com/u/2196373?v=4", "avatar_url": "https://avatars1.githubusercontent.com/u/2196373?v=4",
"profile": "http://webminer.js.org", "profile": "http://webminer.js.org",
"contributions": [ "contributions": [
"bug", "bug"
"doc"
] ]
}, },
{ {
@ -233,6 +223,7 @@
"avatar_url": "https://avatars0.githubusercontent.com/u/1155220?v=4", "avatar_url": "https://avatars0.githubusercontent.com/u/1155220?v=4",
"profile": "https://github.com/escitalopram", "profile": "https://github.com/escitalopram",
"contributions": [ "contributions": [
"bug",
"code" "code"
] ]
}, },
@ -245,97 +236,6 @@
"security", "security",
"maintenance" "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 "contributorsPerLine": 7

58
.eslintrc.js Normal file
View file

@ -0,0 +1,58 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
env: {
browser: true,
es6: true,
node: true,
},
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
document: 'readonly',
navigator: 'readonly',
window: 'readonly',
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:json/recommended',
'plugin:promise/recommended',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
'plugin:node/recommended',
'plugin:sonarjs/recommended',
'plugin:jest/recommended',
'plugin:jest/style',
'prettier',
'prettier/@typescript-eslint',
'prettier/babel',
],
plugins: ['@typescript-eslint', 'json', 'promise', 'import', 'node', 'sonarjs', 'jest', 'optimize-regex'],
rules: {
// Enable
'optimize-regex/optimize-regex': 'error',
// Hack: For some reason we need pass again the extensions
'node/no-missing-import': [
'error',
{
tryExtensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
},
],
// Disable
// https://github.com/benmosher/eslint-plugin-import/issues/1446
'import/named': 'off',
// We don't need this since we are using transpilation
'node/no-unsupported-features/es-syntax': 'off',
'no-process-exit': 'off',
// Too verbose
'sonarjs/no-duplicate-string': 'off',
// Too verbose
'sonarjs/cognitive-complexity': 'off',
},
};

View file

@ -1,12 +1,47 @@
name: ci name: CI
on: on: [push]
pull_request:
branches: [master]
jobs: jobs:
test-and-publish: build:
uses: ./.github/workflows/test-and-publish.yml if: contains(github.event.head_commit.message, '[skip ci]') == false
with: runs-on: ubuntu-18.04
environment: dev env:
secrets: inherit CI: true
strategy:
matrix:
node-version: [10.x, 11.x, 12.x, 13.x]
steps:
- uses: actions/checkout@v1
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Log environment setup
run: |
node -v
yarn -v
- name: Get yarn cache
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v1
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies
run: yarn install
- name: Validate
run: yarn run validate
- name: Push coverage to Codacy
if: matrix.node-version == '13.x'
env:
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
run: yarn run coverage:push
- name: Save coverage report
if: matrix.node-version == '13.x'
uses: actions/upload-artifact@v1
with:
name: coverage
path: coverage

View file

@ -1,35 +1,122 @@
name: release name: Release
on: on:
push: pull_request:
branches: [master] types: [closed]
branches:
- master
jobs: jobs:
test-and-publish: version:
uses: ./.github/workflows/test-and-publish.yml if:
with: github.event.pull_request.merged && contains(github.event.head_commit.message, '[skip ci]') == false &&
environment: production contains(github.event.head_commit.message, '[skip release]') == false
secrets: inherit runs-on: ubuntu-18.04
publish-website:
runs-on: ubuntu-latest
container: container:
image: amazon/aws-cli image: codacy/git-version
needs: [test-and-publish]
environment: 'production'
steps: steps:
- name: Download docs - uses: actions/checkout@v1
uses: actions/download-artifact@v4 - name: Configure Git
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: | run: |
aws s3 sync --region eu-west-1 --delete . s3://diff2html.xyz --metadata-directive REPLACE --cache-control max-age=31557600 git checkout -f "${GITHUB_REF#refs/heads/}"
aws cloudfront create-invalidation --region eu-west-1 --distribution-id $AWS_CF_DISTRIBUTION_ID --paths /index.html /demo.html /sitemap.xml /robots.txt git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
git config user.name "$GITHUB_ACTOR"
- name: Get next version
run: |
# Hack: Set a unique fake name for the release branch to avoid releasing master as the new 3.x major release for now
export NEXT_VERSION="$(/bin/git-version --folder=$PWD --release-branch=FAKE-RELEASE-BRANCH-NAME)"
echo "Next version is ${NEXT_VERSION}"
echo "${NEXT_VERSION}" > version.txt
- name: Save version artifact
uses: actions/upload-artifact@v1
with:
name: version
path: version.txt
- name: Get next npm tag name
run: |
if [ "${GITHUB_REF#refs/heads/}" = "master" ]; then
export PUBLISH_TAG="latest"
elif [ "${GITHUB_REF#refs/heads/}" = "next" ]; then
export PUBLISH_TAG="next"
else
export PUBLISH_TAG="pr"
fi
echo "Next tag is ${PUBLISH_TAG}"
echo "${PUBLISH_TAG}" > tag.txt
- name: Save npm tag name artifact
uses: actions/upload-artifact@v1
with:
name: tag
path: tag.txt
publish:
needs: [version]
runs-on: ubuntu-18.04
env:
CI: true
steps:
- uses: actions/checkout@v1
- name: Download version artifact
uses: actions/download-artifact@v1
with:
name: version
- name: Download npm tag name artifact
uses: actions/download-artifact@v1
with:
name: tag
- uses: actions/setup-node@v1
with:
node-version: 13.x
- run: node -v
- run: yarn -v
- name: Get yarn cache
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v1
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Configure Yarn version
run: |
yarn config set version-tag-prefix ""
yarn config set version-git-message "Release version %s"
- name: Configure Git
run: |
git switch -c ${GITHUB_REF#refs/heads/}
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
git config user.name "$GITHUB_ACTOR"
- name: Install dependencies
run: yarn
- name: Prepare version
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
run: |
yarn version --non-interactive --new-version $(cat version/version.txt)
git push --tags "https://$GITHUB_ACTOR:$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY"
- uses: actions/setup-node@v1
with:
node-version: 13
registry-url: https://registry.npmjs.org/
- name: Publish to NPM
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
run: |
echo "Going to publish version $(cat version/version.txt) to NPM"
yarn publish --tag $(cat tag/tag.txt) --non-interactive --new-version $(cat version/version.txt)
- uses: actions/setup-node@v1
with:
node-version: 13
registry-url: https://npm.pkg.github.com
scope: '@rtfpessoa'
- name: Publish to GitHub
env:
NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
run: |
# HACK: Override npm package name to be able to publish in GitHub
(TMP_FILE=$(mktemp) && jq '.name = "@rtfpessoa/diff2html"' package.json > "${TMP_FILE}" && mv "${TMP_FILE}" package.json)
echo "Going to publish version $(cat version/version.txt) to GitHub"
yarn publish --tag $(cat tag/tag.txt) --non-interactive --new-version $(cat version/version.txt)
# HACK: Restore npm package name
(TMP_FILE=$(mktemp) && jq '.name = "diff2html"' package.json > "${TMP_FILE}" && mv "${TMP_FILE}" package.json)

View file

@ -1,148 +0,0 @@
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/

65
.github/workflows/website.yml vendored Normal file
View file

@ -0,0 +1,65 @@
name: Website
on:
pull_request:
types: [closed]
branches:
- master
jobs:
build:
if:
github.event.pull_request.merged && contains(github.event.head_commit.message, '[skip ci]') == false &&
contains(github.event.head_commit.message, '[skip release]') == false
runs-on: ubuntu-18.04
env:
CI: true
strategy:
matrix:
node-version: [13.x]
steps:
- uses: actions/checkout@v1
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: node -v
- run: yarn -v
- name: Get yarn cache
id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v1
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies
run: yarn
- name: Build website
run: |
yarn run build:templates
yarn run build:website
- name: Save website artifact
if: matrix.node-version == '13.x'
uses: actions/upload-artifact@v1
with:
name: website
path: docs
deploy:
needs: build
runs-on: ubuntu-18.04
steps:
- name: Download website artifact
uses: actions/download-artifact@v1
with:
name: website
- name: Deploy website
env:
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
cd website
aws s3 sync . s3://diff2html.xyz --region eu-west-1

11
.gitignore vendored
View file

@ -29,18 +29,9 @@ bower_components/
# Terraform # Terraform
/terraform/.terraform /terraform/.terraform
# Compiled templates /_target/
/src/diff2html-templates.* /src/diff2html-templates.*
# Compiled website
/docs/ /docs/
# Bundles temporary typescript files compiled by webpack
/bundles-out/
# Web bundles
/bundles/ /bundles/
# CommonJS library
/lib/ /lib/
# ESNext library
/lib-esm/ /lib-esm/

1
.husky/.gitignore vendored
View file

@ -1 +0,0 @@
_

View file

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

View file

@ -3,6 +3,7 @@
"bracketSpacing": true, "bracketSpacing": true,
"htmlWhitespaceSensitivity": "css", "htmlWhitespaceSensitivity": "css",
"insertPragma": false, "insertPragma": false,
"jsxBracketSameLine": false,
"jsxSingleQuote": false, "jsxSingleQuote": false,
"printWidth": 120, "printWidth": 120,
"proseWrap": "always", "proseWrap": "always",

View file

@ -10,9 +10,9 @@
- Before sending a pull request make sure your code is tested. - 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`. - Before sending a pull request for a feature, be sure to run tests with `yarn 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 the same coding style as the rest of the codebase, most of the check can be performed with `yarn run lint`.
- Use `git rebase` (not `git merge`) to sync your work from time to time with the master branch. - Use `git rebase` (not `git merge`) to sync your work from time to time with the master branch.

593
README.md
View file

@ -1,48 +1,22 @@
# diff2html # diff2html
[![npm](https://img.shields.io/npm/v/diff2html)](https://www.npmjs.com/package/diff2html) [![Codacy Quality Badge](https://api.codacy.com/project/badge/Grade/06412dc3f5a14f568778d0db8a1f7dc8)](https://www.codacy.com/app/rtfpessoa/diff2html?utm_source=github.com&utm_medium=referral&utm_content=rtfpessoa/diff2html&utm_campaign=Badge_Grade)
[![node](https://img.shields.io/node/v/diff2html)](https://www.npmjs.com/package/diff2html) [![Codacy Coverage Badge](https://api.codacy.com/project/badge/Coverage/06412dc3f5a14f568778d0db8a1f7dc8)](https://www.codacy.com/app/rtfpessoa/diff2html?utm_source=github.com&utm_medium=referral&utm_content=rtfpessoa/diff2html&utm_campaign=Badge_Coverage)
[![npm](https://img.shields.io/npm/l/diff2html)](https://www.npmjs.com/package/diff2html) [![GitHub CI](https://github.com/rtfpessoa/diff2html/workflows/CI/badge.svg?branch=master)](https://github.com/rtfpessoa/diff2html/actions?query=branch%3Amaster)
[![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](https://img.shields.io/npm/v/diff2html.svg)](https://www.npmjs.com/package/diff2html)
[![npm monthly downloads](https://img.shields.io/npm/dm/diff2html)](https://www.npmjs.com/package/diff2html) [![Dependency Status](https://david-dm.org/rtfpessoa/diff2html.svg)](https://david-dm.org/rtfpessoa/diff2html)
[![npm yearly downloads](https://img.shields.io/npm/dy/diff2html)](https://www.npmjs.com/package/diff2html) [![devDependency Status](https://david-dm.org/rtfpessoa/diff2html/dev-status.svg)](https://david-dm.org/rtfpessoa/diff2html#info=devDependencies)
[![npm downloads](https://img.shields.io/npm/dt/diff2html)](https://www.npmjs.com/package/diff2html) [![cdnjs](https://img.shields.io/cdnjs/v/diff2html)](https://cdnjs.com/libraries/diff2html)
[![jsdelivr CDN Downloads](https://data.jsdelivr.com/v1/package/npm/diff2html/badge)](https://www.jsdelivr.com/package/npm/diff2html) [![node](https://img.shields.io/node/v/diff2html.svg)]() [![npm](https://img.shields.io/npm/l/diff2html.svg)]()
[![All Contributors](https://img.shields.io/badge/all_contributors-22-orange)](#contributors) [![npm](https://img.shields.io/npm/dm/diff2html.svg)](https://www.npmjs.com/package/diff2html)
[![All Contributors](https://img.shields.io/badge/all_contributors-22-orange.svg?style=flat-square)](#contributors-)
[![Gitter](https://badges.gitter.im/rtfpessoa/diff2html.svg)](https://gitter.im/rtfpessoa/diff2html?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
diff2html generates pretty HTML diffs from git diff or unified diff output. diff2html generates pretty HTML diffs from git diff or unified diff output.
## Table of Contents [![NPM](https://nodei.co/npm/diff2html.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/diff2html/)
<!-- 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 ## Features
@ -64,292 +38,55 @@ diff2html generates pretty HTML diffs from git diff or unified diff output.
## Online Example ## Online Example
> Go to [diff2html](https://diff2html.xyz/demo.html) > Go to [diff2html](https://diff2html.xyz/)
## Distributions ## Distributions
- [jsdelivr CDN](https://www.jsdelivr.com/package/npm/diff2html)
- [WebJar](http://www.webjars.org/) - [WebJar](http://www.webjars.org/)
- [Node Library](https://www.npmjs.org/package/diff2html) - [Node Library](https://www.npmjs.org/package/diff2html)
- [NPM CLI](https://www.npmjs.org/package/diff2html-cli) - [NPM CLI](https://www.npmjs.org/package/diff2html-cli)
- Manually use from jsdelivr or build the project: - Manually download and import:
- Browser / Bundle - Browser / Bundle
- Parser and HTML Generator - Parser and HTML Generator
- [bundles/js/diff2html.min.js](https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html.min.js) - includes the - [bundles/js/diff2html.min.js](./bundles/js/diff2html.min.js) - includes the diff parser and html generator
diff parser and html generator
- Wrapper and helper adding syntax highlight, synchronized scroll, and other nice features - 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) - - [bundles/js/diff2html-ui.min.js](./bundles/js/diff2html-ui.min.js) - includes the wrapper of diff2html with
includes the wrapper of diff2html with highlight for all `highlight.js` supported languages 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) - - [bundles/js/diff2html-ui-slim.min.js](./bundles/js/diff2html-ui-slim.min.js) - includes the wrapper of diff2html
includes the wrapper of diff2html with "the most common" `highlight.js` supported languages 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) - - [bundles/js/diff2html-ui-base.min.js](./bundles/js/diff2html-ui-base.min.js) - includes the wrapper of diff2html
includes the wrapper of diff2html without including a `highlight.js` implementation. You can use it without without including a `highlight.js` implementation. You can use it without syntax highlight or by passing your
syntax highlight or by passing your own implementation with the languages you prefer own implementation with the languages you prefer
- NPM / Node.js library - NPM / Node.js library
- ES5 - ES5
- [lib/diff2html.js](https://cdn.jsdelivr.net/npm/diff2html/lib/diff2html.js) - includes the diff parser and html - [lib/diff2html.js](./lib/diff2html.js) - includes the diff parser and html generator
generator - [lib/ui/js/diff2html-ui.js](./lib/ui/js/diff2html-ui.js) - includes the wrapper of diff2html with highlight for
- [lib/ui/js/diff2html-ui.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui.js) - includes the all `highlight.js` supported languages
wrapper of diff2html with highlight for all `highlight.js` supported languages - [lib/ui/js/diff2html-ui-slim.js](./lib/ui/js/diff2html-ui-slim.js) - includes the wrapper of diff2html with "the
- [lib/ui/js/diff2html-ui-slim.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui-slim.js) - most common" `highlight.js` supported languages
includes the wrapper of diff2html with "the most common" `highlight.js` supported languages - [lib/ui/js/diff2html-ui-base.js](./lib/ui/js/diff2html-ui-base.js)
- [lib/ui/js/diff2html-ui-base.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui-base.js)
- ES6 - ES6
- [lib-esm/diff2html.js](https://cdn.jsdelivr.net/npm/diff2html/lib-esm/diff2html.js) - includes the diff parser - [lib-esm/diff2html.js](./lib-esm/diff2html.js) - includes the diff parser and html generator
and html generator - [lib/ui/js/diff2html-ui.js](./lib/ui/js/diff2html-ui.js) - includes the wrapper of diff2html with highlight for
- [lib/ui/js/diff2html-ui.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui.js) - includes the all `highlight.js` supported languages
wrapper of diff2html with highlight for all `highlight.js` supported languages - [lib/ui/js/diff2html-ui-slim.js](./lib/ui/js/diff2html-ui-slim.js) - includes the wrapper of diff2html with "the
- [lib/ui/js/diff2html-ui-slim.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui-slim.js) - most common" `highlight.js` supported languages
includes the wrapper of diff2html with "the most common" `highlight.js` supported languages - [lib/ui/js/diff2html-ui-base.js](./lib/ui/js/diff2html-ui-base.js) - includes the wrapper of diff2html without
- [lib/ui/js/diff2html-ui-base.js](https://cdn.jsdelivr.net/npm/diff2html/lib/ui/js/diff2html-ui-base.js) - including a `highlight.js` implementation. You can use it without syntax highlight or by passing your own
includes the wrapper of diff2html without including a `highlight.js` implementation. You can use it without implementation with the languages you prefer
syntax highlight or by passing your own implementation with the languages you prefer
## Usage ## Diff2Html Usage
Diff2Html can be used in various ways as listed in the [distributions](#distributions) section. The two main ways are: To load correctly in the Browser you always need to include the stylesheet in the final HTML.
- [Diff2HtmlUI](#diff2htmlui-usage): using this wrapper makes it easy to inject the html in the DOM and adds some nice Import the stylesheet
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.
Below you can find more details and examples about each option.
## Diff Text Input
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).
## Diff2HtmlUI Usage
> Simple wrapper to ease simple tasks in the browser such as: code highlight and js effects
- Invoke Diff2html
- Inject output in DOM element
- Enable collapsible file summary list
- Enable syntax highlight of the code in the diffs
### Diff2HtmlUI API
> Create a Diff2HtmlUI instance
```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
```ts
draw(): void
```
> 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 ```html
<!-- CSS --> <!-- CSS -->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css" /> <link rel="stylesheet" type="text/css" href="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 You can also refer to it from a CDN like [CDNJS](https://cdnjs.com/libraries/diff2html).
```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/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 type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
```
> Pass the option `highlight` with value true or invoke `diff2htmlUi.highlightCode()` after `diff2htmlUi.draw()`.
```js
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();
});
```
When using the `auto` color scheme, you will need to specify both the light and dark themes for highlight.js to use.
```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)"
/>
```
#### 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 ### Diff2Html API
@ -365,64 +102,47 @@ function parse(diffInput: string, configuration: Diff2HtmlConfig = {}): DiffFile
function html(diffInput: string | DiffFile[], configuration: Diff2HtmlConfig = {}): string; function html(diffInput: string | DiffFile[], configuration: Diff2HtmlConfig = {}): string;
``` ```
> Check out the [docs/demo.html](./docs/demo.html) for a demo example.
### Diff2Html Configuration ### Diff2Html Configuration
The HTML output accepts a Javascript object with configuration. Possible options: 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'` - `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` - `drawFileList`: show a file list before the diff: `true` or `false`, default is `false`
- `srcPrefix`: add a prefix to all source (before changes) filepaths, default is `''`. Should match the prefix used when - `diffStyle`: show differences level in each line: `word` or `char`, default is `word`
[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 - `matching`: matching level: `'lines'` for matching lines, `'words'` for matching lines and words or `'none'`, default
is `none` is `none`
- `matchWordsThreshold`: similarity threshold for word matching, default is `0.25` - `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 - `matchingMaxComparisons`: perform at most this much comparisons for line matching a block of changes, default is
`2500` `2500`
- `maxLineSizeInBlockForComparison`: maximum number of characters of the bigger line in a block to apply comparison, - `maxLineSizeInBlockForComparison`: maximum number os characters of the bigger line in a block to apply comparison,
default is `200` default is `200`
- `compiledTemplates`: object ([Hogan.js](https://github.com/twitter/hogan.js/) template values) with previously - `maxLineLengthHighlight`: only perform diff changes highlight if lines are smaller than this, default is `10000`
compiled templates to replace parts of the html, default is `{}`. For example: - `renderNothingWhenEmpty`: render nothing if the diff shows no change in its comparison: `true` or `false`, default is
`{ "tag-file-changed": Hogan.compile("<span class="d2h-tag d2h-changed d2h-changed-tag">MODIFIED</span>") }` `false`
- `rawTemplates`: object (string values) with raw not compiled templates to replace parts of the html, default is `{}`. - `compiledTemplates`: object with previously compiled templates to replace parts of the html
For example: `{ "tag-file-changed": "<span class="d2h-tag d2h-changed d2h-changed-tag">MODIFIED</span>" }` - `rawTemplates`: object with raw not compiled templates to replace parts of the html
> For more information regarding the possible templates look into > For more information regarding the possible templates look into
> [src/templates](https://github.com/rtfpessoa/diff2html/tree/master/src/templates) > [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 ### Diff2Html Browser
Import the stylesheet and the library code. Import the stylesheet and the library code
To load correctly in the Browser you need to include the stylesheet in the final HTML.
```html ```html
<!-- CSS --> <!-- CSS -->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css" /> <link rel="stylesheet" type="text/css" href="bundles/css/diff2html.min.css" />
<!-- Javascripts --> <!-- Javascripts -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html.min.js"></script> <script type="text/javascript" src="bundles/js/diff2html.min.js"></script>
``` ```
It will now be available as a global variable named `Diff2Html`. It will now be available as a global variable named `Diff2Html`.
```js ```js
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
var diffHtml = Diff2Html.html('<Unified Diff String>', { var diffHtml = global.Diff2Html.html('<Unified Diff String>', {
drawFileList: true, drawFileList: true,
matching: 'lines', matching: 'lines',
outputFormat: 'side-by-side', outputFormat: 'side-by-side',
@ -437,12 +157,12 @@ document.addEventListener('DOMContentLoaded', () => {
const Diff2html = require('diff2html'); const Diff2html = require('diff2html');
const diffJson = Diff2html.parse('<Unified Diff String>'); const diffJson = Diff2html.parse('<Unified Diff String>');
const diffHtml = Diff2html.html(diffJson, { drawFileList: true }); const diffHtml = Diff2html.html(diffJson, { drawFileList: true });
console.log(diffHtml); document.getElementById('destination-elem-id').innerHTML = diffHtml;
``` ```
### Diff2Html Examples ### Diff2Html Examples
#### Example with Angular #### Diff2Html Angular Example
- Typescript - Typescript
@ -470,7 +190,7 @@ export class AppDiffComponent implements OnInit {
- HTML - HTML
```html ```html
<!doctype html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>diff2html</title> <title>diff2html</title>
@ -489,7 +209,7 @@ export class AppDiffComponent implements OnInit {
] ]
``` ```
#### Example with Vue.js #### Diff2Html Vue.js Example
```vue ```vue
<template> <template>
@ -520,6 +240,131 @@ export default {
</script> </script>
``` ```
## Diff2HtmlUI
> Simple wrapper to ease simple tasks in the browser such as: code highlight and js effects
- Invoke Diff2html
- Inject output in DOM element
- Enable collapsible file summary list
- Enable syntax highlight of the code in the diffs
### Diff2HtmlUI API
> Create a Diff2HtmlUI instance
```ts
constructor(diffInput: string | DiffFile[], target: HTMLElement) // diff2html-ui, diff2html-ui-slim
constructor(diffInput: string | DiffFile[], target: HTMLElement, config: Diff2HtmlUIConfig = {}, hljs?: HighlightJS) // diff2html-ui-base
```
> Generate and inject in the document the Pretty HTML representation of the diff
```ts
draw(): void
```
> Enable extra features
```ts
synchronisedScroll(): void
fileListToggle(startVisible: boolean): void
highlightCode(): void
```
> Check out the [docs/demo.html](./docs/demo.html) for a demo example.
### 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`
- `smartSelection`: allow selection of the code without including line numbers of line prefixes: `true` or `false`,
default is `true`
> NOTE: All the options from Diff2Html are also valid configurations in Diff2HtmlUI
### Diff2HtmlUI Browser
#### Mandatory HTML resource imports
```html
<!-- CSS -->
<link rel="stylesheet" type="text/css" href="bundles/css/diff2html.min.css" />
<!-- Javascripts -->
<script type="text/javascript" src="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(diffString, targetElement, configuration);
// or
const diff2htmlUi = new Diff2HtmlUI(diffJson, targetElement, configuration);
```
#### Draw
```js
diff2htmlUi.draw();
```
#### Syntax Highlight
```html
<!-- Stylesheet -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/github.min.css" />
<link rel="stylesheet" type="text/css" href="bundles/css/diff2html.min.css" />
<!-- Javascripts -->
<script type="text/javascript" src="bundles/js/diff2html-ui.min.js"></script>
```
> Pass the option `highlight` with value true or invoke `diff2htmlUi.highlightCode()` after `diff2htmlUi.draw()`.
```js
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 = { inputFormat: 'json', drawFileList: true, matching: 'lines', highlight: true };
const diff2htmlUi = new Diff2HtmlUI(diffString, targetElement, configuration);
diff2htmlUi.draw();
diff2htmlUi.highlightCode();
});
```
#### Collapsable File Summary List
> Add the dependencies.
```html
<!-- Javascripts -->
<script type="text/javascript" src="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(lineDiffExample, targetElement, { drawFileList: true, matching: 'lines' });
diff2htmlUi.draw();
diff2htmlUi.fileListToggle(false);
});
```
## Troubleshooting ## Troubleshooting
### 1. Out of memory or Slow execution ### 1. Out of memory or Slow execution
@ -533,12 +378,12 @@ export default {
- Disable the line matching algorithm, by setting the option `{"matching": "none"}` when invoking diff2html - Disable the line matching algorithm, by setting the option `{"matching": "none"}` when invoking diff2html
## Contribute ## Contributions
This is a developer friendly project, all the contributions are welcome. To contribute just send a pull request with 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. your changes following the guidelines described in `CONTRIBUTING.md`. I will try to review them as soon as possible.
## Contributors ## Contributors
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
@ -547,53 +392,41 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<!-- markdownlint-disable --> <!-- markdownlint-disable -->
<table> <table>
<tr> <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://rtfpessoa.xyz"><img src="https://avatars0.githubusercontent.com/u/902384?v=4" 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/stockmind"><img src="https://avatars3.githubusercontent.com/u/5653847?v=4" 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="https://github.com/lantian"><img src="https://avatars3.githubusercontent.com/u/535545?v=4" 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://www.nick-brewer.com"><img src="https://avatars1.githubusercontent.com/u/129300?v=4" 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://heyitsmattwade.com"><img src="https://avatars0.githubusercontent.com/u/8504000?v=4" 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></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="http://mrfyda.github.io"><img src="https://avatars1.githubusercontent.com/u/593860?v=4" 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> <td align="center"><a href="https://github.com/nmatpt"><img src="https://avatars2.githubusercontent.com/u/5034733?v=4" 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>
<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="https://saino.me/"><img src="https://avatars0.githubusercontent.com/u/1567423?v=4" 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></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://www.jamesmonger.com"><img src="https://avatars2.githubusercontent.com/u/2037007?v=4" 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="http://wesssel.github.io/"><img src="https://avatars2.githubusercontent.com/u/7767299?v=4" width="100px;" alt=""/><br /><sub><b>Wessel van der Pal</b></sub></a><br /><a href="#security-wesssel" title="Security">🛡️</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://jung-kim.github.io"><img src="https://avatars2.githubusercontent.com/u/5281068?v=4" 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="https://github.com/sss0791"><img src="https://avatars1.githubusercontent.com/u/1446970?v=4" 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></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="http://researcher.watson.ibm.com/researcher/view.php?person=us-nickm"><img src="https://avatars3.githubusercontent.com/u/4741620?v=4" 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></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> <td align="center"><a href="https://github.com/samiraguiar"><img src="https://avatars0.githubusercontent.com/u/13439135?v=4" 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>
<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://twitter.com/pubkeypubkey"><img src="https://avatars3.githubusercontent.com/u/8926560?v=4" width="100px;" alt=""/><br /><sub><b>pubkey</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/commits?author=pubkey" title="Documentation">📖</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://github.com/iliyaZelenko"><img src="https://avatars1.githubusercontent.com/u/13103045?v=4" 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://akr.am"><img src="https://avatars0.githubusercontent.com/u/1823771?v=4" 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></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="https://github.com/emarcotte"><img src="https://avatars0.githubusercontent.com/u/249390?v=4" 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="http://twitter.com/dimasabanin"><img src="https://avatars0.githubusercontent.com/u/8316?v=4" width="100px;" alt=""/><br /><sub><b>Dima Sabanin</b></sub></a><br /><a href="#maintenance-dsabanin" title="Maintenance">🚧</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="https://github.com/benabbottnz"><img src="https://avatars2.githubusercontent.com/u/2616473?v=4" 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> <td align="center"><a href="http://webminer.js.org"><img src="https://avatars1.githubusercontent.com/u/2196373?v=4" 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></td>
</tr> </tr>
<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/Rantanen"><img src="https://avatars0.githubusercontent.com/u/385385?v=4" 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/extend1994"><img src="https://avatars2.githubusercontent.com/u/13430892?v=4" 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/escitalopram"><img src="https://avatars0.githubusercontent.com/u/1155220?v=4" width="100px;" alt=""/><br /><sub><b>escitalopram</b></sub></a><br /><a href="https://github.com/rtfpessoa/diff2html/issues?q=author%3Aescitalopram" title="Bug reports">🐛</a> <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="https://github.com/apps/dependabot"><img src="https://avatars0.githubusercontent.com/in/29110?v=4" 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> </tr>
</table> </table>
<!-- markdownlint-restore --> <!-- markdownlint-enable -->
<!-- prettier-ignore-end --> <!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END --> <!-- ALL-CONTRIBUTORS-LIST:END -->

View file

@ -1,77 +0,0 @@
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}'],
},
];

View file

@ -3,7 +3,7 @@ module.exports = {
preset: 'ts-jest', preset: 'ts-jest',
testEnvironment: 'node', testEnvironment: 'node',
coverageDirectory: './coverage', coverageDirectory: './coverage',
coverageReporters: ['lcov', 'text', 'html', 'json', 'cobertura', 'clover'], coverageReporters: ['lcov', 'text', 'html'],
collectCoverageFrom: [ collectCoverageFrom: [
'src/**/*.ts', 'src/**/*.ts',
'!src/ui/**', '!src/ui/**',
@ -19,5 +19,4 @@ module.exports = {
lines: 93, lines: 93,
}, },
}, },
prettierPath: require.resolve('prettier-2'),
}; };

18121
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "diff2html", "name": "diff2html",
"version": "3.0.0-beta.1", "version": "3.0.0-master.62f8c16",
"homepage": "https://diff2html.xyz", "homepage": "https://diff2html.xyz",
"description": "Fast Diff to colorized HTML", "description": "Fast Diff to colorized HTML",
"keywords": [ "keywords": [
@ -26,130 +26,128 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/rtfpessoa/diff2html.git" "url": "https://www.github.com/rtfpessoa/diff2html.git"
}, },
"bugs": { "bugs": {
"url": "https://www.github.com/rtfpessoa/diff2html/issues" "url": "https://www.github.com/rtfpessoa/diff2html/issues"
}, },
"engines": { "engines": {
"node": ">=12" "node": ">=10.13"
}, },
"scripts": { "scripts": {
"lint:staged": "lint-staged", "eslint": "eslint --ignore-path .gitignore \"**/*.{js,jsx,ts,tsx,json}\"",
"lint:check": "eslint", "lint:check": "yarn run eslint",
"lint:fix": "eslint --fix", "lint:fix": "yarn run eslint --fix",
"prettier": "prettier --ignore-path .gitignore '**/*.+(js|jsx|ts|tsx|json|css|html|md|mdx)'", "prettier": "prettier --ignore-path .gitignore '**/*.+(js|jsx|ts|tsx|json|css|html|md|mdx)'",
"format:check": "npm run prettier --check", "format:check": "yarn run prettier --check",
"format:fix": "npm run prettier --write", "format:fix": "yarn 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": "yarn run build:css && yarn run build:templates && yarn run build:es5 && yarn run build:esm && yarn run build:bundles && yarn run build:website",
"build:commonjs": "rm -rf lib; tsc -p tsconfig.json -m CommonJS --outDir lib", "build:es5": "rm -rf lib; tsc -p tsconfig.json --outDir lib",
"build:esm": "rm -rf lib-esm; tsc -p tsconfig.json -m ESNext --outDir lib-esm", "build:esm": "rm -rf lib-esm; tsc -p tsconfig.json -m es6 --outDir lib-esm",
"build:bundles": "rm -rf ./bundles/js; webpack --mode production --config webpack.bundles.ts", "build:bundles": "rm -rf ./bundles/js; webpack ---display-reasons --display-modules --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: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: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", "build:website": "rm -rf docs; webpack ---display-reasons --display-modules --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": "is-ci 'test:coverage' 'test:watch'",
"test:coverage": "jest --coverage", "test:coverage": "jest --coverage",
"test:watch": "jest --watch", "test:watch": "jest --watch",
"test:debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand --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:open": "yarn run test:coverage && open ./coverage/index.html",
"coverage:push": "curl -Ls https://coverage.codacy.com/get.sh | bash", "coverage:push": "cat ./coverage/lcov.info | codacy-coverage",
"validate": "npm run build:templates && npm run format:check && npm run lint:check && npm run build && npm run test:coverage", "validate": "yarn run build:templates && yarn run format:check && yarn run lint:check && yarn run build && yarn run test:coverage",
"fix": "npm run format:fix && npm run lint:fix", "fix": "yarn run format:fix && yarn run lint:fix",
"start": "npm run start:website", "start": "yarn run start:website",
"start:website": "webpack serve --mode development --config webpack.website.ts", "start:website": "webpack-dev-server --mode development --config webpack.website.ts",
"preversion": "npm run validate", "preversion": "yarn run validate",
"version": "git add -A package.json", "version": "git add -A package.json"
"prepare": "husky"
}, },
"main": "./lib/diff2html.js", "main": "./lib/diff2html.js",
"module": "./lib-esm/diff2html.js", "module": "./lib-esm/diff2html.js",
"types": "./lib/diff2html.d.ts", "types": "./lib/diff2html.d.ts",
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": { "lint-staged": {
"**/*.+(js|jsx|ts|tsx|json)": [ "**/*.+(js|jsx|ts|tsx|json)": [
"prettier --write", "prettier --write",
"eslint --fix" "eslint --fix",
"git add"
], ],
"**/*.+(css|html|md|mdx)": [ "**/*.+(css|html|md|mdx)": [
"prettier --write" "prettier --write",
], "git add"
"README.md": [
"npm run gen:toc-base"
] ]
}, },
"dependencies": { "dependencies": {
"diff": "^7.0.0", "diff": "4.0.1",
"hogan.js": "3.0.2" "hogan.js": "3.0.2"
}, },
"optionalDependencies": { "optionalDependencies": {
"highlight.js": "11.9.0" "highlight.js": "9.17.1"
}, },
"devDependencies": { "devDependencies": {
"prettier-2": "npm:prettier@^2", "@types/clipboard": "2.0.1",
"@eslint/js": "^9.17.0", "@types/copy-webpack-plugin": "5.0.0",
"@eslint/json": "^0.9.0", "@types/diff": "4.0.2",
"@types/diff": "^6.0.0", "@types/highlight.js": "9.12.3",
"@types/hogan.js": "3.0.5", "@types/hogan.js": "3.0.0",
"@types/jest": "^29.5.14", "@types/html-webpack-plugin": "3.2.1",
"@types/node": "^22.10.2", "@types/jest": "24.0.25",
"@types/nopt": "3.0.32", "@types/mini-css-extract-plugin": "0.9.0",
"all-contributors-cli": "^6.24.0", "@types/mkdirp": "0.5.2",
"autoprefixer": "^10.4.20", "@types/node": "13.1.4",
"bulma": "^1.0.2", "@types/nopt": "3.0.29",
"clipboard": "2.0.11", "@types/webpack": "4.41.1",
"copy-webpack-plugin": "^12.0.2", "@typescript-eslint/eslint-plugin": "2.14.0",
"css-loader": "^7.1.2", "@typescript-eslint/parser": "2.14.0",
"cssnano": "^7.0.6", "autoprefixer": "9.7.3",
"eslint": "^9.17.0", "bulma": "^0.8.0",
"eslint-plugin-jest": "28.10.0", "clipboard": "2.0.4",
"eslint-plugin-promise": "^7.2.1", "codacy-coverage": "3.4.0",
"file-loader": "6.2.0", "copy-webpack-plugin": "5.1.1",
"globals": "^15.14.0", "css-loader": "3.4.1",
"handlebars": "4.7.8", "cssnano": "4.1.10",
"handlebars-loader": "1.7.3", "eslint": "6.8.0",
"html-webpack-plugin": "^5.6.3", "eslint-config-prettier": "6.9.0",
"husky": "^9.1.7", "eslint-plugin-import": "2.19.1",
"image-webpack-loader": "8.1.0", "eslint-plugin-jest": "23.2.0",
"is-ci-cli": "2.2.0", "eslint-plugin-json": "2.0.1",
"jest": "29.7.0", "eslint-plugin-node": "11.0.0",
"lint-staged": "^15.2.11", "eslint-plugin-optimize-regex": "1.1.7",
"markdown-toc": "^1.2.0", "eslint-plugin-promise": "4.2.1",
"mini-css-extract-plugin": "^2.9.2", "eslint-plugin-sonarjs": "0.5.0",
"mkdirp": "3.0.1", "file-loader": "5.0.2",
"nopt": "^8.0.0", "handlebars": "4.5.3",
"postcss": "^8.4.49", "handlebars-loader": "1.7.1",
"postcss-cli": "11.0.0", "html-webpack-plugin": "3.2.0",
"postcss-import": "^16.1.0", "husky": "3.1.0",
"postcss-loader": "^8.1.1", "image-webpack-loader": "6.0.0",
"postcss-preset-env": "^10.1.2", "is-ci-cli": "2.0.0",
"prettier": "^3.4.2", "jest": "24.9.0",
"ts-jest": "^29.2.5", "lint-staged": "9.5.0",
"ts-loader": "9.5.1", "mini-css-extract-plugin": "0.9.0",
"ts-node": "10.9.2", "mkdirp": "0.5.1",
"typescript": "^5.7.2", "nopt": "4.0.1",
"typescript-eslint": "^8.18.2", "postcss": "7.0.26",
"url-loader": "4.1.1", "postcss-cli": "6.1.3",
"webpack": "^5.97.1", "postcss-import": "12.0.1",
"webpack-cli": "^6.0.1", "postcss-loader": "3.0.0",
"webpack-dev-server": "^5.2.0", "postcss-preset-env": "6.7.0",
"whatwg-fetch": "3.6.20" "prettier": "1.19.1",
"ts-jest": "24.2.0",
"ts-loader": "6.2.1",
"ts-node": "8.5.4",
"typescript": "3.7.4",
"url-loader": "3.0.0",
"webpack": "4.41.5",
"webpack-cli": "3.3.10",
"webpack-dev-server": "3.10.1",
"whatwg-fetch": "3.0.0"
}, },
"resolutions": { "resolutions": {
"lodash": ">=4.17.20", "lodash": "4.17.15"
"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"
}, },
"license": "MIT", "license": "MIT",
"files": [ "files": [

View file

@ -67,7 +67,6 @@ function extractFiles(files: string[]): string[] {
`; `;
if (options.version) { if (options.version) {
// eslint-disable-next-line @typescript-eslint/no-require-imports
console.log(require('../package.json').version); console.log(require('../package.json').version);
process.exit(0); process.exit(0);
} }

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,176 @@
import { render } from '../file-list-renderer';
import HoganJsUtils from '../hoganjs-utils';
describe('FileListPrinter', () => {
describe('generateFileList', () => {
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 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 = render(files, hoganUtils);
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 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 = render(files, hoganUtils);
expect(fileHtml).toMatchInlineSnapshot(`
"<div class=\\"d2h-file-list-wrapper\\">
<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>"
`);
});
});
});

View file

@ -1,272 +0,0 @@
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

@ -11,8 +11,8 @@ describe('HoganJsUtils', () => {
}); });
expect(result).toMatchInlineSnapshot(` expect(result).toMatchInlineSnapshot(`
"<tr> "<tr>
<td class="d2h-info"> <td class=\\"d2h-info\\">
<div class="d2h-code-line"> <div class=\\"d2h-code-line d2h-info\\">
File without changes File without changes
</div> </div>
</td> </td>

View file

@ -11,8 +11,8 @@ describe('LineByLineRenderer', () => {
const fileHtml = lineByLineRenderer.generateEmptyDiff(); const fileHtml = lineByLineRenderer.generateEmptyDiff();
expect(fileHtml).toMatchInlineSnapshot(` expect(fileHtml).toMatchInlineSnapshot(`
"<tr> "<tr>
<td class="d2h-info"> <td class=\\"d2h-info\\">
<div class="d2h-code-line"> <div class=\\"d2h-code-line d2h-info\\">
File without changes File without changes
</div> </div>
</td> </td>
@ -23,19 +23,9 @@ describe('LineByLineRenderer', () => {
describe('makeLineHtml', () => { describe('makeLineHtml', () => {
it('should work for insertions', () => { 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 hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {}); const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const fileHtml = lineByLineRenderer.generateSingleLineHtml(file, { const fileHtml = lineByLineRenderer.generateSingleLineHtml({
type: CSSLineClass.INSERTS, type: CSSLineClass.INSERTS,
prefix: '+', prefix: '+',
content: 'test', content: 'test',
@ -44,14 +34,14 @@ describe('LineByLineRenderer', () => {
}); });
expect(fileHtml).toMatchInlineSnapshot(` expect(fileHtml).toMatchInlineSnapshot(`
"<tr> "<tr>
<td class="d2h-code-linenumber d2h-ins"> <td class=\\"d2h-code-linenumber d2h-ins\\">
<div class="line-num1"></div> <div class=\\"line-num1\\"></div>
<div class="line-num2">30</div> <div class=\\"line-num2\\">30</div>
</td> </td>
<td class="d2h-ins"> <td class=\\"d2h-ins\\">
<div class="d2h-code-line"> <div class=\\"d2h-code-line d2h-ins\\">
<span class="d2h-code-line-prefix">+</span> <span class=\\"d2h-code-line-prefix\\">+</span>
<span class="d2h-code-line-ctn">test</span> <span class=\\"d2h-code-line-ctn\\">test</span>
</div> </div>
</td> </td>
</tr>" </tr>"
@ -59,19 +49,9 @@ describe('LineByLineRenderer', () => {
}); });
it('should work for deletions', () => { 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 hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {}); const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const fileHtml = lineByLineRenderer.generateSingleLineHtml(file, { const fileHtml = lineByLineRenderer.generateSingleLineHtml({
type: CSSLineClass.DELETES, type: CSSLineClass.DELETES,
prefix: '-', prefix: '-',
content: 'test', content: 'test',
@ -80,14 +60,14 @@ describe('LineByLineRenderer', () => {
}); });
expect(fileHtml).toMatchInlineSnapshot(` expect(fileHtml).toMatchInlineSnapshot(`
"<tr> "<tr>
<td class="d2h-code-linenumber d2h-del"> <td class=\\"d2h-code-linenumber d2h-del\\">
<div class="line-num1">30</div> <div class=\\"line-num1\\">30</div>
<div class="line-num2"></div> <div class=\\"line-num2\\"></div>
</td> </td>
<td class="d2h-del"> <td class=\\"d2h-del\\">
<div class="d2h-code-line"> <div class=\\"d2h-code-line d2h-del\\">
<span class="d2h-code-line-prefix">-</span> <span class=\\"d2h-code-line-prefix\\">-</span>
<span class="d2h-code-line-ctn">test</span> <span class=\\"d2h-code-line-ctn\\">test</span>
</div> </div>
</td> </td>
</tr>" </tr>"
@ -95,19 +75,9 @@ describe('LineByLineRenderer', () => {
}); });
it('should convert indents into non breakin spaces (2 white spaces)', () => { 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 hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {}); const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const fileHtml = lineByLineRenderer.generateSingleLineHtml(file, { const fileHtml = lineByLineRenderer.generateSingleLineHtml({
type: CSSLineClass.INSERTS, type: CSSLineClass.INSERTS,
prefix: '+', prefix: '+',
content: ' test', content: ' test',
@ -116,14 +86,14 @@ describe('LineByLineRenderer', () => {
}); });
expect(fileHtml).toMatchInlineSnapshot(` expect(fileHtml).toMatchInlineSnapshot(`
"<tr> "<tr>
<td class="d2h-code-linenumber d2h-ins"> <td class=\\"d2h-code-linenumber d2h-ins\\">
<div class="line-num1"></div> <div class=\\"line-num1\\"></div>
<div class="line-num2">30</div> <div class=\\"line-num2\\">30</div>
</td> </td>
<td class="d2h-ins"> <td class=\\"d2h-ins\\">
<div class="d2h-code-line"> <div class=\\"d2h-code-line d2h-ins\\">
<span class="d2h-code-line-prefix">+</span> <span class=\\"d2h-code-line-prefix\\">+</span>
<span class="d2h-code-line-ctn"> test</span> <span class=\\"d2h-code-line-ctn\\"> test</span>
</div> </div>
</td> </td>
</tr>" </tr>"
@ -131,19 +101,9 @@ describe('LineByLineRenderer', () => {
}); });
it('should convert indents into non breakin spaces (4 white spaces)', () => { 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 hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {}); const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const fileHtml = lineByLineRenderer.generateSingleLineHtml(file, { const fileHtml = lineByLineRenderer.generateSingleLineHtml({
type: CSSLineClass.INSERTS, type: CSSLineClass.INSERTS,
prefix: '+', prefix: '+',
content: ' test', content: ' test',
@ -152,14 +112,14 @@ describe('LineByLineRenderer', () => {
}); });
expect(fileHtml).toMatchInlineSnapshot(` expect(fileHtml).toMatchInlineSnapshot(`
"<tr> "<tr>
<td class="d2h-code-linenumber d2h-ins"> <td class=\\"d2h-code-linenumber d2h-ins\\">
<div class="line-num1"></div> <div class=\\"line-num1\\"></div>
<div class="line-num2">30</div> <div class=\\"line-num2\\">30</div>
</td> </td>
<td class="d2h-ins"> <td class=\\"d2h-ins\\">
<div class="d2h-code-line"> <div class=\\"d2h-code-line d2h-ins\\">
<span class="d2h-code-line-prefix">+</span> <span class=\\"d2h-code-line-prefix\\">+</span>
<span class="d2h-code-line-ctn"> test</span> <span class=\\"d2h-code-line-ctn\\"> test</span>
</div> </div>
</td> </td>
</tr>" </tr>"
@ -167,19 +127,9 @@ describe('LineByLineRenderer', () => {
}); });
it('should preserve tabs', () => { 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 hoganUtils = new HoganJsUtils({});
const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {}); const lineByLineRenderer = new LineByLineRenderer(hoganUtils, {});
const fileHtml = lineByLineRenderer.generateSingleLineHtml(file, { const fileHtml = lineByLineRenderer.generateSingleLineHtml({
type: CSSLineClass.INSERTS, type: CSSLineClass.INSERTS,
prefix: '+', prefix: '+',
content: '\ttest', content: '\ttest',
@ -188,14 +138,14 @@ describe('LineByLineRenderer', () => {
}); });
expect(fileHtml).toMatchInlineSnapshot(` expect(fileHtml).toMatchInlineSnapshot(`
"<tr> "<tr>
<td class="d2h-code-linenumber d2h-ins"> <td class=\\"d2h-code-linenumber d2h-ins\\">
<div class="line-num1"></div> <div class=\\"line-num1\\"></div>
<div class="line-num2">30</div> <div class=\\"line-num2\\">30</div>
</td> </td>
<td class="d2h-ins"> <td class=\\"d2h-ins\\">
<div class="d2h-code-line"> <div class=\\"d2h-code-line d2h-ins\\">
<span class="d2h-code-line-prefix">+</span> <span class=\\"d2h-code-line-prefix\\">+</span>
<span class="d2h-code-line-ctn"> test</span> <span class=\\"d2h-code-line-ctn\\"> test</span>
</div> </div>
</td> </td>
</tr>" </tr>"
@ -223,22 +173,18 @@ describe('LineByLineRenderer', () => {
const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs); const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs);
expect(fileHtml).toMatchInlineSnapshot(` expect(fileHtml).toMatchInlineSnapshot(`
"<div id="d2h-781444" class="d2h-file-wrapper" data-lang="js"> "<div id=\\"d2h-781444\\" class=\\"d2h-file-wrapper\\" data-lang=\\"js\\">
<div class="d2h-file-header"> <div class=\\"d2h-file-header\\">
<span class="d2h-file-name-wrapper"> <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"> <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> <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> </svg> <span class=\\"d2h-file-name\\">my/file/name.js</span>
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></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>
<div class="d2h-file-diff"> <div class=\\"d2h-file-diff\\">
<div class="d2h-code-wrapper"> <div class=\\"d2h-code-wrapper\\">
<table class="d2h-diff-table"> <table class=\\"d2h-diff-table\\">
<tbody class="d2h-diff-tbody"> <tbody class=\\"d2h-diff-tbody\\">
<span>Random Html</span> <span>Random Html</span>
</tbody> </tbody>
</table> </table>
@ -267,22 +213,18 @@ describe('LineByLineRenderer', () => {
const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs); const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs);
expect(fileHtml).toMatchInlineSnapshot(` expect(fileHtml).toMatchInlineSnapshot(`
"<div id="d2h-781444" class="d2h-file-wrapper" data-lang="js"> "<div id=\\"d2h-781444\\" class=\\"d2h-file-wrapper\\" data-lang=\\"js\\">
<div class="d2h-file-header"> <div class=\\"d2h-file-header\\">
<span class="d2h-file-name-wrapper"> <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"> <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> <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> </svg> <span class=\\"d2h-file-name\\">my/file/name.js</span>
<span class="d2h-tag d2h-added d2h-added-tag">ADDED</span></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>
<div class="d2h-file-diff"> <div class=\\"d2h-file-diff\\">
<div class="d2h-code-wrapper"> <div class=\\"d2h-code-wrapper\\">
<table class="d2h-diff-table"> <table class=\\"d2h-diff-table\\">
<tbody class="d2h-diff-tbody"> <tbody class=\\"d2h-diff-tbody\\">
<span>Random Html</span> <span>Random Html</span>
</tbody> </tbody>
</table> </table>
@ -311,22 +253,18 @@ describe('LineByLineRenderer', () => {
const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs); const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs);
expect(fileHtml).toMatchInlineSnapshot(` expect(fileHtml).toMatchInlineSnapshot(`
"<div id="d2h-781444" class="d2h-file-wrapper" data-lang="js"> "<div id=\\"d2h-781444\\" class=\\"d2h-file-wrapper\\" data-lang=\\"js\\">
<div class="d2h-file-header"> <div class=\\"d2h-file-header\\">
<span class="d2h-file-name-wrapper"> <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"> <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> <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> </svg> <span class=\\"d2h-file-name\\">my/file/name.js</span>
<span class="d2h-tag d2h-deleted d2h-deleted-tag">DELETED</span></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>
<div class="d2h-file-diff"> <div class=\\"d2h-file-diff\\">
<div class="d2h-code-wrapper"> <div class=\\"d2h-code-wrapper\\">
<table class="d2h-diff-table"> <table class=\\"d2h-diff-table\\">
<tbody class="d2h-diff-tbody"> <tbody class=\\"d2h-diff-tbody\\">
<span>Random Html</span> <span>Random Html</span>
</tbody> </tbody>
</table> </table>
@ -355,22 +293,18 @@ describe('LineByLineRenderer', () => {
const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs); const fileHtml = lineByLineRenderer.makeFileDiffHtml(file, diffs);
expect(fileHtml).toMatchInlineSnapshot(` expect(fileHtml).toMatchInlineSnapshot(`
"<div id="d2h-662683" class="d2h-file-wrapper" data-lang="js"> "<div id=\\"d2h-662683\\" class=\\"d2h-file-wrapper\\" data-lang=\\"js\\">
<div class="d2h-file-header"> <div class=\\"d2h-file-header\\">
<span class="d2h-file-name-wrapper"> <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"> <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> <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> </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> <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>
<div class="d2h-file-diff"> <div class=\\"d2h-file-diff\\">
<div class="d2h-code-wrapper"> <div class=\\"d2h-code-wrapper\\">
<table class="d2h-diff-table"> <table class=\\"d2h-diff-table\\">
<tbody class="d2h-diff-tbody"> <tbody class=\\"d2h-diff-tbody\\">
<span>Random Html</span> <span>Random Html</span>
</tbody> </tbody>
</table> </table>
@ -449,48 +383,44 @@ describe('LineByLineRenderer', () => {
}); });
const html = lineByLineRenderer.render(exampleJson); const html = lineByLineRenderer.render(exampleJson);
expect(html).toMatchInlineSnapshot(` expect(html).toMatchInlineSnapshot(`
"<div class="d2h-wrapper d2h-light-color-scheme"> "<div class=\\"d2h-wrapper\\">
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="txt"> <div id=\\"d2h-675094\\" class=\\"d2h-file-wrapper\\" data-lang=\\"txt\\">
<div class="d2h-file-header"> <div class=\\"d2h-file-header\\">
<span class="d2h-file-name-wrapper"> <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"> <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> <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> </svg> <span class=\\"d2h-file-name\\">sample</span>
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></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>
<div class="d2h-file-diff"> <div class=\\"d2h-file-diff\\">
<div class="d2h-code-wrapper"> <div class=\\"d2h-code-wrapper\\">
<table class="d2h-diff-table"> <table class=\\"d2h-diff-table\\">
<tbody class="d2h-diff-tbody"> <tbody class=\\"d2h-diff-tbody\\">
<tr> <tr>
<td class="d2h-code-linenumber d2h-info"></td> <td class=\\"d2h-code-linenumber d2h-info\\"></td>
<td class="d2h-info"> <td class=\\"d2h-info\\">
<div class="d2h-code-line">@@ -1 +1 @@</div> <div class=\\"d2h-code-line d2h-info\\">@@ -1 +1 @@</div>
</td> </td>
</tr><tr> </tr><tr>
<td class="d2h-code-linenumber d2h-del d2h-change"> <td class=\\"d2h-code-linenumber d2h-del d2h-change\\">
<div class="line-num1">1</div> <div class=\\"line-num1\\">1</div>
<div class="line-num2"></div> <div class=\\"line-num2\\"></div>
</td> </td>
<td class="d2h-del d2h-change"> <td class=\\"d2h-del d2h-change\\">
<div class="d2h-code-line"> <div class=\\"d2h-code-line d2h-del d2h-change\\">
<span class="d2h-code-line-prefix">-</span> <span class=\\"d2h-code-line-prefix\\">-</span>
<span class="d2h-code-line-ctn"><del>test</del></span> <span class=\\"d2h-code-line-ctn\\"><del>test</del></span>
</div> </div>
</td> </td>
</tr><tr> </tr><tr>
<td class="d2h-code-linenumber d2h-ins d2h-change"> <td class=\\"d2h-code-linenumber d2h-ins d2h-change\\">
<div class="line-num1"></div> <div class=\\"line-num1\\"></div>
<div class="line-num2">1</div> <div class=\\"line-num2\\">1</div>
</td> </td>
<td class="d2h-ins d2h-change"> <td class=\\"d2h-ins d2h-change\\">
<div class="d2h-code-line"> <div class=\\"d2h-code-line d2h-ins d2h-change\\">
<span class="d2h-code-line-prefix">+</span> <span class=\\"d2h-code-line-prefix\\">+</span>
<span class="d2h-code-line-ctn"><ins>test1r</ins></span> <span class=\\"d2h-code-line-ctn\\"><ins>test1r</ins></span>
</div> </div>
</td> </td>
</tr> </tr>
@ -523,26 +453,22 @@ describe('LineByLineRenderer', () => {
}); });
const html = lineByLineRenderer.render(exampleJson); const html = lineByLineRenderer.render(exampleJson);
expect(html).toMatchInlineSnapshot(` expect(html).toMatchInlineSnapshot(`
"<div class="d2h-wrapper d2h-light-color-scheme"> "<div class=\\"d2h-wrapper\\">
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="js"> <div id=\\"d2h-675094\\" class=\\"d2h-file-wrapper\\" data-lang=\\"js\\">
<div class="d2h-file-header"> <div class=\\"d2h-file-header\\">
<span class="d2h-file-name-wrapper"> <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"> <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> <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> </svg> <span class=\\"d2h-file-name\\">sample</span>
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></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>
<div class="d2h-file-diff"> <div class=\\"d2h-file-diff\\">
<div class="d2h-code-wrapper"> <div class=\\"d2h-code-wrapper\\">
<table class="d2h-diff-table"> <table class=\\"d2h-diff-table\\">
<tbody class="d2h-diff-tbody"> <tbody class=\\"d2h-diff-tbody\\">
<tr> <tr>
<td class="d2h-info"> <td class=\\"d2h-info\\">
<div class="d2h-code-line"> <div class=\\"d2h-code-line d2h-info\\">
File without changes File without changes
</div> </div>
</td> </td>
@ -555,65 +481,6 @@ describe('LineByLineRenderer', () => {
</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', () => { describe('_generateFileHtml', () => {
@ -670,52 +537,52 @@ describe('LineByLineRenderer', () => {
expect(html).toMatchInlineSnapshot(` expect(html).toMatchInlineSnapshot(`
"<tr> "<tr>
<td class="d2h-code-linenumber d2h-info"></td> <td class=\\"d2h-code-linenumber d2h-info\\"></td>
<td class="d2h-info"> <td class=\\"d2h-info\\">
<div class="d2h-code-line">@@ -1 +1 @@</div> <div class=\\"d2h-code-line d2h-info\\">@@ -1 +1 @@</div>
</td> </td>
</tr><tr> </tr><tr>
<td class="d2h-code-linenumber d2h-cntx"> <td class=\\"d2h-code-linenumber d2h-cntx\\">
<div class="line-num1">1</div> <div class=\\"line-num1\\">1</div>
<div class="line-num2">1</div> <div class=\\"line-num2\\">1</div>
</td> </td>
<td class="d2h-cntx"> <td class=\\"d2h-cntx\\">
<div class="d2h-code-line"> <div class=\\"d2h-code-line d2h-cntx\\">
<span class="d2h-code-line-prefix">&nbsp;</span> <span class=\\"d2h-code-line-prefix\\">&nbsp;</span>
<span class="d2h-code-line-ctn">one context line</span> <span class=\\"d2h-code-line-ctn\\">one context line</span>
</div> </div>
</td> </td>
</tr><tr> </tr><tr>
<td class="d2h-code-linenumber d2h-del d2h-change"> <td class=\\"d2h-code-linenumber d2h-del d2h-change\\">
<div class="line-num1">2</div> <div class=\\"line-num1\\">2</div>
<div class="line-num2"></div> <div class=\\"line-num2\\"></div>
</td> </td>
<td class="d2h-del d2h-change"> <td class=\\"d2h-del d2h-change\\">
<div class="d2h-code-line"> <div class=\\"d2h-code-line d2h-del d2h-change\\">
<span class="d2h-code-line-prefix">-</span> <span class=\\"d2h-code-line-prefix\\">-</span>
<span class="d2h-code-line-ctn"><del>test</del></span> <span class=\\"d2h-code-line-ctn\\"><del>test</del></span>
</div> </div>
</td> </td>
</tr><tr> </tr><tr>
<td class="d2h-code-linenumber d2h-ins d2h-change"> <td class=\\"d2h-code-linenumber d2h-ins d2h-change\\">
<div class="line-num1"></div> <div class=\\"line-num1\\"></div>
<div class="line-num2">2</div> <div class=\\"line-num2\\">2</div>
</td> </td>
<td class="d2h-ins d2h-change"> <td class=\\"d2h-ins d2h-change\\">
<div class="d2h-code-line"> <div class=\\"d2h-code-line d2h-ins d2h-change\\">
<span class="d2h-code-line-prefix">+</span> <span class=\\"d2h-code-line-prefix\\">+</span>
<span class="d2h-code-line-ctn"><ins>test1r</ins></span> <span class=\\"d2h-code-line-ctn\\"><ins>test1r</ins></span>
</div> </div>
</td> </td>
</tr><tr> </tr><tr>
<td class="d2h-code-linenumber d2h-ins"> <td class=\\"d2h-code-linenumber d2h-ins\\">
<div class="line-num1"></div> <div class=\\"line-num1\\"></div>
<div class="line-num2">3</div> <div class=\\"line-num2\\">3</div>
</td> </td>
<td class="d2h-ins"> <td class=\\"d2h-ins\\">
<div class="d2h-code-line"> <div class=\\"d2h-code-line d2h-ins\\">
<span class="d2h-code-line-prefix">+</span> <span class=\\"d2h-code-line-prefix\\">+</span>
<span class="d2h-code-line-ctn">test2r</span> <span class=\\"d2h-code-line-ctn\\">test2r</span>
</div> </div>
</td> </td>
</tr>" </tr>"

View file

@ -5,31 +5,31 @@ describe('Utils', () => {
describe('escapeForHtml', () => { describe('escapeForHtml', () => {
it('should escape & with &amp;', () => { it('should escape & with &amp;', () => {
const result = escapeForHtml('&'); const result = escapeForHtml('&');
expect(result).toBe('&amp;'); expect(result).toEqual('&amp;');
}); });
it('should escape < with &lt;', () => { it('should escape < with &lt;', () => {
const result = escapeForHtml('<'); const result = escapeForHtml('<');
expect(result).toBe('&lt;'); expect(result).toEqual('&lt;');
}); });
it('should escape > with &gt;', () => { it('should escape > with &gt;', () => {
const result = escapeForHtml('>'); const result = escapeForHtml('>');
expect(result).toBe('&gt;'); expect(result).toEqual('&gt;');
}); });
it('should escape " with &quot;', () => { it('should escape " with &quot;', () => {
const result = escapeForHtml('"'); const result = escapeForHtml('"');
expect(result).toBe('&quot;'); expect(result).toEqual('&quot;');
}); });
it("should escape ' with &#x27;", () => { it("should escape ' with &#x27;", () => {
const result = escapeForHtml("'"); const result = escapeForHtml("'");
expect(result).toBe('&#x27;'); expect(result).toEqual('&#x27;');
}); });
it('should escape / with &#x2F;', () => { it('should escape / with &#x2F;', () => {
const result = escapeForHtml('/'); const result = escapeForHtml('/');
expect(result).toBe('&#x2F;'); expect(result).toEqual('&#x2F;');
}); });
it('should escape a string containing HTML code', () => { it('should escape a string containing HTML code', () => {
const result = escapeForHtml(`<a href="/search?q=diff2html">Search 'Diff2Html'</a>`); const result = escapeForHtml(`<a href="/search?q=diff2html">Search 'Diff2Html'</a>`);
expect(result).toBe( expect(result).toEqual(
'&lt;a href=&quot;&#x2F;search?q=diff2html&quot;&gt;Search &#x27;Diff2Html&#x27;&lt;&#x2F;a&gt;', '&lt;a href=&quot;&#x2F;search?q=diff2html&quot;&gt;Search &#x27;Diff2Html&#x27;&lt;&#x2F;a&gt;',
); );
}); });
@ -41,7 +41,7 @@ describe('Utils', () => {
oldName: 'sample.js', oldName: 'sample.js',
newName: 'sample.js', newName: 'sample.js',
}); });
expect(result).toBe('d2h-960013'); expect(result).toEqual('d2h-960013');
}); });
}); });
@ -51,49 +51,49 @@ describe('Utils', () => {
oldName: 'sample.js', oldName: 'sample.js',
newName: 'sample.js', newName: 'sample.js',
}); });
expect(result).toBe('sample.js'); expect(result).toEqual('sample.js');
}); });
it('should generate the file name for a changed file and full rename', () => { it('should generate the file name for a changed file and full rename', () => {
const result = filenameDiff({ const result = filenameDiff({
oldName: 'sample1.js', oldName: 'sample1.js',
newName: 'sample2.js', newName: 'sample2.js',
}); });
expect(result).toBe('sample1.js → sample2.js'); expect(result).toEqual('sample1.js → sample2.js');
}); });
it('should generate the file name for a changed file and prefix rename', () => { it('should generate the file name for a changed file and prefix rename', () => {
const result = filenameDiff({ const result = filenameDiff({
oldName: 'src/path/sample.js', oldName: 'src/path/sample.js',
newName: 'source/path/sample.js', newName: 'source/path/sample.js',
}); });
expect(result).toBe('{src → source}/path/sample.js'); expect(result).toEqual('{src → source}/path/sample.js');
}); });
it('should generate the file name for a changed file and suffix rename', () => { it('should generate the file name for a changed file and suffix rename', () => {
const result = filenameDiff({ const result = filenameDiff({
oldName: 'src/path/sample1.js', oldName: 'src/path/sample1.js',
newName: 'src/path/sample2.js', newName: 'src/path/sample2.js',
}); });
expect(result).toBe('src/path/{sample1.js → sample2.js}'); expect(result).toEqual('src/path/{sample1.js → sample2.js}');
}); });
it('should generate the file name for a changed file and middle rename', () => { it('should generate the file name for a changed file and middle rename', () => {
const result = filenameDiff({ const result = filenameDiff({
oldName: 'src/really/big/path/sample.js', oldName: 'src/really/big/path/sample.js',
newName: 'src/small/path/sample.js', newName: 'src/small/path/sample.js',
}); });
expect(result).toBe('src/{really/big → small}/path/sample.js'); expect(result).toEqual('src/{really/big → small}/path/sample.js');
}); });
it('should generate the file name for a deleted file', () => { it('should generate the file name for a deleted file', () => {
const result = filenameDiff({ const result = filenameDiff({
oldName: 'src/my/file.js', oldName: 'src/my/file.js',
newName: '/dev/null', newName: '/dev/null',
}); });
expect(result).toBe('src/my/file.js'); expect(result).toEqual('src/my/file.js');
}); });
it('should generate the file name for a new file', () => { it('should generate the file name for a new file', () => {
const result = filenameDiff({ const result = filenameDiff({
oldName: '/dev/null', oldName: '/dev/null',
newName: 'src/my/file.js', newName: 'src/my/file.js',
}); });
expect(result).toBe('src/my/file.js'); expect(result).toEqual('src/my/file.js');
}); });
}); });

View file

@ -10,10 +10,10 @@ describe('SideBySideRenderer', () => {
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, {}); const sideBySideRenderer = new SideBySideRenderer(hoganUtils, {});
const fileHtml = sideBySideRenderer.generateEmptyDiff(); const fileHtml = sideBySideRenderer.generateEmptyDiff();
expect(fileHtml).toMatchInlineSnapshot(` expect(fileHtml).toMatchInlineSnapshot(`
{ Object {
"left": "<tr> "left": "<tr>
<td class="d2h-info"> <td class=\\"d2h-info\\">
<div class="d2h-code-side-line"> <div class=\\"d2h-code-side-line d2h-info\\">
File without changes File without changes
</div> </div>
</td> </td>
@ -78,76 +78,76 @@ describe('SideBySideRenderer', () => {
const fileHtml = sideBySideRenderer.generateFileHtml(file); const fileHtml = sideBySideRenderer.generateFileHtml(file);
expect(fileHtml).toMatchInlineSnapshot(` expect(fileHtml).toMatchInlineSnapshot(`
{ Object {
"left": "<tr> "left": "<tr>
<td class="d2h-code-side-linenumber d2h-info"></td> <td class=\\"d2h-code-side-linenumber d2h-info\\"></td>
<td class="d2h-info"> <td class=\\"d2h-info\\">
<div class="d2h-code-side-line">@@ -19,7 +19,7 @@</div> <div class=\\"d2h-code-side-line d2h-info\\">@@ -19,7 +19,7 @@</div>
</td> </td>
</tr><tr> </tr><tr>
<td class="d2h-code-side-linenumber d2h-cntx"> <td class=\\"d2h-code-side-linenumber d2h-cntx\\">
19 19
</td> </td>
<td class="d2h-cntx"> <td class=\\"d2h-cntx\\">
<div class="d2h-code-side-line"> <div class=\\"d2h-code-side-line d2h-cntx\\">
<span class="d2h-code-line-prefix">&nbsp;</span> <span class=\\"d2h-code-line-prefix\\">&nbsp;</span>
<span class="d2h-code-line-ctn">context</span> <span class=\\"d2h-code-line-ctn\\">context</span>
</div> </div>
</td> </td>
</tr><tr> </tr><tr>
<td class="d2h-code-side-linenumber d2h-del d2h-change"> <td class=\\"d2h-code-side-linenumber d2h-del d2h-change\\">
20 20
</td> </td>
<td class="d2h-del d2h-change"> <td class=\\"d2h-del d2h-change\\">
<div class="d2h-code-side-line"> <div class=\\"d2h-code-side-line d2h-del d2h-change\\">
<span class="d2h-code-line-prefix">-</span> <span class=\\"d2h-code-line-prefix\\">-</span>
<span class="d2h-code-line-ctn"><del>removed</del></span> <span class=\\"d2h-code-line-ctn\\"><del>removed</del></span>
</div> </div>
</td> </td>
</tr><tr> </tr><tr>
<td class="d2h-code-side-linenumber d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder"> <td class=\\"d2h-code-side-linenumber d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder\\">
</td> </td>
<td class="d2h-cntx d2h-emptyplaceholder"> <td class=\\"d2h-cntx d2h-emptyplaceholder\\">
<div class="d2h-code-side-line d2h-code-side-emptyplaceholder"> <div class=\\"d2h-code-side-line d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder\\">
<span class="d2h-code-line-prefix">&nbsp;</span> <span class=\\"d2h-code-line-prefix\\">&nbsp;</span>
<span class="d2h-code-line-ctn"><br></span> <span class=\\"d2h-code-line-ctn\\">&nbsp;</span>
</div> </div>
</td> </td>
</tr>", </tr>",
"right": "<tr> "right": "<tr>
<td class="d2h-code-side-linenumber d2h-info"></td> <td class=\\"d2h-code-side-linenumber d2h-info\\"></td>
<td class="d2h-info"> <td class=\\"d2h-info\\">
<div class="d2h-code-side-line">&nbsp;</div> <div class=\\"d2h-code-side-line d2h-info\\"></div>
</td> </td>
</tr><tr> </tr><tr>
<td class="d2h-code-side-linenumber d2h-cntx"> <td class=\\"d2h-code-side-linenumber d2h-cntx\\">
19 19
</td> </td>
<td class="d2h-cntx"> <td class=\\"d2h-cntx\\">
<div class="d2h-code-side-line"> <div class=\\"d2h-code-side-line d2h-cntx\\">
<span class="d2h-code-line-prefix">&nbsp;</span> <span class=\\"d2h-code-line-prefix\\">&nbsp;</span>
<span class="d2h-code-line-ctn">context</span> <span class=\\"d2h-code-line-ctn\\">context</span>
</div> </div>
</td> </td>
</tr><tr> </tr><tr>
<td class="d2h-code-side-linenumber d2h-ins d2h-change"> <td class=\\"d2h-code-side-linenumber d2h-ins d2h-change\\">
20 20
</td> </td>
<td class="d2h-ins d2h-change"> <td class=\\"d2h-ins d2h-change\\">
<div class="d2h-code-side-line"> <div class=\\"d2h-code-side-line d2h-ins d2h-change\\">
<span class="d2h-code-line-prefix">+</span> <span class=\\"d2h-code-line-prefix\\">+</span>
<span class="d2h-code-line-ctn"><ins>added</ins></span> <span class=\\"d2h-code-line-ctn\\"><ins>added</ins></span>
</div> </div>
</td> </td>
</tr><tr> </tr><tr>
<td class="d2h-code-side-linenumber d2h-ins"> <td class=\\"d2h-code-side-linenumber d2h-ins\\">
21 21
</td> </td>
<td class="d2h-ins"> <td class=\\"d2h-ins\\">
<div class="d2h-code-side-line"> <div class=\\"d2h-code-side-line d2h-ins\\">
<span class="d2h-code-line-prefix">+</span> <span class=\\"d2h-code-line-prefix\\">+</span>
<span class="d2h-code-line-ctn">another added</span> <span class=\\"d2h-code-line-ctn\\">another added</span>
</div> </div>
</td> </td>
</tr>", </tr>",
@ -168,26 +168,26 @@ describe('SideBySideRenderer', () => {
}); });
expect(fileHtml).toMatchInlineSnapshot(` expect(fileHtml).toMatchInlineSnapshot(`
{ Object {
"left": "<tr> "left": "<tr>
<td class="d2h-code-side-linenumber d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder"> <td class=\\"d2h-code-side-linenumber d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder\\">
</td> </td>
<td class="d2h-cntx d2h-emptyplaceholder"> <td class=\\"d2h-cntx d2h-emptyplaceholder\\">
<div class="d2h-code-side-line d2h-code-side-emptyplaceholder"> <div class=\\"d2h-code-side-line d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder\\">
<span class="d2h-code-line-prefix">&nbsp;</span> <span class=\\"d2h-code-line-prefix\\">&nbsp;</span>
<span class="d2h-code-line-ctn"><br></span> <span class=\\"d2h-code-line-ctn\\">&nbsp;</span>
</div> </div>
</td> </td>
</tr>", </tr>",
"right": "<tr> "right": "<tr>
<td class="d2h-code-side-linenumber d2h-ins"> <td class=\\"d2h-code-side-linenumber d2h-ins\\">
30 30
</td> </td>
<td class="d2h-ins"> <td class=\\"d2h-ins\\">
<div class="d2h-code-side-line"> <div class=\\"d2h-code-side-line d2h-ins\\">
<span class="d2h-code-line-prefix">+</span> <span class=\\"d2h-code-line-prefix\\">+</span>
<span class="d2h-code-line-ctn">test</span> <span class=\\"d2h-code-line-ctn\\">test</span>
</div> </div>
</td> </td>
</tr>", </tr>",
@ -208,26 +208,26 @@ describe('SideBySideRenderer', () => {
); );
expect(fileHtml).toMatchInlineSnapshot(` expect(fileHtml).toMatchInlineSnapshot(`
{ Object {
"left": "<tr> "left": "<tr>
<td class="d2h-code-side-linenumber d2h-del"> <td class=\\"d2h-code-side-linenumber d2h-del\\">
30 30
</td> </td>
<td class="d2h-del"> <td class=\\"d2h-del\\">
<div class="d2h-code-side-line"> <div class=\\"d2h-code-side-line d2h-del\\">
<span class="d2h-code-line-prefix">-</span> <span class=\\"d2h-code-line-prefix\\">-</span>
<span class="d2h-code-line-ctn">test</span> <span class=\\"d2h-code-line-ctn\\">test</span>
</div> </div>
</td> </td>
</tr>", </tr>",
"right": "<tr> "right": "<tr>
<td class="d2h-code-side-linenumber d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder"> <td class=\\"d2h-code-side-linenumber d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder\\">
</td> </td>
<td class="d2h-cntx d2h-emptyplaceholder"> <td class=\\"d2h-cntx d2h-emptyplaceholder\\">
<div class="d2h-code-side-line d2h-code-side-emptyplaceholder"> <div class=\\"d2h-code-side-line d2h-code-side-emptyplaceholder d2h-cntx d2h-emptyplaceholder\\">
<span class="d2h-code-line-prefix">&nbsp;</span> <span class=\\"d2h-code-line-prefix\\">&nbsp;</span>
<span class="d2h-code-line-ctn"><br></span> <span class=\\"d2h-code-line-ctn\\">&nbsp;</span>
</div> </div>
</td> </td>
</tr>", </tr>",
@ -278,37 +278,33 @@ describe('SideBySideRenderer', () => {
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, { matching: LineMatchingType.LINES }); const sideBySideRenderer = new SideBySideRenderer(hoganUtils, { matching: LineMatchingType.LINES });
const html = sideBySideRenderer.render(exampleJson); const html = sideBySideRenderer.render(exampleJson);
expect(html).toMatchInlineSnapshot(` expect(html).toMatchInlineSnapshot(`
"<div class="d2h-wrapper d2h-light-color-scheme"> "<div class=\\"d2h-wrapper\\">
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="txt"> <div id=\\"d2h-675094\\" class=\\"d2h-file-wrapper\\" data-lang=\\"txt\\">
<div class="d2h-file-header"> <div class=\\"d2h-file-header\\">
<span class="d2h-file-name-wrapper"> <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"> <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> <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> </svg> <span class=\\"d2h-file-name\\">sample</span>
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></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>
<div class="d2h-files-diff"> <div class=\\"d2h-files-diff\\">
<div class="d2h-file-side-diff"> <div class=\\"d2h-file-side-diff\\">
<div class="d2h-code-wrapper"> <div class=\\"d2h-code-wrapper\\">
<table class="d2h-diff-table"> <table class=\\"d2h-diff-table\\">
<tbody class="d2h-diff-tbody"> <tbody class=\\"d2h-diff-tbody\\">
<tr> <tr>
<td class="d2h-code-side-linenumber d2h-info"></td> <td class=\\"d2h-code-side-linenumber d2h-info\\"></td>
<td class="d2h-info"> <td class=\\"d2h-info\\">
<div class="d2h-code-side-line">@@ -1 +1 @@</div> <div class=\\"d2h-code-side-line d2h-info\\">@@ -1 +1 @@</div>
</td> </td>
</tr><tr> </tr><tr>
<td class="d2h-code-side-linenumber d2h-del d2h-change"> <td class=\\"d2h-code-side-linenumber d2h-del d2h-change\\">
1 1
</td> </td>
<td class="d2h-del d2h-change"> <td class=\\"d2h-del d2h-change\\">
<div class="d2h-code-side-line"> <div class=\\"d2h-code-side-line d2h-del d2h-change\\">
<span class="d2h-code-line-prefix">-</span> <span class=\\"d2h-code-line-prefix\\">-</span>
<span class="d2h-code-line-ctn"><del>test</del></span> <span class=\\"d2h-code-line-ctn\\"><del>test</del></span>
</div> </div>
</td> </td>
</tr> </tr>
@ -316,23 +312,23 @@ describe('SideBySideRenderer', () => {
</table> </table>
</div> </div>
</div> </div>
<div class="d2h-file-side-diff"> <div class=\\"d2h-file-side-diff\\">
<div class="d2h-code-wrapper"> <div class=\\"d2h-code-wrapper\\">
<table class="d2h-diff-table"> <table class=\\"d2h-diff-table\\">
<tbody class="d2h-diff-tbody"> <tbody class=\\"d2h-diff-tbody\\">
<tr> <tr>
<td class="d2h-code-side-linenumber d2h-info"></td> <td class=\\"d2h-code-side-linenumber d2h-info\\"></td>
<td class="d2h-info"> <td class=\\"d2h-info\\">
<div class="d2h-code-side-line">&nbsp;</div> <div class=\\"d2h-code-side-line d2h-info\\"></div>
</td> </td>
</tr><tr> </tr><tr>
<td class="d2h-code-side-linenumber d2h-ins d2h-change"> <td class=\\"d2h-code-side-linenumber d2h-ins d2h-change\\">
1 1
</td> </td>
<td class="d2h-ins d2h-change"> <td class=\\"d2h-ins d2h-change\\">
<div class="d2h-code-side-line"> <div class=\\"d2h-code-side-line d2h-ins d2h-change\\">
<span class="d2h-code-line-prefix">+</span> <span class=\\"d2h-code-line-prefix\\">+</span>
<span class="d2h-code-line-ctn"><ins>test1r</ins></span> <span class=\\"d2h-code-line-ctn\\"><ins>test1r</ins></span>
</div> </div>
</td> </td>
</tr> </tr>
@ -363,27 +359,23 @@ describe('SideBySideRenderer', () => {
const sideBySideRenderer = new SideBySideRenderer(hoganUtils, {}); const sideBySideRenderer = new SideBySideRenderer(hoganUtils, {});
const html = sideBySideRenderer.render(exampleJson); const html = sideBySideRenderer.render(exampleJson);
expect(html).toMatchInlineSnapshot(` expect(html).toMatchInlineSnapshot(`
"<div class="d2h-wrapper d2h-light-color-scheme"> "<div class=\\"d2h-wrapper\\">
<div id="d2h-675094" class="d2h-file-wrapper" data-lang="js"> <div id=\\"d2h-675094\\" class=\\"d2h-file-wrapper\\" data-lang=\\"js\\">
<div class="d2h-file-header"> <div class=\\"d2h-file-header\\">
<span class="d2h-file-name-wrapper"> <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"> <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> <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> </svg> <span class=\\"d2h-file-name\\">sample</span>
<span class="d2h-tag d2h-changed d2h-changed-tag">CHANGED</span></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>
<div class="d2h-files-diff"> <div class=\\"d2h-files-diff\\">
<div class="d2h-file-side-diff"> <div class=\\"d2h-file-side-diff\\">
<div class="d2h-code-wrapper"> <div class=\\"d2h-code-wrapper\\">
<table class="d2h-diff-table"> <table class=\\"d2h-diff-table\\">
<tbody class="d2h-diff-tbody"> <tbody class=\\"d2h-diff-tbody\\">
<tr> <tr>
<td class="d2h-info"> <td class=\\"d2h-info\\">
<div class="d2h-code-side-line"> <div class=\\"d2h-code-side-line d2h-info\\">
File without changes File without changes
</div> </div>
</td> </td>
@ -392,10 +384,10 @@ describe('SideBySideRenderer', () => {
</table> </table>
</div> </div>
</div> </div>
<div class="d2h-file-side-diff"> <div class=\\"d2h-file-side-diff\\">
<div class="d2h-code-wrapper"> <div class=\\"d2h-code-wrapper\\">
<table class="d2h-diff-table"> <table class=\\"d2h-diff-table\\">
<tbody class="d2h-diff-tbody"> <tbody class=\\"d2h-diff-tbody\\">
</tbody> </tbody>
</table> </table>
@ -406,81 +398,6 @@ describe('SideBySideRenderer', () => {
</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', () => { describe('processLines', () => {
@ -508,26 +425,26 @@ describe('SideBySideRenderer', () => {
const html = sideBySideRenderer.processChangedLines(false, oldLines, newLines); const html = sideBySideRenderer.processChangedLines(false, oldLines, newLines);
expect(html).toMatchInlineSnapshot(` expect(html).toMatchInlineSnapshot(`
{ Object {
"left": "<tr> "left": "<tr>
<td class="d2h-code-side-linenumber d2h-del d2h-change"> <td class=\\"d2h-code-side-linenumber d2h-del d2h-change\\">
1 1
</td> </td>
<td class="d2h-del d2h-change"> <td class=\\"d2h-del d2h-change\\">
<div class="d2h-code-side-line"> <div class=\\"d2h-code-side-line d2h-del d2h-change\\">
<span class="d2h-code-line-prefix">-</span> <span class=\\"d2h-code-line-prefix\\">-</span>
<span class="d2h-code-line-ctn"><del>test</del></span> <span class=\\"d2h-code-line-ctn\\"><del>test</del></span>
</div> </div>
</td> </td>
</tr>", </tr>",
"right": "<tr> "right": "<tr>
<td class="d2h-code-side-linenumber d2h-ins d2h-change"> <td class=\\"d2h-code-side-linenumber d2h-ins d2h-change\\">
1 1
</td> </td>
<td class="d2h-ins d2h-change"> <td class=\\"d2h-ins d2h-change\\">
<div class="d2h-code-side-line"> <div class=\\"d2h-code-side-line d2h-ins d2h-change\\">
<span class="d2h-code-line-prefix">+</span> <span class=\\"d2h-code-line-prefix\\">+</span>
<span class="d2h-code-line-ctn"><ins>test1r</ins></span> <span class=\\"d2h-code-line-ctn\\"><ins>test1r</ins></span>
</div> </div>
</td> </td>
</tr>", </tr>",

View file

@ -4,18 +4,18 @@ describe('Utils', () => {
describe('escapeForRegExp', () => { describe('escapeForRegExp', () => {
it('should escape markdown link text', () => { it('should escape markdown link text', () => {
const result = escapeForRegExp('[Link](https://diff2html.xyz)'); const result = escapeForRegExp('[Link](https://diff2html.xyz)');
expect(result).toBe('\\[Link\\]\\(https:\\/\\/diff2html\\.xyz\\)'); expect(result).toEqual('\\[Link\\]\\(https:\\/\\/diff2html\\.xyz\\)');
}); });
it('should escape all dangerous characters', () => { it('should escape all dangerous characters', () => {
const result = escapeForRegExp('-[]/{}()*+?.\\^$|'); const result = escapeForRegExp('-[]/{}()*+?.\\^$|');
expect(result).toBe('\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|'); expect(result).toEqual('\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|');
}); });
}); });
describe('unifyPath', () => { describe('unifyPath', () => {
it('should unify windows style path', () => { it('should unify windows style path', () => {
const result = unifyPath('\\Users\\Downloads\\diff.html'); const result = unifyPath('\\Users\\Downloads\\diff.html');
expect(result).toBe('/Users/Downloads/diff.html'); expect(result).toEqual('/Users/Downloads/diff.html');
}); });
}); });

View file

@ -4,9 +4,6 @@ import { escapeForRegExp } from './utils';
export interface DiffParserConfig { export interface DiffParserConfig {
srcPrefix?: string; srcPrefix?: string;
dstPrefix?: string; dstPrefix?: string;
diffMaxChanges?: number;
diffMaxLineLength?: number;
diffTooBigMessage?: (fileIndex: number) => string;
} }
function getExtension(filename: string, language: string): string { function getExtension(filename: string, language: string): string {
@ -185,16 +182,12 @@ export function parse(diffInput: string, config: DiffParserConfig = {}): DiffFil
} }
/* Create block metadata */ /* Create block metadata */
// eslint-disable-next-line
// @ts-ignore
currentBlock = { currentBlock = {
lines: [], lines: [],
// eslint-disable-next-line
// @ts-ignore
oldStartLine: oldLine, oldStartLine: oldLine,
// eslint-disable-next-line
// @ts-ignore
oldStartLine2: oldLine2, oldStartLine2: oldLine2,
// eslint-disable-next-line
// @ts-ignore
newStartLine: newLine, newStartLine: newLine,
header: line, header: line,
}; };
@ -272,11 +265,11 @@ export function parse(diffInput: string, config: DiffParserConfig = {}): DiffFil
const nxtLine = diffLines[lineIndex + 1]; const nxtLine = diffLines[lineIndex + 1];
const afterNxtLine = diffLines[lineIndex + 2]; const afterNxtLine = diffLines[lineIndex + 2];
if (line.startsWith('diff --git') || line.startsWith('diff --combined')) { if (line.startsWith('diff')) {
startFile(); startFile();
// diff --git a/blocked_delta_results.png b/blocked_delta_results.png // diff --git a/blocked_delta_results.png b/blocked_delta_results.png
const gitDiffStart = /^diff --git "?([a-ciow]\/.+)"? "?([a-ciow]\/.+)"?/; const gitDiffStart = /^diff --git "?(.+)"? "?(.+)"?/;
if ((values = gitDiffStart.exec(line))) { if ((values = gitDiffStart.exec(line))) {
possibleOldName = getFilename(values[1], undefined, config.dstPrefix); possibleOldName = getFilename(values[1], undefined, config.dstPrefix);
possibleNewName = getFilename(values[2], undefined, config.srcPrefix); possibleNewName = getFilename(values[2], undefined, config.srcPrefix);
@ -290,27 +283,11 @@ export function parse(diffInput: string, config: DiffParserConfig = {}): DiffFil
return; 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 ( if (
!currentFile || // If we do not have a file yet, we should crete one !currentFile || // If we do not have a file yet, we should crete one
(!currentFile.isGitDiff && (!currentFile.isGitDiff &&
currentFile && // If we already have some file in progress and currentFile && // If we already have some file in progress and
line.startsWith(oldFileNameHeader) && // If we get to an old file path header line 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 // And is followed by the new file path header line and the hunk header line
nxtLine.startsWith(newFileNameHeader) && nxtLine.startsWith(newFileNameHeader) &&
afterNxtLine.startsWith(hunkHeaderPrefix)) afterNxtLine.startsWith(hunkHeaderPrefix))
@ -318,31 +295,6 @@ export function parse(diffInput: string, config: DiffParserConfig = {}): DiffFil
startFile(); 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. * We need to make sure that we have the three lines of the header.
* This avoids cases like the ones described in: * This avoids cases like the ones described in:
@ -458,16 +410,18 @@ export function parse(diffInput: string, config: DiffParserConfig = {}): DiffFil
} else if ((values = index.exec(line))) { } else if ((values = index.exec(line))) {
currentFile.checksumBefore = values[1]; currentFile.checksumBefore = values[1];
currentFile.checksumAfter = values[2]; currentFile.checksumAfter = values[2];
if (values[3]) currentFile.mode = values[3]; values[3] && (currentFile.mode = values[3]);
} else if ((values = combinedIndex.exec(line))) { } else if ((values = combinedIndex.exec(line))) {
currentFile.checksumBefore = [values[2], values[3]]; currentFile.checksumBefore = [values[2], values[3]];
currentFile.checksumAfter = values[1]; currentFile.checksumAfter = values[1];
} else if ((values = combinedMode.exec(line))) { } else if ((values = combinedMode.exec(line))) {
currentFile.oldMode = [values[2], values[3]]; currentFile.oldMode = [values[2], values[3]];
currentFile.newMode = values[1]; currentFile.newMode = values[1];
// eslint-disable-next-line sonarjs/no-duplicated-branches
} else if ((values = combinedNewFile.exec(line))) { } else if ((values = combinedNewFile.exec(line))) {
currentFile.newFileMode = values[1]; currentFile.newFileMode = values[1];
currentFile.isNew = true; currentFile.isNew = true;
// eslint-disable-next-line sonarjs/no-duplicated-branches
} else if ((values = combinedDeletedFile.exec(line))) { } else if ((values = combinedDeletedFile.exec(line))) {
currentFile.deletedFileMode = values[1]; currentFile.deletedFileMode = values[1];
currentFile.isDeleted = true; currentFile.isDeleted = true;

View file

@ -1,5 +1,5 @@
import * as DiffParser from './diff-parser'; import * as DiffParser from './diff-parser';
import { FileListRenderer } from './file-list-renderer'; import * as fileListPrinter from './file-list-renderer';
import LineByLineRenderer, { LineByLineRendererConfig, defaultLineByLineRendererConfig } from './line-by-line-renderer'; import LineByLineRenderer, { LineByLineRendererConfig, defaultLineByLineRendererConfig } from './line-by-line-renderer';
import SideBySideRenderer, { SideBySideRendererConfig, defaultSideBySideRendererConfig } from './side-by-side-renderer'; import SideBySideRenderer, { SideBySideRendererConfig, defaultSideBySideRendererConfig } from './side-by-side-renderer';
import { DiffFile, OutputFormatType } from './types'; import { DiffFile, OutputFormatType } from './types';
@ -32,10 +32,7 @@ export function html(diffInput: string | DiffFile[], configuration: Diff2HtmlCon
const hoganUtils = new HoganJsUtils(config); const hoganUtils = new HoganJsUtils(config);
const { colorScheme } = config; const fileList = config.drawFileList ? fileListPrinter.render(diffJson, hoganUtils) : '';
const fileListRendererConfig = { colorScheme };
const fileList = config.drawFileList ? new FileListRenderer(hoganUtils, fileListRendererConfig).render(diffJson) : '';
const diffOutput = const diffOutput =
config.outputFormat === 'side-by-side' config.outputFormat === 'side-by-side'

View file

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

View file

@ -39,7 +39,7 @@ export default class HoganJsUtils {
try { try {
const template = this.preCompiledTemplates[templateKey]; const template = this.preCompiledTemplates[templateKey];
return template.render(params, partials, indent); return template.render(params, partials, indent);
} catch (_e) { } catch (e) {
throw new Error(`Could not find template to render '${templateKey}'`); throw new Error(`Could not find template to render '${templateKey}'`);
} }
} }

View file

@ -52,10 +52,7 @@ export default class LineByLineRenderer {
}) })
.join('\n'); .join('\n');
return this.hoganUtils.render(genericTemplatesPath, 'wrapper', { return this.hoganUtils.render(genericTemplatesPath, 'wrapper', { content: diffsHtml });
colorScheme: renderUtils.colorSchemeToCss(this.config.colorScheme),
content: diffsHtml,
});
} }
makeFileDiffHtml(file: DiffFile, diffs: string): string { makeFileDiffHtml(file: DiffFile, diffs: string): string {
@ -98,7 +95,7 @@ export default class LineByLineRenderer {
.map(block => { .map(block => {
let lines = this.hoganUtils.render(genericTemplatesPath, 'block-header', { let lines = this.hoganUtils.render(genericTemplatesPath, 'block-header', {
CSSLineClass: renderUtils.CSSLineClass, CSSLineClass: renderUtils.CSSLineClass,
blockHeader: file.isTooBig ? block.header : renderUtils.escapeForHtml(block.header), blockHeader: block.header,
lineClass: 'd2h-code-linenumber', lineClass: 'd2h-code-linenumber',
contentClass: 'd2h-code-line', contentClass: 'd2h-code-line',
}); });
@ -106,14 +103,14 @@ export default class LineByLineRenderer {
this.applyLineGroupping(block).forEach(([contextLines, oldLines, newLines]) => { this.applyLineGroupping(block).forEach(([contextLines, oldLines, newLines]) => {
if (oldLines.length && newLines.length && !contextLines.length) { if (oldLines.length && newLines.length && !contextLines.length) {
this.applyRematchMatching(oldLines, newLines, matcher).map(([oldLines, newLines]) => { this.applyRematchMatching(oldLines, newLines, matcher).map(([oldLines, newLines]) => {
const { left, right } = this.processChangedLines(file, file.isCombined, oldLines, newLines); const { left, right } = this.processChangedLines(file.isCombined, oldLines, newLines);
lines += left; lines += left;
lines += right; lines += right;
}); });
} else if (contextLines.length) { } else if (contextLines.length) {
contextLines.forEach(line => { contextLines.forEach(line => {
const { prefix, content } = renderUtils.deconstructLine(line.content, file.isCombined); const { prefix, content } = renderUtils.deconstructLine(line.content, file.isCombined);
lines += this.generateSingleLineHtml(file, { lines += this.generateSingleLineHtml({
type: renderUtils.CSSLineClass.CONTEXT, type: renderUtils.CSSLineClass.CONTEXT,
prefix: prefix, prefix: prefix,
content: content, content: content,
@ -122,7 +119,7 @@ export default class LineByLineRenderer {
}); });
}); });
} else if (oldLines.length || newLines.length) { } else if (oldLines.length || newLines.length) {
const { left, right } = this.processChangedLines(file, file.isCombined, oldLines, newLines); const { left, right } = this.processChangedLines(file.isCombined, oldLines, newLines);
lines += left; lines += left;
lines += right; lines += right;
} else { } else {
@ -191,7 +188,7 @@ export default class LineByLineRenderer {
return doMatching ? matcher(oldLines, newLines) : [[oldLines, newLines]]; return doMatching ? matcher(oldLines, newLines) : [[oldLines, newLines]];
} }
processChangedLines(file: DiffFile, isCombined: boolean, oldLines: DiffLine[], newLines: DiffLine[]): FileHtml { processChangedLines(isCombined: boolean, oldLines: DiffLine[], newLines: DiffLine[]): FileHtml {
const fileHtml = { const fileHtml = {
right: '', right: '',
left: '', left: '',
@ -243,7 +240,7 @@ export default class LineByLineRenderer {
} }
: undefined; : undefined;
const { left, right } = this.generateLineHtml(file, preparedOldLine, preparedNewLine); const { left, right } = this.generateLineHtml(preparedOldLine, preparedNewLine);
fileHtml.left += left; fileHtml.left += left;
fileHtml.right += right; fileHtml.right += right;
} }
@ -251,14 +248,14 @@ export default class LineByLineRenderer {
return fileHtml; return fileHtml;
} }
generateLineHtml(file: DiffFile, oldLine?: DiffPreparedLine, newLine?: DiffPreparedLine): FileHtml { generateLineHtml(oldLine?: DiffPreparedLine, newLine?: DiffPreparedLine): FileHtml {
return { return {
left: this.generateSingleLineHtml(file, oldLine), left: this.generateSingleLineHtml(oldLine),
right: this.generateSingleLineHtml(file, newLine), right: this.generateSingleLineHtml(newLine),
}; };
} }
generateSingleLineHtml(file: DiffFile, line?: DiffPreparedLine): string { generateSingleLineHtml(line?: DiffPreparedLine): string {
if (line === undefined) return ''; if (line === undefined) return '';
const lineNumberHtml = this.hoganUtils.render(baseTemplatesPath, 'numbers', { const lineNumberHtml = this.hoganUtils.render(baseTemplatesPath, 'numbers', {
@ -273,8 +270,6 @@ export default class LineByLineRenderer {
prefix: line.prefix === ' ' ? '&nbsp;' : line.prefix, prefix: line.prefix === ' ' ? '&nbsp;' : line.prefix,
content: line.content, content: line.content,
lineNumber: lineNumberHtml, lineNumber: lineNumberHtml,
line,
file,
}); });
} }
} }

View file

@ -2,32 +2,16 @@ import * as jsDiff from 'diff';
import { unifyPath, hashCode } from './utils'; import { unifyPath, hashCode } from './utils';
import * as rematch from './rematch'; import * as rematch from './rematch';
import { import { LineMatchingType, DiffStyleType, LineType, DiffLineParts, DiffFile, DiffFileName } from './types';
ColorSchemeType,
DiffFile,
DiffFileName,
DiffLineParts,
DiffStyleType,
LineMatchingType,
LineType,
} from './types';
export type CSSLineClass = export enum CSSLineClass {
| 'd2h-ins' INSERTS = 'd2h-ins',
| 'd2h-del' DELETES = 'd2h-del',
| 'd2h-cntx' CONTEXT = 'd2h-cntx',
| 'd2h-info' INFO = 'd2h-info',
| 'd2h-ins d2h-change' INSERT_CHANGES = 'd2h-ins d2h-change',
| 'd2h-del d2h-change'; DELETE_CHANGES = '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 = { export type HighlightedLines = {
oldLine: { oldLine: {
@ -45,7 +29,6 @@ export interface RenderConfig {
matchWordsThreshold?: number; matchWordsThreshold?: number;
maxLineLengthHighlight?: number; maxLineLengthHighlight?: number;
diffStyle?: DiffStyleType; diffStyle?: DiffStyleType;
colorScheme?: ColorSchemeType;
} }
export const defaultRenderConfig = { export const defaultRenderConfig = {
@ -53,7 +36,6 @@ export const defaultRenderConfig = {
matchWordsThreshold: 0.25, matchWordsThreshold: 0.25,
maxLineLengthHighlight: 10000, maxLineLengthHighlight: 10000,
diffStyle: DiffStyleType.WORD, diffStyle: DiffStyleType.WORD,
colorScheme: ColorSchemeType.LIGHT,
}; };
const separator = '/'; const separator = '/';
@ -86,18 +68,6 @@ export function toCSSClass(lineType: LineType): CSSLineClass {
} }
} }
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 * Prefix length of the hunk lines in the diff
*/ */
@ -123,11 +93,11 @@ export function escapeForHtml(str: string): string {
/** /**
* Deconstructs diff @line by separating the content from the prefix type * Deconstructs diff @line by separating the content from the prefix type
*/ */
export function deconstructLine(line: string, isCombined: boolean, escape = true): DiffLineParts { export function deconstructLine(line: string, isCombined: boolean): DiffLineParts {
const indexToSplit = prefixLength(isCombined); const indexToSplit = prefixLength(isCombined);
return { return {
prefix: line.substring(0, indexToSplit), prefix: line.substring(0, indexToSplit),
content: escape ? escapeForHtml(line.substring(indexToSplit)) : line.substring(indexToSplit), content: escapeForHtml(line.substring(indexToSplit)),
}; };
} }
@ -208,7 +178,9 @@ export function filenameDiff(file: DiffFileName): string {
* Generates a unique string numerical identifier based on the names of the file diff * Generates a unique string numerical identifier based on the names of the file diff
*/ */
export function getHtmlId(file: DiffFileName): string { export function getHtmlId(file: DiffFileName): string {
return `d2h-${hashCode(filenameDiff(file)).toString().slice(-6)}`; return `d2h-${hashCode(filenameDiff(file))
.toString()
.slice(-6)}`;
} }
/** /**
@ -244,18 +216,18 @@ export function diffHighlight(
): HighlightedLines { ): HighlightedLines {
const { matching, maxLineLengthHighlight, matchWordsThreshold, diffStyle } = { ...defaultRenderConfig, ...config }; const { matching, maxLineLengthHighlight, matchWordsThreshold, diffStyle } = { ...defaultRenderConfig, ...config };
const line1 = deconstructLine(diffLine1, isCombined, false); const line1 = deconstructLine(diffLine1, isCombined);
const line2 = deconstructLine(diffLine2, isCombined, false); const line2 = deconstructLine(diffLine2, isCombined);
if (line1.content.length > maxLineLengthHighlight || line2.content.length > maxLineLengthHighlight) { if (line1.content.length > maxLineLengthHighlight || line2.content.length > maxLineLengthHighlight) {
return { return {
oldLine: { oldLine: {
prefix: line1.prefix, prefix: line1.prefix,
content: escapeForHtml(line1.content), content: line1.content,
}, },
newLine: { newLine: {
prefix: line2.prefix, prefix: line2.prefix,
content: escapeForHtml(line2.content), content: line2.content,
}, },
}; };
} }
@ -284,11 +256,10 @@ export function diffHighlight(
const highlightedLine = diff.reduce((highlightedLine, part) => { const highlightedLine = diff.reduce((highlightedLine, part) => {
const elemType = part.added ? 'ins' : part.removed ? 'del' : null; const elemType = part.added ? 'ins' : part.removed ? 'del' : null;
const addClass = changedWords.indexOf(part) > -1 ? ' class="d2h-change"' : ''; const addClass = changedWords.indexOf(part) > -1 ? ' class="d2h-change"' : '';
const escapedValue = escapeForHtml(part.value);
return elemType !== null return elemType !== null
? `${highlightedLine}<${elemType}${addClass}>${escapedValue}</${elemType}>` ? `${highlightedLine}<${elemType}${addClass}>${part.value}</${elemType}>`
: `${highlightedLine}${escapedValue}`; : `${highlightedLine}${part.value}`;
}, ''); }, '');
return { return {

View file

@ -52,10 +52,7 @@ export default class SideBySideRenderer {
}) })
.join('\n'); .join('\n');
return this.hoganUtils.render(genericTemplatesPath, 'wrapper', { return this.hoganUtils.render(genericTemplatesPath, 'wrapper', { content: diffsHtml });
colorScheme: renderUtils.colorSchemeToCss(this.config.colorScheme),
content: diffsHtml,
});
} }
makeFileDiffHtml(file: DiffFile, diffs: FileHtml): string { makeFileDiffHtml(file: DiffFile, diffs: FileHtml): string {
@ -100,7 +97,7 @@ export default class SideBySideRenderer {
return file.blocks return file.blocks
.map(block => { .map(block => {
const fileHtml = { const fileHtml = {
left: this.makeHeaderHtml(block.header, file), left: this.makeHeaderHtml(block.header),
right: this.makeHeaderHtml(''), right: this.makeHeaderHtml(''),
}; };
@ -206,10 +203,10 @@ export default class SideBySideRenderer {
return doMatching ? matcher(oldLines, newLines) : [[oldLines, newLines]]; return doMatching ? matcher(oldLines, newLines) : [[oldLines, newLines]];
} }
makeHeaderHtml(blockHeader: string, file?: DiffFile): string { makeHeaderHtml(blockHeader: string): string {
return this.hoganUtils.render(genericTemplatesPath, 'block-header', { return this.hoganUtils.render(genericTemplatesPath, 'block-header', {
CSSLineClass: renderUtils.CSSLineClass, CSSLineClass: renderUtils.CSSLineClass,
blockHeader: file?.isTooBig ? blockHeader : renderUtils.escapeForHtml(blockHeader), blockHeader: blockHeader,
lineClass: 'd2h-code-side-linenumber', lineClass: 'd2h-code-side-linenumber',
contentClass: 'd2h-code-side-line', contentClass: 'd2h-code-side-line',
}); });
@ -288,8 +285,8 @@ export default class SideBySideRenderer {
type: line?.type || `${renderUtils.CSSLineClass.CONTEXT} d2h-emptyplaceholder`, type: line?.type || `${renderUtils.CSSLineClass.CONTEXT} d2h-emptyplaceholder`,
lineClass: line !== undefined ? lineClass : `${lineClass} d2h-code-side-emptyplaceholder`, lineClass: line !== undefined ? lineClass : `${lineClass} d2h-code-side-emptyplaceholder`,
contentClass: line !== undefined ? contentClass : `${contentClass} d2h-code-side-emptyplaceholder`, contentClass: line !== undefined ? contentClass : `${contentClass} d2h-code-side-emptyplaceholder`,
prefix: line?.prefix === ' ' ? '&nbsp;' : line?.prefix, prefix: line?.prefix === ' ' ? '&nbsp;' : line?.prefix || '&nbsp;',
content: line?.content, content: line?.content || '&nbsp;',
lineNumber: line?.number, lineNumber: line?.number,
}); });
} }

View file

@ -1,4 +1,4 @@
<div class="d2h-file-list-wrapper {{colorScheme}}"> <div class="d2h-file-list-wrapper">
<div class="d2h-file-list-header"> <div class="d2h-file-list-header">
<span class="d2h-file-list-title">Files changed ({{filesNumber}})</span> <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-hide">hide</a>

View file

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

View file

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

View file

@ -3,7 +3,3 @@
<span class="d2h-file-name">{{fileDiffName}}</span> <span class="d2h-file-name">{{fileDiffName}}</span>
{{>fileTag}} {{>fileTag}}
</span> </span>
<label class="d2h-file-collapse">
<input class="d2h-file-collapse-input" type="checkbox" name="viewed" value="viewed">
Viewed
</label>

View file

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

View file

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

View file

@ -62,7 +62,6 @@ export interface DiffFile extends DiffFileName {
isCopy?: boolean; isCopy?: boolean;
isRename?: boolean; isRename?: boolean;
isBinary?: boolean; isBinary?: boolean;
isTooBig?: boolean;
unchangedPercentage?: number; unchangedPercentage?: number;
changedPercentage?: number; changedPercentage?: number;
checksumBefore?: string | string[]; checksumBefore?: string | string[];
@ -70,30 +69,18 @@ export interface DiffFile extends DiffFileName {
mode?: string; mode?: string;
} }
export type OutputFormatType = 'line-by-line' | 'side-by-side'; export enum OutputFormatType {
LINE_BY_LINE = 'line-by-line',
export const OutputFormatType: { [_: string]: OutputFormatType } = { SIDE_BY_SIDE = 'side-by-side',
LINE_BY_LINE: 'line-by-line', }
SIDE_BY_SIDE: 'side-by-side',
}; export enum LineMatchingType {
LINES = 'lines',
export type LineMatchingType = 'lines' | 'words' | 'none'; WORDS = 'words',
NONE = 'none',
export const LineMatchingType: { [_: string]: LineMatchingType } = { }
LINES: 'lines',
WORDS: 'words', export enum DiffStyleType {
NONE: 'none', WORD = 'word',
}; CHAR = 'char',
export type DiffStyleType = 'word' | 'char';
export const DiffStyleType: { [_: string]: DiffStyleType } = {
WORD: 'word',
CHAR: 'char',
};
export enum ColorSchemeType {
AUTO = 'auto',
DARK = 'dark',
LIGHT = 'light',
} }

View file

@ -5,99 +5,15 @@
* *
*/ */
: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 { .d2h-wrapper {
text-align: left; text-align: left;
} }
.d2h-file-header { .d2h-file-header {
display: flex;
height: 35px; height: 35px;
padding: 5px 10px; padding: 5px 10px;
border-bottom: 1px solid var(--d2h-file-header-border-color); border-bottom: 1px solid #d8d8d8;
background-color: var(--d2h-file-header-bg-color); background-color: #f7f7f7;
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 { .d2h-file-stats {
@ -110,18 +26,18 @@
.d2h-lines-added { .d2h-lines-added {
text-align: right; text-align: right;
border: 1px solid var(--d2h-ins-border-color); border: 1px solid #b4e2b4;
border-radius: 5px 0 0 5px; border-radius: 5px 0 0 5px;
color: var(--d2h-ins-label-color); color: #399839;
padding: 2px; padding: 2px;
vertical-align: middle; vertical-align: middle;
} }
.d2h-lines-deleted { .d2h-lines-deleted {
text-align: left; text-align: left;
border: 1px solid var(--d2h-del-border-color); border: 1px solid #e9aeae;
border-radius: 0 5px 5px 0; border-radius: 0 5px 5px 0;
color: var(--d2h-del-label-color); color: #c33;
padding: 2px; padding: 2px;
vertical-align: middle; vertical-align: middle;
margin-left: 1px; margin-left: 1px;
@ -135,6 +51,7 @@
-ms-flex-align: center; -ms-flex-align: center;
align-items: center; align-items: center;
width: 100%; width: 100%;
font-family: 'Source Sans Pro', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 15px; font-size: 15px;
} }
@ -145,30 +62,11 @@
} }
.d2h-file-wrapper { .d2h-file-wrapper {
border: 1px solid var(--d2h-border-color); border: 1px solid #ddd;
border-radius: 3px; border-radius: 3px;
margin-bottom: 1em; 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 { .d2h-diff-table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
@ -177,31 +75,27 @@
} }
.d2h-files-diff { .d2h-files-diff {
display: flex; display: block;
width: 100%; width: 100%;
height: 100%;
} }
.d2h-file-diff { .d2h-file-diff {
overflow-y: hidden; overflow-y: hidden;
} }
.d2h-files-diff.d2h-d-none,
.d2h-file-diff.d2h-d-none {
display: none;
}
.d2h-file-side-diff { .d2h-file-side-diff {
display: inline-block; display: inline-block;
overflow-x: scroll; overflow-x: scroll;
overflow-y: hidden; overflow-y: hidden;
width: 50%; width: 50%;
margin-right: -4px;
margin-bottom: -8px;
} }
.d2h-code-line { .d2h-code-line {
display: inline-block; display: inline-block;
white-space: nowrap; white-space: nowrap;
user-select: none;
width: calc(100% - 16em);
/* Compensate for the absolute positioning of the line numbers */ /* Compensate for the absolute positioning of the line numbers */
padding: 0 8em; padding: 0 8em;
} }
@ -209,29 +103,16 @@
.d2h-code-side-line { .d2h-code-side-line {
display: inline-block; display: inline-block;
white-space: nowrap; white-space: nowrap;
user-select: none;
width: calc(100% - 9em);
/* Compensate for the absolute positioning of the line numbers */ /* Compensate for the absolute positioning of the line numbers */
padding: 0 4.5em; 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-line del,
.d2h-code-side-line del { .d2h-code-side-line del {
display: inline-block; display: inline-block;
margin-top: -1px; margin-top: -1px;
text-decoration: none; text-decoration: none;
background-color: var(--d2h-del-highlight-bg-color); background-color: #ffb6ba;
border-radius: 0.2em; border-radius: 0.2em;
} }
@ -240,7 +121,7 @@
display: inline-block; display: inline-block;
margin-top: -1px; margin-top: -1px;
text-decoration: none; text-decoration: none;
background-color: var(--d2h-ins-highlight-bg-color); background-color: #97f295;
border-radius: 0.2em; border-radius: 0.2em;
text-align: left; text-align: left;
} }
@ -253,6 +134,14 @@
white-space: pre; white-space: pre;
} }
.d2h-code-line-ctn {
display: inline;
background: none;
padding: 0;
word-wrap: normal;
white-space: pre;
}
.line-num1 { .line-num1 {
box-sizing: border-box; box-sizing: border-box;
float: left; float: left;
@ -277,10 +166,10 @@
/* Keep the numbers fixed on line contents scroll */ /* Keep the numbers fixed on line contents scroll */
position: absolute; position: absolute;
display: inline-block; display: inline-block;
background-color: var(--d2h-bg-color); background-color: #fff;
color: var(--d2h-dim-color); color: rgba(0, 0, 0, 0.3);
text-align: right; text-align: right;
border: solid var(--d2h-line-border-color); border: solid #eeeeee;
border-width: 0 1px 0 1px; border-width: 0 1px 0 1px;
cursor: pointer; cursor: pointer;
} }
@ -295,15 +184,14 @@
display: inline-block; display: inline-block;
box-sizing: border-box; box-sizing: border-box;
width: 4em; width: 4em;
background-color: var(--d2h-bg-color); background-color: #fff;
color: var(--d2h-dim-color); color: rgba(0, 0, 0, 0.3);
text-align: right; text-align: right;
border: solid var(--d2h-line-border-color); border: solid #eeeeee;
border-width: 0 1px 0 1px; border-width: 0 1px 0 1px;
cursor: pointer; cursor: pointer;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
padding: 0 0.5em 0 0.5em;
} }
.d2h-code-side-linenumber:after { .d2h-code-side-linenumber:after {
@ -312,20 +200,8 @@
.d2h-code-side-emptyplaceholder, .d2h-code-side-emptyplaceholder,
.d2h-emptyplaceholder { .d2h-emptyplaceholder {
background-color: var(--d2h-empty-placeholder-bg-color); background-color: #f1f1f1;
border-color: var(--d2h-empty-placeholder-border-color); border-color: #e1e1e1;
}
.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;
} }
/* /*
@ -333,27 +209,27 @@
*/ */
.d2h-del { .d2h-del {
background-color: var(--d2h-del-bg-color); background-color: #fee8e9;
border-color: var(--d2h-del-border-color); border-color: #e9aeae;
} }
.d2h-ins { .d2h-ins {
background-color: var(--d2h-ins-bg-color); background-color: #dfd;
border-color: var(--d2h-ins-border-color); border-color: #b4e2b4;
} }
.d2h-info { .d2h-info {
background-color: var(--d2h-info-bg-color); background-color: #f8fafd;
color: var(--d2h-dim-color); color: rgba(0, 0, 0, 0.3);
border-color: var(--d2h-info-border-color); border-color: #d5e4f2;
} }
.d2h-file-diff .d2h-del.d2h-change { .d2h-file-diff .d2h-del.d2h-change {
background-color: var(--d2h-change-del-color); background-color: #fdf2d0;
} }
.d2h-file-diff .d2h-ins.d2h-change { .d2h-file-diff .d2h-ins.d2h-change {
background-color: var(--d2h-change-ins-color); background-color: #ded;
} }
/* /*
@ -366,11 +242,11 @@
.d2h-file-list-wrapper a { .d2h-file-list-wrapper a {
text-decoration: none; text-decoration: none;
color: var(--d2h-moved-label-color); color: #3572b0;
} }
.d2h-file-list-wrapper a:visited { .d2h-file-list-wrapper a:visited {
color: var(--d2h-moved-label-color); color: #3572b0;
} }
.d2h-file-list-header { .d2h-file-list-header {
@ -396,7 +272,7 @@
} }
.d2h-file-list > li { .d2h-file-list > li {
border-bottom: var(--d2h-border-color) solid 1px; border-bottom: #ddd solid 1px;
padding: 5px 10px; padding: 5px 10px;
margin: 0; margin: 0;
} }
@ -418,19 +294,19 @@
} }
.d2h-deleted { .d2h-deleted {
color: var(--d2h-del-label-color); color: #c33;
} }
.d2h-added { .d2h-added {
color: var(--d2h-ins-label-color); color: #399839;
} }
.d2h-changed { .d2h-changed {
color: var(--d2h-change-label-color); color: #d0b44c;
} }
.d2h-moved { .d2h-moved {
color: var(--d2h-moved-label-color); color: #3572b0;
} }
.d2h-tag { .d2h-tag {
@ -440,302 +316,60 @@
font-size: 10px; font-size: 10px;
margin-left: 5px; margin-left: 5px;
padding: 0 2px; padding: 0 2px;
background-color: var(--d2h-bg-color); background-color: #fff;
} }
.d2h-deleted-tag { .d2h-deleted-tag {
border: var(--d2h-del-label-color) 1px solid; border: #c33 1px solid;
} }
.d2h-added-tag { .d2h-added-tag {
border: var(--d2h-ins-label-color) 1px solid; border: #399839 1px solid;
} }
.d2h-changed-tag { .d2h-changed-tag {
border: var(--d2h-change-label-color) 1px solid; border: #d0b44c 1px solid;
} }
.d2h-moved-tag { .d2h-moved-tag {
border: var(--d2h-moved-label-color) 1px solid; border: #3572b0 1px solid;
} }
/** /*
* Dark Mode Colors * Selection util.
*/ */
.d2h-dark-color-scheme { .selecting-left .d2h-code-line,
color: var(--d2h-dark-color); .selecting-left .d2h-code-line *,
background-color: var(--d2h-dark-bg-color); .selecting-right td.d2h-code-linenumber,
.selecting-right td.d2h-code-linenumber *,
.selecting-left .d2h-code-side-line,
.selecting-left .d2h-code-side-line *,
.selecting-right td.d2h-code-side-linenumber,
.selecting-right td.d2h-code-side-linenumber * {
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
} }
.d2h-dark-color-scheme .d2h-file-header { .selecting-left .d2h-code-line::-moz-selection,
background-color: var(--d2h-dark-file-header-bg-color); .selecting-left .d2h-code-line *::-moz-selection,
border-bottom: var(--d2h-dark-file-header-border-color); .selecting-right td.d2h-code-linenumber::-moz-selection,
.selecting-left .d2h-code-side-line::-moz-selection,
.selecting-left .d2h-code-side-line *::-moz-selection,
.selecting-right td.d2h-code-side-linenumber::-moz-selection,
.selecting-right td.d2h-code-side-linenumber *::-moz-selection {
background: transparent;
} }
.d2h-dark-color-scheme .d2h-lines-added { .selecting-left .d2h-code-line::selection,
border: 1px solid var(--d2h-dark-ins-border-color); .selecting-left .d2h-code-line *::selection,
color: var(--d2h-dark-ins-label-color); .selecting-right td.d2h-code-linenumber::selection,
} .selecting-left .d2h-code-side-line::selection,
.selecting-left .d2h-code-side-line *::selection,
.d2h-dark-color-scheme .d2h-lines-deleted { .selecting-right td.d2h-code-side-linenumber::selection,
border: 1px solid var(--d2h-dark-del-border-color); .selecting-right td.d2h-code-side-linenumber *::selection {
color: var(--d2h-dark-del-label-color); background: transparent;
}
.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

@ -1,22 +1,15 @@
import { closeTags, nodeStream, mergeStreams, getLanguage } from './highlight.js-helpers'; import { HighlightJS, ICompiledMode, IHighlightResult, IAutoHighlightResult } from './highlight.js-interface';
import { nodeStream, mergeStreams } from './highlight.js-helpers';
import { html, Diff2HtmlConfig, defaultDiff2HtmlConfig } from '../../diff2html'; import { html, Diff2HtmlConfig, defaultDiff2HtmlConfig } from '../../diff2html';
import { DiffFile } from '../../types'; import { DiffFile } from '../../types';
import { HighlightResult, HLJSApi } from 'highlight.js';
export interface Diff2HtmlUIConfig extends Diff2HtmlConfig { export interface Diff2HtmlUIConfig extends Diff2HtmlConfig {
synchronisedScroll?: boolean; synchronisedScroll?: boolean;
highlight?: boolean; highlight?: boolean;
fileListToggle?: boolean; fileListToggle?: boolean;
fileListStartVisible?: 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; smartSelection?: boolean;
fileContentToggle?: boolean;
stickyFileHeaders?: boolean;
} }
export const defaultDiff2HtmlUIConfig = { export const defaultDiff2HtmlUIConfig = {
@ -25,49 +18,38 @@ export const defaultDiff2HtmlUIConfig = {
highlight: true, highlight: true,
fileListToggle: true, fileListToggle: true,
fileListStartVisible: false, 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, smartSelection: true,
fileContentToggle: true,
stickyFileHeaders: true,
}; };
export class Diff2HtmlUI { export class Diff2HtmlUI {
readonly config: typeof defaultDiff2HtmlUIConfig; readonly config: typeof defaultDiff2HtmlUIConfig;
readonly diffHtml: string; readonly diffHtml: string;
readonly targetElement: HTMLElement; readonly targetElement: HTMLElement;
readonly hljs: HLJSApi | null = null; readonly hljs: HighlightJS | null = null;
currentSelectionColumnId = -1; currentSelectionColumnId = -1;
constructor(target: HTMLElement, diffInput?: string | DiffFile[], config: Diff2HtmlUIConfig = {}, hljs?: HLJSApi) { constructor(diffInput: string | DiffFile[], target: HTMLElement, config: Diff2HtmlUIConfig = {}, hljs?: HighlightJS) {
this.config = { ...defaultDiff2HtmlUIConfig, ...config }; this.config = { ...defaultDiff2HtmlUIConfig, ...config };
this.diffHtml = diffInput !== undefined ? html(diffInput, this.config) : target.innerHTML; this.diffHtml = html(diffInput, this.config);
this.targetElement = target; this.targetElement = target;
if (hljs !== undefined) this.hljs = hljs; if (hljs !== undefined) this.hljs = hljs;
} }
draw(): void { draw(): void {
this.targetElement.innerHTML = this.diffHtml; this.targetElement.innerHTML = this.diffHtml;
if (this.config.smartSelection) this.initSelection();
if (this.config.synchronisedScroll) this.synchronisedScroll(); if (this.config.synchronisedScroll) this.synchronisedScroll();
if (this.config.highlight) this.highlightCode(); if (this.config.highlight) this.highlightCode();
if (this.config.fileListToggle) this.fileListToggle(this.config.fileListStartVisible); if (this.config.fileListToggle) this.fileListToggle(this.config.fileListStartVisible);
if (this.config.fileContentToggle) this.fileContentToggle();
if (this.config.stickyFileHeaders) this.stickyFileHeaders();
} }
synchronisedScroll(): void { synchronisedScroll(): void {
this.targetElement.querySelectorAll('.d2h-file-wrapper').forEach(wrapper => { this.targetElement.querySelectorAll('.d2h-file-wrapper').forEach(wrapper => {
const [left, right] = Array<Element>().slice.call(wrapper.querySelectorAll('.d2h-file-side-diff')); const [left, right] = [].slice.call(wrapper.querySelectorAll('.d2h-file-side-diff')) as HTMLElement[];
if (left === undefined || right === undefined) return; if (left === undefined || right === undefined) return;
const onScroll = (event: Event): void => { const onScroll = (event: Event): void => {
if (event === null || event.target === null) return; if (event === null || event.target === null) return;
if (event.target === left) { if (event.target === left) {
right.scrollTop = left.scrollTop; right.scrollTop = left.scrollTop;
right.scrollLeft = left.scrollLeft; right.scrollLeft = left.scrollLeft;
@ -82,101 +64,75 @@ export class Diff2HtmlUI {
} }
fileListToggle(startVisible: boolean): void { fileListToggle(startVisible: boolean): void {
const showBtn: HTMLElement | null = this.targetElement.querySelector('.d2h-show'); const hashTag = this.getHashTag();
const hideBtn: HTMLElement | null = this.targetElement.querySelector('.d2h-hide');
const fileList: HTMLElement | null = this.targetElement.querySelector('.d2h-file-list'); const showBtn = this.targetElement.querySelector('.d2h-show') as HTMLElement;
const hideBtn = this.targetElement.querySelector('.d2h-hide') as HTMLElement;
const fileList = this.targetElement.querySelector('.d2h-file-list') as HTMLElement;
if (showBtn === null || hideBtn === null || fileList === null) return; if (showBtn === null || hideBtn === null || fileList === null) return;
const show: () => void = () => { function show(): void {
showBtn.style.display = 'none'; showBtn.style.display = 'none';
hideBtn.style.display = 'inline'; hideBtn.style.display = 'inline';
fileList.style.display = 'block'; fileList.style.display = 'block';
}; }
const hide: () => void = () => { function hide(): void {
showBtn.style.display = 'inline'; showBtn.style.display = 'inline';
hideBtn.style.display = 'none'; hideBtn.style.display = 'none';
fileList.style.display = 'none'; fileList.style.display = 'none';
}; }
showBtn.addEventListener('click', () => show()); showBtn.addEventListener('click', () => show());
hideBtn.addEventListener('click', () => hide()); hideBtn.addEventListener('click', () => hide());
const hashTag = this.getHashTag();
if (hashTag === 'files-summary-show') show(); if (hashTag === 'files-summary-show') show();
else if (hashTag === 'files-summary-hide') hide(); else if (hashTag === 'files-summary-hide') hide();
else if (startVisible) show(); else if (startVisible) show();
else hide(); 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 { highlightCode(): void {
const hljs = this.hljs; if (this.hljs === null) {
if (hljs === null) {
throw new Error('Missing a `highlight.js` implementation. Please provide one when instantiating Diff2HtmlUI.'); 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 // Collect all the diff files and execute the highlight on their lines
const files = this.targetElement.querySelectorAll('.d2h-file-wrapper'); const files = this.targetElement.querySelectorAll('.d2h-file-wrapper');
files.forEach(file => { files.forEach(file => {
const language = file.getAttribute('data-lang'); let oldLinesState: ICompiledMode;
let newLinesState: ICompiledMode;
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 // Collect all the code lines and execute the highlight on them
const codeLines = file.querySelectorAll('.d2h-code-line-ctn'); const codeLines = file.querySelectorAll('.d2h-code-line-ctn');
codeLines.forEach(line => { codeLines.forEach(line => {
// HACK: help Typescript know that `this.hljs` is defined since we already checked it
if (this.hljs === null) return;
const text = line.textContent; const text = line.textContent;
const lineParent = line.parentNode; const lineParent = line.parentNode as HTMLElement;
if (text === null || lineParent === null || !this.isElement(lineParent)) return; if (lineParent === null || text === null) return;
const result: HighlightResult = closeTags( const lineState = lineParent.className.indexOf('d2h-del') !== -1 ? oldLinesState : newLinesState;
hljs.highlight(text, {
language: hljsLanguage, const language = file.getAttribute('data-lang');
ignoreIllegals: true, const result =
}), language && this.hljs.getLanguage(language)
); ? this.hljs.highlight(language, text, true, lineState)
: this.hljs.highlightAuto(text);
if (this.instanceOfIHighlightResult(result)) {
if (lineParent.className.indexOf('d2h-del') !== -1) {
oldLinesState = result.top;
} else if (lineParent.className.indexOf('d2h-ins') !== -1) {
newLinesState = result.top;
} else {
oldLinesState = result.top;
newLinesState = result.top;
}
}
const originalStream = nodeStream(line); const originalStream = nodeStream(line);
if (originalStream.length) { if (originalStream.length) {
@ -186,25 +142,14 @@ export class Diff2HtmlUI {
} }
line.classList.add('hljs'); line.classList.add('hljs');
if (result.language) { line.classList.add('result.language');
line.classList.add(result.language);
}
line.innerHTML = result.value; line.innerHTML = result.value;
}); });
}); });
} }
stickyFileHeaders(): void { private instanceOfIHighlightResult(object: IHighlightResult | IAutoHighlightResult): object is IHighlightResult {
this.targetElement.querySelectorAll('.d2h-file-header').forEach(header => { return 'top' in object;
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 { private getHashTag(): string | null {
@ -219,7 +164,65 @@ export class Diff2HtmlUI {
return hashTag; return hashTag;
} }
private isElement(arg?: unknown): arg is Element { private initSelection(): void {
return arg !== null && (arg as Element)?.classList !== undefined; const body = document.getElementsByTagName('body')[0];
const diffTable = body.getElementsByClassName('d2h-diff-table')[0];
diffTable.addEventListener('mousedown', event => {
if (event === null || event.target === null) return;
const mouseEvent = event as MouseEvent;
const target = mouseEvent.target as HTMLElement;
const table = target.closest('.d2h-diff-table');
if (table !== null) {
if (target.closest('.d2h-code-line,.d2h-code-side-line') !== null) {
table.classList.remove('selecting-left');
table.classList.add('selecting-right');
this.currentSelectionColumnId = 1;
} else if (target.closest('.d2h-code-linenumber,.d2h-code-side-linenumber') !== null) {
table.classList.remove('selecting-right');
table.classList.add('selecting-left');
this.currentSelectionColumnId = 0;
}
}
});
diffTable.addEventListener('copy', event => {
const clipboardEvent = event as ClipboardEvent;
const clipboardData = clipboardEvent.clipboardData;
const text = this.getSelectedText();
if (clipboardData === null || text === undefined) return;
clipboardData.setData('text', text);
event.preventDefault();
});
}
private getSelectedText(): string | undefined {
const sel = window.getSelection();
if (sel === null) return;
const range = sel.getRangeAt(0);
const doc = range.cloneContents();
const nodes = doc.querySelectorAll('tr');
const idx = this.currentSelectionColumnId;
let text = '';
if (nodes.length === 0) {
text = doc.textContent || '';
} else {
nodes.forEach((tr, i) => {
const td = tr.cells[tr.cells.length === 1 ? 0 : idx];
if (td === undefined || td.textContent === null) return;
text += (i ? '\n' : '') + td.textContent.replace(/\r\n|\r|\n/g, '');
});
}
return text;
} }
} }

View file

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

View file

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

View file

@ -3,12 +3,13 @@
* Used to highlight selected html elements using context * Used to highlight selected html elements using context
*/ */
import { HighlightResult } from 'highlight.js';
/* Utility functions */ /* Utility functions */
function escapeHTML(value: string): string { function escape(value: string): string {
return value.replace(/&/gm, '&amp;').replace(/</gm, '&lt;').replace(/>/gm, '&gt;'); return value
.replace(/&/gm, '&amp;')
.replace(/</gm, '&lt;')
.replace(/>/gm, '&gt;');
} }
function tag(node: Node): string { function tag(node: Node): string {
@ -62,10 +63,6 @@ export function mergeStreams(original: NodeEvent[], highlighted: NodeEvent[], va
let result = ''; let result = '';
const nodeStack = []; const nodeStack = [];
function isElement(arg?: unknown): arg is Element {
return arg !== null && (arg as Element)?.attributes !== undefined;
}
function selectStream(): NodeEvent[] { function selectStream(): NodeEvent[] {
if (!original.length || !highlighted.length) { if (!original.length || !highlighted.length) {
return original.length ? original : highlighted; return original.length ? original : highlighted;
@ -91,12 +88,9 @@ export function mergeStreams(original: NodeEvent[], highlighted: NodeEvent[], va
} }
function open(node: Node): void { function open(node: Node): void {
if (!isElement(node)) { const htmlNode = node as HTMLElement;
throw new Error('Node is not an Element'); result += `<${tag(node)} ${[].map
} .call(htmlNode.attributes, (attr: Attr) => `${attr.nodeName}="${escape(attr.value)}"`)
result += `<${tag(node)} ${Array<Attr>()
.map.call(node.attributes, attr => `${attr.nodeName}="${escapeHTML(attr.value).replace(/"/g, '&quot;')}"`)
.join(' ')}>`; .join(' ')}>`;
} }
@ -110,7 +104,7 @@ export function mergeStreams(original: NodeEvent[], highlighted: NodeEvent[], va
while (original.length || highlighted.length) { while (original.length || highlighted.length) {
let stream = selectStream(); let stream = selectStream();
result += escapeHTML(value.substring(processed, stream[0].offset)); result += escape(value.substring(processed, stream[0].offset));
processed = stream[0].offset; processed = stream[0].offset;
if (stream === original) { if (stream === original) {
/* /*
@ -134,522 +128,5 @@ export function mergeStreams(original: NodeEvent[], highlighted: NodeEvent[], va
render(stream.splice(0, 1)[0]); render(stream.splice(0, 1)[0]);
} }
} }
return result + escape(value.substr(processed));
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,69 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable @typescript-eslint/interface-name-prefix */
/* eslint-disable @typescript-eslint/camelcase */
export interface HighlightJS {
highlight(name: string, value: string, ignore_illegals?: boolean, continuation?: ICompiledMode): IHighlightResult;
highlightAuto(value: string, languageSubset?: string[]): IAutoHighlightResult;
getLanguage(name: string): IMode;
}
export interface IAutoHighlightResult extends IHighlightResultBase {
second_best?: IAutoHighlightResult;
}
export interface IHighlightResultBase {
relevance: number;
language: string;
value: string;
}
export interface IHighlightResult extends IHighlightResultBase {
top: ICompiledMode;
}
export interface IMode extends IModeBase {
keywords?: any;
contains?: IMode[];
}
// Reference:
// https://github.com/isagalaev/highlight.js/blob/master/docs/reference.rst
export interface IModeBase {
className?: string;
aliases?: string[];
begin?: string | RegExp;
end?: string | RegExp;
case_insensitive?: boolean;
beginKeyword?: string;
endsWithParent?: boolean;
lexems?: string;
illegal?: string;
excludeBegin?: boolean;
excludeEnd?: boolean;
returnBegin?: boolean;
returnEnd?: boolean;
starts?: string;
subLanguage?: string;
subLanguageMode?: string;
relevance?: number;
variants?: IMode[];
}
export interface ICompiledMode extends IModeBase {
compiled: boolean;
contains?: ICompiledMode[];
keywords?: Object;
terminators: RegExp;
terminator_end?: string;
}
export interface IOptions {
classPrefix?: string;
tabReplace?: string;
useBR?: boolean;
languages?: string[];
}

View file

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

View file

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

View file

@ -1,37 +1,28 @@
{ {
"include": ["src/**/*", "typings/**/*"],
"exclude": ["node_modules", "src/__tests__/**"],
"compilerOptions": { "compilerOptions": {
"outDir": "bundles-out", "outDir": "./_target",
"module": "CommonJS", "target": "es5",
"moduleResolution": "Node", "module": "commonjs",
"target": "ES6", "moduleResolution": "node",
"lib": ["ES6", "DOM"], "lib": ["es2015", "dom"],
"jsx": "preserve",
"allowJs": false, "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, "declaration": true,
"declarationMap": true, "declarationMap": true,
"sourceMap": true,
"noEmit": false,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"incremental": false,
"strictNullChecks": true, "strictNullChecks": true,
"removeComments": true, "removeComments": true,
"preserveConstEnums": true, "preserveConstEnums": true,
"sourceMap": true,
"alwaysStrict": true, "alwaysStrict": true,
"strict": true,
"noImplicitAny": true, "noImplicitAny": true,
"noImplicitThis": true "noImplicitThis": true,
} "noUnusedParameters": true,
"noUnusedLocals": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true
},
"include": ["./src/**/*", "./typings/**/*"],
"exclude": ["node_modules", "./src/__tests__/*"]
} }

View file

@ -3,52 +3,17 @@ import path from 'path';
import webpack from 'webpack'; import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin'; import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin'; import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import CopyPlugin from 'copy-webpack-plugin'; import CopyWebpackPlugin from 'copy-webpack-plugin';
const pages = ['index', 'demo']; 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 => { const config: webpack.Configuration[] = pages.map(page => {
return { return {
devServer: {
port: 3000,
open: true,
contentBase: path.join(__dirname, './website'),
},
entry: { entry: {
[page]: `./website/templates/pages/${page}/${page}.ts`, [page]: `./website/templates/pages/${page}/${page}.ts`,
}, },
@ -65,6 +30,21 @@ const config: webpack.Configuration[] = pages.map(page => {
use: 'ts-loader', use: 'ts-loader',
exclude: /node_modules/, exclude: /node_modules/,
}, },
{
test: /\.handlebars$/,
loader: 'handlebars-loader',
options: {
precompileOptions: {
knownHelpersOnly: false,
},
helperDirs: [path.join(__dirname, 'website/templates/helpers')],
partialDirs: [path.join(__dirname, 'website/templates')],
},
},
{
test: /\.(css)$/,
use: [MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 1 } }, 'postcss-loader'],
},
{ {
test: /\.(html)$/, test: /\.(html)$/,
use: { use: {
@ -75,26 +55,30 @@ const config: webpack.Configuration[] = pages.map(page => {
}, },
}, },
{ {
test: /\.handlebars$/, test: /\.woff(2)?(\?v=\d\.\d\.\d)?$/,
loader: 'handlebars-loader', use: [
options: { {
inlineRequires: '/images/', loader: 'url-loader',
precompileOptions: { options: {
knownHelpersOnly: false, limit: 1000,
mimetype: 'application/font-woff',
},
}, },
helperDirs: [path.join(__dirname, 'website/templates/helpers')], ],
partialDirs: [path.join(__dirname, 'website/templates')],
},
}, },
{ {
test: /\.(gif|png|jpe?g|webp)$/i, test: /\.(ttf|eot|svg)(\?v=\d\.\d\.\d)?$/,
loader: 'file-loader',
},
{
test: /\.(jpeg|jpg|png|gif)$/,
use: [ use: [
{ {
loader: 'file-loader', loader: 'file-loader',
options: { options: {
name: '[name].[ext]?[hash]', name: '[name].[ext]',
outputPath: 'images', outputPath: 'images/',
esModule: false, useRelativePath: true,
}, },
}, },
{ {
@ -121,29 +105,41 @@ const config: webpack.Configuration[] = pages.map(page => {
}, },
], ],
}, },
{
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), plugins: [
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 CopyWebpackPlugin([
{ from: 'website/favicon.ico', to: 'favicon.ico' },
{ from: 'website/robots.txt', to: 'robots.txt' },
{ from: 'website/sitemap.xml', to: 'sitemap.xml' },
]),
],
}; };
}); });

View file

@ -2,7 +2,7 @@
<div class="hero-body"> <div class="hero-body">
<div class="container"> <div class="container">
<h1 class="title is-size-1 is-spaced">Demo<a href="#help"> <h1 class="title is-size-1 is-spaced">Demo<a href="#help">
<svg fill="currentColor" height="32" class="octicon octicon-unverified" viewBox="0 0 16 16" version="1.1" width="64" <svg height="32" class="octicon octicon-unverified" viewBox="0 0 16 16" version="1.1" width="64"
aria-hidden="true"> aria-hidden="true">
<path <path
d="M15.67 7.06l-1.08-1.34c-.17-.22-.28-.48-.31-.77l-.19-1.7a1.51 1.51 0 0 0-1.33-1.33l-1.7-.19c-.3-.03-.56-.16-.78-.33L8.94.32c-.55-.44-1.33-.44-1.88 0L5.72 1.4c-.22.17-.48.28-.77.31l-1.7.19c-.7.08-1.25.63-1.33 1.33l-.19 1.7c-.03.3-.16.56-.33.78L.32 7.05c-.44.55-.44 1.33 0 1.88l1.08 1.34c.17.22.28.48.31.77l.19 1.7c.08.7.63 1.25 1.33 1.33l1.7.19c.3.03.56.16.78.33l1.34 1.08c.55.44 1.33.44 1.88 0l1.34-1.08c.22-.17.48-.28.77-.31l1.7-.19c.7-.08 1.25-.63 1.33-1.33l.19-1.7c.03-.3.16-.56.33-.78l1.08-1.34c.44-.55.44-1.33 0-1.88zM9 11.5c0 .28-.22.5-.5.5h-1c-.27 0-.5-.22-.5-.5v-1c0-.28.23-.5.5-.5h1c.28 0 .5.22.5.5v1zm1.56-4.89c-.06.17-.17.33-.3.47-.13.16-.14.19-.33.38-.16.17-.31.3-.52.45-.11.09-.2.19-.28.27-.08.08-.14.17-.19.27-.05.1-.08.19-.11.3-.03.11-.03.13-.03.25H7.13c0-.22 0-.31.03-.48.03-.19.08-.36.14-.52.06-.14.14-.28.25-.42.11-.13.23-.25.41-.38.27-.19.36-.3.48-.52.12-.22.2-.38.2-.59 0-.27-.06-.45-.2-.58-.13-.13-.31-.19-.58-.19-.09 0-.19.02-.3.05-.11.03-.17.09-.25.16-.08.07-.14.11-.2.2a.41.41 0 0 0-.09.28h-2c0-.38.13-.56.27-.83.16-.27.36-.5.61-.67.25-.17.55-.3.88-.38.33-.08.7-.13 1.09-.13.44 0 .83.05 1.17.13.34.09.63.22.88.39.23.17.41.38.55.63.13.25.19.55.19.88 0 .22 0 .42-.08.59l-.02-.01z"> d="M15.67 7.06l-1.08-1.34c-.17-.22-.28-.48-.31-.77l-.19-1.7a1.51 1.51 0 0 0-1.33-1.33l-1.7-.19c-.3-.03-.56-.16-.78-.33L8.94.32c-.55-.44-1.33-.44-1.88 0L5.72 1.4c-.22.17-.48.28-.77.31l-1.7.19c-.7.08-1.25.63-1.33 1.33l-.19 1.7c-.03.3-.16.56-.33.78L.32 7.05c-.44.55-.44 1.33 0 1.88l1.08 1.34c.17.22.28.48.31.77l.19 1.7c.08.7.63 1.25 1.33 1.33l1.7.19c.3.03.56.16.78.33l1.34 1.08c.55.44 1.33.44 1.88 0l1.34-1.08c.22-.17.48-.28.77-.31l1.7-.19c.7-.08 1.25-.63 1.33-1.33l.19-1.7c.03-.3.16-.56.33-.78l1.08-1.34c.44-.55.44-1.33 0-1.88zM9 11.5c0 .28-.22.5-.5.5h-1c-.27 0-.5-.22-.5-.5v-1c0-.28.23-.5.5-.5h1c.28 0 .5.22.5.5v1zm1.56-4.89c-.06.17-.17.33-.3.47-.13.16-.14.19-.33.38-.16.17-.31.3-.52.45-.11.09-.2.19-.28.27-.08.08-.14.17-.19.27-.05.1-.08.19-.11.3-.03.11-.03.13-.03.25H7.13c0-.22 0-.31.03-.48.03-.19.08-.36.14-.52.06-.14.14-.28.25-.42.11-.13.23-.25.41-.38.27-.19.36-.3.48-.52.12-.22.2-.38.2-.59 0-.27-.06-.45-.2-.58-.13-.13-.31-.19-.58-.19-.09 0-.19.02-.3.05-.11.03-.17.09-.25.16-.08.07-.14.11-.2.2a.41.41 0 0 0-.09.28h-2c0-.38.13-.56.27-.83.16-.27.36-.5.61-.67.25-.17.55-.3.88-.38.33-.08.7-.13 1.09-.13.44 0 .83.05 1.17.13.34.09.63.22.88.39.23.17.41.38.55.63.13.25.19.55.19.88 0 .22 0 .42-.08.59l-.02-.01z">
@ -33,7 +33,7 @@
</div> </div>
<div class="columns is-desktop is-multiline"> <div class="columns is-desktop is-multiline">
<div class="column is-one-sixth-widescreen"> <div class="column is-one-fifth-widescreen">
<label title="Output format of the HTML, either line by line or side by side"> <label title="Output format of the HTML, either line by line or side by side">
<p>Output Format</p> <p>Output Format</p>
<select class="options-label-value" id="diff-url-options-output-format" name="outputFormat"> <select class="options-label-value" id="diff-url-options-output-format" name="outputFormat">
@ -42,24 +42,14 @@
</select> </select>
</label> </label>
</div> </div>
<div class="column is-one-sixth-widescreen"> <div class="column is-one-fifth-widescreen">
<label title="Color scheme to render. Auto uses user preference.">
<p>Color Scheme</p>
<select class="options-label-value" id="diff-url-options-color-scheme" name="colorScheme">
<option value="light" selected>Light</option>
<option value="dark">Dark</option>
<option value="auto">Auto</option>
</select>
</label>
</div>
<div class="column is-one-sixth-widescreen">
<label title="Show the file list summary before the diff"> <label title="Show the file list summary before the diff">
<p>File Summary</p> <p>File Summary</p>
<input class="options-label-value" id="diff-url-options-show-files" type="checkbox" name="drawFileList" <input class="options-label-value" id="diff-url-options-show-files" type="checkbox" name="drawFileList"
checked /> checked />
</label> </label>
</div> </div>
<div class="column is-one-sixth-widescreen"> <div class="column is-one-fifth-widescreen">
<label title="Level of matching for the comparison algorithm"> <label title="Level of matching for the comparison algorithm">
<p>Matching Type</p> <p>Matching Type</p>
<select class="options-label-value" id="diff-url-options-matching" name="matching"> <select class="options-label-value" id="diff-url-options-matching" name="matching">
@ -69,14 +59,14 @@
</select> </select>
</label> </label>
</div> </div>
<div class="column is-one-sixth-widescreen"> <div class="column is-one-fifth-widescreen">
<label title="Similarity threshold for the matching algorithm"> <label title="Similarity threshold for the matching algorithm">
<p>Words Threshold</p> <p>Words Threshold</p>
<input class="options-label-value" id="diff-url-options-match-words-threshold" type="number" <input class="options-label-value" id="diff-url-options-match-words-threshold" type="number"
name="matchWordsThreshold" value="0.25" step="0.05" min="0" max="1" /> name="matchWordsThreshold" value="0.25" step="0.05" min="0" max="1" />
</label> </label>
</div> </div>
<div class="column is-one-sixth-widescreen"> <div class="column is-one-fifth-widescreen">
<label title="Maximum number of comparison performed by the matching algorithm in a block of changes"> <label title="Maximum number of comparison performed by the matching algorithm in a block of changes">
<p>Max Comparisons</p> <p>Max Comparisons</p>
<input class="options-label-value" id="diff-url-options-matching-max-comparisons" type="number" <input class="options-label-value" id="diff-url-options-matching-max-comparisons" type="number"
@ -99,7 +89,7 @@
<ul> <ul>
<li class="block"> <li class="block">
<p class="has-text-weight-bold">Why should I use this instead of GitHub, Bitbucket or GitLab?</p> <p class="has-text-weight-bold">Why should I use this instead of GitHub, Bitbucket or GitLab?</p>
<p>There are multiple advantages to using diff2html, but they can be divided in two categories: portability and <p>There are multiple advantages to using diff2html, but they can be devided in two categories: portability and
features. features.
</p> </p>
<p> <p>
@ -110,7 +100,7 @@
Regarding features you can have code syntax highlight, line similarity matching (similar lines are together), Regarding features you can have code syntax highlight, line similarity matching (similar lines are together),
line by Line and side by side diffs and easy code selection. line by Line and side by side diffs and easy code selection.
</p> </p>
<p>All of this is completely independent of the provider you use to version the code.</p> <p>All of this is completly independent of the provider you use to version the code.</p>
</li> </li>
<li class="block"> <li class="block">
<p class="has-text-weight-bold">What urls are supported in this demo?</p> <p class="has-text-weight-bold">What urls are supported in this demo?</p>
@ -122,7 +112,7 @@
<p>Just copy the url from the page, which should contain all the customizations and reference for the diff you <p>Just copy the url from the page, which should contain all the customizations and reference for the diff you
introduced.</p> introduced.</p>
<p>ex: <a <p>ex: <a
href="demo.html?matching=none&matchWordsThreshold=0.25&maxLineLengthHighlight=10000&diffStyle=word&renderNothingWhenEmpty=0&matchingMaxComparisons=2500&maxLineSizeInBlockForComparison=200&outputFormat=line-by-line&drawFileList=1&synchronisedScroll=1&highlight=1&fileListToggle=1&fileListStartVisible=0&diff=https://github.com/rtfpessoa/diff2html/pull/106">https://diff2html.xyz/demo.html?matching=none&matchWordsThreshold=0.25&maxLineLengthHighlight=10000&diffStyle=word&renderNothingWhenEmpty=0&matchingMaxComparisons=2500&maxLineSizeInBlockForComparison=200&outputFormat=line-by-line&drawFileList=1&synchronisedScroll=1&highlight=1&fileListToggle=1&fileListStartVisible=0&diff=https://github.com/rtfpessoa/diff2html/pull/106</a> href="demo.html?matching=none&matchWordsThreshold=0.25&maxLineLengthHighlight=10000&diffStyle=word&renderNothingWhenEmpty=0&matchingMaxComparisons=2500&maxLineSizeInBlockForComparison=200&outputFormat=line-by-line&drawFileList=1&synchronisedScroll=1&highlight=1&fileListToggle=1&fileListStartVisible=0&smartSelection=1&diff=https://github.com/rtfpessoa/diff2html/pull/106">https://diff2html.xyz/demo.html?matching=none&matchWordsThreshold=0.25&maxLineLengthHighlight=10000&diffStyle=word&renderNothingWhenEmpty=0&matchingMaxComparisons=2500&maxLineSizeInBlockForComparison=200&outputFormat=line-by-line&drawFileList=1&synchronisedScroll=1&highlight=1&fileListToggle=1&fileListStartVisible=0&smartSelection=1&diff=https://github.com/rtfpessoa/diff2html/pull/106</a>
</p> </p>
</li> </li>
<li class="block"> <li class="block">
@ -144,4 +134,4 @@
providing better diff support for existing online services. providing better diff support for existing online services.
</p> </p>
</div> </div>
</section> </section>

View file

@ -5,21 +5,3 @@
.diff-url-btn.is-small { .diff-url-btn.is-small {
font-size: 0.85rem; font-size: 0.85rem;
} }
.d2h-dark-color-scheme .title {
color: #fff;
}
.d2h-dark-color-scheme .footer {
background-color: #2d2d2d;
}
@media (prefers-color-scheme: dark) {
.d2h-auto-color-scheme .title {
color: #fff;
}
.d2h-auto-color-scheme .footer {
background-color: #2d2d2d;
}
}

View file

@ -2,11 +2,9 @@ import { Diff2HtmlUI, defaultDiff2HtmlUIConfig, Diff2HtmlUIConfig } from '../../
import '../../../main.ts'; import '../../../main.ts';
import '../../../main.css'; import '../../../main.css';
import './github-highlights.css'; import 'highlight.js/styles/github.css';
import '../../../../src/ui/css/diff2html.css'; import '../../../../src/ui/css/diff2html.css';
import './demo.css'; import './demo.css';
import { colorSchemeToCss } from '../../../../src/render-utils';
import { ColorSchemeType } from '../../../../src/types';
/* /*
* Example URLs: * Example URLs:
@ -23,8 +21,7 @@ import { ColorSchemeType } from '../../../../src/types';
type URLParams = { type URLParams = {
diff?: string; diff?: string;
diffTooBigMessage?: string; [key: string]: string | boolean | number | undefined;
[key: string]: string | boolean | number | Map<string, string> | undefined;
}; };
const searchParam = 'diff'; const searchParam = 'diff';
@ -67,14 +64,11 @@ function prepareRequest(url: string): Request {
let fetchUrl; let fetchUrl;
const headers = new Headers(); const headers = new Headers();
const githubCommitUrl = const githubCommitUrl = /^https?:\/\/(?:www\.)?github\.com\/(.*?)\/(.*?)\/commit\/(.*?)(?:\.diff)?(?:\.patch)?(?:\/.*)?$/;
/^https?:\/\/(?:www\.)?github\.com\/(.*?)\/(.*?)\/commit\/(.*?)(?:\.diff)?(?:\.patch)?(?:\/.*)?$/;
const githubPrUrl = /^https?:\/\/(?:www\.)?github\.com\/(.*?)\/(.*?)\/pull\/(.*?)(?:\.diff)?(?:\.patch)?(?:\/.*)?$/; const githubPrUrl = /^https?:\/\/(?:www\.)?github\.com\/(.*?)\/(.*?)\/pull\/(.*?)(?:\.diff)?(?:\.patch)?(?:\/.*)?$/;
const gitlabCommitUrl = const gitlabCommitUrl = /^https?:\/\/(?:www\.)?gitlab\.com\/(.*?)\/(.*?)\/commit\/(.*?)(?:\.diff)?(?:\.patch)?(?:\/.*)?$/;
/^https?:\/\/(?:www\.)?gitlab\.com\/(.*?)\/(.*?)\/commit\/(.*?)(?:\.diff)?(?:\.patch)?(?:\/.*)?$/; const gitlabPrUrl = /^https?:\/\/(?:www\.)?gitlab\.com\/(.*?)\/(.*?)\/merge_requests\/(.*?)(?:\.diff)?(?:\.patch)?(?:\/.*)?$/;
const gitlabPrUrl =
/^https?:\/\/(?:www\.)?gitlab\.com\/(.*?)\/(.*?)\/merge_requests\/(.*?)(?:\.diff)?(?:\.patch)?(?:\/.*)?$/;
const bitbucketCommitUrl = /^https?:\/\/(?:www\.)?bitbucket\.org\/(.*?)\/(.*?)\/commits\/(.*?)(?:\/raw)?(?:\/.*)?$/; const bitbucketCommitUrl = /^https?:\/\/(?:www\.)?bitbucket\.org\/(.*?)\/(.*?)\/commits\/(.*?)(?:\/raw)?(?:\/.*)?$/;
const bitbucketPrUrl = /^https?:\/\/(?:www\.)?bitbucket\.org\/(.*?)\/(.*?)\/pull-requests\/(.*?)(?:\/.*)?$/; const bitbucketPrUrl = /^https?:\/\/(?:www\.)?bitbucket\.org\/(.*?)\/(.*?)\/pull-requests\/(.*?)(?:\/.*)?$/;
@ -112,7 +106,8 @@ function prepareRequest(url: string): Request {
} else if ((values = bitbucketPrUrl.exec(url))) { } else if ((values = bitbucketPrUrl.exec(url))) {
fetchUrl = bitbucketUrlGen(values[1], values[2], 'pullrequests', values[3]); fetchUrl = bitbucketUrlGen(values[1], values[2], 'pullrequests', values[3]);
} else { } else {
fetchUrl = url; console.info('Could not parse url, using the provided url.');
fetchUrl = 'https://crossorigin.me/' + url;
} }
return { return {
@ -122,17 +117,11 @@ function prepareRequest(url: string): Request {
} }
function getConfiguration(urlParams: URLParams): Diff2HtmlUIConfig { function getConfiguration(urlParams: URLParams): Diff2HtmlUIConfig {
// Removing `diff` and `diffTooBigMessage` form `urlParams` to avoid being inserted // Removing `diff` form `urlParams` to avoid being inserted
const { diff, diffTooBigMessage, ...urlParamsRest } = urlParams; // eslint-disable-next-line @typescript-eslint/no-unused-vars
const { diff, ...urlParamsRest } = urlParams;
const defaultColorScheme: ColorSchemeType =
window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
? ColorSchemeType.DARK
: ColorSchemeType.LIGHT;
const config: URLParams = { const config: URLParams = {
...defaultDiff2HtmlUIConfig, ...defaultDiff2HtmlUIConfig,
colorScheme: defaultColorScheme,
...urlParamsRest, ...urlParamsRest,
}; };
@ -140,8 +129,8 @@ function getConfiguration(urlParams: URLParams): Diff2HtmlUIConfig {
const newObject = !Number.isNaN(Number(v)) const newObject = !Number.isNaN(Number(v))
? { [k]: Number(v) } ? { [k]: Number(v) }
: v === 'true' || v === 'false' : v === 'true' || v === 'false'
? { [k]: Boolean(v) } ? { [k]: Boolean(v) }
: { [k]: v }; : { [k]: v };
return { ...object, ...newObject }; return { ...object, ...newObject };
}, {}); }, {});
} }
@ -162,17 +151,10 @@ async function getDiff(request: Request): Promise<string> {
} }
function draw(diffString: string, config: Diff2HtmlUIConfig, elements: Elements): void { function draw(diffString: string, config: Diff2HtmlUIConfig, elements: Elements): void {
const diff2htmlUi = new Diff2HtmlUI(elements.structure.diffTarget, diffString, config); const diff2htmlUi = new Diff2HtmlUI(diffString, elements.structure.diffTarget, config);
setBodyColorScheme(diff2htmlUi.config.colorScheme);
diff2htmlUi.draw(); diff2htmlUi.draw();
} }
function setBodyColorScheme(colorScheme: ColorSchemeType): void {
document.body.className = colorSchemeToCss(colorScheme);
}
async function prepareInitialState(elements: Elements): Promise<[Diff2HtmlUIConfig, string]> { async function prepareInitialState(elements: Elements): Promise<[Diff2HtmlUIConfig, string]> {
const urlParams = getParamsFromSearch(window.location.search); const urlParams = getParamsFromSearch(window.location.search);
const currentUrl = (urlParams && urlParams[searchParam]) || 'https://github.com/rtfpessoa/diff2html/pull/106'; const currentUrl = (urlParams && urlParams[searchParam]) || 'https://github.com/rtfpessoa/diff2html/pull/106';
@ -188,21 +170,23 @@ async function prepareInitialState(elements: Elements): Promise<[Diff2HtmlUIConf
} }
function updateBrowserUrl(config: Diff2HtmlUIConfig, newDiffUrl: string): void { function updateBrowserUrl(config: Diff2HtmlUIConfig, newDiffUrl: string): void {
const paramString = Object.entries(config) if (history.pushState) {
.map(([k, v]) => k + '=' + v) const paramString = Object.entries(config)
.join('&'); .map(([k, v]) => k + '=' + v)
const newPageUrl = .join('&');
window.location.protocol + const newPageUrl =
'//' + window.location.protocol +
window.location.host + '//' +
window.location.pathname + window.location.host +
'?' + window.location.pathname +
paramString + '?' +
'&' + paramString +
searchParam + '&' +
'=' + searchParam +
newDiffUrl; '=' +
window.history.pushState({ path: newPageUrl }, '', newPageUrl); newDiffUrl;
window.history.pushState({ path: newPageUrl }, '', newPageUrl);
}
} }
type Elements = { type Elements = {
@ -215,7 +199,6 @@ type Elements = {
}; };
options: { options: {
outputFormat: HTMLInputElement; outputFormat: HTMLInputElement;
colorScheme: HTMLInputElement;
matching: HTMLInputElement; matching: HTMLInputElement;
wordsThreshold: HTMLInputElement; wordsThreshold: HTMLInputElement;
matchingMaxComparisons: HTMLInputElement; matchingMaxComparisons: HTMLInputElement;
@ -225,33 +208,8 @@ type Elements = {
}; };
}; };
function isHTMLInputElement(arg?: unknown): arg is HTMLInputElement {
return arg !== null && (arg as HTMLInputElement)?.value !== undefined;
}
function getHTMLInputElementById(id: string): HTMLInputElement {
const element = document.getElementById(id);
if (!isHTMLInputElement(element)) {
throw new Error(`Could not find html input element with id '${id}'`);
}
return element;
}
function getHTMLElementById(id: string): HTMLElement {
const element = document.getElementById(id);
if (element === null) {
throw new Error(`Could not find html element with id '${id}'`);
}
return element;
}
document.addEventListener('DOMContentLoaded', async () => { document.addEventListener('DOMContentLoaded', async () => {
// Improves browser compatibility // Improves browser compatibility
// eslint-disable-next-line @typescript-eslint/no-require-imports
require('whatwg-fetch'); require('whatwg-fetch');
const drawAndUpdateUrl = async ( const drawAndUpdateUrl = async (
@ -268,34 +226,32 @@ document.addEventListener('DOMContentLoaded', async () => {
const elements: Elements = { const elements: Elements = {
structure: { structure: {
diffTarget: getHTMLElementById('url-diff-container'), diffTarget: document.getElementById('url-diff-container') as HTMLElement,
}, },
url: { url: {
input: getHTMLInputElementById('url'), input: document.getElementById('url') as HTMLInputElement,
button: getHTMLElementById('url-btn'), button: document.getElementById('url-btn') as HTMLElement,
}, },
options: { options: {
outputFormat: getHTMLInputElementById('diff-url-options-output-format'), outputFormat: document.getElementById('diff-url-options-output-format') as HTMLInputElement,
colorScheme: getHTMLInputElementById('diff-url-options-color-scheme'), matching: document.getElementById('diff-url-options-matching') as HTMLInputElement,
matching: getHTMLInputElementById('diff-url-options-matching'), wordsThreshold: document.getElementById('diff-url-options-match-words-threshold') as HTMLInputElement,
wordsThreshold: getHTMLInputElementById('diff-url-options-match-words-threshold'), matchingMaxComparisons: document.getElementById('diff-url-options-matching-max-comparisons') as HTMLInputElement,
matchingMaxComparisons: getHTMLInputElementById('diff-url-options-matching-max-comparisons'),
}, },
checkboxes: { checkboxes: {
drawFileList: getHTMLInputElementById('diff-url-options-show-files'), drawFileList: document.getElementById('diff-url-options-show-files') as HTMLInputElement,
}, },
}; };
let [config, diffString] = await prepareInitialState(elements); let [config, diffString] = await prepareInitialState(elements);
// Update HTML inputs from any changes in URL // Update HTML inputs from any changes in URL
if (config.outputFormat) elements.options.outputFormat.value = config.outputFormat; config.outputFormat && (elements.options.outputFormat.value = config.outputFormat);
if (config.colorScheme) elements.options.colorScheme.value = config.colorScheme; config.drawFileList && (elements.checkboxes.drawFileList.checked = config.drawFileList);
if (config.drawFileList) elements.checkboxes.drawFileList.checked = config.drawFileList; config.matching && (elements.options.matching.value = config.matching);
if (config.matching) elements.options.matching.value = config.matching; config.matchWordsThreshold && (elements.options.wordsThreshold.value = config.matchWordsThreshold.toString());
if (config.matchWordsThreshold) elements.options.wordsThreshold.value = config.matchWordsThreshold.toString(); config.matchingMaxComparisons &&
if (config.matchingMaxComparisons) (elements.options.matchingMaxComparisons.value = config.matchingMaxComparisons.toString());
elements.options.matchingMaxComparisons.value = config.matchingMaxComparisons.toString();
Object.entries(elements.options).forEach(([option, element]) => Object.entries(elements.options).forEach(([option, element]) =>
element.addEventListener('change', () => { element.addEventListener('change', () => {

View file

@ -1,332 +0,0 @@
/*!
Theme Adapted from highlight.js github theme
Theme: Modified GitHub Dark
Description: Dark theme as seen on github.com, modified for diff2html demo
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Outdated base version: https://github.com/primer/github-syntax-dark
Current colors taken from GitHub's CSS
*/
pre code.hljs {
display: block;
overflow-x: auto;
padding: 1em;
}
code.hljs {
padding: 3px 5px;
}
.hljs {
color: #24292e;
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
color: #d73a49;
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
color: #6f42c1;
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id,
.hljs-variable {
color: #005cc5;
}
.hljs-meta .hljs-string,
.hljs-regexp,
.hljs-string {
color: #032f62;
}
.hljs-built_in,
.hljs-symbol {
color: #e36209;
}
.hljs-code,
.hljs-comment,
.hljs-formula {
color: #6a737d;
}
.hljs-name,
.hljs-quote,
.hljs-selector-pseudo,
.hljs-selector-tag {
color: #22863a;
}
.hljs-subst {
color: #24292e;
}
.hljs-section {
color: #005cc5;
font-weight: 700;
}
.hljs-bullet {
color: #735c0f;
}
.hljs-emphasis {
color: #24292e;
font-style: italic;
}
.hljs-strong {
color: #24292e;
font-weight: 700;
}
.hljs-addition {
color: #22863a;
background-color: #f0fff4;
}
.hljs-deletion {
color: #b31d28;
background-color: #ffeef0;
}
.d2h-dark-color-scheme .hljs {
color: #c9d1d9;
}
.d2h-dark-color-scheme .hljs-doctag,
.d2h-dark-color-scheme .hljs-keyword,
.d2h-dark-color-scheme .hljs-meta .hljs-keyword,
.d2h-dark-color-scheme .hljs-template-tag,
.d2h-dark-color-scheme .hljs-template-variable,
.d2h-dark-color-scheme .hljs-type,
.d2h-dark-color-scheme .hljs-variable.language_ {
color: #ff7b72;
}
.d2h-dark-color-scheme .hljs-title,
.d2h-dark-color-scheme .hljs-title.class_,
.d2h-dark-color-scheme .hljs-title.class_.inherited__,
.d2h-dark-color-scheme .hljs-title.function_ {
color: #d2a8ff;
}
.d2h-dark-color-scheme .hljs-attr,
.d2h-dark-color-scheme .hljs-attribute,
.d2h-dark-color-scheme .hljs-literal,
.d2h-dark-color-scheme .hljs-meta,
.d2h-dark-color-scheme .hljs-number,
.d2h-dark-color-scheme .hljs-operator,
.d2h-dark-color-scheme .hljs-selector-attr,
.d2h-dark-color-scheme .hljs-selector-class,
.d2h-dark-color-scheme .hljs-selector-id,
.d2h-dark-color-scheme .hljs-variable {
color: #79c0ff;
}
.d2h-dark-color-scheme .hljs-meta .hljs-string,
.d2h-dark-color-scheme .hljs-regexp,
.d2h-dark-color-scheme .hljs-string {
color: #a5d6ff;
}
.d2h-dark-color-scheme .hljs-built_in,
.d2h-dark-color-scheme .hljs-symbol {
color: #ffa657;
}
.d2h-dark-color-scheme .hljs-code,
.d2h-dark-color-scheme .hljs-comment,
.d2h-dark-color-scheme .hljs-formula {
color: #8b949e;
}
.d2h-dark-color-scheme .hljs-name,
.d2h-dark-color-scheme .hljs-quote,
.d2h-dark-color-scheme .hljs-selector-pseudo,
.d2h-dark-color-scheme .hljs-selector-tag {
color: #7ee787;
}
.d2h-dark-color-scheme .hljs-subst {
color: #c9d1d9;
}
.d2h-dark-color-scheme .hljs-section {
color: #1f6feb;
font-weight: 700;
}
.d2h-dark-color-scheme .hljs-bullet {
color: #f2cc60;
}
.d2h-dark-color-scheme .hljs-emphasis {
color: #c9d1d9;
font-style: italic;
}
.d2h-dark-color-scheme .hljs-strong {
color: #c9d1d9;
font-weight: 700;
}
.d2h-dark-color-scheme .hljs-addition {
color: #aff5b4;
background-color: #033a16;
}
.d2h-dark-color-scheme .hljs-deletion {
color: #ffdcd7;
background-color: #67060c;
}
@media (prefers-color-scheme: dark) {
.d2h-auto-color-scheme .hljs {
color: #c9d1d9;
}
.d2h-auto-color-scheme .hljs-doctag,
.d2h-auto-color-scheme .hljs-keyword,
.d2h-auto-color-scheme .hljs-meta .hljs-keyword,
.d2h-auto-color-scheme .hljs-template-tag,
.d2h-auto-color-scheme .hljs-template-variable,
.d2h-auto-color-scheme .hljs-type,
.d2h-auto-color-scheme .hljs-variable.language_ {
color: #ff7b72;
}
.d2h-auto-color-scheme .hljs-title,
.d2h-auto-color-scheme .hljs-title.class_,
.d2h-auto-color-scheme .hljs-title.class_.inherited__,
.d2h-auto-color-scheme .hljs-title.function_ {
color: #d2a8ff;
}
.d2h-auto-color-scheme .hljs-attr,
.d2h-auto-color-scheme .hljs-attribute,
.d2h-auto-color-scheme .hljs-literal,
.d2h-auto-color-scheme .hljs-meta,
.d2h-auto-color-scheme .hljs-number,
.d2h-auto-color-scheme .hljs-operator,
.d2h-auto-color-scheme .hljs-selector-attr,
.d2h-auto-color-scheme .hljs-selector-class,
.d2h-auto-color-scheme .hljs-selector-id,
.d2h-auto-color-scheme .hljs-variable {
color: #79c0ff;
}
.d2h-auto-color-scheme .hljs-meta .hljs-string,
.d2h-auto-color-scheme .hljs-regexp,
.d2h-auto-color-scheme .hljs-string {
color: #a5d6ff;
}
.d2h-auto-color-scheme .hljs-built_in,
.d2h-auto-color-scheme .hljs-symbol {
color: #ffa657;
}
.d2h-auto-color-scheme .hljs-code,
.d2h-auto-color-scheme .hljs-comment,
.d2h-auto-color-scheme .hljs-formula {
color: #8b949e;
}
.d2h-auto-color-scheme .hljs-name,
.d2h-auto-color-scheme .hljs-quote,
.d2h-auto-color-scheme .hljs-selector-pseudo,
.d2h-auto-color-scheme .hljs-selector-tag {
color: #7ee787;
}
.d2h-auto-color-scheme .hljs-subst {
color: #c9d1d9;
}
.d2h-auto-color-scheme .hljs-section {
color: #1f6feb;
font-weight: 700;
}
.d2h-auto-color-scheme .hljs-bullet {
color: #f2cc60;
}
.d2h-auto-color-scheme .hljs-emphasis {
color: #c9d1d9;
font-style: italic;
}
.d2h-auto-color-scheme .hljs-strong {
color: #c9d1d9;
font-weight: 700;
}
.d2h-auto-color-scheme .hljs-addition {
color: #aff5b4;
background-color: #033a16;
}
.d2h-auto-color-scheme .hljs-deletion {
color: #ffdcd7;
background-color: #67060c;
}
}
@media (prefers-color-scheme: light) {
.d2h-auto-color-scheme .hljs {
color: #24292e;
}
.d2h-auto-color-scheme .hljs-doctag,
.d2h-auto-color-scheme .hljs-keyword,
.d2h-auto-color-scheme .hljs-meta .hljs-keyword,
.d2h-auto-color-scheme .hljs-template-tag,
.d2h-auto-color-scheme .hljs-template-variable,
.d2h-auto-color-scheme .hljs-type,
.d2h-auto-color-scheme .hljs-variable.language_ {
color: #d73a49;
}
.d2h-auto-color-scheme .hljs-title,
.d2h-auto-color-scheme .hljs-title.class_,
.d2h-auto-color-scheme .hljs-title.class_.inherited__,
.d2h-auto-color-scheme .hljs-title.function_ {
color: #6f42c1;
}
.d2h-auto-color-scheme .hljs-attr,
.d2h-auto-color-scheme .hljs-attribute,
.d2h-auto-color-scheme .hljs-literal,
.d2h-auto-color-scheme .hljs-meta,
.d2h-auto-color-scheme .hljs-number,
.d2h-auto-color-scheme .hljs-operator,
.d2h-auto-color-scheme .hljs-selector-attr,
.d2h-auto-color-scheme .hljs-selector-class,
.d2h-auto-color-scheme .hljs-selector-id,
.d2h-auto-color-scheme .hljs-variable {
color: #005cc5;
}
.d2h-auto-color-scheme .hljs-meta .hljs-string,
.d2h-auto-color-scheme .hljs-regexp,
.d2h-auto-color-scheme .hljs-string {
color: #032f62;
}
.d2h-auto-color-scheme .hljs-built_in,
.d2h-auto-color-scheme .hljs-symbol {
color: #e36209;
}
.d2h-auto-color-scheme .hljs-code,
.d2h-auto-color-scheme .hljs-comment,
.d2h-auto-color-scheme .hljs-formula {
color: #6a737d;
}
.d2h-auto-color-scheme .hljs-name,
.d2h-auto-color-scheme .hljs-quote,
.d2h-auto-color-scheme .hljs-selector-pseudo,
.d2h-auto-color-scheme .hljs-selector-tag {
color: #22863a;
}
.d2h-auto-color-scheme .hljs-subst {
color: #24292e;
}
.d2h-auto-color-scheme .hljs-section {
color: #005cc5;
font-weight: 700;
}
.d2h-auto-color-scheme .hljs-bullet {
color: #735c0f;
}
.d2h-auto-color-scheme .hljs-emphasis {
color: #24292e;
font-style: italic;
}
.d2h-auto-color-scheme .hljs-strong {
color: #24292e;
font-weight: 700;
}
.d2h-auto-color-scheme .hljs-addition {
color: #22863a;
background-color: #f0fff4;
}
.d2h-auto-color-scheme .hljs-deletion {
color: #b31d28;
background-color: #ffeef0;
}
}

View file

@ -12,7 +12,7 @@
</div> </div>
<div class="container has-text-centered"> <div class="container has-text-centered">
<figure class="image"> <figure class="image">
<img class="hero-img" src="./images/snapshot-3.png" <img class="hero-img" src="images/snapshot-3.png"
alt="Diff2Html pretty html side-by-side example render"> alt="Diff2Html pretty html side-by-side example render">
</figure> </figure>
</div> </div>
@ -72,7 +72,7 @@
<span class="button clipboard" data-clipboard-text="npm install diff2html" title="Copy">Copy</span> <span class="button clipboard" data-clipboard-text="npm install diff2html" title="Copy">Copy</span>
</div> </div>
<p> <p>
<a href="https://github.com/rtfpessoa/diff2html#usage" target="_blank" rel="noopener" rel="noreferrer"> <a href="https://github.com/rtfpessoa/diff2html#how-to-use" target="_blank">
Find usage examples in the Docs Find usage examples in the Docs
</a> </a>
</p> </p>
@ -85,8 +85,7 @@
<div class="column"> <div class="column">
<h4 class="title is-size-4 is-spaced"><strong>With command line integration</strong></h4> <h4 class="title is-size-4 is-spaced"><strong>With command line integration</strong></h4>
<p>We work hard to make sure you can have your diffs in a simple and flexible <p>We work hard to make sure you can have your diffs in a simple and flexible
way. Go here for the <a href="https://github.com/rtfpessoa/diff2html-cli#readme" target="_blank" way. Go here for the <a href="https://github.com/rtfpessoa/diff2html-cli" target="_blank"></a>full
rel="noopener" rel="noreferrer">full
documentation</a>. documentation</a>.
</p> </p>
</div> </div>
@ -115,386 +114,129 @@
<hr> <hr>
<div id="users" class="container"> <div id="users" class="container">
<h4 class="title is-size-4 is-spaced"><strong>Sponsor</strong></h4> <h4 class="title is-size-4 is-spaced"><strong>Projects using diff2html</strong></h4>
<div class="columns is-desktop is-multiline"> <div class="columns is-desktop is-multiline">
<div class="column is-half-widescreen is-flex"> <div class="column is-one-quarter-widescreen box">
<div class="box is-flex is-fullwidth is-vertical"> <header>
<header> <p class="is-size-5 has-text-weight-bold">diff2html-cli</p>
<div class="flex"> </header>
<div class="flex"> <section>
<img src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg" style="height: 30px;"> <p>diff2html from your terminal to the browser.</p>
<div> </section>
<div style="margin-left: 5px;" class="is-size-5 has-text-weight-bold flex">Hugging Face</div> <footer>
</div> <a href="https://github.com/rtfpessoa/diff2html-cli" target="_blank">View GitHub</a>
</header> </footer>
<section class="media-content">
<p>The AI community building the future. Build, train and deploy state of the art models powered by the reference open source in natural language processing.</p>
</section>
<footer>
<a href="https://huggingface.co/" target="_blank" rel="noopener"
rel="noreferrer">Website</a>
</footer>
</div>
</div> </div>
</div> <div class="column is-one-quarter-widescreen box">
<header>
<h4 class="title is-size-4 is-spaced"><strong>Other projects using diff2html</strong></h4> <p class="is-size-5 has-text-weight-bold">Codacy</p>
<div class="columns is-desktop is-multiline"> </header>
<div class="column is-one-quarter-widescreen is-flex" style="min-height: 166px;"> <section>
<div class="box is-flex is-fullwidth is-vertical"> <p>Check code style, security, duplication, complexity and coverage on every change.</p>
<header> </section>
<p class="is-size-5 has-text-weight-bold">Exercism</p> <footer>
</header> <a href="https://www.codacy.com" target="_blank">Website</a>
<section class="media-content"> </footer>
<p>Get really good at programming.</p>
</section>
<footer>
<a href="https://exercism.org/" target="_blank" rel="noopener"
rel="noreferrer">Website</a>
</footer>
</div>
</div> </div>
<div class="column is-one-quarter-widescreen box">
<div class="column is-one-quarter-widescreen is-flex"> <header>
<div class="box is-flex is-fullwidth is-vertical"> <p class="is-size-5 has-text-weight-bold">Ungit</p>
<header> </header>
<p class="is-size-5 has-text-weight-bold">diff2html-cli</p> <section>
</header> <p>The easiest way to use git. On any platform. Anywhere.</p>
<section class="media-content"> </section>
<p>diff2html from your terminal to the browser.</p> <footer>
</section> <a href="https://github.com/FredrikNoren/ungit" target="_blank">View GitHub</a>
<footer> </footer>
<a href="https://github.com/rtfpessoa/diff2html-cli" target="_blank" rel="noopener"
rel="noreferrer">View GitHub</a>
</footer>
</div>
</div> </div>
<div class="column is-one-quarter-widescreen box">
<div class="column is-one-quarter-widescreen is-flex"> <header>
<div class="box is-flex is-fullwidth is-vertical"> <p class="is-size-5 has-text-weight-bold">Diffy</p>
<header> </header>
<p class="is-size-5 has-text-weight-bold">Codacy</p> <section>
</header> <p>Share your diffs and explain your ideas without committing.</p>
<section class="media-content"> </section>
<p>Automate code reviews on your commits and pull requests.</p> <footer>
</section> <a href="https://diffy.org/" target="_blank">Website</a>
<footer> </footer>
<a href="https://www.codacy.com" target="_blank" rel="noopener" rel="noreferrer">Website</a>
</footer>
</div>
</div> </div>
<div class="column is-one-quarter-widescreen box">
<div class="column is-one-quarter-widescreen is-flex"> <header>
<div class="box is-flex is-fullwidth is-vertical"> <p class="is-size-5 has-text-weight-bold">diff-pane</p>
<header> </header>
<p class="is-size-5 has-text-weight-bold">Ungit</p> <section>
</header> <p>Atom - Diff two panes.</p>
<section class="media-content"> </section>
<p>The easiest way to use git. On any platform. Anywhere.</p> <footer>
</section> <a href="https://github.com/t-ari/diff-pane" target="_blank">View GitHub</a>
<footer> </footer>
<a href="https://github.com/FredrikNoren/ungit" target="_blank" rel="noopener"
rel="noreferrer">View GitHub</a>
</footer>
</div>
</div> </div>
<div class="column is-one-quarter-widescreen box">
<div class="column is-one-quarter-widescreen is-flex"> <header>
<div class="box is-flex is-fullwidth is-vertical"> <p class="is-size-5 has-text-weight-bold">node-giff</p>
<header> </header>
<p class="is-size-5 has-text-weight-bold">Diffy</p> <section>
</header> <p>Display git diff on browser.</p>
<section class="media-content"> </section>
<p>Share your diffs and explain your ideas without committing.</p> <footer>
</section> <a href="https://github.com/do7be/node-giff" target="_blank">View GitHub</a>
<footer> </footer>
<a href="https://diffy.org/" target="_blank" rel="noopener" rel="noreferrer">Website</a>
</footer>
</div>
</div> </div>
<div class="column is-one-quarter-widescreen box">
<div class="column is-one-quarter-widescreen is-flex"> <header>
<div class="box is-flex is-fullwidth is-vertical"> <p class="is-size-5 has-text-weight-bold">edgar-monitor</p>
<header> </header>
<p class="is-size-5 has-text-weight-bold">Jenkins</p> <section>
</header> <p>A module that processes new Edgar filings and sends out notifications.</p>
<section class="media-content"> </section>
<p>Show diffs between builds</p> <footer>
</section> <a href="https://github.com/buzzfeed-openlab/edgar-monitor" target="_blank">View GitHub</a>
<footer> </footer>
<a href="https://plugins.jenkins.io/last-changes/" target="_blank"
rel="noopener" rel="noreferrer">Website</a>
</footer>
</div>
</div> </div>
<div class="column is-one-quarter-widescreen box">
<div class="column is-one-quarter-widescreen is-flex"> <header>
<div class="box is-flex is-fullwidth is-vertical"> <p class="is-size-5 has-text-weight-bold">node-git</p>
<header> </header>
<p class="is-size-5 has-text-weight-bold">code-annotation</p> <section>
</header> <p>Execute Git Command by Node.js.</p>
<section class="media-content"> </section>
<p>Code Annotation Tool.</p> <footer>
</section> <a href="https://github.com/liangshuai/node-git" target="_blank">View GitHub</a>
<footer> </footer>
<a href="https://github.com/src-d/code-annotation" target="_blank" rel="noopener"
rel="noreferrer">View GitHub</a>
</footer>
</div>
</div> </div>
<div class="column is-one-quarter-widescreen box">
<div class="column is-one-quarter-widescreen is-flex"> <header>
<div class="box is-flex is-fullwidth is-vertical"> <p class="is-size-5 has-text-weight-bold">Jenkins</p>
<header> </header>
<p class="is-size-5 has-text-weight-bold">Chef Automate</p> <section>
</header> <p>Show diffs between builds</p>
<section class="media-content"> </section>
<p>DevOps Dashboard for Complete Operational Visibility.</p> <footer>
</section> <a href="https://wiki.jenkins-ci.org/display/JENKINS/Last+Changes+Plugin"
<footer> target="_blank">Website</a>
<a href="https://automate.chef.io/" target="_blank" rel="noopener" rel="noreferrer">Website</a> </footer>
</footer>
</div>
</div> </div>
<div class="column is-one-quarter-widescreen box">
<div class="column is-one-quarter-widescreen is-flex"> <header>
<div class="box is-flex is-fullwidth is-vertical"> <p class="is-size-5 has-text-weight-bold">Light Review</p>
<header> </header>
<p class="is-size-5 has-text-weight-bold">GraphQL Schema Diff</p> <section>
</header> <p>Code Reviews with maximum control for the leading developers</p>
<section class="media-content"> </section>
<p>Detects dangerous and breaking changes in GraphQL schemas.</p> <footer>
</section> <a href="http://light-review.com/" target="_blank">Website</a>
<footer> </footer>
<a href="https://github.com/fabsrc/graphql-schema-diff" target="_blank" rel="noopener"
rel="noreferrer">View GitHub</a>
</footer>
</div>
</div> </div>
<div class="column is-one-quarter-widescreen box">
<div class="column is-one-quarter-widescreen is-flex"> <header>
<div class="box is-flex is-fullwidth is-vertical"> <p class="is-size-5 has-text-weight-bold">Simple Git</p>
<header> </header>
<p class="is-size-5 has-text-weight-bold">cypress-plugin-snapshots</p> <section>
</header> <p>A simple package to be able to drive GIT</p>
<section class="media-content"> </section>
<p>Plugin for snapshot tests in Cypress.io.</p> <footer>
</section> <a href="https://github.com/mauricioszabo/simple-git" target="_blank">View GitHub</a>
<footer> </footer>
<a href="https://github.com/meinaart/cypress-plugin-snapshots" target="_blank" rel="noopener"
rel="noreferrer">View GitHub</a>
</footer>
</div>
</div>
<div class="column is-one-quarter-widescreen is-flex">
<div class="box is-flex is-fullwidth is-vertical">
<header>
<p class="is-size-5 has-text-weight-bold">git-explorer</p>
</header>
<section class="media-content">
<p>Offline-first support for previewing local git repositories.</p>
</section>
<footer>
<a href="https://github.com/thescientist13/git-explorer" target="_blank" rel="noopener"
rel="noreferrer">View GitHub</a>
</footer>
</div>
</div>
<div class="column is-one-quarter-widescreen is-flex">
<div class="box is-flex is-fullwidth is-vertical">
<header>
<p class="is-size-5 has-text-weight-bold">edgar-monitor</p>
</header>
<section class="media-content">
<p>A module that processes new Edgar filings and sends out notifications.</p>
</section>
<footer>
<a href="https://github.com/buzzfeed-openlab/edgar-monitor" target="_blank" rel="noopener"
rel="noreferrer">View GitHub</a>
</footer>
</div>
</div>
<div class="column is-one-quarter-widescreen is-flex">
<div class="box is-flex is-fullwidth is-vertical">
<header>
<p class="is-size-5 has-text-weight-bold">Simple Git</p>
</header>
<section class="media-content">
<p>A simple package to be able to drive GIT.</p>
</section>
<footer>
<a href="https://github.com/mauricioszabo/simple-git" target="_blank" rel="noopener"
rel="noreferrer">View GitHub</a>
</footer>
</div>
</div>
<div class="column is-one-quarter-widescreen is-flex">
<div class="box is-flex is-fullwidth is-vertical">
<header>
<p class="is-size-5 has-text-weight-bold">jsreport</p>
</header>
<section class="media-content">
<p>javascript based business reporting platform</p>
</section>
<footer>
<a href="https://jsreport.net" target="_blank" rel="noopener"
rel="noreferrer">Website</a>
</footer>
</div>
</div>
<div class="column is-one-quarter-widescreen is-flex">
<div class="box is-flex is-fullwidth is-vertical">
<header>
<p class="is-size-5 has-text-weight-bold">dendron</p>
</header>
<section class="media-content">
<p>Knowledge Management. Redefined.</p>
</section>
<footer>
<a href="https://www.dendron.so" target="_blank" rel="noopener"
rel="noreferrer">Website</a>
</footer>
</div>
</div>
<div class="column is-one-quarter-widescreen is-flex">
<div class="box is-flex is-fullwidth is-vertical">
<header>
<p class="is-size-5 has-text-weight-bold">jest-stare</p>
</header>
<section class="media-content">
<p>Jest HTML Reporter and Results Processor</p>
</section>
<footer>
<a href="https://dkelosky.github.io/jest-stare/" target="_blank" rel="noopener"
rel="noreferrer">Website</a>
</footer>
</div>
</div>
<div class="column is-one-quarter-widescreen is-flex">
<div class="box is-flex is-fullwidth is-vertical">
<header>
<p class="is-size-5 has-text-weight-bold">Wiki.js</p>
</header>
<section class="media-content">
<p>The most powerful and extensible open source Wiki software</p>
</section>
<footer>
<a href="https://js.wiki/" target="_blank" rel="noopener"
rel="noreferrer">Website</a>
</footer>
</div>
</div>
<div class="column is-one-quarter-widescreen is-flex">
<div class="box is-flex is-fullwidth is-vertical">
<header>
<p class="is-size-5 has-text-weight-bold">Salto</p>
</header>
<section class="media-content">
<p>Understand Your Business Apps' Configuration</p>
</section>
<footer>
<a href="https://www.salto.io/" target="_blank" rel="noopener"
rel="noreferrer">Website</a>
</footer>
</div>
</div>
<div class="column is-one-quarter-widescreen is-flex">
<div class="box is-flex is-fullwidth is-vertical">
<header>
<p class="is-size-5 has-text-weight-bold">intuit Design Systems CLI</p>
</header>
<section class="media-content">
<p>A CLI toolbox for creating design systems</p>
</section>
<footer>
<a href="https://intuit.github.io/design-systems-cli/#/" target="_blank" rel="noopener"
rel="noreferrer">Website</a>
</footer>
</div>
</div>
<div class="column is-one-quarter-widescreen is-flex">
<div class="box is-flex is-fullwidth is-vertical">
<header>
<p class="is-size-5 has-text-weight-bold">Camunda Modeler</p>
</header>
<section class="media-content">
<p>Automate processes and decisions in a modern, standards-based way</p>
</section>
<footer>
<a href="https://camunda.com/download/modeler/" target="_blank" rel="noopener"
rel="noreferrer">Website</a>
</footer>
</div>
</div>
<div class="column is-one-quarter-widescreen is-flex">
<div class="box is-flex is-fullwidth is-vertical">
<header>
<p class="is-size-5 has-text-weight-bold">igit</p>
</header>
<section class="media-content">
<p>Tool for managing pull requests</p>
</section>
<footer>
<a href="https://igit.dev/" target="_blank" rel="noopener"
rel="noreferrer">Website</a>
</footer>
</div>
</div>
<div class="column is-one-quarter-widescreen is-flex">
<div class="box is-flex is-fullwidth is-vertical">
<header>
<p class="is-size-5 has-text-weight-bold">Crowi</p>
</header>
<section class="media-content">
<p>Empower the team with sharing your knowledge</p>
</section>
<footer>
<a href="https://site.crowi.wiki/" target="_blank" rel="noopener"
rel="noreferrer">Website</a>
</footer>
</div>
</div>
<div class="column is-one-quarter-widescreen is-flex">
<div class="box is-flex is-fullwidth is-vertical">
<header>
<p class="is-size-5 has-text-weight-bold">Robusta</p>
</header>
<section class="media-content">
<p>Open source Kubernetes troubleshooting and automation platform</p>
</section>
<footer>
<a href="https://docs.robusta.dev/master/" target="_blank" rel="noopener"
rel="noreferrer">Website</a>
</footer>
</div>
</div>
<div class="column is-one-quarter-widescreen is-flex">
<div class="box is-flex is-fullwidth is-vertical">
<header>
<p class="is-size-5 has-text-weight-bold">git-tabular-diff</p>
</header>
<section class="media-content">
<p>Displays a tabular difference of a csv file or a split difference of any file in an Atom pane</p>
</section>
<footer>
<a href="https://github.com/jstritch/git-tabular-diff" target="_blank" rel="noopener"
rel="noreferrer">View GitHub</a>
</footer>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -505,14 +247,12 @@
<h4 class="title is-size-4 is-spaced"><strong>Open Source</strong></h4> <h4 class="title is-size-4 is-spaced"><strong>Open Source</strong></h4>
<p>diff2html is open source. <p>diff2html is open source.
If you'd like to be part of the diff2html community or help improve it, If you'd like to be part of the diff2html community or help improve it,
find more information on <a href="https://github.com/rtfpessoa/diff2html" target="_blank" rel="noopener" find more information on <a href="https://github.com/rtfpessoa/diff2html" target="_blank">GitHub</a> and
rel="noreferrer">GitHub</a> and <a href="https://gitter.im/rtfpessoa/diff2html" target="_blank">Gitter</a>. Need any help?
<a href="https://gitter.im/rtfpessoa/diff2html" target="_blank" rel="noopener" rel="noreferrer">Gitter</a>.
Need any help?
</p> </p>
<a href="https://github.com/rtfpessoa/diff2html#usage" target="_blank" rel="noopener" rel="noreferrer"> <a href="https://github.com/rtfpessoa/diff2html#how-to-use" target="_blank">
Read more in the Docs Read more in the Docs
</a> </a>
</div> </div>
</section> </section>

View file

@ -1,4 +1,5 @@
.image img.hero-img { .image img.hero-img {
background-image: url(images/snapshot-3.png);
display: block; display: block;
height: auto; height: auto;
width: 70%; width: 70%;
@ -8,11 +9,7 @@
} }
.hero-booticon { .hero-booticon {
font-family: font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
Helvetica Neue,
Helvetica,
Arial,
sans-serif;
margin: 0 auto 30px; margin: 0 auto 30px;
width: 100%; width: 100%;
font-size: 8vw; font-size: 8vw;
@ -46,20 +43,16 @@
cursor: pointer; cursor: pointer;
} }
.flex { .button.clipboard:hover {
color: #000;
background-color: #dcdfe4;
}
#users .columns .column {
display: flex; display: flex;
flex-direction: row;
justify-content: flex-start;
}
.is-fullwidth {
flex-grow: 1;
}
.is-vertical {
flex-direction: column; flex-direction: column;
} }
.align-middle { #users .columns .column section {
vertical-align: middle; height: 100%;
} }

View file

@ -4,4 +4,5 @@ import '../../../main.ts';
import '../../../main.css'; import '../../../main.css';
import './index.css'; import './index.css';
// eslint-disable-next-line no-new
new Clipboard(document.getElementsByClassName('clipboard')[0]); new Clipboard(document.getElementsByClassName('clipboard')[0]);

View file

@ -6,11 +6,8 @@
<!--[if IE]> <!--[if IE]>
<meta http-equiv='X-UA-Compatible' content='IE=edge,chrome=1'/> <meta http-equiv='X-UA-Compatible' content='IE=edge,chrome=1'/>
<![endif]--> <![endif]-->
<meta http-equiv="Cache-Control" content="public, must-revalidate, proxy-revalidate, max-age=0" />
<meta name="theme-color" content="#FFFFFF"/>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Diff parser and pretty html generator"> <meta name="description" content="Diff parser and pretty html generator">
<meta name="keywords" content="diff2html,git,diff,unified,pretty,html,css,javaccript"> <meta name="keywords" content="diff2html,git,diff,unified,pretty,html,css,javaccript">
<meta name="author" content="Rodrigo Fernandes (rtfpessoa)"> <meta name="author" content="Rodrigo Fernandes (rtfpessoa)">
@ -60,10 +57,10 @@
<a class="navbar-item" href="index.html#install">Getting Started</a> <a class="navbar-item" href="index.html#install">Getting Started</a>
<a class="navbar-item" href="index.html#cli">CLI</a> <a class="navbar-item" href="index.html#cli">CLI</a>
<a class="navbar-item" href="demo.html">Demo</a> <a class="navbar-item" href="demo.html">Demo</a>
<a class="navbar-item" href="https://github.com/rtfpessoa/diff2html#usage" target="_blank" <a class="navbar-item" href="https://github.com/rtfpessoa/diff2html#how-to-use"
rel="noopener" rel="noreferrer">Docs</a> target="_blank">Docs</a>
<a class="navbar-item" href="https://github.com/rtfpessoa/diff2html/issues/new" target="_blank" <a class="navbar-item" href="https://github.com/rtfpessoa/diff2html/issues/new"
rel="noopener" rel="noreferrer">Support</a> target="_blank">Support</a>
</div> </div>
</div> </div>
</div> </div>
@ -75,11 +72,11 @@
<div class="content has-text-centered"> <div class="content has-text-centered">
<p> <p>
Designed and built with <span class="hero-red">❤</span> by Designed and built with <span class="hero-red">❤</span> by
<a href="https://twitter.com/rtfpessoa" target="_blank" rel="noopener" rel="noreferrer">@rtfpessoa</a>. <a href="https://twitter.com/rtfpessoa" target="_blank">@rtfpessoa</a>.
</p> </p>
<p> <p>
<a class="footer-list-link" href="https://github.com/rtfpessoa/diff2html#usage" target="_blank" <a class="footer-list-link" href="https://github.com/rtfpessoa/diff2html#how-to-use"
rel="noopener" rel="noreferrer">FAQ</a> target="_blank">FAQ</a>
- -
<a class="footer-list-link" href="https://diff2html.xyz">diff2html</a> <a class="footer-list-link" href="https://diff2html.xyz">diff2html</a>
</p> </p>
@ -92,7 +89,7 @@
"@type": "SoftwareSourceCode", "@type": "SoftwareSourceCode",
"name": "diff2html", "name": "diff2html",
"author": "Rodrigo Fernandes", "author": "Rodrigo Fernandes",
"image": "https://diff2html.xyz/images/snapshot-3.png", "image": "https://diff2html.xyz/img/snapshot-3.png",
"description": "Diff parser and pretty html generator.", "description": "Diff parser and pretty html generator.",
"codeRepository": "https://github.com/rtfpessoa/diff2html", "codeRepository": "https://github.com/rtfpessoa/diff2html",
"programmingLanguage": "JavaScript", "programmingLanguage": "JavaScript",
@ -102,4 +99,4 @@
</script> </script>
</body> </body>
</html> </html>

10449
yarn.lock Normal file

File diff suppressed because it is too large Load diff