diff --git a/assets/hdiff/hdiff.png b/assets/hdiff/hdiff.png new file mode 100644 index 0000000000..2ca473cb57 Binary files /dev/null and b/assets/hdiff/hdiff.png differ diff --git a/packages/hdiff/LICENSE b/packages/hdiff/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/packages/hdiff/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/hdiff/README.md b/packages/hdiff/README.md new file mode 100644 index 0000000000..3499d36018 --- /dev/null +++ b/packages/hdiff/README.md @@ -0,0 +1,119 @@ + + +# ![hdiff](https://media.thi.ng/umbrella/banners/thing-hdiff.svg?421a59f3) + +[![npm version](https://img.shields.io/npm/v/@thi.ng/hdiff.svg)](https://www.npmjs.com/package/@thi.ng/hdiff) +![npm downloads](https://img.shields.io/npm/dm/@thi.ng/hdiff.svg) +[![Twitter Follow](https://img.shields.io/twitter/follow/thing_umbrella.svg?style=flat-square&label=twitter)](https://twitter.com/thing_umbrella) + +This project is part of the +[@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo. + +- [About](#about) + - [Status](#status) +- [Installation](#installation) + - [CLI installation & usage](#cli-installation--usage) +- [Dependencies](#dependencies) +- [API](#api) + - [computeDiff()](#computediff) + - [generateHtml()](#generatehtml) + - [compileTheme()](#compiletheme) +- [Authors](#authors) +- [License](#license) + +## About + +String diffing w/ hiccup output for further processing, e.g. with [@thi.ng/hdom](https://github.com/thi-ng/umbrella/tree/develop/packages/hdom), [@thi.ng/hiccup](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup). Includes CLI util to generate HTML, with theme support and code folding. + +![screenshot of example output](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/hdiff/hdiff.png) + +### Status + +**ALPHA** - bleeding edge / work-in-progress + +## Installation + +```bash +yarn add @thi.ng/hdiff +``` + +```html +// ES module + + +// UMD + +``` + +Package sizes (gzipped, pre-treeshake): ESM: 1.56 KB / CJS: 1.63 KB / UMD: 1.68 KB + +### CLI installation & usage + +Current limitations: + +- output always written to stdout only +- only single theme available for now (easy to add new ones, PRs welcome!) + +```bash +npx @thi.ng/hdiff + +# any text files +npx hdiff file-a.txt file-b.txt > diff.html + +# git revisions for given file (in local repo) +# rev can be any commit-ish ID understood by git (sha1, tag, etc.) +npx hdiff rel-file-path rev1 rev2 > diff.html + +# example +npx hdiff packages/webgl/src/shader.ts develop~500 HEAD > diff.html +``` + +## Dependencies + +- [@thi.ng/diff](https://github.com/thi-ng/umbrella/tree/develop/packages/diff) +- [@thi.ng/hiccup](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup) +- [@thi.ng/hiccup-css](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup-css) +- [@thi.ng/strings](https://github.com/thi-ng/umbrella/tree/develop/packages/strings) + +## API + +[Generated API docs](https://docs.thi.ng/umbrella/hdiff/) + +### computeDiff() + +Signature: `computeDiff(a: string, b: string) => any[]` + +Takes two strings and performs line-based diff, then formats result as +tree of HTML elements in +[@thi.ng/hiccup](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup) +format. + +The generated format only uses the following data attributes: + +- `data-diff`: diff status for each `code` line (`"+"`, `"-"` or `" "`) +- `data-lnum`: formatted line number for each `code` line +- `data-fold`: indicates folded `pre`-block +- `data-fold-range`: line number range string + +### generateHtml() + +Signature: `generateHtml(header: any[], body: any[], theme: Theme) => string` + +Takes two hiccup trees for header and body and an optional theme. +Compiles theme into CSS, serializes hiccup trees and returns complete +HTML document as string. + +### compileTheme() + +Signature: `compileTheme(theme: Theme) => string` + +Compiles a theme config into a complete CSS stylesheet string (using +[@thi.ng/hiccup-css](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup-css)). + +## Authors + +Karsten Schmidt + +## License + +© 2018 - 2020 Karsten Schmidt // Apache Software License 2.0 diff --git a/packages/hdiff/api-extractor.json b/packages/hdiff/api-extractor.json new file mode 100644 index 0000000000..94972e6bed --- /dev/null +++ b/packages/hdiff/api-extractor.json @@ -0,0 +1,3 @@ +{ + "extends": "../../api-extractor.json" +} diff --git a/packages/hdiff/bin/cli.js b/packages/hdiff/bin/cli.js new file mode 100755 index 0000000000..ce6be409a8 --- /dev/null +++ b/packages/hdiff/bin/cli.js @@ -0,0 +1,27 @@ +#!/usr/bin/env node + +const fs = require("fs"); +const cproc = require("child_process"); +const hdiff = require("@thi.ng/hdiff"); + +let src1; +let src2; +let headerBody; + +const args = process.argv.slice(2); +if (args.length === 3) { + const [path, rev1, rev2] = args; + src1 = cproc.execSync(`git show ${rev1}:${path}`).toString(); + src2 = cproc.execSync(`git show ${rev2}:${path}`).toString(); + headerBody = ["header", ["h1", path], ["code", `${rev1} ⇌ ${rev2}`]]; +} else if (args.length === 2) { + const [rev1, rev2] = args; + src1 = fs.readFileSync(rev1).toString(); + src2 = fs.readFileSync(rev2).toString(); + headerBody = ["header", ["h1", "File diff"], ["code", `${rev1} ⇌ ${rev2}`]]; +} else { + console.log("Usage:\n\thdiff file1 file2\n\thdiff relpath gitrev1 gitrev2"); + process.exit(1); +} + +console.log(hdiff.generateHtml(hdiff.computeDiff(src1, src2), headerBody)); diff --git a/packages/hdiff/package.json b/packages/hdiff/package.json new file mode 100644 index 0000000000..09d2b88256 --- /dev/null +++ b/packages/hdiff/package.json @@ -0,0 +1,79 @@ +{ + "name": "@thi.ng/hdiff", + "version": "0.0.1", + "description": "String diffing w/ hiccup output for further processing, e.g. with @thi.ng/hdom, @thi.ng/hiccup. Includes CLI util to generate HTML, with theme support and code folding", + "module": "./index.js", + "main": "./lib/index.js", + "umd:main": "./lib/index.umd.js", + "typings": "./index.d.ts", + "bin": { + "hdiff": "bin/cli.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/thi-ng/umbrella.git" + }, + "homepage": "https://github.com/thi-ng/umbrella/tree/master/packages/hdiff#readme", + "funding": { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + }, + "author": "Karsten Schmidt ", + "license": "Apache-2.0", + "scripts": { + "build": "yarn clean && yarn build:es6 && node ../../scripts/bundle-module", + "build:release": "yarn clean && yarn build:es6 && node ../../scripts/bundle-module all", + "build:es6": "tsc --declaration", + "build:test": "rimraf build && tsc -p test/tsconfig.json", + "test": "mocha test", + "cover": "nyc mocha test && nyc report --reporter=lcov", + "clean": "rimraf *.js *.d.ts *.map .nyc_output build coverage doc lib", + "doc:readme": "ts-node -P ../../tools/tsconfig.json ../../tools/src/readme.ts", + "doc:ae": "mkdir -p .ae/doc .ae/temp && node_modules/.bin/api-extractor run --local --verbose", + "doc": "node_modules/.bin/typedoc --mode modules --out doc src", + "pub": "yarn build:release && yarn publish --access public" + }, + "devDependencies": { + "@istanbuljs/nyc-config-typescript": "^1.0.1", + "@microsoft/api-extractor": "^7.8.0", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.1", + "mocha": "^7.1.2", + "nyc": "^15.0.1", + "ts-node": "^8.10.1", + "typedoc": "^0.17.6", + "typescript": "^3.9.2" + }, + "dependencies": { + "@thi.ng/diff": "^3.2.22", + "@thi.ng/hiccup": "^3.2.24", + "@thi.ng/hiccup-css": "^1.1.26", + "@thi.ng/strings": "^1.8.9" + }, + "files": [ + "*.js", + "*.d.ts", + "lib", + "bin" + ], + "keywords": [ + "cli", + "css", + "diff", + "es6", + "file", + "git", + "hiccup", + "html", + "theme", + "typescript" + ], + "publishConfig": { + "access": "public" + }, + "sideEffects": false, + "thi.ng": { + "status": "alpha", + "year": 2018 + } +} diff --git a/packages/hdiff/src/api.ts b/packages/hdiff/src/api.ts new file mode 100644 index 0000000000..55d77dbfc8 --- /dev/null +++ b/packages/hdiff/src/api.ts @@ -0,0 +1,60 @@ +import type { IObjectOf } from "@thi.ng/api"; + +export type BgFg = [string, string]; +export type BgFgBorder = [string, string, string]; + +export interface Theme { + selection: BgFg; + header: BgFg; + diff: { + add: { + main: BgFg; + word: BgFg; + side: BgFgBorder; + }; + del: { + main: BgFg; + word: BgFg; + side: BgFgBorder; + }; + nochange: { + main: BgFg; + side: BgFgBorder; + }; + fold: BgFg; + hover: { + main: BgFg; + side: BgFgBorder; + }; + }; +} + +export const THEMES: IObjectOf = { + default: { + selection: ["black", "white"], + header: ["ghostwhite", "black"], + diff: { + add: { + main: ["#dfe", "#000"], + side: ["lightgreen", "#393", "#9c9"], + word: ["lightgreen", "#000"], + }, + del: { + main: ["#fde", "#000"], + side: ["lightcoral", "#933", "#c99"], + word: ["orangered", "#000"], + }, + nochange: { + main: ["#fff", "#000"], + side: ["#eee", "#999", "#ccc"], + }, + fold: ["whitesmoke", "#666"], + hover: { + main: ["papayawhip", "#000"], + side: ["papayawhip", "#993", "#cc9"], + }, + }, + }, +}; + +export const DEFAULT_THEME = THEMES.default; diff --git a/packages/hdiff/src/diff.ts b/packages/hdiff/src/diff.ts new file mode 100644 index 0000000000..824e176ba7 --- /dev/null +++ b/packages/hdiff/src/diff.ts @@ -0,0 +1,78 @@ +import { diffArray, DiffMode } from "@thi.ng/diff"; +import { escape } from "@thi.ng/hiccup"; +import { padLeft } from "@thi.ng/strings"; + +const FMT_LN = padLeft(4, " "); + +export const computeDiff = (a: string, b: string) => { + const edits = diffArray( + a.split("\n"), + b.split("\n"), + DiffMode.ONLY_DISTANCE_LINEAR + ).linear!; + for (let i = 0; i < edits.length; i += 3) { + const lineID = edits[i]; + if (lineID) updateOffset(edits, i, lineID); + } + const result: any[] = ["div", {}]; + let block: any[] | undefined; + let numSame = 0; + for (let i = 0; i < edits.length; i += 3) { + if (!block) block = ["pre", {}]; + const lineID = edits[i]; + if (lineID == 0) { + numSame++; + if (numSame > 2) { + numSame = 0; + // scan forward to check if foldable + let j = i; + do { + j += 3; + } while (j < edits.length && edits[j] === 0); + if (j - i > 12) { + result.push(block, foldedBlock(edits, i, j - 6)); + block = undefined; + i = j - 9; + continue; + } + } + } else { + numSame = 0; + } + block!.push(codeLine(edits, i, true)); + } + if (block) result.push(block); + return result; +}; + +const updateOffset = (edits: any[], i: number, delta: number) => { + for (; i < edits.length; i += 3) { + if (edits[i] === 0) edits[i + 1] += delta; + } +}; + +const codeLine = (edits: any[], i: number, body = false): any[] => [ + "code", + { + "data-diff": ["-", " ", "+"][edits[i] + 1], + "data-lnum": FMT_LN(edits[i + 1] + 1), + }, + body ? escape(edits[i + 2]) : null, +]; + +const foldedBlock = (edits: any[], i: number, j: number): any[] => { + const block = [ + "pre", + { "data-fold": true }, + [ + "code", + { + "data-fold-range": `${edits[i + 4]} - ${edits[j + 1]}`, + }, + ], + ]; + for (; i < j; i += 3) { + block.push(codeLine(edits, i, true)); + } + return block; +}; diff --git a/packages/hdiff/src/html.ts b/packages/hdiff/src/html.ts new file mode 100644 index 0000000000..05b24731ef --- /dev/null +++ b/packages/hdiff/src/html.ts @@ -0,0 +1,33 @@ +import { serialize } from "@thi.ng/hiccup"; +import { DEFAULT_THEME, Theme } from "./api"; +import { compileTheme } from "./theme"; + +export const generateHtml = ( + diff: any[], + headerBody: any[], + theme: Theme = DEFAULT_THEME +) => + serialize([ + ["!DOCTYPE", "html"], + [ + "html", + [ + "head", + ["meta", { charset: "UTF-8" }], + [ + "meta", + { + name: "viewport", + content: "width=device-width, initial-scale=1.0", + }, + ], + [ + "meta", + { name: "generator", content: "https://thi.ng/hdiff" }, + ], + ["title", "hdiff"], + ["style", compileTheme(theme)], + ], + ["body", headerBody, ["main", diff]], + ], + ]); diff --git a/packages/hdiff/src/index.ts b/packages/hdiff/src/index.ts new file mode 100644 index 0000000000..f44296b761 --- /dev/null +++ b/packages/hdiff/src/index.ts @@ -0,0 +1,4 @@ +export * from "./api"; +export * from "./diff"; +export * from "./html"; +export * from "./theme"; diff --git a/packages/hdiff/src/theme.ts b/packages/hdiff/src/theme.ts new file mode 100644 index 0000000000..59bd6df32f --- /dev/null +++ b/packages/hdiff/src/theme.ts @@ -0,0 +1,135 @@ +import { css } from "@thi.ng/hiccup-css"; +import { BgFg, BgFgBorder, Theme } from "./api"; + +const block = { display: "block" }; +const none = { display: "none" }; +const lnum = "attr(data-lnum)"; + +export const compileTheme = (theme: Theme): string => + css( + [ + [ + "body", + "div", + "pre", + "code", + { + "box-sizing": "border-box", + margin: 0, + }, + ], + ["body", { "font-family": "sans-serif" }], + [ + "header", + bgfg(theme.header), + { + position: "fixed", + top: 0, + width: "100%", + padding: "0.5rem", + "box-shadow": "-8px 0 8px #666", + }, + [ + "h1", + { + margin: "0 0 0.25rem 0", + padding: "0 0 0.25rem 0", + "border-bottom": "1px dotted currentColor", + "font-weight": 100, + }, + ], + ["code", { "font-size": "66%" }], + ], + ["main", { "margin-top": "5rem" }], + [ + "pre", + { + "font-size": "0.8rem", + "line-height": "1.2rem", + }, + [ + "> code", + block, + [ + ":before", + bgfgBorder(theme.diff.nochange.side), + { + padding: "0.23rem 0.5rem 0.23rem 1rem", + "margin-right": "1rem", + "font-size": "0.64rem", + }, + ], + [ + ":hover", + bgfg(theme.diff.hover.main), + [":before", bgfgBorder(theme.diff.hover.side)], + ], + ], + [ + "[data-fold]", + [ + "> code[data-fold-range]:before", + block, + bgfg(theme.diff.fold), + { + content: `"⥣ ⋯⋯ Folded lines: " attr(data-fold-range) " ⋯⋯ ⥥"`, + width: "100%", + border: "1px dotted", + }, + ], + [`> code${diffAttr(" ")}`, none], + [ + ":hover", + [ + `> code${diffAttr(" ")}`, + block, + bgfg(theme.diff.hover.main), + ], + [`> code[data-fold-range]:before`, none], + ], + ], + ], + diffMode(theme.diff.add, "+", `" " ${lnum} " +"`), + diffMode(theme.diff.del, "-", `${lnum} " -"`), + diffMode(theme.diff.nochange, " ", `" " ${lnum} " "`), + [ + "code", + { + "font-size": "1em", + "font-family": "Consolas, monaco, monospace", + }, + ], + ["*::selection", bgfg(theme.selection)], + ] + // { format: PRETTY } + ); + +const bgfg = ([background, color]: BgFg) => ({ + background, + color, +}); + +const bgfgBorder = ([background, color, br]: BgFgBorder) => ({ + background, + color, + "border-right": `1px solid ${br}`, +}); + +const diffAttr = (id: string) => `[data-diff="${id}"]`; + +const diffMode = ( + { main, side, word }: { main: BgFg; word?: BgFg; side: BgFgBorder }, + mode: string, + content: string +) => [ + `code${diffAttr(mode)}`, + bgfg(main), + word ? [`> span${diffAttr(mode)}`, bgfg(word)] : null, + [ + ":before", + { + ...bgfgBorder(side), + content, + }, + ], +]; diff --git a/packages/hdiff/test/index.ts b/packages/hdiff/test/index.ts new file mode 100644 index 0000000000..ba28599982 --- /dev/null +++ b/packages/hdiff/test/index.ts @@ -0,0 +1,6 @@ +// import * as assert from "assert"; +// import { } from "../src"; + +describe("hdiff", () => { + it("tests pending"); +}); diff --git a/packages/hdiff/test/tsconfig.json b/packages/hdiff/test/tsconfig.json new file mode 100644 index 0000000000..f6e63560dd --- /dev/null +++ b/packages/hdiff/test/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../build", + "module": "commonjs" + }, + "include": [ + "./**/*.ts", + "../src/**/*.ts" + ] +} diff --git a/packages/hdiff/tpl.readme.md b/packages/hdiff/tpl.readme.md new file mode 100644 index 0000000000..0511e68cc6 --- /dev/null +++ b/packages/hdiff/tpl.readme.md @@ -0,0 +1,100 @@ +# ${pkg.banner} + +[![npm version](https://img.shields.io/npm/v/${pkg.name}.svg)](https://www.npmjs.com/package/${pkg.name}) +![npm downloads](https://img.shields.io/npm/dm/${pkg.name}.svg) +[![Twitter Follow](https://img.shields.io/twitter/follow/thing_umbrella.svg?style=flat-square&label=twitter)](https://twitter.com/thing_umbrella) + +This project is part of the +[@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo. + + + +## About + +${pkg.description} + +![screenshot of example output](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/hdiff/hdiff.png) + +${status} + +${supportPackages} + +${relatedPackages} + +${blogPosts} + +## Installation + +${pkg.install} + +${pkg.size} + +### CLI installation & usage + +Current limitations: + +- output always written to stdout only +- only single theme available for now (easy to add new ones, PRs welcome!) + +```bash +npx @thi.ng/hdiff + +# any text files +npx hdiff file-a.txt file-b.txt > diff.html + +# git revisions for given file (in local repo) +# rev can be any commit-ish ID understood by git (sha1, tag, etc.) +npx hdiff rel-file-path rev1 rev2 > diff.html + +# example +npx hdiff packages/webgl/src/shader.ts develop~500 HEAD > diff.html +``` + +## Dependencies + +${pkg.deps} + +${examples} + +## API + +${docLink} + +### computeDiff() + +Signature: `computeDiff(a: string, b: string) => any[]` + +Takes two strings and performs line-based diff, then formats result as +tree of HTML elements in +[@thi.ng/hiccup](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup) +format. + +The generated format only uses the following data attributes: + +- `data-diff`: diff status for each `code` line (`"+"`, `"-"` or `" "`) +- `data-lnum`: formatted line number for each `code` line +- `data-fold`: indicates folded `pre`-block +- `data-fold-range`: line number range string + +### generateHtml() + +Signature: `generateHtml(header: any[], body: any[], theme: Theme) => string` + +Takes two hiccup trees for header and body and an optional theme. +Compiles theme into CSS, serializes hiccup trees and returns complete +HTML document as string. + +### compileTheme() + +Signature: `compileTheme(theme: Theme) => string` + +Compiles a theme config into a complete CSS stylesheet string (using +[@thi.ng/hiccup-css](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup-css)). + +## Authors + +${authors} + +## License + +© ${copyright} // ${license} diff --git a/packages/hdiff/tsconfig.json b/packages/hdiff/tsconfig.json new file mode 100644 index 0000000000..893b9979c5 --- /dev/null +++ b/packages/hdiff/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": ".", + "module": "es6", + "target": "es6" + }, + "include": [ + "./src/**/*.ts" + ] +}