diff --git a/.circleci/config.yml b/.circleci/config.yml index 57f0f67..f02b047 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,62 +1,194 @@ -version: 2 +version: 2.1 -reference: - build-common: &common-build - working_directory: ~/diff2html-cli - steps: &common-steps +jobs: + checkout-and-version: + docker: + - image: codacy/git-version + working_directory: ~/workdir + steps: - checkout + - run: + name: Get next version + command: | + # 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 + - run: + name: Get next npm tag name + command: | + 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 + - persist_to_workspace: + root: ~/workdir + paths: + - '*' + + build-common: &common-build + docker: + - image: node + working_directory: ~/workdir + steps: + - attach_workspace: + at: ~/workdir - restore_cache: - key: dependency-cache-{{ checksum "yarn.lock" }} - - run: yarn + key: yarn-cache-{{ checksum "yarn.lock" }} + - run: + name: Log environment setup + command: | + node -v + yarn -v + - run: + name: Install dependencies + command: yarn - save_cache: - key: dependency-cache-{{ checksum "yarn.lock" }} + key: yarn-cache-{{ checksum "yarn.lock" }} paths: - - ./node_modules - - run: yarn run build - - run: yarn run coverage + - /usr/local/share/.cache/yarn + - run: yarn run validate + - store_artifacts: + path: coverage + - store_test_results: + path: coverage build-latest: &latest-build - working_directory: ~/diff2html-cli + docker: + - image: node + working_directory: ~/workdir steps: - - checkout + - attach_workspace: + at: ~/workdir - restore_cache: - key: dependency-cache-{{ checksum "yarn.lock" }} - - run: yarn + key: yarn-cache-{{ checksum "yarn.lock" }} + - run: + name: Log environment setup + command: | + node -v + yarn -v + - run: + name: Install dependencies + command: yarn - save_cache: - key: dependency-cache-{{ checksum "yarn.lock" }} + key: yarn-cache-{{ checksum "yarn.lock" }} paths: - - ./node_modules - - run: yarn run test - - run: yarn run lint - - run: yarn run coverage - - run: yarn run codacy + - /usr/local/share/.cache/yarn + - run: yarn run validate + - store_artifacts: + path: coverage + - store_test_results: + path: coverage + - run: yarn run coverage:push + - persist_to_workspace: + root: ~/workdir + paths: + - '*' -jobs: - build-node_8: + build-node-10: <<: *common-build docker: - - image: node:8 + - image: node:10 - build-node_10: + build-node-11: <<: *common-build docker: - - image: node:10 + - image: node:11 - build-node_11: + build-node-12: <<: *common-build docker: - - image: node:11 + - image: node:12 - build-node_12: + build-node-13: <<: *latest-build docker: - - image: node:12 + - image: node:13 + + publish_library: + docker: + - image: node:13 + working_directory: ~/workdir + steps: + - attach_workspace: + at: ~/workdir + - run: + name: Configure Yarn version + command: | + yarn config set version-tag-prefix "" + yarn config set version-git-message "Release version %s" + - run: + name: Configure Git + command: | + git config user.email "circleci@users.noreply.github.com" + git config user.name "CircleCI" + - run: + name: Version package + command: | + # Update version in packages to publish + yarn version --non-interactive --new-version $(cat .version) + - run: + name: Setup npm credentials + command: | + 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 + - run: + name: Publish npm package + command: | + # Publish package versions to npmjs.org + yarn publish --tag $(cat .tag) --non-interactive --new-version $(cat .version) + - run: + name: Setup gpr credentials + command: | + rm -f .npmrc + touch .npmrc + echo "//npm.pkg.github.com/:_authToken=${GPR_AUTH_TOKEN}" >> .npmrc + echo "@rtfpessoa:registry=https://npm.pkg.github.com/" >> .npmrc + echo "access=public" >> .npmrc + echo "save-exact=true" >> .npmrc + - run: + name: Publish gpr package + command: | + # HACK: Override npm package name to be able to publish in GitHub + sed -i 's/^ "name":.*/ "name": "@rtfpessoa\/diff2html-cli",/g' package.json + echo "Going to publish version $(cat .version) to GitHub" + yarn publish --tag $(cat .tag) --non-interactive --new-version $(cat .version) + # HACK: Restore npm package name + sed -i 's/^ "name":.*/ "name": "diff2html-cli",/g' package.json workflows: - version: 2 - build: + validate-and-publish: jobs: - - build-node_8 - - build-node_10 - - build-node_11 - - build-node_12 + - checkout-and-version + - build-node-10: + requires: + - checkout-and-version + - build-node-11: + requires: + - checkout-and-version + - build-node-12: + requires: + - checkout-and-version + - build-node-13: + requires: + - checkout-and-version + - publish_approval: + type: approval + requires: + - build-node-10 + - build-node-11 + - build-node-12 + - build-node-13 + - publish_library: + requires: + - publish_approval + \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..3015a69 --- /dev/null +++ b/.eslintrc.js @@ -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', + }, +}; diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 2ab5590..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module", - "ecmaFeatures": { - "experimentalObjectRestSpread": true, - "jsx": true - } - }, - "env": { - "es6": true, - "node": true, - "browser": true, - "commonjs": true, - "jquery": true, - "phantomjs": true, - "jasmine": true, - "mocha": true, - "amd": true, - "worker": true, - "qunit": true - }, - "plugins": ["standard", "node", "import", "promise", "@typescript-eslint", "jest"], - "globals": { - "document": false, - "navigator": false, - "window": false - }, - "extends": [ - "plugin:@typescript-eslint/recommended", - "plugin:jest/recommended", - "plugin:promise/recommended", - "standard", - "plugin:prettier/recommended" - ] -} diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index cb4942b..12cc386 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,31 +1,22 @@ --- -name: Bug report -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' ---- +name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' ---**Describe the bug** A +clear and concise description of what the bug is. -**Describe the bug** -A clear and concise description of what the bug is. +**To Reproduce** Steps to reproduce the behavior: -**To Reproduce** -Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error -**Expected behavior** -A clear and concise description of what you expected to happen. +**Expected behavior** A clear and concise description of what you expected to happen. -**Screenshots** -If applicable, add screenshots to help explain your problem. +**Screenshots** If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. Windows, Linux, Mac] - - Version [e.g. 22] -**Additional context** -Add any other context about the problem here. +- OS: [e.g. Windows, Linux, Mac] +- Version [e.g. 22] + +**Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7..dd53cd8 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,20 +1,12 @@ --- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] +name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' ---**Is your feature +request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always +frustrated when [...] -**Describe the solution you'd like** -A clear and concise description of what you want to happen. +**Describe the solution you'd like** A clear and concise description of what you want to happen. -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. +**Describe alternatives you've considered** A clear and concise description of any alternative solutions or features +you've considered. -**Additional context** -Add any other context or screenshots about the feature request here. +**Additional context** Add any other context or screenshots about the feature request here. diff --git a/.gitignore b/.gitignore index e4ef476..0bbd19c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +# Eclipse +.classpath +.project +.settings/ + # Intellij .idea/ *.iml @@ -6,13 +11,22 @@ # Mac .DS_Store +# Maven +log/ +target/ + # Node -/node_modules/ -/npm-debug.log -/yarn-error.log +node_modules/ +npm-debug.log +yarn-error.log # Coverage -/coverage/ +coverage/ + +# Bower +bower_components/ + +# Terraform +/terraform/.terraform -# Build -/build/ +/lib/ diff --git a/.prettierrc.json b/.prettierrc.json index 0a57730..9b887a9 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,9 +1,17 @@ { - "tabWidth": 2, + "arrowParens": "avoid", + "bracketSpacing": true, + "htmlWhitespaceSensitivity": "css", + "insertPragma": false, + "jsxBracketSameLine": false, + "jsxSingleQuote": false, "printWidth": 120, - "useTabs": false, + "proseWrap": "always", + "quoteProps": "as-needed", + "requirePragma": false, "semi": true, - "trailingComma": "none", - "bracketSpacing": true, - "arrowParens": "always" + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all", + "useTabs": false } diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index e0e6195..c995da3 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,75 +2,60 @@ ## Our Pledge -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, sex characteristics, gender identity and expression, -level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making +participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, +disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, +socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards -Examples of behavior that contributes to creating a positive environment -include: +Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take +appropriate and fair corrective action in response to any instances of unacceptable behavior. -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, +issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any +contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the +project or its community. Examples of representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed representative at an online or offline +event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at rtfrodrigo [at] gmail [dot] com. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at +rtfrodrigo [at] gmail [dot] com. All complaints will be reviewed and investigated and will result in a response that is +deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with +regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent +repercussions as determined by other members of the project's leadership. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at +https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org -For answers to common questions about this code of conduct, see -https://www.contributor-covenant.org/faq +For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3848296..cb80f58 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,29 +2,31 @@ ### Main rules -* Before you open a ticket or send a pull request, [search](https://github.com/rtfpessoa/diff2html-cli/issues) for previous discussions about the same feature or issue. Add to the earlier ticket if you find one. +- Before you open a ticket or send a pull request, [search](https://github.com/rtfpessoa/diff2html-cli/issues) for + previous discussions about the same feature or issue. Add to the earlier ticket if you find one. -* If you're proposing a new feature, make sure you create an issue to let other contributors know what you are working on. +- If you're proposing a new feature, make sure you create an issue to let other contributors know what you are working + on. -* Before sending a pull request make sure your code is tested. +- Before sending a pull request make sure your code is tested. -* Before sending a pull request for a feature, be sure to run tests with `npm test`. +- Before sending a pull request for a feature, be sure to run tests with `npm test`. -* Use the same coding style as the rest of the codebase, most of the check can be performed with `npm run style`. +- Use the same coding style as the rest of the codebase, most of the check can be performed with `npm run style`. -* 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. -* After creating your pull request make sure the build is passing on [CircleCI](https://circleci.com/gh/rtfpessoa/diff2html-cli) -and that [Codacy](https://www.codacy.com/app/Codacy/diff2html-cli) is also confident in the code quality. +- After creating your pull request make sure the build is passing on + [CircleCI](https://circleci.com/gh/rtfpessoa/diff2html-cli) and that + [Codacy](https://www.codacy.com/app/Codacy/diff2html-cli) is also confident in the code quality. ### Commit Style -Writing good commit logs is important. A commit log should describe what changed and why. -Follow these guidelines when writing one: +Writing good commit logs is important. A commit log should describe what changed and why. Follow these guidelines when +writing one: -1. The first line should be 50 characters or less and contain a short - description of the change prefixed with the name of the changed - subsystem (e.g. "net: add localAddress and localPort to Socket"). +1. The first line should be 50 characters or less and contain a short description of the change prefixed with the name + of the changed subsystem (e.g. "net: add localAddress and localPort to Socket"). 2. Keep the second line blank. 3. Wrap all other lines at 72 columns. @@ -47,14 +49,11 @@ nicely even when it is indented. By making a contribution to this project, I certify that: -* (a) The contribution was created in whole or in part by me and I - have the right to submit it under the open source license indicated - in the file; or -* (b) The contribution is based upon previous work that, to the best - of my knowledge, is covered under an appropriate open source license - and I have the right under that license to submit that work with - modifications, whether created in whole or in part by me, under the - same open source license (unless I am permitted to submit under a - different license), as indicated in the file; or -* (c) The contribution was provided directly to me by some other - person who certified (a), (b) or (c) and I have not modified it. +- (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source + license indicated in the file; or +- (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate + open source license and I have the right under that license to submit that work with modifications, whether created in + whole or in part by me, under the same open source license (unless I am permitted to submit under a different + license), as indicated in the file; or +- (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not + modified it. diff --git a/README.md b/README.md index 6982cf6..b55c463 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # diff2html-cli -[![Codacy Quality Badge](https://api.codacy.com/project/badge/Grade/e6139937d72f40ed8b3920d53c74298a)](https://www.codacy.com/app/rtfpessoa/diff2html-cli?utm_source=github.com&utm_medium=referral&utm_content=rtfpessoa/diff2html-cli&utm_campaign=Badge_Grade) +[![Codacy Quality Badge](https://api.codacy.com/project/badge/Grade/e6139937d72f40ed8b3920d53c74298a)](https://www.codacy.com/app/rtfpessoa/diff2html-cli?utm_source=github.com&utm_medium=referral&utm_content=rtfpessoa/diff2html-cli&utm_campaign=Badge_Grade) [![Codacy Coverage Badge](https://api.codacy.com/project/badge/Coverage/e6139937d72f40ed8b3920d53c74298a)](https://www.codacy.com/app/rtfpessoa/diff2html-cli?utm_source=github.com&utm_medium=referral&utm_content=rtfpessoa/diff2html-cli&utm_campaign=Badge_Coverage) [![Circle CI](https://circleci.com/gh/rtfpessoa/diff2html-cli.svg?style=svg)](https://circleci.com/gh/rtfpessoa/diff2html-cli) @@ -8,28 +8,45 @@ [![David](https://img.shields.io/david/rtfpessoa/diff2html-cli.svg)](https://david-dm.org/rtfpessoa/diff2html-cli) [![David](https://img.shields.io/david/dev/rtfpessoa/diff2html-cli.svg)](https://david-dm.org/rtfpessoa/diff2html-cli) -[![node](https://img.shields.io/node/v/diff2html-cli.svg)]() -[![npm](https://img.shields.io/npm/l/diff2html-cli.svg)]() +[![node](https://img.shields.io/node/v/diff2html-cli.svg)]() [![npm](https://img.shields.io/npm/l/diff2html-cli.svg)]() [![npm](https://img.shields.io/npm/dm/diff2html-cli.svg)](https://www.npmjs.com/package/diff2html-cli) [![Gitter](https://badges.gitter.im/rtfpessoa/diff2html.svg)](https://gitter.im/rtfpessoa/diff2html?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) Diff to Html generates pretty HTML diffs from unified and git diff output in your terminal +## Table of Contents + + + +- [Features](#features) +- [Online Example](#online-example) +- [Distributions](#distributions) +- [Setup](#setup) +- [Usage](#usage) + - [Custom HTML wrapper template](#custom-html-wrapper-template) + - [Examples:](#examples) +- [Contributions](#contributions) +- [Developing](#developing) +- [License](#license) +- [Thanks](#thanks) + + + ## Features -* Unified diff and Git diff input +- Unified diff and Git diff input -* `line-by-line` and `side-by-side` diff +- `line-by-line` and `side-by-side` diff -* new and old line numbers +- new and old line numbers -* inserted and removed lines +- inserted and removed lines -* GitHub like style +- GitHub like style -* Code syntax highlight +- Code syntax highlight -* Line similarity matching +- Line similarity matching ## Online Example @@ -37,15 +54,16 @@ Diff to Html generates pretty HTML diffs from unified and git diff output in you ## Distributions -* [WebJar](http://www.webjars.org/) +- [WebJar](http://www.webjars.org/) -* [Node Module](https://www.npmjs.org/package/diff2html) +- [Node Module](https://www.npmjs.org/package/diff2html) -* [Bower Package](http://bower.io/search/?q=diff2html) +- [Bower Package](http://bower.io/search/?q=diff2html) -* [Node CLI](https://www.npmjs.org/package/diff2html-cli) +- [Node CLI](https://www.npmjs.org/package/diff2html-cli) -* Manually download and import [diff2html.min.js](https://raw.githubusercontent.com/rtfpessoa/diff2html/master/dist/diff2html.min.js) into your page +- Manually download and import + [diff2html.min.js](https://raw.githubusercontent.com/rtfpessoa/diff2html/master/dist/diff2html.min.js) into your page ## Setup @@ -55,33 +73,35 @@ Diff to Html generates pretty HTML diffs from unified and git diff output in you Usage: diff2html [options] -- [diff args] -| flag | alias | description | choices | default | -| --- | --- | --- | --- | --- | -| -s | --style | Output style | `line`, `side` | `line` | -| --sc | --synchronisedScroll | Synchronised horizontal scroll | `true`, `false` | `true` | -| --hc | --highlightCode | Highlight code | `true`, `false` | `true` | -| --su | --summary | Show files summary | `closed`, `open`, `hidden` | `closed` | -| --d | --diffStyle | Diff style | `word`, `char` | `word` | -| --lm | --matching | Diff line matching type | `lines`, `words`, `none` | `none` | -| --lmt | --matchWordsThreshold | Diff line matching word threshold | | `0.25` | -| --lmm | --matchingMaxComparisons | Diff line matching maximum line comparisons of a block of changes | `2500` | -| --hwt | --htmlWrapperTemplate | Path to custom template to be rendered when using the `html` output format | _[string]_ | -| -f | --format | Output format | `html`, `json` | `html` | -| -i | --input | Diff input source | `file`, `command`, `stdin` | `command` | -| -o | --output | Output destination | `preview`, `stdout` | `preview` | -| -u | --diffy | Upload to diffy.org | `browser`, `pbcopy`, `print` | | -| -F | --file | Send output to file (overrides output option) | _[string]_ | | -| --ig | --ignore | Ignore particular files from the diff | _[string]_ | | -| -v | --version | Show version number | | | -| -h | --help | Show help | | | +| flag | alias | description | choices | default | +| ----- | ------------------------ | -------------------------------------------------------------------------- | ---------------------------- | --------- | +| -s | --style | Output style | `line`, `side` | `line` | +| --sc | --synchronisedScroll | Synchronised horizontal scroll | `true`, `false` | `true` | +| --hc | --highlightCode | Highlight code | `true`, `false` | `true` | +| --su | --summary | Show files summary | `closed`, `open`, `hidden` | `closed` | +| --d | --diffStyle | Diff style | `word`, `char` | `word` | +| --lm | --matching | Diff line matching type | `lines`, `words`, `none` | `none` | +| --lmt | --matchWordsThreshold | Diff line matching word threshold | | `0.25` | +| --lmm | --matchingMaxComparisons | Diff line matching maximum line comparisons of a block of changes | `2500` | +| --hwt | --htmlWrapperTemplate | Path to custom template to be rendered when using the `html` output format | _[string]_ | +| -f | --format | Output format | `html`, `json` | `html` | +| -i | --input | Diff input source | `file`, `command`, `stdin` | `command` | +| -o | --output | Output destination | `preview`, `stdout` | `preview` | +| -u | --diffy | Upload to diffy.org | `browser`, `pbcopy`, `print` | | +| -F | --file | Send output to file (overrides output option) | _[string]_ | | +| --ig | --ignore | Ignore particular files from the diff | _[string]_ | | +| -v | --version | Show version number | | | +| -h | --help | Show help | | | ### Custom HTML wrapper template -The template is a very based on a simple replace of several placeholders as coded https://github.com/rtfpessoa/diff2html-cli/blob/master/src/cli.ts#L40 +The template is a very based on a simple replace of several placeholders as coded +https://github.com/rtfpessoa/diff2html-cli/blob/master/src/cli.ts#L40 -To provide a custom template you need to make sure you have the following comments and imports in your HTML, exactly as they are here: +To provide a custom template you need to make sure you have the following comments and imports in your HTML, exactly as +they are here: -* Inside the `` tag +- Inside the `` tag ``` @@ -100,7 +120,7 @@ To provide a custom template you need to make sure you have the following commen ``` -* Inside the `` tag +- Inside the `` tag ```
@@ -111,33 +131,40 @@ To provide a custom template you need to make sure you have the following commen ### Examples: `diff2html -s line -f html -d word -i command -o preview -- -M HEAD~1` -- diff last commit, line by line, word comparison between lines, previewed in the browser and input from git diff command + +- diff last commit, line by line, word comparison between lines, previewed in the browser and input from git diff + command `diff2html -i file -- my-file-diff.diff` + - reading the input from a file `diff -u file1.txt file2.txt | diff2html -i stdin` + - reading diff from stdin `diff2html -f json -o stdout -- -M HEAD~1` + - print json format to stdout `diff2html -F my-pretty-diff.html -- -M HEAD~1` -- print to file + +- print to file `diff2html -F my-pretty-diff.html --hwt my-custom-template.html -- -M HEAD~1` -- print to file using custom markup templates can include the following variables + +- print to file using custom markup templates can include the following variables `diff2html --ig package-lock.json --ig yarn.lock` + - Ignore `package-lock.json` and `yarn.lock` from the generated diff _NOTE_: notice the `--` in the examples ## Contributions -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. +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. ## Developing @@ -149,6 +176,7 @@ Copyright 2014-2019 Rodrigo Fernandes. Released under the terms of the MIT licen ## Thanks -This project is inspired in [pretty-diff](https://github.com/scottgonzalez/pretty-diff) by [Scott González](https://github.com/scottgonzalez). +This project is inspired in [pretty-diff](https://github.com/scottgonzalez/pretty-diff) by +[Scott González](https://github.com/scottgonzalez). --- diff --git a/SECURITY.md b/SECURITY.md index bf8c8ec..2a52c7a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -9,34 +9,28 @@ ## Reporting a Vulnerability -We take all security bugs in `diff2html-cli` seriously. -Thank you for the help improving the security of `diff2html-cli`. -We appreciate your efforts and responsible disclosure and -will make every effort to acknowledge your contributions. +We take all security bugs in `diff2html-cli` seriously. Thank you for the help improving the security of +`diff2html-cli`. We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your +contributions. Report security bugs by emailing the lead maintainer at `rtfrodrigo [at] gmail [dot] com`. -The lead maintainer will acknowledge your email within 48 hours, and will send a -more detailed response within 48 hours indicating the next steps in handling -your report. After the initial reply to your report, the security team will -endeavor to keep you informed of the progress towards a fix and full -announcement, and may ask for additional information or guidance. +The lead maintainer will acknowledge your email within 48 hours, and will send a more detailed response within 48 hours +indicating the next steps in handling your report. After the initial reply to your report, the security team will +endeavor to keep you informed of the progress towards a fix and full announcement, and may ask for additional +information or guidance. -Report security bugs in third-party modules to the person or team maintaining -the module. +Report security bugs in third-party modules to the person or team maintaining the module. ## Disclosure Policy -When the security team receives a security bug report, they will assign it to a -primary handler. This person will coordinate the fix and release process, -involving the following steps: +When the security team receives a security bug report, they will assign it to a primary handler. This person will +coordinate the fix and release process, involving the following steps: - * Confirm the problem and determine the affected versions. - * Audit code to find any potential similar problems. - * Prepare fixes for all releases still under maintenance. These fixes will be - released as fast as possible. +- Confirm the problem and determine the affected versions. +- Audit code to find any potential similar problems. +- Prepare fixes for all releases still under maintenance. These fixes will be released as fast as possible. ## Comments on this Policy -If you have suggestions on how this process could be improved please submit a -pull request. +If you have suggestions on how this process could be improved please submit a pull request. diff --git a/bin/diff2html b/bin/diff2html index 210725c..29baf6a 100755 --- a/bin/diff2html +++ b/bin/diff2html @@ -1,3 +1,3 @@ #!/usr/bin/env node -require("../build/main.js").main(); +require("../lib/main.js").main(); diff --git a/jest.config.js b/jest.config.js index 97d8be5..b5ce8f5 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,15 +1,16 @@ module.exports = { verbose: true, - preset: "ts-jest", - testEnvironment: "node", - coverageDirectory: "./coverage", - coverageReporters: ["lcov", "text", "html"], + preset: 'ts-jest', + testEnvironment: 'node', + coverageDirectory: './coverage', + coverageReporters: ['lcov', 'text', 'html', 'json', 'cobertura', 'clover'], + collectCoverageFrom: ['src/**/*.ts', '!src/__tests__/**', '!node_modules/**'], coverageThreshold: { global: { - statements: 40, - branches: 15, + statements: 35, + branches: 14, functions: 33, - lines: 40 - } - } + lines: 34, + }, + }, }; diff --git a/package.json b/package.json index 86bbc03..be345fe 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "diff2html-cli", "version": "4.2.1", - "homepage": "https://www.github.com/rtfpessoa/diff2html-cli", + "homepage": "https://diff2html.xyz/index.html#cli", "description": "Fast Diff to colorized HTML", "keywords": [ "git", @@ -32,25 +32,36 @@ "url": "https://www.github.com/rtfpessoa/diff2html-cli/issues" }, "engines": { - "node": "8.* || >=10" + "node": ">=10.13" }, "preferGlobal": true, "scripts": { - "prepare": "yarn run build", - "build": "tsc", - "lint": "eslint '*/**/*.{js,jsx,ts,tsx}'", - "style": "yarn run lint", - "test": "jest", - "coverage": "jest --collectCoverage", - "coverage-html": "yarn run coverage && open ./coverage/index.html", - "codacy": "cat ./coverage/lcov.info | codacy-coverage", - "preversion": "yarn run test", - "postversion": "git push && git push --tags" + "eslint": "eslint --ignore-path .gitignore \"**/*.{js,jsx,ts,tsx,json}\"", + "lint:check": "yarn run eslint", + "lint:fix": "yarn run eslint --fix", + "prettier": "prettier --ignore-path .gitignore '**/*.+(js|jsx|ts|tsx|json|css|html|md|mdx)'", + "format:check": "yarn run prettier --check", + "format:fix": "yarn run prettier --write", + "build": "yarn run build:es5", + "build:es5": "rm -rf lib; tsc -p tsconfig.json --outDir lib", + "build:toc-base": "markdown-toc --maxdepth 3 --bullets='-' -i", + "build:toc": "yarn run build:toc-base README.md", + "test": "is-ci 'test:coverage' 'test:watch'", + "test:coverage": "jest --coverage", + "test:watch": "jest --watch", + "test:debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand --watch", + "coverage:open": "yarn run test:coverage && open ./coverage/index.html", + "coverage:push": "cat ./coverage/lcov.info | codacy-coverage", + "validate": "yarn run format:check && yarn run lint:check && yarn run build && yarn run test:coverage", + "fix": "yarn run format:fix && yarn run lint:fix", + "preversion": "yarn run validate", + "version": "git add -A package.json" }, "bin": { "diff2html": "./bin/diff2html" }, - "main": "./build/main.js", + "main": "./lib/diff2html.js", + "types": "./lib/diff2html.d.ts", "dependencies": { "clipboardy": "^2.1.0", "diff2html": "^2.12.1", @@ -59,30 +70,34 @@ "yargs": "^15.0.2" }, "devDependencies": { - "@types/jest": "24.0.25", - "@types/node": "^13.1.2", "@types/request": "2.48.4", - "@typescript-eslint/eslint-plugin": "2.15.0", + "@types/jest": "24.9.0", + "@types/node": "13.1.8", + "@typescript-eslint/eslint-plugin": "2.16.0", "@typescript-eslint/parser": "2.16.0", - "codacy-coverage": "^3.4.0", + "codacy-coverage": "3.4.0", "eslint": "6.8.0", "eslint-config-prettier": "6.9.0", - "eslint-config-standard": "14.1.0", "eslint-plugin-import": "2.20.0", "eslint-plugin-jest": "23.6.0", + "eslint-plugin-json": "2.0.1", "eslint-plugin-node": "11.0.0", - "eslint-plugin-prettier": "3.1.2", - "eslint-plugin-promise": "^4.2.1", - "eslint-plugin-standard": "^4.0.1", + "eslint-plugin-optimize-regex": "1.1.7", + "eslint-plugin-promise": "4.2.1", + "eslint-plugin-sonarjs": "0.5.0", + "husky": "4.0.10", + "is-ci-cli": "2.0.0", "jest": "24.9.0", + "lint-staged": "9.5.0", + "markdown-toc": "^1.2.0", "prettier": "1.19.1", "ts-jest": "24.3.0", - "typescript": "^3.5.3" + "typescript": "3.7.5" }, "license": "MIT", "files": [ "bin", - "build", + "lib", "template.html" ] } diff --git a/src/__tests__/cli-tests.ts b/src/__tests__/cli-tests.ts index a1eea0d..d15855a 100644 --- a/src/__tests__/cli-tests.ts +++ b/src/__tests__/cli-tests.ts @@ -1,58 +1,58 @@ -import * as cli from "../cli"; -import * as http from "../http-utils"; -import * as utils from "../utils"; +import * as cli from '../cli'; +import * as http from '../http-utils'; +import * as utils from '../utils'; beforeEach(() => jest.clearAllMocks()); -describe("cli", () => { - describe("getInput", () => { - test("should readFile when inputType is `file`", async () => { - const readFileSpy = jest.spyOn(utils, "readFile").mockImplementationOnce(() => "contents"); +describe('cli', () => { + describe('getInput', () => { + test('should readFile when inputType is `file`', async () => { + const readFileSpy = jest.spyOn(utils, 'readFile').mockImplementationOnce(() => 'contents'); - await cli.getInput("file", ["lol", "foo"], []); + await cli.getInput('file', ['lol', 'foo'], []); expect(readFileSpy).toHaveBeenCalledTimes(1); - expect(readFileSpy).toHaveBeenCalledWith("lol"); + expect(readFileSpy).toHaveBeenCalledWith('lol'); }); - test("should readStdin when inputType is `stdin`", async () => { - const readStdinSpy = jest.spyOn(utils, "readStdin").mockImplementationOnce(() => Promise.resolve("contents")); + test('should readStdin when inputType is `stdin`', async () => { + const readStdinSpy = jest.spyOn(utils, 'readStdin').mockImplementationOnce(() => Promise.resolve('contents')); - await cli.getInput("stdin", ["lol"], []); + await cli.getInput('stdin', ['lol'], []); expect(readStdinSpy).toHaveBeenCalledTimes(1); expect(readStdinSpy).toHaveBeenCalledWith(); }); - test("should readStdin when inputType is `command`", async () => { - const executeSpy = jest.spyOn(utils, "execute").mockImplementationOnce(() => ""); + test('should readStdin when inputType is `command`', async () => { + const executeSpy = jest.spyOn(utils, 'execute').mockImplementationOnce(() => ''); - await cli.getInput("command", ["lol", "foo"], []); + await cli.getInput('command', ['lol', 'foo'], []); expect(executeSpy).toHaveBeenCalledTimes(1); expect(executeSpy).toHaveBeenCalledWith('git diff "lol" "foo" --no-color '); }); }); - describe("preview", () => { - test("should call `utils.writeFile`", () => { + describe('preview', () => { + test('should call `utils.writeFile`', () => { // eslint-disable-next-line @typescript-eslint/no-empty-function - const writeFileSpy = jest.spyOn(utils, "writeFile").mockImplementationOnce(() => {}); + const writeFileSpy = jest.spyOn(utils, 'writeFile').mockImplementationOnce(() => {}); - cli.preview("a", "b"); + cli.preview('a', 'b'); expect(writeFileSpy).toHaveBeenCalledTimes(1); }); }); - describe("postToDiffy", () => { - test("should call `http.put`", async () => { - const putSpy = jest.spyOn(http, "put").mockImplementationOnce(() => Promise.resolve({ id: "foo" })); + describe('postToDiffy', () => { + test('should call `http.put`', async () => { + const putSpy = jest.spyOn(http, 'put').mockImplementationOnce(() => Promise.resolve({ id: 'foo' })); - await cli.postToDiffy("a", "print"); + await cli.postToDiffy('a', 'print'); expect(putSpy).toHaveBeenCalledTimes(1); - expect(putSpy).toHaveBeenCalledWith("https://diffy.org/api/diff/", { diff: "a" }); + expect(putSpy).toHaveBeenCalledWith('https://diffy.org/api/diff/', { diff: 'a' }); }); }); }); diff --git a/src/__tests__/utils-tests.ts b/src/__tests__/utils-tests.ts index 1c145a4..0fd865a 100644 --- a/src/__tests__/utils-tests.ts +++ b/src/__tests__/utils-tests.ts @@ -1,26 +1,26 @@ -import * as utils from "../utils"; +import * as utils from '../utils'; -const stringToCompare = "This is a rand0m preTTy string to write and read a file"; +const stringToCompare = 'This is a rand0m preTTy string to write and read a file'; -describe("utils", () => { - test("should write and read file synchronously", () => { - utils.writeFile("/tmp/file.test", stringToCompare); +describe('utils', () => { + test('should write and read file synchronously', () => { + utils.writeFile('/tmp/file.test', stringToCompare); - const contentRead = utils.readFile("/tmp/file.test"); + const contentRead = utils.readFile('/tmp/file.test'); expect(stringToCompare).toBe(contentRead); }); - test("should execute command in shell", () => { - const echoedValue = "echoed string"; + test('should execute command in shell', () => { + const echoedValue = 'echoed string'; const result = utils.execute(`echo "${echoedValue}"`); expect(result).toBe(`${echoedValue}\n`); }); - test("should replace exactly string", () => { - const result = utils.replaceExactly("my long and nice text", "long", "$&beautiful"); + test('should replace exactly string', () => { + const result = utils.replaceExactly('my long and nice text', 'long', '$&beautiful'); - expect(result).toBe("my $&beautiful and nice text"); + expect(result).toBe('my $&beautiful and nice text'); }); }); diff --git a/src/cli.ts b/src/cli.ts index a23d64c..1c59711 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,22 +1,22 @@ -import * as fs from "fs"; -import * as os from "os"; -import * as path from "path"; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; -import * as clipboardy from "clipboardy"; -import * as opn from "open"; -import { Diff2Html } from "diff2html"; +import * as clipboardy from 'clipboardy'; +import open = require('open'); +import { Diff2Html } from 'diff2html'; -import * as http from "./http-utils"; -import * as log from "./logger"; -import { Configuration, InputType, DiffyType } from "./types"; -import * as utils from "./utils"; +import * as http from './http-utils'; +import * as log from './logger'; +import { Configuration, InputType, DiffyType } from './types'; +import * as utils from './utils'; function runGitDiff(gitArgsArr: string[], ignore: string[]): string { - const baseArgs = gitArgsArr.length > 0 ? gitArgsArr.map((arg) => `"${arg}"`) : ["-M", "-C", "HEAD"]; - const colorArgs = gitArgsArr.indexOf("--no-color") < 0 ? ["--no-color"] : []; - const ignoreArgs = ignore.map((file) => `":(exclude)${file}"`); + const baseArgs = gitArgsArr.length > 0 ? gitArgsArr.map(arg => `"${arg}"`) : ['-M', '-C', 'HEAD']; + const colorArgs = gitArgsArr.indexOf('--no-color') < 0 ? ['--no-color'] : []; + const ignoreArgs = ignore.map(file => `":(exclude)${file}"`); - const diffCommand = `git diff ${baseArgs.join(" ")} ${colorArgs.join(" ")} ${ignoreArgs.join(" ")}`; + const diffCommand = `git diff ${baseArgs.join(' ')} ${colorArgs.join(' ')} ${ignoreArgs.join(' ')}`; return utils.execute(diffCommand); } @@ -24,12 +24,12 @@ function runGitDiff(gitArgsArr: string[], ignore: string[]): string { function prepareHTML(diffHTMLContent: string, config: Configuration): string { const template = utils.readFile(config.htmlWrapperTemplate); - const diff2htmlPath = path.join(path.dirname(require.resolve("diff2html")), ".."); + const diff2htmlPath = path.join(path.dirname(require.resolve('diff2html')), '..'); - const cssFilePath = path.resolve(diff2htmlPath, "dist", "diff2html.min.css"); + const cssFilePath = path.resolve(diff2htmlPath, 'dist', 'diff2html.min.css'); const cssContent = utils.readFile(cssFilePath); - const jsUiFilePath = path.resolve(diff2htmlPath, "dist", "diff2html-ui.min.js"); + const jsUiFilePath = path.resolve(diff2htmlPath, 'dist', 'diff2html-ui.min.js'); const jsUiContent = utils.readFile(jsUiFilePath); /* HACK: @@ -37,25 +37,25 @@ function prepareHTML(diffHTMLContent: string, config: Configuration): string { * This will avoid the replacements from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_string_as_a_parameter */ return [ - { searchValue: "", replaceValue: `` }, - { searchValue: "", replaceValue: `` }, + { searchValue: '', replaceValue: `` }, + { searchValue: '', replaceValue: `` }, { - searchValue: "//diff2html-fileListCloseable", - replaceValue: `diff2htmlUi.fileListCloseable("#diff", ${config.showFilesOpen});` + searchValue: '//diff2html-fileListCloseable', + replaceValue: `diff2htmlUi.fileListCloseable("#diff", ${config.showFilesOpen});`, }, { - searchValue: "//diff2html-synchronisedScroll", - replaceValue: `diff2htmlUi.synchronisedScroll("#diff", ${config.synchronisedScroll});` + searchValue: '//diff2html-synchronisedScroll', + replaceValue: `diff2htmlUi.synchronisedScroll("#diff", ${config.synchronisedScroll});`, }, { - searchValue: "//diff2html-highlightCode", - replaceValue: config.highlightCode ? `diff2htmlUi.highlightCode("#diff");` : "" + searchValue: '//diff2html-highlightCode', + replaceValue: config.highlightCode ? `diff2htmlUi.highlightCode("#diff");` : '', }, - { searchValue: "", replaceValue: diffHTMLContent } + { searchValue: '', replaceValue: diffHTMLContent }, ].reduce( (previousValue, replacement) => utils.replaceExactly(previousValue, replacement.searchValue, replacement.replaceValue), - template + template, ); } @@ -67,10 +67,10 @@ function prepareHTML(diffHTMLContent: string, config: Configuration): string { */ export async function getInput(inputType: InputType, inputArgs: string[], ignore: string[]): Promise { switch (inputType) { - case "file": + case 'file': return utils.readFile(inputArgs[0]); - case "stdin": + case 'stdin': return utils.readStdin(); default: @@ -85,10 +85,10 @@ export function getOutput(options: Diff2Html.Options, config: Configuration, inp const diffJson = Diff2Html.getJsonFromDiff(input, options); - if (config.formatType === "html") { - const htmlContent = Diff2Html.getPrettyHtml(diffJson, { ...options, inputFormat: "json" }); + if (config.formatType === 'html') { + const htmlContent = Diff2Html.getPrettyHtml(diffJson, { ...options, inputFormat: 'json' }); return prepareHTML(htmlContent, config); - } else if (config.formatType === "json") { + } else if (config.formatType === 'json') { return JSON.stringify(diffJson); } @@ -99,20 +99,20 @@ export function preview(content: string, format: string): void { const filename = `diff.${format}`; const filePath: string = path.resolve(os.tmpdir(), filename); utils.writeFile(filePath, content); - opn(filePath, { wait: false }); + open(filePath, { wait: false }); } export async function postToDiffy(diff: string, diffyOutput: DiffyType): Promise { - const response = await http.put<{ id: string }>("https://diffy.org/api/diff/", { diff: diff }); + const response = await http.put<{ id: string }>('https://diffy.org/api/diff/', { diff: diff }); const url = `https://diffy.org/diff/${response.id}`; - log.print("Link powered by https://diffy.org"); + log.print('Link powered by https://diffy.org'); log.print(url); - if (diffyOutput === "browser") { - opn(url); - } else if (diffyOutput === "pbcopy") { + if (diffyOutput === 'browser') { + open(url); + } else if (diffyOutput === 'pbcopy') { clipboardy.writeSync(url); } diff --git a/src/configuration.ts b/src/configuration.ts index fc3c5cd..28e1536 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -1,22 +1,22 @@ -import * as path from "path"; +import * as path from 'path'; -import { Configuration } from "./types"; -import { Argv } from "./yargs"; +import { Configuration } from './types'; +import { Argv } from './yargs'; export function parseArgv(argv: Argv): [Diff2Html.Options, Configuration] { const diff2htmlOptions: Diff2Html.Options = { - inputFormat: "diff", - outputFormat: argv.style === "side" ? "side-by-side" : "line-by-line", - showFiles: argv.summary !== "hidden", + inputFormat: 'diff', + outputFormat: argv.style === 'side' ? 'side-by-side' : 'line-by-line', + showFiles: argv.summary !== 'hidden', diffStyle: argv.diffStyle, matching: argv.matching, matchWordsThreshold: argv.matchWordsThreshold, - matchingMaxComparisons: argv.matchingMaxComparisons + matchingMaxComparisons: argv.matchingMaxComparisons, }; - const defaultWrapperTemplate = path.resolve(__dirname, "..", "template.html"); + const defaultWrapperTemplate = path.resolve(__dirname, '..', 'template.html'); const configuration: Configuration = { - showFilesOpen: argv.summary === "open" || false, + showFilesOpen: argv.summary === 'open' || false, synchronisedScroll: argv.synchronisedScroll, highlightCode: argv.highlightCode, formatType: argv.format, @@ -25,7 +25,7 @@ export function parseArgv(argv: Argv): [Diff2Html.Options, Configuration] { inputSource: argv.input, diffyType: argv.diffy, htmlWrapperTemplate: argv.htmlWrapperTemplate || defaultWrapperTemplate, - ignore: argv.ignore || [] + ignore: argv.ignore || [], }; return [diff2htmlOptions, configuration]; diff --git a/src/http-utils.ts b/src/http-utils.ts index 296b916..c2f9dd6 100644 --- a/src/http-utils.ts +++ b/src/http-utils.ts @@ -1,29 +1,29 @@ -import * as request from "request"; +import request from 'request'; export function put(url: string, payload: object): Promise { return new Promise((resolve, reject): void => { request({ url: url, - method: "PUT", + method: 'PUT', headers: {}, body: payload, - json: true + json: true, }) - .on("response", (response) => { - response.on("data", (body) => { + .on('response', response => { + response.on('data', body => { try { - const jsonObj = JSON.parse(body.toString("utf8")); + const jsonObj = JSON.parse(body.toString('utf8')); if (jsonObj as T) { return resolve(jsonObj); - } else if (jsonObj.error !== undefined && typeof jsonObj.error === "string") { + } else if (jsonObj.error !== undefined && typeof jsonObj.error === 'string') { return reject(new Error(jsonObj.error)); } else { return reject( new Error( `Failed to read response. Body: - ${body.toString("utf8")}` - ) + ${body.toString('utf8')}`, + ), ); } } catch (err) { @@ -31,6 +31,6 @@ export function put(url: string, payload: object): Promise } }); }) - .on("error", (err) => reject(err)); + .on('error', err => reject(err)); }); } diff --git a/src/main.ts b/src/main.ts index 7a37c77..cff9681 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,8 @@ -import * as cli from "./cli"; -import { parseArgv } from "./configuration"; -import * as log from "./logger"; -import * as utils from "./utils"; -import * as yargs from "./yargs"; +import * as cli from './cli'; +import { parseArgv } from './configuration'; +import * as log from './logger'; +import * as utils from './utils'; +import * as yargs from './yargs'; export async function main(): Promise { try { @@ -12,7 +12,7 @@ export async function main(): Promise { const input = await cli.getInput(configuration.inputSource, argv.extraArguments, configuration.ignore); if (!input) { - log.error("The input is empty. Try again."); + log.error('The input is empty. Try again.'); yargs.help(); return; } @@ -26,9 +26,9 @@ export async function main(): Promise { if (configuration.outputDestinationFile) utils.writeFile(configuration.outputDestinationFile, output); - if (configuration.outputDestinationType === "preview") { + if (configuration.outputDestinationType === 'preview') { cli.preview(output, configuration.formatType); - } else if (configuration.outputDestinationType === "stdout") { + } else if (configuration.outputDestinationType === 'stdout') { log.print(output); } } catch (error) { diff --git a/src/types.ts b/src/types.ts index 3eadc8a..a1bd043 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,11 +1,11 @@ -export type StyleType = "line" | "side"; -export type SummaryType = "closed" | "open" | "hidden"; -export type DiffStyleType = "word" | "char"; -export type LineMatchingType = "lines" | "words" | "none"; -export type FormatType = "html" | "json"; -export type InputType = "file" | "command" | "stdin"; -export type OutputType = "preview" | "stdout"; -export type DiffyType = "browser" | "pbcopy" | "print"; +export type StyleType = 'line' | 'side'; +export type SummaryType = 'closed' | 'open' | 'hidden'; +export type DiffStyleType = 'word' | 'char'; +export type LineMatchingType = 'lines' | 'words' | 'none'; +export type FormatType = 'html' | 'json'; +export type InputType = 'file' | 'command' | 'stdin'; +export type OutputType = 'preview' | 'stdout'; +export type DiffyType = 'browser' | 'pbcopy' | 'print'; export type Configuration = { synchronisedScroll: boolean; diff --git a/src/utils.ts b/src/utils.ts index f1fe235..e2002d7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,5 @@ -import * as childProcess from "child_process"; -import * as fs from "fs"; +import * as childProcess from 'child_process'; +import * as fs from 'fs'; export function exists(filePath: string): boolean { try { @@ -10,17 +10,17 @@ export function exists(filePath: string): boolean { } export function readFile(filePath: string): string { - return fs.readFileSync(filePath, "utf8"); + return fs.readFileSync(filePath, 'utf8'); } export function readStdin(): Promise { return new Promise((resolve): void => { - let content = ""; + let content = ''; process.stdin.resume(); - process.stdin.on("data", (buf) => { - content += buf.toString("utf8"); + process.stdin.on('data', buf => { + content += buf.toString('utf8'); }); - process.stdin.on("end", () => resolve(content)); + process.stdin.on('end', () => resolve(content)); }); } @@ -29,7 +29,7 @@ export function writeFile(filePath: string, content: string): void { } export function execute(cmd: string): string { - return childProcess.execSync(cmd).toString("utf8"); + return childProcess.execSync(cmd).toString('utf8'); } export function replaceExactly(value: string, searchValue: string, replaceValue: string): string { diff --git a/src/yargs.ts b/src/yargs.ts index 551f404..d406c5d 100644 --- a/src/yargs.ts +++ b/src/yargs.ts @@ -1,4 +1,4 @@ -import * as yargs from "yargs"; +import * as yargs from 'yargs'; import { StyleType, @@ -8,8 +8,8 @@ import { InputType, OutputType, DiffyType, - DiffStyleType -} from "./types"; + DiffStyleType, +} from './types'; export type Argv = { style: StyleType; @@ -33,169 +33,169 @@ export type Argv = { export function setup(): Argv { const currentYear = new Date().getFullYear(); - const styleChoices: StyleType[] = ["line", "side"]; - const summaryChoices: SummaryType[] = ["closed", "open", "hidden"]; - const diffStyleChoices: DiffStyleType[] = ["word", "char"]; - const matchingChoices: LineMatchingType[] = ["lines", "words", "none"]; - const formatChoices: FormatType[] = ["html", "json"]; - const inputChoices: InputType[] = ["file", "command", "stdin"]; - const outputChoices: OutputType[] = ["preview", "stdout"]; - const diffyChoices: DiffyType[] = ["browser", "pbcopy", "print"]; + const styleChoices: StyleType[] = ['line', 'side']; + const summaryChoices: SummaryType[] = ['closed', 'open', 'hidden']; + const diffStyleChoices: DiffStyleType[] = ['word', 'char']; + const matchingChoices: LineMatchingType[] = ['lines', 'words', 'none']; + const formatChoices: FormatType[] = ['html', 'json']; + const inputChoices: InputType[] = ['file', 'command', 'stdin']; + const outputChoices: OutputType[] = ['preview', 'stdout']; + const diffyChoices: DiffyType[] = ['browser', 'pbcopy', 'print']; const argv = yargs - .usage("Usage: diff2html [options] -- [diff args]") + .usage('Usage: diff2html [options] -- [diff args]') .options({ style: { - alias: "s", - describe: "Output style", + alias: 's', + describe: 'Output style', nargs: 1, - type: "string", + type: 'string', choices: styleChoices, - default: "line" - } + default: 'line', + }, }) .options({ synchronisedScroll: { - alias: "sc", - describe: "Synchronised horizontal scroll", - type: "boolean", - default: true - } + alias: 'sc', + describe: 'Synchronised horizontal scroll', + type: 'boolean', + default: true, + }, }) .options({ highlightCode: { - alias: "hc", - describe: "Highlight Code", - type: "boolean", - default: true - } + alias: 'hc', + describe: 'Highlight Code', + type: 'boolean', + default: true, + }, }) .options({ summary: { - alias: "su", - describe: "Show files summary", - type: "string", + alias: 'su', + describe: 'Show files summary', + type: 'string', choices: summaryChoices, - default: "closed" - } + default: 'closed', + }, }) .options({ diff: { - alias: "d", - describe: "Diff style", + alias: 'd', + describe: 'Diff style', nargs: 1, - type: "string", + type: 'string', choices: diffStyleChoices, - default: "word" - } + default: 'word', + }, }) .options({ matching: { - alias: "lm", - describe: "Diff line matching type", + alias: 'lm', + describe: 'Diff line matching type', nargs: 1, - type: "string", + type: 'string', choices: matchingChoices, - default: "none" - } + default: 'none', + }, }) .options({ matchWordsThreshold: { - alias: "lmt", - describe: "Diff line matching word threshold", + alias: 'lmt', + describe: 'Diff line matching word threshold', nargs: 1, - type: "number", - default: 0.25 - } + type: 'number', + default: 0.25, + }, }) .options({ matchingMaxComparisons: { - alias: "lmm", - describe: "Diff line matching maximum line comparisons of a block of changes", + alias: 'lmm', + describe: 'Diff line matching maximum line comparisons of a block of changes', nargs: 1, - type: "number", - default: 1000 - } + type: 'number', + default: 1000, + }, }) .options({ format: { - alias: "f", - describe: "Output format", + alias: 'f', + describe: 'Output format', nargs: 1, - type: "string", + type: 'string', choices: formatChoices, - default: "html" - } + default: 'html', + }, }) .options({ input: { - alias: "i", - describe: "Diff input source", + alias: 'i', + describe: 'Diff input source', nargs: 1, - type: "string", + type: 'string', choices: inputChoices, - default: "command" - } + default: 'command', + }, }) .options({ output: { - alias: "o", - describe: "Output destination", + alias: 'o', + describe: 'Output destination', nargs: 1, - type: "string", + type: 'string', choices: outputChoices, - default: "preview" - } + default: 'preview', + }, }) .options({ diffy: { - alias: "u", - describe: "Upload to diffy.org", + alias: 'u', + describe: 'Upload to diffy.org', nargs: 1, - type: "string", - choices: diffyChoices - } + type: 'string', + choices: diffyChoices, + }, }) .options({ file: { - alias: "F", - describe: "Send output to file (overrides output option)", + alias: 'F', + describe: 'Send output to file (overrides output option)', nargs: 1, - type: "string" - } + type: 'string', + }, }) .options({ htmlWrapperTemplate: { - alias: "hwt", - describe: "Use a custom template when generating markup", + alias: 'hwt', + describe: 'Use a custom template when generating markup', nargs: 1, - type: "string" - } + type: 'string', + }, }) .options({ ignore: { - alias: "ig", - describe: "ignore a file", + alias: 'ig', + describe: 'ignore a file', nargs: 1, - type: "array" - } + type: 'array', + }, }) .example( - "diff2html -s line -f html -d word -i command -o preview -- -M HEAD~1", - "diff last commit, line by line, word comparison between lines," + - "previewed in the browser and input from git diff command" + 'diff2html -s line -f html -d word -i command -o preview -- -M HEAD~1', + 'diff last commit, line by line, word comparison between lines,' + + 'previewed in the browser and input from git diff command', ) - .example("diff2html -i file -- my-file-diff.diff", "reading the input from a file") - .example("diff2html -f json -o stdout -- -M HEAD~1", "print json format to stdout") - .example("diff2html -F my-pretty-diff.html -- -M HEAD~1", "print to file") - .example("diff2html --ig package-lock.json --ig yarn.lock", "ignore two particular files when generating the diff") - .help("h") - .alias("v", "version") - .alias("h", "help") + .example('diff2html -i file -- my-file-diff.diff', 'reading the input from a file') + .example('diff2html -f json -o stdout -- -M HEAD~1', 'print json format to stdout') + .example('diff2html -F my-pretty-diff.html -- -M HEAD~1', 'print to file') + .example('diff2html --ig package-lock.json --ig yarn.lock', 'ignore two particular files when generating the diff') + .help('h') + .alias('v', 'version') + .alias('h', 'help') .epilog( `© 2014-${currentYear} rtfpessoa For more information, check out https://diff2html.xyz/ - For support, check out https://github.com/rtfpessoa/diff2html-cli` + For support, check out https://github.com/rtfpessoa/diff2html-cli`, ) .strict(true) .recommendCommands().argv; @@ -211,11 +211,11 @@ export function setup(): Argv { input: argv.input as InputType, output: argv.output as OutputType, diffy: argv.diffy as DiffyType, - ignore: (argv.ignore || []).map((e) => e.toString()), - extraArguments: argv._ || [] + ignore: (argv.ignore || []).map(e => e.toString()), + extraArguments: argv._ || [], }; } export function help(): void { - yargs.showHelp("log"); + yargs.showHelp('log'); } diff --git a/tsconfig.json b/tsconfig.json index 6b01608..3262295 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,17 +1,18 @@ { "compilerOptions": { - "outDir": "./build", + "outDir": "./lib", "target": "es5", "module": "commonjs", + "moduleResolution": "node", + "lib": ["es2015"], "allowJs": false, + "declaration": true, + "declarationMap": true, "strictNullChecks": true, "removeComments": true, "preserveConstEnums": true, "sourceMap": true, "alwaysStrict": true, - "declaration": true, - "declarationMap": true, - "moduleResolution": "node", "strict": true, "noImplicitAny": true, "noImplicitThis": true, @@ -19,7 +20,8 @@ "noUnusedLocals": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "forceConsistentCasingInFileNames": true + "forceConsistentCasingInFileNames": true, + "esModuleInterop": true }, "include": ["./src/*"], "exclude": ["node_modules", "./src/__tests__/*"]