├── .editorconfig ├── .github └── workflows │ └── build-ci.yml ├── .gitignore ├── .nvmrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── lerna.json ├── package-lock.json ├── package.json ├── packages ├── babel-plugin-transform-diffhtml │ ├── .gitignore │ ├── .mocharc.json │ ├── README.md │ ├── babel.config.js │ ├── example │ │ ├── .babelrc │ │ ├── create-tree-output.js │ │ ├── package.json │ │ └── tagged-template-input.js │ ├── index.js │ ├── lib │ │ └── index.js │ ├── package-lock.json │ ├── package.json │ └── test │ │ ├── _setup.js │ │ ├── assertions.js │ │ └── fixtures.js ├── babel-preset-diffhtml-imports │ ├── README.md │ ├── index.js │ ├── package-lock.json │ ├── package.json │ └── utils │ │ ├── replace-cjs.js │ │ └── replace-esm.js ├── diffhtml-components │ ├── .babelrc │ ├── .gitignore │ ├── .mocharc.json │ ├── README.md │ ├── component.js │ ├── index.js │ ├── jsconfig.json │ ├── lib │ │ ├── before-mount.js │ │ ├── component.js │ │ ├── create-side-effect.js │ │ ├── create-state.js │ │ ├── index.js │ │ ├── lifecycle │ │ │ ├── component-will-unmount.js │ │ │ └── invoke-refs.js │ │ ├── middleware.js │ │ ├── render-component.js │ │ └── util │ │ │ ├── binding.js │ │ │ ├── global.js │ │ │ ├── internals.js │ │ │ ├── symbols.js │ │ │ └── types.js │ ├── package-lock.json │ ├── package.json │ ├── rollup.component.config.js │ ├── rollup.main.config.js │ └── test │ │ ├── _setup.js │ │ ├── component.js │ │ ├── integration │ │ ├── component.js │ │ ├── hooks.js │ │ └── web-component.js │ │ └── util │ │ └── validate-caches.js ├── diffhtml-devtools │ ├── .babelrc │ ├── .gitignore │ ├── .jshintrc │ ├── .travis.yml │ ├── Gruntfile.coffee │ ├── LICENSE │ ├── README.md │ ├── build │ │ └── tasks │ │ │ ├── browserify.coffee │ │ │ ├── clean.coffee │ │ │ ├── compress.coffee │ │ │ ├── copy.coffee │ │ │ ├── jshint.coffee │ │ │ ├── mocha.coffee │ │ │ ├── shell.coffee │ │ │ └── watch.coffee │ ├── chrome-extension │ │ ├── _locales │ │ │ ├── en │ │ │ │ └── messages.json │ │ │ └── es │ │ │ │ └── messages.json │ │ ├── key.pem │ │ └── manifest.json │ ├── demo │ │ └── index.html │ ├── lib │ │ ├── icons │ │ │ ├── logo-128-invert.png │ │ │ ├── logo-128.png │ │ │ ├── logo-16-invert.png │ │ │ ├── logo-16.png │ │ │ ├── logo-48-invert.png │ │ │ └── logo-48.png │ │ ├── markup │ │ │ ├── devtools.html │ │ │ └── panel.html │ │ ├── scripts │ │ │ ├── background.js │ │ │ ├── bridge.js │ │ │ ├── components │ │ │ │ ├── footer.js │ │ │ │ ├── navigation.js │ │ │ │ ├── panels.js │ │ │ │ ├── split-view.js │ │ │ │ ├── transaction-detail.js │ │ │ │ ├── transaction-row.js │ │ │ │ └── vtree.js │ │ │ ├── contentscript.js │ │ │ ├── devtools.js │ │ │ ├── index.js │ │ │ ├── injector.js │ │ │ ├── panels │ │ │ │ ├── health.js │ │ │ │ ├── help.js │ │ │ │ ├── middleware.js │ │ │ │ ├── mounts.js │ │ │ │ ├── settings.js │ │ │ │ └── transactions.js │ │ │ ├── semantic-ui │ │ │ │ └── table.js │ │ │ └── utils │ │ │ │ └── format-patches.js │ │ └── styles │ │ │ ├── index.css │ │ │ ├── theme.css │ │ │ └── tooltip.css │ ├── package-lock.json │ └── package.json ├── diffhtml-middleware-linter │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── index.js │ ├── lib │ │ └── index.js │ ├── package-lock.json │ ├── package.json │ ├── rollup.config.js │ └── test.html ├── diffhtml-middleware-logger │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── index.js │ ├── lib │ │ └── index.js │ ├── package-lock.json │ ├── package.json │ └── rollup.config.js ├── diffhtml-middleware-service-worker │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── index.js │ ├── lib │ │ └── index.js │ ├── package-lock.json │ ├── package.json │ └── rollup.config.js ├── diffhtml-middleware-synthetic-events │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── index.js │ ├── lib │ │ └── index.js │ ├── package-lock.json │ ├── package.json │ └── rollup.config.js ├── diffhtml-middleware-verify-state │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── index.js │ ├── lib │ │ └── index.js │ ├── package-lock.json │ ├── package.json │ └── rollup.config.js ├── diffhtml-middleware-worker │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── index.js │ ├── jsconfig.json │ ├── lib │ │ ├── create-web-socket.js │ │ ├── create-worker.js │ │ ├── get-uuid.js │ │ ├── index.js │ │ ├── main-task.js │ │ ├── to-object-url.js │ │ ├── util │ │ │ ├── binding.js │ │ │ ├── global.js │ │ │ ├── node-buffer.js │ │ │ ├── node-worker-threads.js │ │ │ └── types.js │ │ └── worker-task.js │ ├── package-lock.json │ ├── package.json │ └── rollup.config.js ├── diffhtml-react-compat │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── index.js │ ├── lib │ │ ├── children.js │ │ ├── fake-prop-types.js │ │ ├── index.js │ │ └── pure-component.js │ ├── package-lock.json │ ├── package.json │ ├── rollup.config.js │ ├── server.js │ └── test-utils.js ├── diffhtml-rust-parser │ ├── .cargo │ │ └── config │ ├── Cargo.lock │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── examples │ │ └── webpack │ │ │ ├── README.md │ │ │ ├── babel.config.mjs │ │ │ ├── index.html │ │ │ ├── index.js │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── webpack.config.mjs │ │ │ └── webpack.config.prod.mjs │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── parser.rs │ └── test │ │ └── index.js ├── diffhtml-static-sync │ ├── .babelrc │ ├── README.md │ ├── index.js │ ├── lib │ │ ├── socket.js │ │ ├── util │ │ │ ├── client-script.js │ │ │ └── escape.js │ │ └── watch.js │ ├── package-lock.json │ ├── package.json │ └── sync.js ├── diffhtml-website │ ├── .babelrc │ ├── .gitignore │ ├── README.md │ ├── build │ │ ├── copy.js │ │ ├── generate.js │ │ ├── util │ │ │ └── flatten-pages.js │ │ └── watch.js │ ├── components │ │ ├── layout.js │ │ └── nav.js │ ├── config.json │ ├── package-lock.json │ ├── package.json │ ├── pages │ │ ├── api.md │ │ ├── components.md │ │ ├── devtools.md │ │ ├── index.md │ │ ├── middleware.md │ │ ├── parser.md │ │ └── tools.md │ └── public │ │ ├── images │ │ ├── chrome-developer-mode.png │ │ ├── diffhtml-logo-fit.png │ │ ├── diffhtml-logo.png │ │ └── yarn-logo.svg │ │ ├── scripts │ │ ├── highlight.min.js │ │ ├── index.js │ │ └── promises-keyframe.js │ │ └── styles │ │ └── index.css └── diffhtml │ ├── .babelrc │ ├── .jshintrc │ ├── .mocharc.json │ ├── .npmignore │ ├── README.md │ ├── index.js │ ├── jsconfig.json │ ├── lib │ ├── html.js │ ├── index.js │ ├── inner-html.js │ ├── lite.js │ ├── node │ │ ├── create.js │ │ └── patch.js │ ├── outer-html.js │ ├── release.js │ ├── tasks │ │ ├── end-as-transaction.js │ │ ├── parse-new-tree.js │ │ ├── patch-node.js │ │ ├── reconcile-trees.js │ │ ├── should-update.js │ │ └── sync-trees.js │ ├── to-string.js │ ├── transaction.js │ ├── tree │ │ ├── create.js │ │ └── sync.js │ ├── use.js │ ├── util │ │ ├── config.js │ │ ├── decode-entities.js │ │ ├── escape.js │ │ ├── global.js │ │ ├── has-module.js │ │ ├── internals.js │ │ ├── make-measure.js │ │ ├── memory.js │ │ ├── parse.js │ │ ├── pool.js │ │ ├── process.js │ │ ├── symbols.js │ │ └── types.js │ └── version.js │ ├── lite.js │ ├── package-lock.json │ ├── package.json │ ├── rollup.lite.config.js │ ├── rollup.main.config.js │ └── test │ ├── _setup.js │ ├── devtools.js │ ├── html.js │ ├── integration │ ├── basics.js │ ├── inner.js │ ├── outer.js │ ├── svg.js │ └── tagged-template.js │ ├── node.js │ ├── tasks.js │ ├── to-string.js │ ├── transaction.js │ ├── tree.js │ ├── use.js │ ├── util.js │ └── util │ ├── create-supplemental.js │ └── validate-memory.js ├── post-cjs.sh └── release.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.js] 4 | end_of_line = lf 5 | insert_final_newline = false 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | -------------------------------------------------------------------------------- /.github/workflows/build-ci.yml: -------------------------------------------------------------------------------- 1 | name: build-ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | test: 11 | name: "Build and Test" 12 | runs-on: ubuntu-latest 13 | if: github.event.pull_request.draft == false 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: actions/setup-node@v3 17 | with: 18 | node-version-file: '.nvmrc' 19 | - uses: bahmutov/npm-install@v1.6.0 20 | - uses: actions-rs/toolchain@v1 21 | with: 22 | toolchain: nightly 23 | target: wasm32-unknown-unknown 24 | - uses: jetli/wasm-bindgen-action@v0.1.0 25 | with: 26 | version: '0.2.88' 27 | - run: rustup target add wasm32-unknown-unknown 28 | - uses: browser-actions/setup-chrome@latest 29 | - run: chrome --version 30 | - run: npm run build 31 | - run: npm run test-cov 32 | - name: Publish to coveralls.io 33 | uses: coverallsapp/github-action@v2.2.1 34 | with: 35 | github-token: ${{ github.token }} 36 | files: ./packages/diffhtml/coverage/lcov.info ./packages/diffhtml-components/coverage/lcov.info 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | docs 4 | coverage 5 | target 6 | lerna-debug.log 7 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING.md 2 | 3 | ## How to Make a Release (Modern) 4 | 5 | IMPORTANT: Edit the top level variables related to the versions to publish in `release.sh`. 6 | 7 | Then run the following command: 8 | 9 | ``` sh 10 | ./release.sh 11 | ``` 12 | 13 | ## How to Make a Release (Legacy) 14 | 15 | While Lerna takes care of most of the hardwork, we'll want to still make sure 16 | to follow a series of instructions to cut a release. 17 | 18 | ### Bump the READMEs and JavaScript files 19 | 20 | Ensure all READMes and JavaScript files are updated to the new version using 21 | `sed`. Make a commit after changing them. 22 | 23 | For example changing from `1.0.0-beta.8` to `1.0.0-beta.9`: 24 | 25 | ``` sh 26 | # Update all README.md files. 27 | sed -i -e 's/1.0.0-beta.8/1.0.0-beta.9/g' **/README.md 28 | 29 | # Update all JavaScript files. 30 | sed -i -e 's/1.0.0-beta.8/1.0.0-beta.9/g' **/lib/*.js 31 | 32 | # Update the root package version. 33 | sed -i -e 's/1.0.0-beta.8/1.0.0-beta.9/g' ./package.json 34 | 35 | # Update the DevTools version. It cannot use letters or dashes. 36 | sed -i -e 's/1.0.0.8/1.0.0.9/g' ./packages/diffhtml-devtools/chrome-extension/manifest.json 37 | 38 | # Make a descriptive commit. 39 | git commit -am"Update JavaScript and REAMDE files to 1.0.0-beta.9" 40 | ``` 41 | 42 | ### Build and test! 43 | 44 | Before doing any more release commands, ensure you're in master and it builds 45 | and tests cleanly from the root directory. 46 | 47 | ``` sh 48 | npm run build 49 | npm test 50 | ``` 51 | 52 | Note this may generate new files, if so, commit these as a dist build. 53 | 54 | ### Lerna publish 55 | 56 | Run the `lerna publish` command and follow the instructions to tag your 57 | release. This will create the tags and push and do everything else, including 58 | actually publishing to npm, which is great! 59 | 60 | ``` sh 61 | lerna publish 62 | ``` 63 | 64 | Enter in your new version (using custom), unless you see it listed. 65 | 66 | This will automatically crawl nested `package.json` files and update diffhtml 67 | dependencies to the latest version. 68 | 69 | ### Ensure all dependencies are updated 70 | 71 | Lerna often misses updating references to packages. By running this you ensure 72 | all packages reference the latest version. 73 | 74 | ```sh 75 | # Update the root package version. 76 | sed -i -e 's/1.0.0-beta.8/1.0.0-beta.9/g' ./packages/*/package.json 77 | ``` 78 | 79 | ### Update website 80 | 81 | The website pulls version information from the package.json files. The version 82 | gets bumped from the previous command. Now you need to run: 83 | 84 | ```sh 85 | lerna run build --scope=diffhtml-website 86 | ``` 87 | 88 | To get the latest docs files built. Commit and push these: 89 | 90 | ```sh 91 | git commit -am"Update website to 1.0.0-beta.9" 92 | git push 93 | ``` 94 | 95 | ### Take a bow 96 | 97 | You've earned it. 98 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Tim Branyen (@tbranyen) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "6.0.3", 3 | "packages": [ 4 | "packages/*" 5 | ], 6 | "version": "1.0.0-beta.30" 7 | } 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diffhtml", 3 | "private": true, 4 | "version": "1.0.0-beta.30", 5 | "description": "Build web user interfaces with JavaScript", 6 | "type": "module", 7 | "scripts": { 8 | "build": "lerna run build", 9 | "test": "lerna run test", 10 | "test-cov": "lerna run test-cov", 11 | "postinstall": "lerna bootstrap" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/tbranyen/diffhtml.git" 16 | }, 17 | "keywords": [ 18 | "diffhtml", 19 | "virtual dom", 20 | "components", 21 | "user interfaces" 22 | ], 23 | "author": "Tim Branyen (@tbranyen)", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/tbranyen/diffhtml/issues" 27 | }, 28 | "homepage": "https://diffhtml.org/", 29 | "devDependencies": { 30 | "lerna": "^6.0.3" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/babel-plugin-transform-diffhtml/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | test/.__fixtures_* 4 | -------------------------------------------------------------------------------- /packages/babel-plugin-transform-diffhtml/.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": ["test/_setup.js"], 3 | "loader": "extless-loader", 4 | "recursive": true, 5 | "colors": true, 6 | "bail": true, 7 | "parallel": true, 8 | "jobs": 2 9 | } 10 | -------------------------------------------------------------------------------- /packages/babel-plugin-transform-diffhtml/README.md: -------------------------------------------------------------------------------- 1 | # <±/> babel-plugin-transform-diffhtml 2 | 3 | *Babel transform for pre-parsing and compiling diffHTML template functions to 4 | createTree calls which reduces runtime HTML parse cost.* 5 | 6 | Latest version: 1.0.0-beta.30 7 | 8 | Refer to the website [https://diffhtml.org/tools.html#babel-transform](https://diffhtml.org/tools.html#babel-transform) for documentation. 9 | 10 | ## Installation 11 | 12 | Using npm: 13 | 14 | ```sh 15 | npm install --save-dev babel-plugin-transform-diffhtml 16 | ``` 17 | 18 | or using yarn: 19 | 20 | ```sh 21 | yarn add babel-plugin-transform-diffhtml --dev 22 | ``` 23 | 24 | ## Simple example 25 | 26 | `.babelrc`: 27 | 28 | ```json 29 | { 30 | "plugins": [ 31 | ["babel-plugin-transform-diffhtml", { "createTree": "_diffhtml.html" }] 32 | ] 33 | } 34 | ``` 35 | 36 | `input.js`: 37 | 38 | ```js 39 | import { innerHTML, html } from 'diffhtml'; 40 | 41 | function main() { 42 | innerHTML(document.body, html` 43 |

Hello world

44 | `); 45 | } 46 | 47 | main(); 48 | ``` 49 | 50 | `babel input.js`: 51 | 52 | ``` 53 | var _vtree = _diffhtml.html("#text", "Hello world"); 54 | 55 | import { innerHTML, html } from 'diffhtml'; 56 | 57 | function main() { 58 | innerHTML(document.body, _diffhtml.html("div", {}, [_diffhtml.html("h1", {}, [_diffhtml.html("#text", "Hello world")])])); 59 | } 60 | 61 | main(); 62 | ``` 63 | 64 | You may need to tweak the "createTree" option to get the appropriate output in 65 | the source to match how diffHTML is embedded and what build system you used. -------------------------------------------------------------------------------- /packages/babel-plugin-transform-diffhtml/babel.config.js: -------------------------------------------------------------------------------- 1 | import { parse } from 'diffhtml-rust-parser'; 2 | 3 | export default { 4 | env: { 5 | normal: { 6 | plugins: [['module:./dist/es/index.js', { createTree: 'html' }]] 7 | }, 8 | 9 | wasm: { 10 | plugins: [ 11 | ['module:./dist/es/index.js', { 12 | createTree: 'html', 13 | parse, 14 | }] 15 | ] 16 | } 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /packages/babel-plugin-transform-diffhtml/example/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["transform-diffhtml"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/babel-plugin-transform-diffhtml/example/create-tree-output.js: -------------------------------------------------------------------------------- 1 | const { html, innerHTML } = require('diffhtml/lite'); 2 | 3 | // Render a div with dynamic children and onclick 4 | function renderTime(time) { 5 | innerHTML(document.body, [diff.createTree("button", { "onclick": e => renderTime(new Date()) }, [diff.createTree('#text', null, "Get time")]), diff.createTree('#text', null, "\n "), diff.createTree("output", {}, [time])]); 6 | } 7 | 8 | renderTime(new Date()); 9 | -------------------------------------------------------------------------------- /packages/babel-plugin-transform-diffhtml/example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "private": true, 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "babel tagged-template-input.js -o create-tree-output.js" 8 | }, 9 | "keywords": [], 10 | "author": "Tim Branyen (@tbranyen)", 11 | "license": "MIT", 12 | "devDependencies": { 13 | "babel-plugin-transform-diffhtml": "file:../", 14 | "babel-cli": "^6.24.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/babel-plugin-transform-diffhtml/example/tagged-template-input.js: -------------------------------------------------------------------------------- 1 | const { html, innerHTML } = require('diffhtml/lite'); 2 | 3 | // Render a div with dynamic children and onclick 4 | function renderTime(time) { 5 | innerHTML(document.body, html` 6 | 7 | ${time} 8 | `); 9 | } 10 | 11 | renderTime(new Date()); 12 | -------------------------------------------------------------------------------- /packages/babel-plugin-transform-diffhtml/index.js: -------------------------------------------------------------------------------- 1 | export * from './dist/es/index.js'; 2 | -------------------------------------------------------------------------------- /packages/babel-plugin-transform-diffhtml/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-transform-diffhtml", 3 | "version": "1.0.0-beta.30", 4 | "description": "Compiles tagged templates into createTree calls", 5 | "main": "dist/es/index.js", 6 | "module": "dist/es/index.js", 7 | "type": "module", 8 | "scripts": { 9 | "build": "npm run clean && npm run build-cjs && npm run build-esm", 10 | "build-cjs": "NODE_OPTIONS=--experimental-wasm-modules NODE_ENV=cjs babel lib -d dist/cjs && sh ../../post-cjs.sh", 11 | "build-esm": "NODE_OPTIONS=--experimental-wasm-modules NODE_ENV=esm babel lib -d dist/es", 12 | "clean": "rm -rf dist/* && mkdir -p dist", 13 | "watch": "NODE_ENV=umd watchify --im -s transform -g babelify lib/index.js -o dist/index.js -v", 14 | "build-fixtures": "npm run build-normal-fixtures && npm run build-wasm-fixtures", 15 | "build-normal-fixtures": "NODE_ENV=normal NODE_OPTIONS=--experimental-wasm-modules babel --ignore node_modules test/fixtures.js -o test/.__fixtures_normal__.js", 16 | "build-wasm-fixtures": "NODE_ENV=wasm NODE_OPTIONS=--experimental-wasm-modules babel --ignore node_modules test/fixtures.js -o test/.__fixtures_wasm__.js", 17 | "test": "NODE_ENV=test npm run build-fixtures && mocha test/assertions --experimental-wasm-modules", 18 | "test-watch": "npm run test -- -w" 19 | }, 20 | "author": "Tim Branyen (@tbranyen)", 21 | "license": "MIT", 22 | "devDependencies": { 23 | "@babel/cli": "^7.19.3", 24 | "@babel/core": "^7.20.2", 25 | "@babel/register": "^7.18.9", 26 | "babel-plugin-transform-runtime": "^6.23.0", 27 | "babelify": "^10.0.0", 28 | "babylon": "^6.18.0", 29 | "browserify": "^17.0.0", 30 | "diffhtml": "^1.0.0-beta.30", 31 | "diffhtml-rust-parser": "^1.0.0-beta.30", 32 | "extless-loader": "^1.0.0", 33 | "jsdom": "^20.0.2", 34 | "mocha": "^10.1.0", 35 | "watchify": "^4.0.0" 36 | }, 37 | "gitHead": "091b497588f6f48221630431e2f1eeb7f2db37cb" 38 | } 39 | -------------------------------------------------------------------------------- /packages/babel-plugin-transform-diffhtml/test/_setup.js: -------------------------------------------------------------------------------- 1 | import { JSDOM } from 'jsdom'; 2 | 3 | const instance = new JSDOM(); 4 | const { window } = instance; 5 | 6 | Object.assign(global, { 7 | document: window.document, 8 | Element: window.Element, 9 | location: window.location, 10 | window, 11 | }); 12 | 13 | console.json = (...a) => a.forEach(o => console.log(JSON.stringify(o, null, 2))); 14 | -------------------------------------------------------------------------------- /packages/babel-preset-diffhtml-imports/README.md: -------------------------------------------------------------------------------- 1 | # <±/> babel-preset-diffhtml-imports 2 | 3 | *Babel preset used internally to get common behavior across packages. It should 4 | not be used externally.* 5 | 6 | Latest version: 1.0.0-beta.30 -------------------------------------------------------------------------------- /packages/babel-preset-diffhtml-imports/index.js: -------------------------------------------------------------------------------- 1 | const { join } = require('path'); 2 | 3 | const ModuleRewrite = require('babel-plugin-module-rewrite').default; 4 | const OptionalChaining = require('@babel/plugin-proposal-optional-chaining').default; 5 | const ObjectRestSpread = require('@babel/plugin-proposal-object-rest-spread').default; 6 | const ClassProperties = require('@babel/plugin-proposal-class-properties').default; 7 | const ModulesCommonJS = require('@babel/plugin-transform-modules-commonjs').default; 8 | const AddModuleExports = require('babel-plugin-add-module-exports'); 9 | const Classes = require('@babel/plugin-transform-classes').default; 10 | const BlockScoping = require('@babel/plugin-transform-block-scoping').default; 11 | const ArrowFunctions = require('@babel/plugin-transform-arrow-functions').default; 12 | const ShorthandProperties = require('@babel/plugin-transform-shorthand-properties').default; 13 | const ObjectDestructuring = require('@babel/plugin-transform-destructuring').default; 14 | const Parameters = require('@babel/plugin-transform-parameters').default; 15 | const Spread = require('@babel/plugin-transform-spread').default; 16 | const TemplateLiterals = require('@babel/plugin-transform-template-literals').default; 17 | const ForOf = require('@babel/plugin-transform-for-of').default; 18 | const ComputedProperties = require('@babel/plugin-transform-computed-properties').default; 19 | const ElementClasses = require('babel-plugin-transform-custom-element-classes'); 20 | const NewTarget = require('@babel/plugin-transform-new-target'); 21 | const OptionalCatchBinding = require('@babel/plugin-proposal-optional-catch-binding').default; 22 | 23 | 24 | const esmFunc = join(__dirname, 'utils/replace-esm.js'); 25 | const cjsFunc = join(__dirname, 'utils/replace-cjs.js'); 26 | 27 | const { NODE_ENV } = process.env; 28 | const exportObj = {}; 29 | 30 | if (NODE_ENV === 'umd' || NODE_ENV === 'min') { 31 | exportObj.plugins = [ 32 | ]; 33 | exportObj.minified = true; 34 | } 35 | 36 | if (NODE_ENV === 'cjs') { 37 | exportObj.plugins = [ 38 | AddModuleExports, 39 | [ModuleRewrite, { replaceFunc: cjsFunc }], 40 | ModulesCommonJS, 41 | ObjectRestSpread, 42 | ClassProperties, 43 | ElementClasses, 44 | OptionalChaining, 45 | OptionalCatchBinding, 46 | ]; 47 | exportObj.minified = true; 48 | } 49 | 50 | if (NODE_ENV === 'esm') { 51 | exportObj.plugins = [ 52 | [ModuleRewrite, { replaceFunc: esmFunc }], 53 | ObjectRestSpread, 54 | ClassProperties, 55 | ]; 56 | exportObj.minified = true; 57 | } 58 | 59 | if (NODE_ENV === 'test' || NODE_ENV === 'test+cov') { 60 | exportObj.plugins = [ 61 | AddModuleExports, 62 | [ModuleRewrite, { replaceFunc: cjsFunc }], 63 | ModulesCommonJS, 64 | ObjectRestSpread, 65 | ClassProperties, 66 | OptionalChaining, 67 | OptionalCatchBinding, 68 | ]; 69 | }; 70 | 71 | module.exports = () => exportObj; 72 | -------------------------------------------------------------------------------- /packages/babel-preset-diffhtml-imports/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-preset-diffhtml-imports", 3 | "private": true, 4 | "version": "1.0.0-beta.30", 5 | "description": "Used to normalize import paths to diffHTML modules based on env", 6 | "main": "index.js", 7 | "author": "Tim Branyen (@tbranyen)", 8 | "license": "MIT", 9 | "dependencies": { 10 | "@babel/core": "^7.22.8", 11 | "@babel/plugin-proposal-class-properties": "^7.16.7", 12 | "@babel/plugin-proposal-object-rest-spread": "^7.17.3", 13 | "@babel/plugin-proposal-optional-catch-binding": "^7.16.7", 14 | "@babel/plugin-proposal-optional-chaining": "^7.16.7", 15 | "@babel/plugin-transform-arrow-functions": "^7.16.7", 16 | "@babel/plugin-transform-block-scoping": "^7.16.7", 17 | "@babel/plugin-transform-classes": "^7.16.7", 18 | "@babel/plugin-transform-computed-properties": "^7.16.7", 19 | "@babel/plugin-transform-destructuring": "^7.17.7", 20 | "@babel/plugin-transform-for-of": "^7.16.7", 21 | "@babel/plugin-transform-modules-commonjs": "^7.17.7", 22 | "@babel/plugin-transform-new-target": "^7.16.7", 23 | "@babel/plugin-transform-parameters": "^7.16.7", 24 | "@babel/plugin-transform-shorthand-properties": "^7.16.7", 25 | "@babel/plugin-transform-spread": "^7.16.7", 26 | "@babel/plugin-transform-template-literals": "^7.16.7", 27 | "babel-plugin-add-module-exports": "^1.0.4", 28 | "babel-plugin-module-rewrite": "^0.2.0", 29 | "babel-plugin-transform-custom-element-classes": "^0.1.0", 30 | "find-package-json": "^1.2.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/babel-preset-diffhtml-imports/utils/replace-cjs.js: -------------------------------------------------------------------------------- 1 | module.exports = originalPath => { 2 | if (originalPath.indexOf('diffhtml') !== 0) { 3 | return originalPath; 4 | } 5 | 6 | return originalPath.replace(/diffhtml(.*)\/lib\/(.*)/, (full, sub, file) => { 7 | return `diffhtml${sub}/dist/cjs/${file}`; 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /packages/babel-preset-diffhtml-imports/utils/replace-esm.js: -------------------------------------------------------------------------------- 1 | const { extname, dirname, join, relative } = require('path'); 2 | const finder = require('find-package-json'); 3 | 4 | module.exports = (originalPath, filePath) => { 5 | // FIXME For now in ESM we need to disable loading prop-types. Now that it's 6 | // MIT we can fork and rebrand it as diffhtml-prop-types. 7 | if (originalPath.indexOf('prop-types') === 0) { 8 | return './fake-prop-types.js'; 9 | } 10 | 11 | if (originalPath[0] === '.' || originalPath[0] === '/') { 12 | // Ensure this path has a trailing extension. 13 | if (!extname(originalPath)) { 14 | originalPath += '.js'; 15 | } 16 | 17 | return originalPath; 18 | } 19 | 20 | return originalPath.replace(/diffhtml(.*)\/lib\/(.*)|diffhtml(.*)?/, (full, sub, file, sub2) => { 21 | const path = dirname(finder(filePath).next().value.__path); 22 | let newPath = null; 23 | 24 | if (sub && file) { 25 | newPath = `diffhtml${sub}/${file}`; 26 | } 27 | else if (file) { 28 | newPath = `diffhtml/${file}`; 29 | } 30 | else if (sub2) { 31 | const [ name, ...path ] = sub2.split('/'); 32 | newPath = `diffhtml${name}/${path.length ? path.join('/') : ''}`; 33 | } 34 | else { 35 | newPath = 'diffhtml'; 36 | } 37 | 38 | return newPath; 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /packages/diffhtml-components/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["diffhtml-imports"], 3 | "plugins": [ 4 | "babel-plugin-transform-class-properties", 5 | "babel-plugin-transform-custom-element-classes", 6 | "@babel/plugin-transform-classes" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /packages/diffhtml-components/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/diffhtml-components/.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": ["../diffhtml/lib/index.js", "test/_setup.js"], 3 | "loader": "extless-loader", 4 | "recursive": true, 5 | "colors": true, 6 | "bail": true, 7 | "parallel": true, 8 | "jobs": 2 9 | } 10 | -------------------------------------------------------------------------------- /packages/diffhtml-components/component.js: -------------------------------------------------------------------------------- 1 | export { default } from './dist/es/component.js'; 2 | -------------------------------------------------------------------------------- /packages/diffhtml-components/index.js: -------------------------------------------------------------------------------- 1 | export * from './dist/es/index.js'; 2 | -------------------------------------------------------------------------------- /packages/diffhtml-components/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "emitDeclarationOnly": true, 4 | "noEmit": false, 5 | "strict": true, 6 | "target": "es2017", 7 | "declaration": true, 8 | "checkJs": true, 9 | "noUnusedParameters": true, 10 | "noUnusedLocals": true, 11 | "outDir": "./dist/typings", 12 | "lib": ["es2017", "dom"] 13 | }, 14 | "include": ["lib/**/*"] 15 | } 16 | -------------------------------------------------------------------------------- /packages/diffhtml-components/lib/before-mount.js: -------------------------------------------------------------------------------- 1 | import componentWillUnmount from './lifecycle/component-will-unmount'; 2 | import { invokeRef, invokeRefsForVTree } from './lifecycle/invoke-refs'; 3 | import diff from './util/binding'; 4 | import { Transaction } from './util/types'; 5 | 6 | const { createNode, NodeCache, PATCH_TYPE, decodeEntities } = diff.Internals; 7 | const uppercaseEx = /[A-Z]/g; 8 | 9 | /** 10 | * @param {Transaction} transaction 11 | */ 12 | export default transaction => { 13 | if (transaction.aborted) { 14 | return; 15 | } 16 | 17 | const { patches } = transaction; 18 | const { length } = patches; 19 | 20 | let i = 0; 21 | 22 | while (true) { 23 | const patchType = patches[i]; 24 | 25 | // Exhausted remaining patches. 26 | if (i === length) { 27 | break; 28 | } 29 | 30 | switch(patchType) { 31 | case PATCH_TYPE.SET_ATTRIBUTE: { 32 | const vTree = patches[i + 1]; 33 | const name = patches[i + 2]; 34 | const value = patches[i + 3]; 35 | 36 | uppercaseEx.lastIndex = 0; 37 | 38 | // Normalize uppercase attributes. 39 | if (uppercaseEx.test(name)) { 40 | uppercaseEx.lastIndex = 0; 41 | 42 | const newName = name.replace( 43 | uppercaseEx, 44 | (/** @type {string} */ ch) => `-${ch.toLowerCase()}`, 45 | ); 46 | 47 | if (value && typeof value === 'string') { 48 | const decodedValue = decodeEntities(value); 49 | 50 | if (NodeCache.has(vTree)) { 51 | /** @type {HTMLElement} */(NodeCache.get(vTree)).setAttribute(newName, decodedValue); 52 | } 53 | } 54 | } 55 | 56 | if (name === 'ref') { 57 | invokeRef(createNode(vTree), vTree); 58 | } 59 | 60 | i += 4; 61 | break; 62 | } 63 | 64 | case PATCH_TYPE.REMOVE_ATTRIBUTE: { 65 | i += 3; 66 | break; 67 | } 68 | 69 | case PATCH_TYPE.REPLACE_CHILD: { 70 | const oldTree = patches[i + 2]; 71 | 72 | componentWillUnmount(oldTree); 73 | 74 | i += 3; 75 | break; 76 | } 77 | 78 | case PATCH_TYPE.NODE_VALUE: 79 | case PATCH_TYPE.INSERT_BEFORE: { 80 | i += 4; 81 | break; 82 | } 83 | 84 | case PATCH_TYPE.REMOVE_CHILD: { 85 | const vTree = patches[i + 1]; 86 | invokeRefsForVTree(vTree, null); 87 | componentWillUnmount(vTree); 88 | 89 | i += 2; 90 | break; 91 | } 92 | } 93 | } 94 | }; 95 | -------------------------------------------------------------------------------- /packages/diffhtml-components/lib/create-side-effect.js: -------------------------------------------------------------------------------- 1 | import { $$hooks } from './util/symbols'; 2 | import { ActiveRenderState } from './util/types'; 3 | 4 | /** 5 | * Allow a function component to hook into lifecycle methods in a manner 6 | * consistent with class components. Meaning you can leverage existing lifecycle 7 | * events in a function component. 8 | * 9 | * @param {Function=} didMountOrUpdate - A function that is called whenever the 10 | * component is mounted. To hook into component updates, return a function. This 11 | * returned function will be called whenever the component updates. 12 | * 13 | * @param {Function=} unMount - A function that is called whenever a component 14 | * is unmounted. 15 | * 16 | * @returns {void} 17 | */ 18 | export function createSideEffect(didMountOrUpdate, unMount) { 19 | if (ActiveRenderState.length === 0) { 20 | throw new Error('Cannot create side effect unless in render function'); 21 | } 22 | 23 | if (typeof didMountOrUpdate !== 'function' && typeof unMount !== 'function') { 24 | throw new Error('Missing function for side effect'); 25 | } 26 | 27 | const [ activeComponent ] = ActiveRenderState; 28 | const hooks = activeComponent[$$hooks]; 29 | const isSet = Boolean(hooks.fns[hooks.i]); 30 | 31 | // Indicate this hook has been installed. 32 | hooks.fns[hooks.i] = true; 33 | 34 | // Increment the hooks count. 35 | activeComponent[$$hooks].i += 1; 36 | 37 | // Short-circuit if effects are already installed. 38 | if (isSet) { 39 | return; 40 | } 41 | 42 | // Schedule a componentDidMount if a function was provided 43 | if (typeof didMountOrUpdate === 'function') { 44 | const oldDidMount = activeComponent.componentDidMount; 45 | 46 | // Install the componentDidMount lifecycle hook. 47 | activeComponent.componentDidMount = () => { 48 | if (typeof oldDidMount === 'function') { 49 | oldDidMount(); 50 | } 51 | 52 | // Install the componentDidUpdate lifecycle hook. 53 | const didUpdate = didMountOrUpdate(); 54 | const oldComponentDidUpdate = activeComponent.componentDidUpdate; 55 | 56 | // Then if the user specifies a return function, use that as didUpdate 57 | if (typeof didUpdate === 'function') { 58 | activeComponent.componentDidUpdate = () => { 59 | if (typeof oldComponentDidUpdate === 'function') { 60 | oldComponentDidUpdate(); 61 | } 62 | 63 | didUpdate(); 64 | }; 65 | } 66 | }; 67 | } 68 | 69 | // Install the componentWillUnmount lifecycle hook. 70 | if (typeof unMount === 'function') { 71 | const oldWillUnmount = activeComponent.componentWillUnmount; 72 | 73 | activeComponent.componentWillUnmount = () => { 74 | if (typeof oldWillUnmount === 'function') { 75 | oldWillUnmount(); 76 | } 77 | 78 | unMount(); 79 | }; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /packages/diffhtml-components/lib/create-state.js: -------------------------------------------------------------------------------- 1 | import { $$hooks } from './util/symbols'; 2 | import { ActiveRenderState } from './util/types'; 3 | 4 | /** 5 | * Create state allows a class or function component to create state from the 6 | * render function. Any other attempt to use this will fail. All createState 7 | * invocations must be in the exact same order, every render. 8 | * 9 | * Borrowed from the concept introduced by React, useState. This will be used by 10 | * the React Compatibility later renamed as `useState`. The reason this is named 11 | * `createState` is due to no guarentees of parity with React hooks. 12 | * 13 | * @param {any} defaultValue 14 | * 15 | * @returns {any[]} 16 | */ 17 | export function createState(defaultValue = {}) { 18 | if (ActiveRenderState.length === 0) { 19 | throw new Error('Cannot create state unless in render function'); 20 | } 21 | 22 | const [ activeComponent ] = ActiveRenderState; 23 | const hooks = activeComponent[$$hooks]; 24 | const activeHook = hooks.fns[hooks.i]; 25 | const currentValue = activeHook ? activeHook[0] : defaultValue; 26 | const retVal = activeHook || [currentValue]; 27 | 28 | /** 29 | * @param {any} newValue 30 | */ 31 | const setState = newValue => { 32 | retVal[0] = newValue; 33 | return activeComponent.forceUpdate(); 34 | }; 35 | 36 | if (retVal.length === 1) { 37 | retVal.push(setState); 38 | } 39 | 40 | // Return currentValue and setState. 41 | hooks.fns[hooks.i] = retVal; 42 | 43 | // Increment the hooks count. 44 | hooks.i += 1; 45 | 46 | return retVal; 47 | } 48 | -------------------------------------------------------------------------------- /packages/diffhtml-components/lib/index.js: -------------------------------------------------------------------------------- 1 | import * as Internals from './util/internals'; 2 | 3 | export { createState } from './create-state'; 4 | export { createSideEffect } from './create-side-effect'; 5 | export { default as Component } from './component'; 6 | export { Internals }; 7 | -------------------------------------------------------------------------------- /packages/diffhtml-components/lib/lifecycle/component-will-unmount.js: -------------------------------------------------------------------------------- 1 | import { ComponentTreeCache, InstanceCache, VTree } from '../util/types'; 2 | import diff from '../util/binding'; 3 | import { $$hooks } from '../util/symbols'; 4 | 5 | const { release, Internals } = diff; 6 | 7 | /** 8 | * This is called whenever a component is removed or in the case of a function 9 | * component is called whenever a component is updated. 10 | * 11 | * @param {VTree} vTree - The respecting tree pointing to the component 12 | */ 13 | export default function componentWillUnmount(vTree) { 14 | const componentTree = ComponentTreeCache.get(vTree); 15 | 16 | /** @type {VTree[]} */ 17 | const childTrees = []; 18 | 19 | ComponentTreeCache.forEach((parentTree, childTree) => { 20 | if (parentTree === componentTree) { 21 | childTrees.push(childTree); 22 | } 23 | }); 24 | 25 | const domNode = Internals.NodeCache.get(vTree); 26 | 27 | // Clean up attached Shadow DOM. 28 | if (domNode && /** @type {any} */ (domNode).shadowRoot) { 29 | release(/** @type {any} */ (domNode).shadowRoot); 30 | } 31 | 32 | vTree.childNodes.forEach(componentWillUnmount); 33 | 34 | if (!InstanceCache.has(componentTree)) { 35 | return; 36 | } 37 | 38 | const instance = InstanceCache.get(componentTree); 39 | InstanceCache.delete(componentTree); 40 | 41 | // Empty out all hooks for gc. If using a stateless class or function, they 42 | // may not have this value set. 43 | if (instance[$$hooks]) { 44 | instance[$$hooks].fns.length = 0; 45 | instance[$$hooks].i = 0; 46 | } 47 | 48 | ComponentTreeCache.delete(vTree); 49 | 50 | // If there is a parent, ensure it is called recursively. 51 | if (ComponentTreeCache.has(componentTree)) { 52 | componentWillUnmount(componentTree); 53 | } 54 | 55 | // Ensure this is a stateful component. Stateless components do not get 56 | // lifecycle events yet. 57 | instance && instance.componentWillUnmount && instance.componentWillUnmount(); 58 | } 59 | -------------------------------------------------------------------------------- /packages/diffhtml-components/lib/lifecycle/invoke-refs.js: -------------------------------------------------------------------------------- 1 | import { EMPTY, ComponentTreeCache, InstanceCache, VTree } from '../util/types'; 2 | import diff from '../util/binding'; 3 | 4 | const { Internals } = diff; 5 | 6 | /** 7 | * @param {any} target 8 | * @param {VTree} vTree 9 | * @param {HTMLElement | VTree | null=} value 10 | */ 11 | export const invokeRef = (target = EMPTY.OBJ, vTree, value) => { 12 | let { ref } = target.props || target; 13 | 14 | // Allow refs to be passed to HTML elements. When in a DOM environment 15 | // a Node will be passed to the ref function and assigned. 16 | if (!ref) { 17 | target = Internals.NodeCache.get(vTree); 18 | ref = vTree.attributes.ref; 19 | } 20 | 21 | // Only backfill the value with target if no value was provided (either null 22 | // or a truthy value). 23 | if (value === undefined) { 24 | value = target; 25 | } 26 | 27 | if (typeof ref === 'function') { 28 | ref(value); 29 | } 30 | else if (typeof ref === 'object' && ref) { 31 | ref.current = value; 32 | } 33 | else if (typeof ref === 'string') { 34 | target.refs = { ...target.refs, [ref]: value }; 35 | } 36 | }; 37 | 38 | /** 39 | * @param {VTree} vTree - Element to start search, crawl for refs to invoke 40 | * @param {HTMLElement | VTree | null} value - Value to populate ref with 41 | */ 42 | export function invokeRefsForVTree(vTree, value) { 43 | const componentTree = ComponentTreeCache.get(vTree); 44 | 45 | if (vTree.childNodes.length) { 46 | vTree.childNodes.filter(Boolean).forEach(childNode => { 47 | invokeRefsForVTree(childNode, value); 48 | }); 49 | } 50 | 51 | const instance = InstanceCache.get(componentTree || vTree); 52 | 53 | if (!instance) { 54 | invokeRef(Internals.NodeCache.get(vTree), vTree, value); 55 | return; 56 | } 57 | 58 | // If any instances exist, loop through them and invoke the respective `ref` 59 | // logic. 60 | invokeRef(instance, vTree, value); 61 | } 62 | -------------------------------------------------------------------------------- /packages/diffhtml-components/lib/util/binding.js: -------------------------------------------------------------------------------- 1 | import globalThis from './global'; 2 | import { DIFF_BINDING } from './types'; 3 | 4 | /** 5 | * @returns {DIFF_BINDING} 6 | */ 7 | export default /** @type {DIFF_BINDING} */ ( 8 | /** @type {any} */ (globalThis)[Symbol.for('diffHTML')] 9 | ); -------------------------------------------------------------------------------- /packages/diffhtml-components/lib/util/global.js: -------------------------------------------------------------------------------- 1 | /** @type {{ [key: string]: any }} */ 2 | export default typeof global === 'object' ? global : (typeof window === 'object' ? window : self) || {}; 3 | -------------------------------------------------------------------------------- /packages/diffhtml-components/lib/util/internals.js: -------------------------------------------------------------------------------- 1 | import { ComponentTreeCache, InstanceCache } from './types'; 2 | 3 | export const caches = { 4 | ComponentTreeCache, 5 | InstanceCache, 6 | }; -------------------------------------------------------------------------------- /packages/diffhtml-components/lib/util/symbols.js: -------------------------------------------------------------------------------- 1 | export const $$render = Symbol.for('diff.render'); 2 | export const $$vTree = Symbol.for('diff.vTree'); 3 | export const $$timeout = Symbol.for('diff.timeout'); 4 | export const $$unsubscribe = Symbol.for('diff.unsubscribe'); 5 | export const $$type = Symbol.for('diff.type'); 6 | export const $$hooks = Symbol.for('diff.hooks'); 7 | export const $$insertAfter = Symbol.for('diff.after'); 8 | export const $$diffHTML = Symbol.for('diffHTML'); 9 | -------------------------------------------------------------------------------- /packages/diffhtml-components/lib/util/types.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {{ [type: string]: any }} 3 | */ 4 | export const EMPTY = { 5 | OBJ: {}, 6 | ARR: [], 7 | BOOL: true, 8 | FUN: () => {}, 9 | }; 10 | 11 | /** 12 | * @typedef {any[]} ActiveTreeCache 13 | * @type {ActiveTreeCache} 14 | */ 15 | export const ActiveRenderState = []; 16 | 17 | export const ComponentTreeCache = new Map(); 18 | 19 | /** 20 | * @typedef {Map} InstanceCache 21 | * @type {InstanceCache} 22 | */ 23 | export const InstanceCache = new Map(); 24 | 25 | /** 26 | * @typedef {Map>} MountCache 27 | * @type {MountCache} 28 | */ 29 | export const MountCache = new Map(); 30 | 31 | /** 32 | * @typedef {Function} RenderFunction 33 | * 34 | * @property {Props} props 35 | * @property {State} state 36 | * 37 | * @returns {VTree[] | VTree | null | undefined} 38 | */ 39 | export const RenderFunction = EMPTY.OBJ; 40 | 41 | /** @typedef {{ [key: string]: any }} Props */ 42 | export const Props = EMPTY.OBJ; 43 | 44 | /** @typedef {{ [key: string]: any }} Context */ 45 | export const Context = EMPTY.OBJ; 46 | 47 | /** @typedef {{ [key: string]: any }} State */ 48 | export const State = EMPTY.OBJ; 49 | 50 | /** @typedef {import('diffhtml/dist/typings/util/types').ValidNode & { shadowRoot: any }} ValidNode */ 51 | export const ValidNode = EMPTY.OBJ; 52 | 53 | /** @typedef {import('diffhtml/dist/typings/util/types').VTree} VTree */ 54 | export const VTree = EMPTY.OBJ; 55 | 56 | /** @typedef {import('diffhtml/dist/typings/transaction').default} Transaction */ 57 | export const Transaction = EMPTY.OBJ; 58 | 59 | /** @typedef {import('diffhtml/dist/typings/index')} DIFF_BINDING */ 60 | export const DIFF_BINDING = EMPTY.OBJ; 61 | -------------------------------------------------------------------------------- /packages/diffhtml-components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diffhtml-components", 3 | "version": "1.0.0-beta.30", 4 | "main": "dist/cjs/index", 5 | "type": "module", 6 | "module": "dist/es/index", 7 | "devDependencies": { 8 | "@babel/cli": "^7.17.6", 9 | "@babel/core": "^7.17.8", 10 | "@babel/plugin-transform-classes": "^7.16.7", 11 | "@types/mocha": "^9.1.0", 12 | "babel-plugin-transform-class-properties": "^6.24.1", 13 | "babel-plugin-transform-custom-element-classes": "^0.1.0", 14 | "babel-plugin-transform-react-jsx": "^6.24.1", 15 | "babel-preset-diffhtml-imports": "^1.0.0-beta.30", 16 | "c8": "^7.11.0", 17 | "diffhtml": "^1.0.0-beta.30", 18 | "extless-loader": "^1.0.0", 19 | "jsdom": "^19.0.0", 20 | "mocha": "^9.2.2", 21 | "rollup": "^2.70.1", 22 | "rollup-plugin-babel": "^4.4.0", 23 | "rollup-plugin-commonjs": "^10.1.0", 24 | "rollup-plugin-hypothetical": "^2.1.1", 25 | "rollup-plugin-node-resolve": "^5.2.0", 26 | "rollup-plugin-replace": "^2.2.0", 27 | "rollup-plugin-visualizer": "^5.6.0", 28 | "rollup-watch": "^4.3.1", 29 | "typescript": "^4.6.3", 30 | "uglify-js": "^3.15.3" 31 | }, 32 | "scripts": { 33 | "prepublishOnly": "npm run build", 34 | "clean": "rm -rf dist/* && mkdir -p dist", 35 | "check-js": "tsc -p jsconfig.json", 36 | "build": "npm run clean && npm run check-js && npm run build-main && npm run build-esm && npm run build-cjs && npm run build-min", 37 | "build-main": "NODE_ENV=umd rollup -c rollup.main.config.js", 38 | "build-cjs": "NODE_ENV=cjs babel lib -d dist/cjs && sh ../../post-cjs.sh", 39 | "build-esm": "NODE_ENV=esm babel lib -d dist/es", 40 | "build-umd": "NODE_ENV=umd rollup -c rollup.config.js", 41 | "build-min": "NODE_ENV=min rollup -c rollup.main.config.js && uglifyjs dist/components.min.js -o dist/components.min.js -m -c", 42 | "watch-main": "NODE_ENV=umd rollup -c rollup.main.config.js -w", 43 | "test": "mocha", 44 | "test-watch": "NODE_ENV=test mocha -w test/_setup test/**", 45 | "test-cov": "NODE_ENV=test+cov c8 -r=lcov mocha test/_setup test/**" 46 | }, 47 | "gitHead": "091b497588f6f48221630431e2f1eeb7f2db37cb" 48 | } 49 | -------------------------------------------------------------------------------- /packages/diffhtml-components/rollup.component.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import nodeResolve from 'rollup-plugin-node-resolve'; 3 | import replace from 'rollup-plugin-replace'; 4 | import commonjs from 'rollup-plugin-commonjs'; 5 | import Visualizer from 'rollup-plugin-visualizer'; 6 | 7 | const entries = { 8 | min: 'lib/component.js', 9 | umd: 'lib/component.js', 10 | }; 11 | 12 | const dests = { 13 | min: 'dist/component.min.js', 14 | umd: 'dist/component.js', 15 | } 16 | 17 | const { NODE_ENV = 'umd' } = process.env; 18 | 19 | export default { 20 | input: entries[NODE_ENV], 21 | context: 'this', 22 | external: ['diffhtml'], 23 | 24 | output: [{ 25 | file: dests[NODE_ENV], 26 | format: 'umd', 27 | exports: 'default', 28 | name: 'Component', 29 | sourcemap: false, 30 | globals: { diffhtml: 'diff' }, 31 | }], 32 | 33 | plugins: [ 34 | NODE_ENV === 'min' && replace({ 'process.env.NODE_ENV': JSON.stringify('production') }), 35 | babel(), 36 | nodeResolve({ mainFields: ['main', 'module'] }), 37 | commonjs({ include: 'node_modules/**', }), 38 | NODE_ENV === 'umd' && Visualizer({ filename: './dist/component-build-size.html' }), 39 | ].filter(Boolean), 40 | }; 41 | -------------------------------------------------------------------------------- /packages/diffhtml-components/rollup.main.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import nodeResolve from 'rollup-plugin-node-resolve'; 3 | import replace from 'rollup-plugin-replace'; 4 | import commonjs from 'rollup-plugin-commonjs'; 5 | import Visualizer from 'rollup-plugin-visualizer'; 6 | 7 | const entries = { 8 | min: 'lib/index.js', 9 | umd: 'lib/index.js', 10 | }; 11 | 12 | const dests = { 13 | min: 'dist/components.min.js', 14 | umd: 'dist/components.js', 15 | } 16 | 17 | const { NODE_ENV = 'umd' } = process.env; 18 | 19 | export default { 20 | input: entries[NODE_ENV], 21 | context: 'this', 22 | external: ['diffhtml'], 23 | 24 | output: [{ 25 | file: dests[NODE_ENV], 26 | format: 'umd', 27 | name: 'components', 28 | globals: { diffhtml: 'diff' }, 29 | }], 30 | 31 | plugins: [ 32 | NODE_ENV === 'min' && replace({ 'process.env.NODE_ENV': JSON.stringify('production') }), 33 | babel(), 34 | nodeResolve({ mainFields: ['main', 'module'] }), 35 | commonjs({ include: 'node_modules/**' }), 36 | NODE_ENV === 'umd' && Visualizer({ filename: './dist/components-build-size.html' }), 37 | ].filter(Boolean), 38 | }; 39 | -------------------------------------------------------------------------------- /packages/diffhtml-components/test/_setup.js: -------------------------------------------------------------------------------- 1 | import { JSDOM } from 'jsdom'; 2 | import globalThis from '../lib/util/global'; 3 | import diff from '../lib/util/binding'; 4 | 5 | const { assign } = Object; 6 | 7 | globalThis.newJSDOMSandbox = () => { 8 | const instance = new JSDOM(``); 9 | const { window } = instance; 10 | const { document, HTMLElement, customElements } = window; 11 | 12 | assign(globalThis, { document, HTMLElement, customElements, window }); 13 | 14 | // Ensure middleware cache is always empty before running. Sometimes we can 15 | // get into an inconsistent state and this helps ensure freshness. 16 | const oldMiddleware = diff.Internals.MiddlewareCache.keys()[0]; 17 | 18 | if (oldMiddleware) { 19 | oldMiddleware.unsubscribe(); 20 | } 21 | } 22 | 23 | newJSDOMSandbox(); -------------------------------------------------------------------------------- /packages/diffhtml-components/test/util/validate-caches.js: -------------------------------------------------------------------------------- 1 | import { strictEqual } from 'assert'; 2 | import diff from '../../lib/util/binding'; 3 | import { 4 | ActiveRenderState, 5 | ComponentTreeCache, 6 | InstanceCache, 7 | MountCache, 8 | } from '../../lib/util/types'; 9 | 10 | /** 11 | * Validates that the caches has been successfully cleaned per render. 12 | */ 13 | export default function validateCaches() { 14 | strictEqual(ActiveRenderState.length, 0, 'The ActiveRenderState global should be empty'); 15 | strictEqual(ComponentTreeCache.size, 0, 'The ComponentTree cache should be empty'); 16 | strictEqual(InstanceCache.size, 0, 'The Instance cache should be empty'); 17 | strictEqual(MountCache.size, 0, 'The Mount cache should be empty'); 18 | 19 | // Validate that core diffHTML is in a clean state. 20 | validateMemory(); 21 | } 22 | 23 | const { 24 | Pool: { memory, size }, 25 | memory: { gc }, 26 | StateCache, 27 | NodeCache, 28 | MiddlewareCache, 29 | CreateTreeHookCache, 30 | CreateNodeHookCache, 31 | SyncTreeHookCache, 32 | ReleaseHookCache, 33 | } = diff.Internals; 34 | 35 | function validateMemory() { 36 | // Run garbage collection after each test. 37 | gc(); 38 | 39 | strictEqual(memory.protected.size, 0, 40 | 'Should not leave leftover protected elements in memory'); 41 | 42 | strictEqual(memory.allocated.size, 0, 43 | 'Should not leave leftover element allocations in memory'); 44 | 45 | strictEqual(memory.free.size, size, 46 | 'Should bring the free pool size back to the default'); 47 | 48 | strictEqual(StateCache.size, 0, 'The state cache should be empty'); 49 | strictEqual(NodeCache.size, 0, 'The node cache should be empty'); 50 | strictEqual(MiddlewareCache.size, 0, 'The middleware cache should be empty'); 51 | 52 | // Ensure specific middleware caches are empty as well. 53 | strictEqual(CreateTreeHookCache.size, 0, 'The create tree hook cache should be empty'); 54 | strictEqual(CreateNodeHookCache.size, 0, 'The create node hook cache should be empty'); 55 | strictEqual(SyncTreeHookCache.size, 0, 'The sync tree hook cache should be empty'); 56 | strictEqual(ReleaseHookCache.size, 0, 'The release hook cache should be empty'); 57 | } 58 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "add-module-exports", 4 | "@babel/plugin-proposal-object-rest-spread", 5 | "@babel/plugin-proposal-class-properties", 6 | "@babel/plugin-transform-modules-commonjs", 7 | "@babel/plugin-transform-arrow-functions" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/.gitignore: -------------------------------------------------------------------------------- 1 | /build/tools 2 | /node_modules 3 | /chrome-extension/dist 4 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "eqnull": true, 3 | "node": true, 4 | "browser": true, 5 | "esnext": true, 6 | "proto": true, 7 | "globals": { 8 | "$": true, 9 | "chrome": true, 10 | "self": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/.travis.yml: -------------------------------------------------------------------------------- 1 | # Install Chrome in Travis-CI. 2 | before_install: 3 | - "sudo apt-get update -qq" 4 | - "sudo apt-get remove chromium-browser" 5 | - "echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | sudo debconf-set-selections" 6 | - "sudo apt-get install ttf-mscorefonts-installer" 7 | - "sudo apt-get install x-ttcidfont-conf" 8 | - "sudo mkfontdir" 9 | - "sudo apt-get install defoma libgl1-mesa-dri xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic libappindicator1" 10 | - "wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb" 11 | - "sudo mkdir -p /usr/share/desktop-directories" 12 | - "sudo dpkg -i google-chrome-stable_current_amd64.deb" 13 | - "sudo apt-get install -f" 14 | - "sudo dpkg -i google-chrome-stable_current_amd64.deb" 15 | - "export CHROME_SANDBOX=/opt/google/chrome/chrome-sandbox" 16 | - "sudo rm -f $CHROME_SANDBOX" 17 | - "sudo wget https://googledrive.com/host/0B5VlNZ_Rvdw6NTJoZDBSVy1ZdkE -O $CHROME_SANDBOX" 18 | - "sudo chown root:root $CHROME_SANDBOX; sudo chmod 4755 $CHROME_SANDBOX" 19 | - "sudo md5sum $CHROME_SANDBOX" 20 | - "sudo chmod 1777 /dev/shm" 21 | - "export DISPLAY=:99.0" 22 | - "sh -e /etc/init.d/xvfb start" 23 | - "sleep 2" 24 | - "cat /etc/init.d/xvfb" 25 | language: node_js 26 | node_js: 27 | - 0.10 28 | env: 29 | global: 30 | - secure: "ZUeyY/wcPM40iP7eGZxPCrOEOBjjhvW9tZJ5ZLxD8n4qB3HVdcU/tstUSuBF/GgTG7BgnLC3TMUP2l6pP6li/JOxfHXrEqVNwrG8VHhpVNMdogVaL/3d0UER/s2cT2ioU7B3bkc4YFc1vR67h235KLaVip8YhMpcF1hhR1P7SC0=" 31 | - secure: "q5oQpQRFCAfe+y6FgG7Jie48H2glHdAmatbanlYv35WVOwIk2G2SEzKrqSmMgx9mEwWhgK80+8i1bhj85x9uLCXPgJuCTqX4tjdqpL5GcQ354LDveHaXI4NjynrrUg8LF4VtEn8SOQOok7DWcI/9kcAGVfnZmlPQ3fiUdJlcXEE=" 32 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/Gruntfile.coffee: -------------------------------------------------------------------------------- 1 | module.exports = -> 2 | @loadTasks 'build/tasks' 3 | 4 | @registerTask 'env-middleware', -> process.env.NODE_ENV = 'middleware' 5 | @registerTask 'env-production', -> process.env.NODE_ENV = 'production' 6 | 7 | @registerTask 'default', [ 8 | 'clean:chrome-extension' 9 | 'env-middleware' 10 | 'browserify:bridge' 11 | 'env-production' 12 | 'chrome-extension' 13 | ] 14 | 15 | @registerTask 'test', [ 16 | 'default' 17 | 'mochaTest:chrome-extension' 18 | ] 19 | 20 | @registerTask 'chrome-extension', [ 21 | 'copy:chrome-extension' 22 | 'browserify:chrome-extension' 23 | 'compress:chrome-extension' 24 | 'shell:chrome-extension' 25 | 'shell:clean-css' 26 | ] 27 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Tim Branyen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/README.md: -------------------------------------------------------------------------------- 1 | # <±/> diffHTML DevTools 2 | 3 | WIP 4 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/build/tasks/browserify.coffee: -------------------------------------------------------------------------------- 1 | module.exports = -> 2 | @loadNpmTasks 'grunt-browserify' 3 | 4 | @config 'browserify', 5 | 'bridge': 6 | files: 7 | 'chrome-extension/dist/extension/js/bridge.js': ['lib/scripts/bridge.js'] 8 | 9 | options: 10 | transform: ['babelify', 'brfs'] 11 | plugin: ['browserify-derequire'] 12 | 13 | browserifyOptions: 14 | standalone: 'devTools' 15 | 16 | 'chrome-extension': 17 | files: 18 | 'chrome-extension/dist/extension/js/index.js': ['lib/scripts/index.js'] 19 | 'chrome-extension/dist/extension/js/contentscript.js': ['lib/scripts/contentscript.js'] 20 | 'chrome-extension/dist/extension/js/devtools.js': ['lib/scripts/devtools.js'] 21 | 'chrome-extension/dist/extension/js/background.js': ['lib/scripts/background.js'] 22 | 23 | options: 24 | transform: [ 25 | 'babelify', 26 | 'brfs', 27 | ["aliasify", { global: true }] 28 | ] 29 | 30 | exclude: ['fs'] 31 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/build/tasks/clean.coffee: -------------------------------------------------------------------------------- 1 | module.exports = -> 2 | @loadNpmTasks 'grunt-contrib-clean' 3 | 4 | @config 'clean', 5 | 'chrome-extension': ['chrome-extension/dist'] 6 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/build/tasks/compress.coffee: -------------------------------------------------------------------------------- 1 | module.exports = -> 2 | @loadNpmTasks 'grunt-contrib-compress' 3 | 4 | @config 'compress', 5 | 'chrome-extension': 6 | options: 7 | archive: 'chrome-extension/dist/extension.zip' 8 | mode: 'zip' 9 | 10 | files: [ 11 | { src: ['**/*'], expand: true, cwd: 'lib' } 12 | { 13 | src: [ 14 | 'key.pem' 15 | '_locales/**' 16 | ] 17 | expand: true 18 | cwd: 'chrome-extension' 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/build/tasks/copy.coffee: -------------------------------------------------------------------------------- 1 | module.exports = -> 2 | @loadNpmTasks 'grunt-contrib-copy' 3 | 4 | chromeDest = 'chrome-extension/dist/extension' 5 | assetsDest = 'chrome-extension/dist/extension/styles/' 6 | 7 | npmDeps = [ 8 | 'node_modules/diffhtml/dist/diffhtml.js' 9 | 'node_modules/diffhtml-logger/dist/logger.js' 10 | 'node_modules/diffhtml-components/dist/cjs/*.js' 11 | 'node_modules/diffhtml-synthetic-events/dist/synthetic-events.js' 12 | 'node_modules/semantic-ui-css/semantic.min.css' 13 | 'node_modules/chartist/dist/*' 14 | ] 15 | 16 | icons = [ 17 | 'assets/**/*' 18 | ] 19 | 20 | @config 'copy', 21 | 'chrome-extension': 22 | files: [ 23 | { 24 | src: icons 25 | expand: true 26 | cwd: 'node_modules/semantic-ui-icon' 27 | dest: assetsDest 28 | } 29 | { 30 | src: npmDeps 31 | expand: true 32 | dest: chromeDest 33 | } 34 | { 35 | src: [ 36 | '**/*' 37 | ] 38 | expand: true 39 | cwd: 'module' 40 | dest: chromeDest 41 | } 42 | { 43 | src: [ 44 | '**/*' 45 | '!_assets/**' 46 | ] 47 | expand: true 48 | cwd: 'lib' 49 | dest: chromeDest 50 | } 51 | { 52 | src: [ 53 | 'manifest.json' 54 | '_locales/**' 55 | ] 56 | expand: true 57 | cwd: 'chrome-extension' 58 | dest: chromeDest 59 | } 60 | ] 61 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/build/tasks/jshint.coffee: -------------------------------------------------------------------------------- 1 | module.exports = -> 2 | @loadNpmTasks 'grunt-contrib-jshint' 3 | 4 | # Run your source code through JSHint's defaults. 5 | @config 'jshint', 6 | options: 7 | jshintrc: '.jshintrc' 8 | 9 | files: [ 10 | 'lib/**/*.js' 11 | ] 12 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/build/tasks/mocha.coffee: -------------------------------------------------------------------------------- 1 | module.exports = -> 2 | @loadNpmTasks 'grunt-mocha-test' 3 | 4 | @config 'mochaTest', 5 | options: 6 | clearRequireCache: true 7 | 8 | 'chrome-extension': 9 | src: [ 10 | 'test/integration/setup.js' 11 | 'test/integration/extension-driver.js' 12 | 'chrome-extension/test/configure.js' 13 | 'test/integration/tests/**/*.js' 14 | ] 15 | 16 | 'lib': 17 | src: [ 18 | 'test/unit/runner.js' 19 | 'test/unit/tests/**/*.js' 20 | ] 21 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/build/tasks/shell.coffee: -------------------------------------------------------------------------------- 1 | path = require 'path' 2 | fs = require 'fs' 3 | 4 | module.exports = -> 5 | @loadNpmTasks 'grunt-shell' 6 | 7 | env = process.env 8 | 9 | chrome = 'echo Skipping Chrome' 10 | 11 | # https://code.google.com/p/selenium/wiki/ChromeDriver#Requirements 12 | if process.platform is 'linux' 13 | chrome = '/usr/bin/google-chrome' 14 | 15 | if not fs.existsSync chrome 16 | chrome = '/usr/bin/google-chrome-stable' 17 | 18 | else if process.platform is 'darwin' 19 | chrome = '"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"' 20 | else if process.platform is 'win32' 21 | chrome = '"' + 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe' + '"' 22 | 23 | if not fs.existsSync chrome 24 | chrome = '"' + 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe' + '"' 25 | 26 | @config 'shell', 27 | 'chrome-extension': 28 | command: [ 29 | chrome 30 | '--pack-extension=' + path.resolve('chrome-extension/dist/extension') 31 | '--pack-extension-key=' + path.resolve('chrome-extension/key.pem') 32 | '--no-message-box' 33 | '--headless' 34 | ].join(' ') 35 | 36 | 'clean-css': 37 | command: [ 38 | 'cleancss -o', 39 | path.resolve('chrome-extension/dist/extension/styles/theme.css'), 40 | path.resolve('lib/styles/theme.css'), 41 | ].join(' ') 42 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/build/tasks/watch.coffee: -------------------------------------------------------------------------------- 1 | module.exports = -> 2 | @loadNpmTasks 'grunt-contrib-watch' 3 | 4 | @config 'watch', 5 | 'chrome-extension': 6 | files: [ 7 | 'chrome-extension/**/*' 8 | '!chrome-extension/dist/**/*' 9 | 'lib/**/*' 10 | ] 11 | 12 | tasks: [ 13 | 'browserify:bridge' 14 | 'chrome-extension' 15 | ] 16 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/chrome-extension/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extName": { 3 | "message": "Tipsy", 4 | "description": "Text string (no longer than 45 characters) extension name" 5 | }, 6 | "extDescription": { 7 | "message": "Support the creators of the content you love!", 8 | "description": "Text string (no longer than 132 characters) describing the extension. Description appears in chrome://extensions." 9 | }, 10 | "browserActionTitle": { 11 | "message": "Tipsy", 12 | "description": "Browser action default_title text string" 13 | } 14 | } -------------------------------------------------------------------------------- /packages/diffhtml-devtools/chrome-extension/_locales/es/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extName": { 3 | "message": "Chrome Extension Base", 4 | "description": "Text string (no longer than 45 characters) extension name" 5 | }, 6 | "extDescription": { 7 | "message": "Use as the basis for new extensions!", 8 | "description": "Text string (no longer than 132 characters) describing the extension. Description appears in chrome://extensions." 9 | }, 10 | "browserActionTitle": { 11 | "message": "Chrome Extension Base", 12 | "description": "Browser action default_title text string" 13 | } 14 | } -------------------------------------------------------------------------------- /packages/diffhtml-devtools/chrome-extension/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDdLQrbsHbaohtU 3 | GgB5F3QR6X0ofI6y8WPB4PlhCFX4zK0pp8OxEX4+M/I3lTJFkD3iLmlVO9AYb52E 4 | W0ZLpAzOP0BUipruEIsejB/P0qgzP/gEm2PktQ3QogOaJCZxDFCp20BQX0X9ezNE 5 | wVocvm7GGIEcf2Cjw39bB4Vp7mgHsAsmTxewDsmiLHIBXPBf+Icipiy6tYYmFz/I 6 | yG2MvXuMSVFegfHIBixjRPx/8xmQT1iE3IGI4hLu8PRRNfpb8C+yNRuWQW2gp+LW 7 | WUZPGvTUEilOAwTqqJW7YnSR9TgOK1k8Pp+VGebVV7vwyTh/iAPBnO6ZBga8yOb3 8 | xVqmF45JAgMBAAECggEAYw30KfW7FSm6wYyvn4vQcOE4K3S1WBDh04fVSA66qiXI 9 | e7pl2xxxhJwxI5GPJTZ3cJ/GjuStyvPaANf8AI5lKc2MGxDEWFBSbgjlimbW67T/ 10 | d9i8AUbQ/BpDMLp1+PVB/wBxqk0xBFgz2twZZnwnElMRJ9koR8+bbwJMTuf18VKi 11 | AyxSw/Gfx2Pi2UPzc7FBtwEBX2rd1JnSWg3Im6DzhkfgFZcur5KNBm894ynU7GRl 12 | yQve3VznQ3Jzilj2xngb44rPpWl5O3b0Wqnrn1iPTl2vGa0920LD+fYzvkyzYseh 13 | kcAuHpMv/AzWNIJF9TFCoBTsne3rGaylfKhksAt9HQKBgQD7nZKG30f7bI1JcaT4 14 | eJ0J+0ucUL99s/BxFWbE0Sslb+CasW7PHSmgq9oW5hp8zDsLeaLEcobRKsQcwPuF 15 | VIPBddDOMwMKgReUAsdGUGf6Y0FxnFFY7cuUXVWgb5B5meQ1bfY90ghCTT0hRPaS 16 | P5uT3HxIGi7EAgY5EPwQ+zAOwwKBgQDhB67CXykAG6aEiuEw7DWYzN2BJ8j20sbz 17 | 7Whr78bZrjzbJ/3pjsq/ZQJ05DikG6DNa1zWON8cZes1fhl+3Ri7IdyulXtC4yLL 18 | uqYr6HBfCoYG3pVSE6hAo6iz0thHUOI/zNZKT4RVFuTHQ4wDsX/0Rl1SyGXQJuO5 19 | wsPwKsj2AwKBgCy8Q0T/hcjJ8ATS08Xpi+Iub68HHES5LVKtv2vW1Jj/XyuhyFXC 20 | lZgfddMEbkkp9oV/xtSumBGwTNXf6dg2woYu8ET5BN1lPk/ufoed3B7EbupIJJ5v 21 | CPcD8SlpLIKyPcTSHCm5ogZHvUqg/EXcUUjktqQLI61tvrV+s5JBVrYJAoGBANsh 22 | ixm2Zwu24VnSj+X/L1YjsVPTNUy+BoWE25m4PfC+Tn6vm//zUBY/O7wufcW5Lca7 23 | 1QS7DvDtgrVtnVA/55RbLjZIVGbXHow7rxO03rB+Y/OOjuQFRmPjuyWZnYkdB6VP 24 | SCHG+zuM9q3gZhk2oT5zwu8ZPKQNKtc7BWj7kQSXAoGAEe6okFX/7m6sp4KSFuoE 25 | IiQF4tr3T8jTWacn4U/ie14mx15w+D+hxx3510ojXXkPIrkPz5flfdfveQC5wdml 26 | nZCTDgzu9uC6OJgfLI8VjI2kY7RD8rgO1AXx7qrA+ZNONKx128kVsKrEc/NCNsTu 27 | +G3jSyQWwG+JLGxLKIWlk+s= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/chrome-extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diffHTML DevTools", 3 | "version": "1.0.0.30", 4 | "description": "Official diffHTML Chrome DevTools Extension", 5 | "permissions": [ 6 | "tabs", 7 | "*://*/*", 8 | "http://*/*", 9 | "https://*/*" 10 | ], 11 | "background": { 12 | "scripts": ["js/background.js"] 13 | }, 14 | "content_scripts": [{ 15 | "matches": ["", "*://*/*", "http://*/*", "https://*/*"], 16 | "js": ["js/contentscript.js"], 17 | "run_at": "document_start" 18 | }], 19 | "web_accessible_resources": [ 20 | "devtools.es5.js", 21 | "scripts/*", 22 | "scripts/**/*", 23 | "styles/*", 24 | "styles/**/*", 25 | "node_modules/**/*", 26 | "markup/*", 27 | "markup/**/*", 28 | "icons/*", 29 | "icons/**/*" 30 | ], 31 | "icons": { 32 | "16": "icons/logo-16.png", 33 | "48": "icons/logo-48.png", 34 | "128": "icons/logo-128.png" 35 | }, 36 | "devtools_page": "markup/devtools.html", 37 | "manifest_version": 2, 38 | "default_locale": "en" 39 | } 40 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | diffHTML Demo 8 | 9 | 10 | 11 |
12 |
13 | 14 | 15 | 16 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/icons/logo-128-invert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbranyen/diffhtml/f25d1c4acf8da35debb1e1f3f4af868f011ae0ab/packages/diffhtml-devtools/lib/icons/logo-128-invert.png -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/icons/logo-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbranyen/diffhtml/f25d1c4acf8da35debb1e1f3f4af868f011ae0ab/packages/diffhtml-devtools/lib/icons/logo-128.png -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/icons/logo-16-invert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbranyen/diffhtml/f25d1c4acf8da35debb1e1f3f4af868f011ae0ab/packages/diffhtml-devtools/lib/icons/logo-16-invert.png -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/icons/logo-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbranyen/diffhtml/f25d1c4acf8da35debb1e1f3f4af868f011ae0ab/packages/diffhtml-devtools/lib/icons/logo-16.png -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/icons/logo-48-invert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbranyen/diffhtml/f25d1c4acf8da35debb1e1f3f4af868f011ae0ab/packages/diffhtml-devtools/lib/icons/logo-48-invert.png -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/icons/logo-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbranyen/diffhtml/f25d1c4acf8da35debb1e1f3f4af868f011ae0ab/packages/diffhtml-devtools/lib/icons/logo-48.png -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/markup/devtools.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/markup/panel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |

Could not locate diffHTML

10 |
11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/scripts/background.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const connections = new Map(); 4 | const savedRequests = []; 5 | 6 | chrome.runtime.onConnect.addListener(port => { 7 | if (port.name === 'devtools-page') { 8 | const devToolsListener = message => { 9 | connections.set(message.tabId, port); 10 | 11 | savedRequests.forEach(([id, request]) => { 12 | connections.get(id).postMessage(request); 13 | }); 14 | 15 | if (message.name === 'init') { 16 | chrome.tabs.executeScript(message.tabId, { 17 | file: message.scriptToInject, 18 | }); 19 | } 20 | }; 21 | 22 | savedRequests.length = 0; 23 | 24 | port.onMessage.addListener(devToolsListener); 25 | 26 | port.onDisconnect.addListener(port => { 27 | port.onMessage.removeListener(devToolsListener); 28 | 29 | connections.forEach((prevPort, tabId) => { 30 | if (prevPort === port) { 31 | connections.delete(tabId); 32 | } 33 | }); 34 | }); 35 | } 36 | }); 37 | 38 | // Receive message from content script and relay to the devTools page for the 39 | // current tab 40 | chrome.runtime.onMessage.addListener((request, sender) => { 41 | // Messages from content scripts should have sender.tab set 42 | if (sender.tab) { 43 | const { id } = sender.tab; 44 | 45 | if (connections.has(id)) { 46 | connections.get(id).postMessage(request); 47 | } 48 | else { 49 | savedRequests.push([id, request]); 50 | } 51 | } 52 | 53 | return true; 54 | }); 55 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/scripts/components/footer.js: -------------------------------------------------------------------------------- 1 | import { html } from 'diffhtml'; 2 | import { Component } from 'diffhtml-components'; 3 | 4 | class DevtoolsFooter extends Component { 5 | static defaultProps = { 6 | version: '', 7 | } 8 | 9 | render() { 10 | return html` 11 | 12 | 13 | 14 | 26 | `; 27 | } 28 | 29 | styles() { 30 | return ` 31 | :host { 32 | background-color: #fff; 33 | color: #333; 34 | padding: 16px; 35 | font-weight: bold; 36 | text-align: right; 37 | border-top: 1px solid rgba(34, 36, 38, 0.1); 38 | z-index: 200; 39 | position: relative; 40 | } 41 | 42 | .icon.input { 43 | width: calc(100% - 540px); 44 | margin-right: 30px; 45 | } 46 | `; 47 | } 48 | } 49 | 50 | customElements.define('devtools-footer', DevtoolsFooter); 51 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/scripts/components/panels.js: -------------------------------------------------------------------------------- 1 | import { html } from 'diffhtml'; 2 | import { Component } from 'diffhtml-components'; 3 | 4 | class DevtoolsPanels extends Component { 5 | static defaultProps = { 6 | route: '', 7 | activeRoute: '', 8 | } 9 | 10 | render() { 11 | const { route, activeRoute } = this.props; 12 | 13 | return html` 14 | 15 | 16 |
17 | ${route === activeRoute && html``} 18 |
19 | `; 20 | } 21 | 22 | styles() { 23 | const { route, activeRoute } = this.props; 24 | const isActive = route === activeRoute; 25 | 26 | return ` 27 | :host { 28 | display: ${isActive ? 'flex' : 'none'}; 29 | flex: auto; 30 | position: relative; 31 | overflow-y: auto; 32 | } 33 | 34 | .panel { 35 | box-sizing: border-box; 36 | display: flex; 37 | flex: 1; 38 | flex-direction: column; 39 | } 40 | `; 41 | } 42 | } 43 | 44 | customElements.define('devtools-panels', DevtoolsPanels); 45 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/scripts/components/split-view.js: -------------------------------------------------------------------------------- 1 | import { html } from 'diffhtml'; 2 | import { Component } from 'diffhtml-components'; 3 | 4 | class DevtoolsSplitView extends Component { 5 | render() { 6 | return html` 7 | 8 | 9 |
10 | 11 |
12 | `; 13 | } 14 | 15 | styles() { 16 | return ` 17 | :host { 18 | display: flex; 19 | flex: 1; 20 | flex-direction: row; 21 | height: 100%; 22 | } 23 | 24 | .split-view { 25 | display: flex; 26 | width: 100%; 27 | height: 100%; 28 | } 29 | `; 30 | } 31 | } 32 | 33 | customElements.define('devtools-split-view', DevtoolsSplitView); 34 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/scripts/contentscript.js: -------------------------------------------------------------------------------- 1 | // The content script is used by the extension to communicate with the UI 2 | // layer. 3 | // Check injector.js for how to communicate from the UI layer to the extension. 4 | 5 | // This uses browserify fs to embed. It's pretty buggy and not ideal. There is 6 | // a better way to do this, but I haven't looked into it yet. 7 | const path = require('path'); 8 | const fs = require('fs'); 9 | const bridgeModule = fs.readFileSync(path.join(__dirname, '/../../chrome-extension/dist/extension/js/bridge.js'), 'utf8'); 10 | const injectorModule = fs.readFileSync(path.join(__dirname, '/injector.js'), 'utf8'); 11 | const { parse } = JSON; 12 | 13 | // Only initialize once per page, saves resources. 14 | if (!document.documentElement.__diffHTMLDevToolsInit) { 15 | document.documentElement.__diffHTMLDevToolsInit = true; 16 | 17 | const middleware = document.createElement('script') 18 | middleware.appendChild(document.createTextNode(bridgeModule)); 19 | document.documentElement.appendChild(middleware); 20 | middleware.remove(); 21 | 22 | const injector = document.createElement('script') 23 | injector.appendChild(document.createTextNode(injectorModule)); 24 | document.documentElement.appendChild(injector); 25 | injector.remove(); 26 | 27 | const postMessage = body => chrome.runtime.sendMessage(body); 28 | 29 | chrome.runtime.onMessage.addListener(ev => document.dispatchEvent( 30 | new CustomEvent(`diffHTML:${ev.type}`, { 31 | detail: ev, 32 | }) 33 | )); 34 | 35 | const postEvent = ev => postMessage(ev.detail); 36 | 37 | document.addEventListener('diffHTML:activated', postEvent); 38 | document.addEventListener('diffHTML:start', postEvent); 39 | document.addEventListener('diffHTML:end', postEvent); 40 | document.addEventListener('diffHTML:ping', postEvent); 41 | } 42 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/scripts/devtools.js: -------------------------------------------------------------------------------- 1 | chrome.devtools.panels.create('diffHTML', null, 'markup/panel.html', null); 2 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/scripts/injector.js: -------------------------------------------------------------------------------- 1 | // The injector is used to communicate from the UI layer to the extension. 2 | // The extension communicates to the UI layer in contentscript.js 3 | if (!window.__diffHTMLDevTools) { 4 | // This file gets loaded into the browser where the application is running. 5 | // It provides the bridge into the extension. 6 | const triggerEvent = (action, data) => { 7 | document.dispatchEvent(new CustomEvent(`diffHTML:${action}`, { 8 | detail: JSON.stringify({ action, data }) 9 | })); 10 | }; 11 | 12 | // A global hook for the devtools which is picked up by the application. 13 | window.__diffHTMLDevTools = () => ({ 14 | activate(args={}) { 15 | triggerEvent('activated', args); 16 | return this; 17 | }, 18 | 19 | startTransaction(startDate, args={}) { 20 | triggerEvent('start', { startDate, args }); 21 | return this; 22 | }, 23 | 24 | endTransaction(startDate, endDate, args={}) { 25 | triggerEvent('end', { startDate, endDate, args }); 26 | return this; 27 | }, 28 | 29 | ping() { 30 | triggerEvent('ping'); 31 | return this; 32 | }, 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/scripts/panels/settings.js: -------------------------------------------------------------------------------- 1 | import { html } from 'diffhtml'; 2 | import { Component } from 'diffhtml-components'; 3 | 4 | class DevtoolsSettingsPanel extends Component { 5 | state = { 6 | isExpanded: false, 7 | } 8 | 9 | render() { 10 | const { isExpanded } = this.state; 11 | 12 | return html` 13 | 14 | 15 | 16 |
17 |

this.setState({ isExpanded: !isExpanded })}> 18 | Settings 19 |

20 | 21 | ${isExpanded && html` 22 |

23 | Alter the internals of diffHTML. Useful for toggling performance and 24 | changing how sampling transactions works. 25 |

26 | `} 27 |
28 | 29 |
30 |
31 | 32 | Performance 33 |
34 | 35 |
36 |

37 |

38 | 39 | 40 |
41 |

42 |
43 | 44 |
45 | 46 | Transaction Sampling 47 |
48 | 49 |
50 |

51 | 52 |

53 |
54 |
55 | `; 56 | } 57 | 58 | shouldComponentUpdate() { 59 | return this.props.activeRoute === '#settings'; 60 | } 61 | 62 | styles() { 63 | return ` 64 | :host { 65 | display: block; 66 | } 67 | 68 | h3 { 69 | cursor: pointer; 70 | } 71 | 72 | .ui.segment { 73 | border-left: 0; 74 | border-right: 0; 75 | border-top: 0; 76 | margin-top: 0; 77 | position: sticky; 78 | top: 0; 79 | z-index: 100; 80 | background: #FFF; 81 | color: #333; 82 | border-radius: 0 !important; 83 | user-select: none; 84 | } 85 | 86 | .ui.accordion { 87 | box-shadow: none !important; 88 | } 89 | `; 90 | } 91 | } 92 | 93 | customElements.define('devtools-settings-panel', DevtoolsSettingsPanel); 94 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/scripts/utils/format-patches.js: -------------------------------------------------------------------------------- 1 | import { Internals } from 'diffhtml'; 2 | 3 | const { PATCH_TYPE } = Internals; 4 | 5 | export default function formatPatches(patches) { 6 | const simplified = []; 7 | 8 | let i = 0; 9 | 10 | while (true) { 11 | const patchType = patches[i]; 12 | 13 | if (i === patches.length) { 14 | break; 15 | } 16 | 17 | switch(patchType) { 18 | case PATCH_TYPE.SET_ATTRIBUTE: { 19 | const vTree = patches[i + 1]; 20 | const name = patches[i + 2]; 21 | const newValue = patches[i + 3]; 22 | 23 | simplified.push({ 24 | type: 'setAttribute', 25 | vTree, 26 | name, 27 | newValue, 28 | }); 29 | 30 | i += 4; 31 | break; 32 | } 33 | 34 | case PATCH_TYPE.REMOVE_ATTRIBUTE: { 35 | const vTree = patches[i + 1]; 36 | const name = patches[i + 2]; 37 | 38 | simplified.push({ 39 | type: 'removeAttribute', 40 | vTree, 41 | name, 42 | }); 43 | 44 | i += 3; 45 | break; 46 | } 47 | 48 | case PATCH_TYPE.NODE_VALUE: { 49 | const vTree = patches[i + 1]; 50 | const newValue = patches[i + 2]; 51 | const oldValue = patches[i + 3]; 52 | 53 | simplified.push({ 54 | type: 'nodeValue', 55 | vTree, 56 | newValue, 57 | oldValue, 58 | }); 59 | 60 | i += 4; 61 | break; 62 | } 63 | 64 | case PATCH_TYPE.INSERT_BEFORE: { 65 | const vTree = patches[i + 1]; 66 | const newTree = patches[i + 2]; 67 | const refTree = patches[i + 3]; 68 | 69 | simplified.push({ 70 | type: 'insertBefore', 71 | vTree, 72 | newTree, 73 | refTree, 74 | }); 75 | 76 | i += 4; 77 | break; 78 | } 79 | 80 | case PATCH_TYPE.REPLACE_CHILD: { 81 | const newTree = patches[i + 1]; 82 | const oldTree = patches[i + 2]; 83 | 84 | simplified.push({ 85 | type: 'replaceChild', 86 | newTree, 87 | oldTree, 88 | }); 89 | 90 | i += 3; 91 | break; 92 | } 93 | 94 | case PATCH_TYPE.REMOVE_CHILD: { 95 | const vTree = patches[i + 1]; 96 | 97 | simplified.push({ 98 | type: 'removeChild', 99 | vTree, 100 | }); 101 | 102 | i += 2; 103 | break; 104 | } 105 | } 106 | } 107 | 108 | return simplified; 109 | } 110 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/styles/index.css: -------------------------------------------------------------------------------- 1 | @import "/styles/theme.css"; 2 | 3 | body, main { 4 | color: #333; 5 | padding: 0; 6 | margin: 0; 7 | background-color: #FFF; 8 | display: flex; 9 | width: 100%; 10 | height: 100%; 11 | flex-direction: column; 12 | font-size: 100%; 13 | } 14 | 15 | main[data-theme="dark"] { 16 | background-color: #222222; 17 | } 18 | 19 | body::-webkit-scrollbar { 20 | width: 5px; 21 | position: absolute; 22 | } 23 | 24 | body::-webkit-scrollbar-track { 25 | background: #ddd; 26 | } 27 | 28 | body::-webkit-scrollbar-thumb { 29 | background: #666; 30 | } 31 | 32 | h1:first-child { 33 | border-bottom: 1px solid #CCCCCC; 34 | font-size: 16px; 35 | font-weight: normal; 36 | padding: 10px; 37 | box-sizing: border-box; 38 | margin: 0; 39 | position: sticky; 40 | top: 0; 41 | user-select: none; 42 | } 43 | 44 | [data-theme="dark"] h1:first-child { 45 | border-bottom: none; 46 | } 47 | 48 | #not-found { 49 | text-align: center; 50 | font-size: 20px; 51 | font-weight: normal; 52 | box-sizing: border-box; 53 | margin: 0; 54 | user-select: none; 55 | line-height: 100vh; 56 | } 57 | 58 | #not-found img { 59 | position: relative; 60 | top: 17px; 61 | margin-left: 5px; 62 | margin-right: 5px; 63 | } 64 | 65 | [data-theme="dark"] #not-found { 66 | color: #BDC6CF; 67 | background: transparent; 68 | } 69 | 70 | .retry button { 71 | -webkit-appearance: none; 72 | padding: 20px; 73 | height: 120px; 74 | width: 120px; 75 | text-align: center; 76 | color: #333; 77 | font-size: 60px; 78 | font-weight: bold; 79 | background-color: #E2E2E2; 80 | border-radius: 60px; 81 | margin-top: 20px; 82 | opacity: .2; 83 | transition: opacity 600ms; 84 | cursor: pointer; 85 | } 86 | 87 | .retry button:active { 88 | outline: none !important; 89 | } 90 | 91 | .retry button:focus { 92 | outline: none !important; 93 | } 94 | 95 | .retry button:hover { 96 | opacity: 1; 97 | } 98 | 99 | .rows:empty:after { 100 | display: block; 101 | background-color: #fafafa; 102 | height: 46px; 103 | line-height: 46px; 104 | content: "No transactions"; 105 | color: #333; 106 | padding-left: 20px; 107 | border-bottom: 1px dotted #a3a3a3; 108 | } 109 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/styles/theme.css: -------------------------------------------------------------------------------- 1 | @import '/node_modules/semantic-ui-css/semantic.min.css'; 2 | @import '/node_modules/semantic-ui-icon/icon.min.css'; 3 | 4 | .ui.inverted.input > input { 5 | background: #3E3E3E; 6 | } 7 | 8 | /* Patch in missing Semantic UI inverted styles */ 9 | 10 | .ui.styled.accordion { 11 | background: inherit; 12 | } 13 | 14 | .ui.styled.accordion.inverted { 15 | color: #C5C5C5; 16 | } 17 | 18 | .ui.toggle.checkbox.inverted .box, .ui.toggle.checkbox.inverted label { 19 | color: #FFF; 20 | } 21 | 22 | .ui.toggle.checkbox.inverted label:before { 23 | box-shadow: rgba(255, 255, 255, 0.5) 0px 0px 0px 2px inset; 24 | } 25 | 26 | .ui.toggle.checkbox.inverted input { 27 | border: 1px solid #FFF; 28 | } 29 | 30 | .ui.attached.tabular.menu a { 31 | color: #757575; 32 | } 33 | 34 | .ui.attached.tabular.menu.inverted a { 35 | color: #FFFFFF; 36 | } 37 | 38 | .ui.toggle.checkbox.inverted input:checked~.box, 39 | .ui.toggle.checkbox.inverted input:checked~label { 40 | color: #FFF !important; 41 | } 42 | 43 | .ui.table { 44 | border-radius: 0; 45 | margin: 0; 46 | } 47 | 48 | .ui.table thead tr th { 49 | border-radius: 0; 50 | } 51 | 52 | .ui.attached.menu { 53 | border-color: #DEDEDE; 54 | } 55 | 56 | .ui.attached.menu .item, 57 | .ui.attached.menu .active.item { 58 | border-top: 0; 59 | border-radius: 0 !important; 60 | } 61 | 62 | .ui.attached.menu .active.item { 63 | border-color: #DEDEDE; 64 | } 65 | 66 | .ui.attached.tab { 67 | border: none; 68 | flex: 1; 69 | padding: 0; 70 | } 71 | 72 | .ui.item.inverted { 73 | border-color: transparent !important; 74 | } 75 | 76 | .ui.segmentui.segment.inverted { 77 | border-bottom: 2px solid rgb(51, 51, 51); 78 | } 79 | 80 | .ui.attached.tabular.menuui.inverted { 81 | border-bottom: 1px solid rgb(51, 51, 51); 82 | } 83 | 84 | .ui.inverted.segment, .ui.primary.inverted.segment { 85 | background: #222; 86 | } 87 | 88 | [data-position~=top][data-tooltip]:before { 89 | z-index: 1002; 90 | } 91 | 92 | [data-tooltip][data-position="bottom left"]:hover:after, [data-tooltip][data-position="bottom right"]:hover:after, [data-tooltip][data-position="top left"]:hover:after, [data-tooltip][data-position="top right"]:hover:after { 93 | z-index: 1001; 94 | } 95 | 96 | h3 { 97 | font-size: 15px; 98 | } 99 | -------------------------------------------------------------------------------- /packages/diffhtml-devtools/lib/styles/tooltip.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Tooltip Styles 3 | * https://codepen.io/cbracco/pen/qzukg 4 | */ 5 | 6 | /* Add this attribute to the element that needs a tooltip */ 7 | [data-tooltip] { 8 | position: relative; 9 | z-index: 2; 10 | cursor: pointer; 11 | } 12 | 13 | /* Hide the tooltip content by default */ 14 | [data-tooltip]:before, 15 | [data-tooltip]:after { 16 | visibility: hidden; 17 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; 18 | filter: progid: DXImageTransform.Microsoft.Alpha(Opacity=0); 19 | opacity: 0; 20 | pointer-events: none; 21 | } 22 | 23 | /* Position tooltip above the element */ 24 | [data-tooltip]:before { 25 | position: absolute; 26 | bottom: 150%; 27 | left: 50%; 28 | margin-bottom: 5px; 29 | margin-left: -80px; 30 | padding: 7px; 31 | width: 160px; 32 | -webkit-border-radius: 3px; 33 | -moz-border-radius: 3px; 34 | border-radius: 3px; 35 | background-color: #000; 36 | background-color: hsla(0, 0%, 20%, 0.9); 37 | color: #fff; 38 | content: attr(data-tooltip); 39 | text-align: center; 40 | font-size: 14px; 41 | line-height: 1.2; 42 | } 43 | 44 | /* Triangle hack to make tooltip look like a speech bubble */ 45 | [data-tooltip]:after { 46 | position: absolute; 47 | bottom: 150%; 48 | left: 50%; 49 | margin-left: -5px; 50 | width: 0; 51 | border-top: 5px solid #000; 52 | border-top: 5px solid hsla(0, 0%, 20%, 0.9); 53 | border-right: 5px solid transparent; 54 | border-left: 5px solid transparent; 55 | content: " "; 56 | font-size: 0; 57 | line-height: 0; 58 | } 59 | 60 | /* Show tooltip content on hover */ 61 | [data-tooltip]:hover:before, 62 | [data-tooltip]:hover:after { 63 | visibility: visible; 64 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; 65 | filter: progid: DXImageTransform.Microsoft.Alpha(Opacity=100); 66 | opacity: 1; 67 | } -------------------------------------------------------------------------------- /packages/diffhtml-devtools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diffhtml-devtools", 3 | "private": true, 4 | "main": "module/devtools.es5.js", 5 | "devDependencies": { 6 | "@babel/core": "^7.17.8", 7 | "@babel/plugin-proposal-class-properties": "^7.16.7", 8 | "@babel/plugin-proposal-object-rest-spread": "^7.17.3", 9 | "@babel/plugin-transform-arrow-functions": "^7.16.7", 10 | "@babel/plugin-transform-classes": "^7.16.7", 11 | "@babel/plugin-transform-modules-commonjs": "^7.17.7", 12 | "aliasify": "^2.1.0", 13 | "babel-plugin-add-module-exports": "^1.0.4", 14 | "babel-plugin-transform-custom-element-classes": "^0.1.0", 15 | "babel-plugin-transform-react-jsx": "^6.24.1", 16 | "babel-preset-es2015": "^6.24.1", 17 | "babelify": "^10.0.0", 18 | "brfs": "^2.0.2", 19 | "browserify": "^17.0.0", 20 | "browserify-derequire": "^1.1.1", 21 | "clean-css-cli": "^5.6.0", 22 | "coffeescript": "^2.6.1", 23 | "derequire": "^2.1.1", 24 | "es6-module-loader": "^0.17.11", 25 | "es6-module-transpiler": "^0.10.0", 26 | "grunt": "^1.5.3", 27 | "grunt-browserify": "^6.0.0", 28 | "grunt-cli": "^1.4.3", 29 | "grunt-contrib-clean": "^2.0.0", 30 | "grunt-contrib-compress": "^2.0.0", 31 | "grunt-contrib-copy": "^1.0.0", 32 | "grunt-contrib-jshint": "^3.2.0", 33 | "grunt-contrib-stylus": "^1.2.0", 34 | "grunt-contrib-watch": "^1.1.0", 35 | "grunt-mocha-test": "^0.13.3", 36 | "grunt-shell": "^4.0.0", 37 | "knox": "^0.5.0", 38 | "madge": "^5.0.1", 39 | "mocha": "^9.2.2", 40 | "promise": "^8.1.0", 41 | "prop-types": "^15.8.1", 42 | "semantic-ui-css": "^2.4.1", 43 | "semantic-ui-icon": "^2.3.3", 44 | "uglifyify": "^5.0.2", 45 | "unique-selector": "^0.5.0" 46 | }, 47 | "aliasify": { 48 | "aliases": { 49 | "react": "diffhtml-react-compat", 50 | "react-dom": "diffhtml-react-compat" 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-linter/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["diffhtml-imports"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-linter/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-linter/README.md: -------------------------------------------------------------------------------- 1 | # <±/> diffHTML Linter Middleware 2 | 3 | Stable Version: 1.0.0-beta.30 4 | 5 | Use to validate your markup for inconsistencies and bad practices using 6 | [HTMLHint](https://github.com/htmlhint/HTMLHint) rules. 7 | 8 | ##### Installation 9 | 10 | ``` sh 11 | npm install diffhtml-middleware-linter 12 | ``` 13 | 14 | ##### Example 15 | 16 | ``` javascript 17 | import { use } from 'diffhtml'; 18 | import linter from 'diffhtml-middleware-linter'; 19 | 20 | use(linter()); 21 | ``` 22 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-linter/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './dist/es/index.js'; 2 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-linter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diffhtml-middleware-linter", 3 | "version": "1.0.0-beta.30", 4 | "description": "Lints HTML parsed by diffHTML", 5 | "type": "module", 6 | "main": "dist/cjs/index", 7 | "module": "dist/es/index", 8 | "scripts": { 9 | "prepublishOnly": "npm run build", 10 | "clean": "rm -rf dist/* && mkdir -p dist", 11 | "build": "npm run clean && npm run build-umd && npm run build-cjs && npm run build-esm && npm run build-min", 12 | "build-cjs": "NODE_ENV=cjs babel lib -d dist/cjs && sh ../../post-cjs.sh", 13 | "build-esm": "NODE_ENV=esm babel lib -d dist/es", 14 | "build-umd": "NODE_ENV=umd rollup -c rollup.config.js", 15 | "build-min": "NODE_ENV=min rollup -c rollup.config.js && uglifyjs dist/linter.min.js -o dist/linter.min.js -m -c", 16 | "watch": "NODE_ENV=umd rollup -c rollup.config.js -w" 17 | }, 18 | "keywords": [ 19 | "diffhtml", 20 | "linter", 21 | "middleware" 22 | ], 23 | "author": "Tim Branyen (@tbranyen)", 24 | "license": "MIT", 25 | "devDependencies": { 26 | "@babel/cli": "^7.12.10", 27 | "@babel/core": "^7.12.10", 28 | "babel-preset-diffhtml-imports": "^1.0.0-beta.30", 29 | "diffhtml": "^1.0.0-beta.30", 30 | "rollup": "^1.21.4", 31 | "rollup-plugin-babel": "^4.3.3", 32 | "rollup-plugin-node-resolve": "^5.2.0", 33 | "rollup-plugin-replace": "^2.2.0", 34 | "rollup-plugin-visualizer": "^2.6.0", 35 | "rollup-watch": "^4.3.1", 36 | "uglify-js": "^3.12.4" 37 | }, 38 | "gitHead": "091b497588f6f48221630431e2f1eeb7f2db37cb" 39 | } 40 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-linter/rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import nodeResolve from 'rollup-plugin-node-resolve'; 3 | import replace from 'rollup-plugin-replace'; 4 | import Visualizer from 'rollup-plugin-visualizer'; 5 | 6 | const entries = { 7 | min: 'lib/index.js', 8 | umd: 'lib/index.js', 9 | }; 10 | 11 | const dests = { 12 | min: 'dist/linter.min.js', 13 | umd: 'dist/linter.js', 14 | } 15 | 16 | const { NODE_ENV = 'umd' } = process.env; 17 | 18 | export const input = entries[NODE_ENV]; 19 | export const context = 'this'; 20 | export const external = ['diffhtml']; 21 | 22 | export const output = [{ 23 | file: dests[NODE_ENV], 24 | format: 'umd', 25 | exports: 'default', 26 | name: 'linter', 27 | sourcemap: false, 28 | globals: { diffhtml: 'diff' }, 29 | }]; 30 | 31 | export const plugins = [ 32 | NODE_ENV === 'min' && replace({ 'process.env.NODE_ENV': JSON.stringify('production') }), 33 | babel(), 34 | nodeResolve({ mainFields: ['module'] }), 35 | NODE_ENV === 'umd' && Visualizer({ filename: './dist/build-size.html' }), 36 | ]; 37 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-linter/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Test 8 | 9 | 10 | 11 | 12 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-logger/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["diffhtml-imports"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-logger/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-logger/README.md: -------------------------------------------------------------------------------- 1 | # <±/> diffHTML Logger Middleware 2 | 3 | Stable Version: 1.0.0-beta.30 4 | 5 | Use with diffHTML to `console.log` out [render 6 | transaction](https://diffhtml.org/#transaction) operations. This will nest 7 | sub-component renders if they happen during a parent render. 8 | 9 | ![middleware](https://cloud.githubusercontent.com/assets/181635/23392088/32cacd8a-fd2e-11e6-9b95-e3124d827eea.png) 10 | 11 | ##### Installation 12 | 13 | ``` sh 14 | npm install diffhtml-middleware-logger 15 | ``` 16 | 17 | ##### Example 18 | 19 | ``` javascript 20 | import { use } from 'diffhtml'; 21 | import logger from 'diffhtml-middleware-logger'; 22 | 23 | use(logger()); 24 | ``` 25 | 26 | This is not a very performant middleware, so please do not use this in 27 | production where performance is critical. Use in development. 28 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-logger/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './dist/es/index.js'; 2 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-logger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diffhtml-middleware-logger", 3 | "version": "1.0.0-beta.30", 4 | "description": "Logs out render transactions to the console", 5 | "type": "module", 6 | "main": "dist/cjs/index", 7 | "module": "dist/es/index", 8 | "scripts": { 9 | "prepublishOnly": "npm run build", 10 | "clean": "rm -rf dist/* && mkdir -p dist", 11 | "build": "npm run clean && npm run build-umd && npm run build-cjs && npm run build-esm && npm run build-min", 12 | "build-cjs": "NODE_ENV=cjs babel lib -d dist/cjs && sh ../../post-cjs.sh", 13 | "build-esm": "NODE_ENV=esm babel lib -d dist/es", 14 | "build-umd": "NODE_ENV=umd rollup -c rollup.config.js", 15 | "build-min": "NODE_ENV=min rollup -c rollup.config.js && uglifyjs dist/logger.min.js -o dist/logger.min.js -m -c", 16 | "watch": "NODE_ENV=umd rollup -c rollup.config.js -w" 17 | }, 18 | "keywords": [ 19 | "diffhtml", 20 | "logger", 21 | "middleware" 22 | ], 23 | "author": "Tim Branyen (@tbranyen)", 24 | "license": "MIT", 25 | "devDependencies": { 26 | "@babel/cli": "^7.12.10", 27 | "@babel/core": "^7.12.10", 28 | "babel-preset-diffhtml-imports": "^1.0.0-beta.30", 29 | "diffhtml": "^1.0.0-beta.30", 30 | "rollup": "^1.21.4", 31 | "rollup-plugin-babel": "^4.3.3", 32 | "rollup-plugin-node-resolve": "^5.2.0", 33 | "rollup-plugin-replace": "^2.2.0", 34 | "rollup-plugin-visualizer": "^2.6.0", 35 | "rollup-watch": "^4.3.1", 36 | "uglify-js": "^3.12.4" 37 | }, 38 | "gitHead": "091b497588f6f48221630431e2f1eeb7f2db37cb" 39 | } 40 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-logger/rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import nodeResolve from 'rollup-plugin-node-resolve'; 3 | import replace from 'rollup-plugin-replace'; 4 | import Visualizer from 'rollup-plugin-visualizer'; 5 | 6 | const entries = { 7 | min: 'lib/index.js', 8 | umd: 'lib/index.js', 9 | }; 10 | 11 | const dests = { 12 | min: 'dist/logger.min.js', 13 | umd: 'dist/logger.js', 14 | } 15 | 16 | const { NODE_ENV = 'umd' } = process.env; 17 | 18 | export const input = entries[NODE_ENV]; 19 | export const context = 'this'; 20 | export const external = ['diffhtml']; 21 | 22 | export const output = [{ 23 | file: dests[NODE_ENV], 24 | format: 'umd', 25 | exports: 'default', 26 | name: 'logger', 27 | sourcemap: false, 28 | globals: { diffhtml: 'diff' }, 29 | }]; 30 | 31 | export const plugins = [ 32 | NODE_ENV === 'min' && replace({ 'process.env.NODE_ENV': JSON.stringify('production') }), 33 | babel(), 34 | nodeResolve({ mainFields: ['module'] }), 35 | NODE_ENV === 'umd' && Visualizer({ filename: './dist/build-size.html' }), 36 | ]; 37 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-service-worker/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["diffhtml-imports"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-service-worker/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-service-worker/README.md: -------------------------------------------------------------------------------- 1 | # <±/> diffHTML ServiceWorker Middleware 2 | 3 | Stable Version: 1.0.0-beta.30 4 | 5 | while this does not really benefit from being a middleware, i made it as such 6 | since it looks nice when added to `use()` and can be disabled if the middleware 7 | is unsubscribed. i've added some developer niceties, like auto clearing the 8 | caches for development. it's also possible to disable the worker completely if 9 | you get annoyed with some behavior. 10 | 11 | ## Installation 12 | 13 | ``` sh 14 | npm install diffhtml-middleware-service-worker 15 | ``` 16 | 17 | ## Example 18 | 19 | ``` javascript 20 | import { use } from 'diffhtml'; 21 | import serviceWorker from 'diffhtml-middleware-service-worker'; 22 | 23 | // Defaults shown, these are all optional values. 24 | use(serviceWorker({ 25 | url: '/service-worker.js', 26 | scope: '/', 27 | autoClearCaches: location.search.includes('diff_autoclear'), 28 | disable: location.search.includes('diff_disable'), 29 | quiet: location.search.includes('diff_quiet'), 30 | options: {}, 31 | })); 32 | ``` 33 | 34 | ## Options 35 | 36 | These options are available to change depending on the use case. It is 37 | entirely possible to get away with simply: 38 | 39 | ``` javascript 40 | use(serviceWorker()); 41 | ``` 42 | 43 | If you meet the the default use case. 44 | 45 | ### `url` 46 | 47 | Specifies which Service Worker URL to load. 48 | 49 | ### `scope` 50 | 51 | Specifices the path to use, called scope since it restricts what content is 52 | visible. 53 | 54 | ### `autoClearCaches` 55 | 56 | Allow the middleware to automatically clear all caches. By default this is 57 | disabled as it defeats the point of a service worker and may muck with other 58 | apps running on localhost. Can be toggled via the query param 59 | `?diff_autoclear`. 60 | 61 | ### `disable` 62 | 63 | Unregisters all Service Workers and does not try to register a new one. This 64 | can be toggled via the query param: `?diff_disable`. 65 | 66 | ### `quiet` 67 | 68 | Do not log anything to the console. Can be toggled via the query param 69 | `?diffhtml_quiet`. 70 | 71 | ### `options` 72 | 73 | Defaults to an empty object, gets spread into the Service Worker registration 74 | call. 75 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-service-worker/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './dist/es/index.js'; 2 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-service-worker/lib/index.js: -------------------------------------------------------------------------------- 1 | const { assign } = Object; 2 | 3 | // Wipe all caches for this domain. 4 | const clear = ({ quiet }) => caches.keys().then(cachesNames => { 5 | return Promise.all(cachesNames.map(cacheName => { 6 | return caches.delete(cacheName).then(function () { 7 | if (!quiet) { 8 | console.warn(`diffHTML ServiceWorker: Deleted cache ${cacheName}`); 9 | } 10 | }); 11 | })) 12 | }); 13 | 14 | // Wipe out all the service workers for this domain. 15 | const unregister = ({ quiet }) => navigator.serviceWorker.getRegistrations().then(regs => { 16 | for (let reg of regs) { 17 | reg.unregister(); 18 | 19 | if (!quiet) { 20 | console.warn('diffHTML ServiceWorker: Unregistering worker', reg); 21 | } 22 | } 23 | }); 24 | 25 | // Simplify's creating a service worker with diffHTML. 26 | const wrapper = ({ 27 | // Default to the root /service-worker.js. 28 | url = '/service-worker.js', 29 | 30 | // Allow the middleware to automatically clear all caches. By default this is 31 | // disabled as it defeats the point of a service worker and may muck with 32 | // other apps running on localhost. 33 | autoClearCaches = location.search.includes('diff_autoclear'), 34 | 35 | // Useful for debugging, wipes and doesn't set. Default to the query param 36 | // for disable. 37 | disable = location.search.includes('diff_disable'), 38 | 39 | // Useful for debugging, logs out service worker events. Default to the query 40 | // param to enable. 41 | quiet = location.search.includes('diff_quiet'), 42 | 43 | // Defaults to the page root, gets set into the service worker options. 44 | scope = '/', 45 | 46 | // The remaining options to be spread into the serviceWorker configuration. 47 | options = {}, 48 | }) => assign(() => {}, { 49 | displayName: 'serviceWorkerTask', 50 | 51 | subscribe() { 52 | const { serviceWorker } = navigator; 53 | 54 | let chain = Promise.resolve(); 55 | 56 | // If the user wants to work on the service worker, we need to clear the 57 | // old ones out first. 58 | if (autoClearCaches) { 59 | chain = chain.then(() => clear({ quiet })); 60 | } 61 | 62 | // If we're disabling, then clear the c 63 | if (disable) { 64 | // Iterate through all service workers and remove the old ones. 65 | chain = chain.then(() => unregister({ quiet })); 66 | } 67 | 68 | // If the user disables the service worker, do not attempt to re-register 69 | // it. 70 | chain = chain.then(() => { 71 | if (!disable) { 72 | return serviceWorker.register(url, { scope, ...options }) 73 | } 74 | }); 75 | 76 | // If not in quiet mode, echo out some standard messaging and give access 77 | // to the objects. 78 | if (!quiet) { 79 | chain 80 | .then(reg => reg && console.warn( 81 | 'diffHTML ServiceWorker: Registration succeeded', reg 82 | )) 83 | .catch(err => err && console.warn( 84 | 'diffHTML ServiceWorker: Registration failed', err 85 | )); 86 | } 87 | }, 88 | 89 | unsubscribe() { 90 | unregister({ quiet }); 91 | }, 92 | }); 93 | 94 | export default opts => wrapper(opts || {}); 95 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-service-worker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diffhtml-middleware-service-worker", 3 | "version": "1.0.0-beta.30", 4 | "description": "Helps with service workers", 5 | "type": "module", 6 | "main": "dist/cjs/index", 7 | "module": "dist/es/index", 8 | "scripts": { 9 | "prepublishOnly": "npm run build", 10 | "clean": "rm -rf dist/* && mkdir -p dist", 11 | "build": "npm run clean && npm run build-umd && npm run build-cjs && npm run build-esm && npm run build-min", 12 | "build-cjs": "NODE_ENV=cjs babel lib -d dist/cjs && sh ../../post-cjs.sh", 13 | "build-esm": "NODE_ENV=esm babel lib -d dist/es", 14 | "build-umd": "NODE_ENV=umd rollup -c rollup.config.js", 15 | "build-min": "NODE_ENV=min rollup -c rollup.config.js && uglifyjs dist/service-worker.min.js -o dist/service-worker.min.js -m -c", 16 | "watch": "NODE_ENV=umd rollup -c rollup.config.js -w" 17 | }, 18 | "keywords": [ 19 | "diffhtml", 20 | "service workers", 21 | "middleware" 22 | ], 23 | "author": "Tim Branyen (@tbranyen)", 24 | "license": "MIT", 25 | "devDependencies": { 26 | "@babel/cli": "^7.12.10", 27 | "@babel/core": "^7.12.10", 28 | "babel-preset-diffhtml-imports": "^1.0.0-beta.30", 29 | "diffhtml": "^1.0.0-beta.30", 30 | "rollup": "^1.21.4", 31 | "rollup-plugin-babel": "^4.3.3", 32 | "rollup-plugin-node-resolve": "^5.2.0", 33 | "rollup-plugin-replace": "^2.2.0", 34 | "rollup-plugin-visualizer": "^2.6.0", 35 | "rollup-watch": "^4.3.1", 36 | "uglify-js": "^3.12.4" 37 | }, 38 | "gitHead": "091b497588f6f48221630431e2f1eeb7f2db37cb" 39 | } 40 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-service-worker/rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import nodeResolve from 'rollup-plugin-node-resolve'; 3 | import replace from 'rollup-plugin-replace'; 4 | import Visualizer from 'rollup-plugin-visualizer'; 5 | 6 | const entries = { 7 | min: 'lib/index.js', 8 | umd: 'lib/index.js', 9 | }; 10 | 11 | const dests = { 12 | min: 'dist/service-worker.min.js', 13 | umd: 'dist/service-worker.js', 14 | } 15 | 16 | const { NODE_ENV = 'umd' } = process.env; 17 | 18 | export const input = entries[NODE_ENV]; 19 | export const context = 'this'; 20 | export const external = ['diffhtml']; 21 | 22 | export const output = [{ 23 | file: dests[NODE_ENV], 24 | format: 'umd', 25 | exports: 'default', 26 | name: 'serviceWorker', 27 | sourcemap: false, 28 | globals: { diffhtml: 'diff' }, 29 | }]; 30 | 31 | export const plugins = [ 32 | NODE_ENV === 'min' && replace({ 'process.env.NODE_ENV': JSON.stringify('production') }), 33 | babel(), 34 | nodeResolve({ mainFields: ['module'] }), 35 | NODE_ENV === 'umd' && Visualizer({ filename: './dist/build-size.html' }), 36 | ]; 37 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-synthetic-events/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["diffhtml-imports"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-synthetic-events/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-synthetic-events/README.md: -------------------------------------------------------------------------------- 1 | # <±/> diffHTML Synthetic Events Middleware 2 | 3 | Stable Version: 1.0.0-beta.30 4 | 5 | Changes the event binding from inline event handlers like `onclick = fn` to use 6 | `addEventListener`. Hooks are attached to the `body` element and coordinated 7 | using event delegation. 8 | 9 | ## Installation 10 | 11 | ``` sh 12 | npm install diffhtml-middleware-synthetic-events 13 | ``` 14 | 15 | ## Example 16 | 17 | ``` js 18 | import { use, html, innerHTML } from 'diffhtml'; 19 | import syntheticEvents from 'diffhtml-middleware-synthetic-events'; 20 | 21 | use(syntheticEvents()); 22 | 23 | function render() { 24 | return html` 25 |
console.log(e)} /> 26 | `; 27 | } 28 | 29 | // Binds the event on div using `addEventListener`. 30 | innerHTML(document.body, render()); 31 | ``` 32 | 33 | A good use case for this middleware is building a Chrome Extension where using 34 | inline event handlers is not possible. Supports non-bubbling events via the 35 | `useCapture` flag. 36 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-synthetic-events/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './dist/es/index.js'; 2 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-synthetic-events/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diffhtml-middleware-synthetic-events", 3 | "version": "1.0.0-beta.30", 4 | "description": "Global event delegation middleware, avoids inline events", 5 | "type": "module", 6 | "main": "dist/cjs/index", 7 | "module": "dist/es/index", 8 | "scripts": { 9 | "prepublishOnly": "npm run build", 10 | "clean": "rm -rf dist/* && mkdir -p dist", 11 | "build": "npm run clean && npm run build-umd && npm run build-cjs && npm run build-esm && npm run build-min", 12 | "build-cjs": "NODE_ENV=cjs babel lib -d dist/cjs && sh ../../post-cjs.sh", 13 | "build-esm": "NODE_ENV=esm babel lib -d dist/es", 14 | "build-umd": "NODE_ENV=umd rollup -c rollup.config.js", 15 | "build-min": "NODE_ENV=min rollup -c rollup.config.js && uglifyjs dist/synthetic-events.min.js -o dist/synthetic-events.min.js -m -c", 16 | "watch": "NODE_ENV=umd rollup -c rollup.config.js -w" 17 | }, 18 | "keywords": [ 19 | "diffhtml", 20 | "middleware", 21 | "synthetic", 22 | "events" 23 | ], 24 | "author": "Tim Branyen (@tbranyen)", 25 | "license": "MIT", 26 | "devDependencies": { 27 | "@babel/cli": "^7.12.10", 28 | "@babel/core": "^7.12.10", 29 | "babel-preset-diffhtml-imports": "^1.0.0-beta.30", 30 | "diffhtml": "^1.0.0-beta.30", 31 | "rollup": "^1.21.4", 32 | "rollup-plugin-babel": "^4.3.3", 33 | "rollup-plugin-node-resolve": "^5.2.0", 34 | "rollup-plugin-replace": "^2.2.0", 35 | "rollup-plugin-visualizer": "^2.6.0", 36 | "rollup-watch": "^4.3.1", 37 | "uglify-js": "^3.12.4" 38 | }, 39 | "gitHead": "091b497588f6f48221630431e2f1eeb7f2db37cb" 40 | } 41 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-synthetic-events/rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import nodeResolve from 'rollup-plugin-node-resolve'; 3 | import replace from 'rollup-plugin-replace'; 4 | import Visualizer from 'rollup-plugin-visualizer'; 5 | 6 | const entries = { 7 | min: 'lib/index.js', 8 | umd: 'lib/index.js', 9 | }; 10 | 11 | const dests = { 12 | min: 'dist/synthetic-events.min.js', 13 | umd: 'dist/synthetic-events.js', 14 | } 15 | 16 | const { NODE_ENV = 'umd' } = process.env; 17 | 18 | export const input = entries[NODE_ENV]; 19 | export const context = 'this'; 20 | export const external = ['diffhtml']; 21 | 22 | export const output = [{ 23 | file: dests[NODE_ENV], 24 | format: 'umd', 25 | exports: 'default', 26 | name: 'syntheticEvents', 27 | sourcemap: false, 28 | globals: { diffhtml: 'diff' }, 29 | }]; 30 | 31 | export const plugins = [ 32 | NODE_ENV === 'min' && replace({ 'process.env.NODE_ENV': JSON.stringify('production') }), 33 | babel(), 34 | nodeResolve({ mainFields: ['module'] }), 35 | NODE_ENV === 'umd' && Visualizer({ filename: './dist/build-size.html' }), 36 | ]; 37 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-verify-state/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["diffhtml-imports"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-verify-state/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-verify-state/README.md: -------------------------------------------------------------------------------- 1 | # <±/> diffHTML Verify State Middleware 2 | 3 | Stable Version: 1.0.0-beta.30 4 | 5 | Asserts that a render properly updated the old Virtual Tree and the DOM. Will 6 | recursively search for inconsistencies, displays warnings unless debugging is 7 | enabled, then it throws errors instead. 8 | 9 | ![verify-state](https://cloud.githubusercontent.com/assets/181635/23392650/1d7dfdcc-fd32-11e6-8f41-b412279cea55.png) 10 | 11 | ##### Installation 12 | 13 | ``` sh 14 | npm install diffhtml-middleware-verify-state 15 | ``` 16 | 17 | ##### Example 18 | 19 | ``` javascript 20 | import { use } from 'diffhtml'; 21 | import verifyState from 'diffhtml-middleware-verify-state'; 22 | 23 | // Throw when debug is truthy (when location.search has `?debug`) 24 | use(verifyState({ debug: location.search.includes('debug') })); 25 | ``` 26 | 27 | This is not a very performant middleware, so please do not use this in 28 | production where performance is critical. Use in development. 29 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-verify-state/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './dist/es/index.js'; 2 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-verify-state/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diffhtml-middleware-verify-state", 3 | "version": "1.0.0-beta.30", 4 | "description": "Verifies render state middleware, useful for sanity checking", 5 | "type": "module", 6 | "main": "dist/cjs/index", 7 | "module": "dist/es/index", 8 | "scripts": { 9 | "prepublishOnly": "npm run build", 10 | "clean": "rm -rf dist/* && mkdir -p dist", 11 | "build": "npm run clean && npm run build-umd && npm run build-cjs && npm run build-esm && npm run build-min", 12 | "build-cjs": "NODE_ENV=cjs babel lib -d dist/cjs && sh ../../post-cjs.sh", 13 | "build-esm": "NODE_ENV=esm babel lib -d dist/es", 14 | "build-umd": "NODE_ENV=umd rollup -c rollup.config.js", 15 | "build-min": "NODE_ENV=min rollup -c rollup.config.js && uglifyjs dist/verify-state.min.js -o dist/verify-state.min.js -m -c", 16 | "watch": "NODE_ENV=umd rollup -c rollup.config.js -w" 17 | }, 18 | "keywords": [ 19 | "diffhtml", 20 | "middleware", 21 | "verify state" 22 | ], 23 | "author": "Tim Branyen", 24 | "license": "MIT", 25 | "devDependencies": { 26 | "@babel/cli": "^7.12.10", 27 | "@babel/core": "^7.12.10", 28 | "babel-preset-diffhtml-imports": "^1.0.0-beta.30", 29 | "diffhtml": "^1.0.0-beta.30", 30 | "rollup": "^1.21.4", 31 | "rollup-plugin-babel": "^4.3.3", 32 | "rollup-plugin-hypothetical": "^2.1.0", 33 | "rollup-plugin-node-resolve": "^5.2.0", 34 | "rollup-plugin-replace": "^2.2.0", 35 | "rollup-plugin-visualizer": "^2.6.0", 36 | "rollup-watch": "^4.3.1", 37 | "uglify-js": "^3.12.4" 38 | }, 39 | "gitHead": "091b497588f6f48221630431e2f1eeb7f2db37cb" 40 | } 41 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-verify-state/rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import nodeResolve from 'rollup-plugin-node-resolve'; 3 | import replace from 'rollup-plugin-replace'; 4 | import Visualizer from 'rollup-plugin-visualizer'; 5 | 6 | const entries = { 7 | min: 'lib/index.js', 8 | umd: 'lib/index.js', 9 | }; 10 | 11 | const dests = { 12 | min: 'dist/verify-state.min.js', 13 | umd: 'dist/verify-state.js', 14 | } 15 | 16 | const { NODE_ENV = 'umd' } = process.env; 17 | 18 | export const input = entries[NODE_ENV]; 19 | export const context = 'this'; 20 | export const external = ['diffhtml']; 21 | 22 | export const output = [{ 23 | file: dests[NODE_ENV], 24 | format: 'umd', 25 | exports: 'default', 26 | name: 'verifyState', 27 | sourcemap: false, 28 | globals: { diffhtml: 'diff' }, 29 | }]; 30 | 31 | export const plugins = [ 32 | replace({ 'process.env.NODE_ENV': JSON.stringify('production') }), 33 | babel(), 34 | nodeResolve({ mainFields: ['module'] }), 35 | NODE_ENV === 'umd' && Visualizer({ filename: './dist/build-size.html' }), 36 | ]; 37 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["diffhtml-imports"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/README.md: -------------------------------------------------------------------------------- 1 | # <±/> diffHTML Worker Middleware 2 | 3 | Stable Version: 1.0.0-beta.23 4 | 5 | Used to separate the diffing and patching stages of rendering across threads. 6 | The main thread receives payloads containing mutations to apply, all UI logic 7 | is isolated within a worker thread. 8 | 9 | Can be used with Node.js servers and pass patches to the client using Web 10 | Sockets. 11 | 12 | ## Installation 13 | 14 | ``` sh 15 | npm install diffhtml-middleware-worker 16 | ``` 17 | 18 | ## Usage 19 | 20 | When you create a worker, you bind it to a mount point and all `innerHTML` or 21 | `outerHTML` calls that get made in the worker are intercepted and passed to the 22 | main thread. 23 | 24 | ### In a browser 25 | 26 | To create a web worker in the browser, import the `createWebWorker` function. 27 | Invoke this with a file path pointing to the location of your module that 28 | contains the rendering logic. This file path will be explained more after the 29 | following code snippet. 30 | 31 | You must import either the core or lite version in the main thread to do the 32 | actual patching. The lite version is preferable due to the smaller filesize. 33 | Then register the middleware into the main thread. 34 | 35 | ```js 36 | import { use } from 'https://diffhtml.org/core/lite'; 37 | import { mainTask, createWebWorker } from 'https://diffhtml.org/middleware-worker'; 38 | 39 | use(mainTask()); 40 | ``` 41 | 42 | Once the middleware is registered, you can create web workers using the helper 43 | function. If you already have a worker you can pass it instead of a string to 44 | have the events hooked up. 45 | 46 | ```js 47 | const mount = document.body; 48 | createWebWorker('./path/to/worker.js', { mount }); 49 | ``` 50 | 51 | The above code will create a worker that exists at `worker.js` and proxy all 52 | rendering from it into the mount point, in this case the `` tag. You 53 | must register the `workerTask` middleware inside the worker to ensure patches 54 | are properly passed to the main thread. 55 | 56 | ```js 57 | import { use, html, createTree, innerHTML } from 'https://diffhtml.org/core'; 58 | import { workerTask } from 'https://diffhtml.org/middleware-worker'; 59 | 60 | use(workerTask()); 61 | ``` 62 | 63 | In the worker you must create a placeholder to render into. This will 64 | emulate the mount in the main thread. 65 | 66 | ```js 67 | // Create an empty fragment to render into, this represents the body tag 68 | // in the main thread. 69 | const placeholder = createTree(); 70 | 71 | // Any outerHTML or innerHTML calls will be proxied to the main thread mount 72 | // point. 73 | innerHTML(placeholder, html` 74 |

Hello world from a worker thread!

75 | `); 76 | ``` 77 | 78 | ### With Node.js 79 | 80 | 81 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/index.js: -------------------------------------------------------------------------------- 1 | export * from './dist/es/index.js'; 2 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "module": "esnext", 5 | "target": "es2017", 6 | "declaration": true, 7 | "checkJs": true, 8 | "noUnusedParameters": true, 9 | "noUnusedLocals": true, 10 | "outDir": "./dist/typings", 11 | "lib": ["es2017", "dom"] 12 | }, 13 | "include": ["lib/**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/lib/create-web-socket.js: -------------------------------------------------------------------------------- 1 | import diff from './util/binding'; 2 | 3 | const { innerHTML, html } = diff; 4 | const { parse } = JSON; 5 | 6 | export const createWebSocket = (socketUrl, { mount, socketOptions }) => { 7 | const socket = new WebSocket(socketUrl, socketOptions); 8 | 9 | socket.addEventListener('message', async e => { 10 | const { type, ...rest } = parse(e.data); 11 | 12 | // TODO Deprecate the 'clear' event. This is currently used in the Node 13 | // worker when an error is encountered. This cleans up the markup to avoid 14 | // issues during rendering. 15 | if (type === 'clear') { 16 | mount.innerHTML = ''; 17 | } 18 | 19 | if (type === 'patches') { 20 | innerHTML(mount, null, { patches: rest.patches }); 21 | } 22 | 23 | if (type === 'log') { 24 | const { level, message } = rest; 25 | console[level](...[].concat(message)); 26 | } 27 | }); 28 | 29 | return socket; 30 | }; 31 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/lib/create-worker.js: -------------------------------------------------------------------------------- 1 | import { type } from './util/types'; 2 | import NodeWorker from './util/node-worker-threads'; 3 | 4 | const isNode = typeof global !== 'undefined'; 5 | const SafeWorker = typeof Worker !== 'undefined' ? Worker : NodeWorker; 6 | const { stringify } = JSON; 7 | 8 | /** 9 | * 10 | * @param {string} workerInit - Location of worker script, Object URL pointing 11 | * to Blob, or a Worker instance itself. 12 | * @param {Object} workerOpts 13 | * @returns {Worker} 14 | */ 15 | export const createWorker = (workerInit, workerOpts = {}) => callback => { 16 | const safeWorkerOpts = workerOpts || {}; 17 | 18 | let worker = null; 19 | 20 | // If the init is a worker object already, simply assign the value and 21 | // options. 22 | if (type(workerInit) === 'worker') { 23 | worker = workerInit; 24 | Object.assign(worker, safeWorkerOpts); 25 | } 26 | // Otherwise, use the workerInit as the value for creating a new worker. 27 | else { 28 | worker = new SafeWorker(workerInit, { ...safeWorkerOpts }); 29 | } 30 | 31 | const onMessage = message => { 32 | const { type, ...rest } = isNode ? message : message.data; 33 | 34 | if (type === 'patches') { 35 | callback({ type, ...rest }); 36 | } 37 | }; 38 | 39 | const onError = (error) => { 40 | // Extra logging to the Node console, in the browser the error will bubble 41 | // automatically so this isn't needed. 42 | if (isNode) { 43 | console.error(error); 44 | } 45 | 46 | callback({ 47 | type: 'log', 48 | level: 'error', 49 | message: String(error.stack || error.message), 50 | }); 51 | 52 | return true; 53 | }; 54 | 55 | if (isNode) { 56 | worker.on('message', onMessage); 57 | worker.on('error', onError); 58 | } 59 | else { 60 | worker.onmessage = onMessage; 61 | worker.onerror = onError; 62 | } 63 | 64 | return worker; 65 | }; 66 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/lib/get-uuid.js: -------------------------------------------------------------------------------- 1 | import globalThis from './util/global'; 2 | import nodeBuffer from './util/node-buffer'; 3 | 4 | let Blob = globalThis.Blob; 5 | 6 | // First attempt to load using Node.js 7 | if (typeof Blob === 'undefined' && nodeBuffer) { 8 | Blob = nodeBuffer.Blob; 9 | } 10 | 11 | // If still not available, throw an error. 12 | if (typeof Blob === 'undefined') { 13 | throw new Error('Missing required Blob dependency'); 14 | } 15 | 16 | // Extract UUID from object URL generated for an arbitrary blob. 17 | export const getUUID = () => URL.createObjectURL(new Blob()).slice(31); 18 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/lib/index.js: -------------------------------------------------------------------------------- 1 | export { getUUID } from './get-uuid'; 2 | export { toObjectURL } from './to-object-url'; 3 | export { createWebSocket } from './create-web-socket'; 4 | export { createWorker } from './create-worker'; 5 | export { mainTask } from './main-task'; 6 | export { workerTask } from './worker-task'; 7 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/lib/main-task.js: -------------------------------------------------------------------------------- 1 | import diff from './util/binding'; 2 | 3 | const { Internals } = diff; 4 | 5 | const { assign } = Object; 6 | const { stringify } = JSON; 7 | const linker = new Map(); 8 | 9 | export const mainTask = ({ 10 | // TODO Merge socket and worker args together into one named bridge 11 | // or something similar. 12 | // 13 | // WebSocket connection for the main thread. 14 | socket = null, 15 | worker = null, 16 | } = {}) => assign(function webWorkerTask(transaction) { 17 | const currentTasks = transaction.tasks; 18 | const indexOfSyncTrees = currentTasks.indexOf(Internals.tasks.syncTrees); 19 | 20 | // Only run this middleware when patches are present. 21 | if (!('patches' in transaction.config)) { 22 | return; 23 | } 24 | 25 | const link = vTree => { 26 | if (vTree && vTree.childNodes) { 27 | Internals.Pool.memory.protected.add(vTree); 28 | linker.set(vTree.__link, vTree); 29 | vTree.childNodes.forEach(x => link(x)); 30 | } 31 | }; 32 | 33 | // Replace syncTrees with injectPatches 34 | currentTasks.splice(indexOfSyncTrees, 1, function injectPatches() { 35 | transaction.patches = transaction.config.patches.map(x => { 36 | // Handle dom events, custom element properties, etc. Any time you need 37 | // a function, we proxy the call and the arguments. 38 | if (x && x.__caller) { 39 | return function(e) { 40 | // TODO handled by synthetic events middleware 41 | e.preventDefault(); 42 | e.stopPropagation(); 43 | 44 | if (socket) { 45 | socket.send(stringify({ type: 'event', ...x })); 46 | } 47 | else { 48 | worker.postMessage({ type: 'event', ...x }); 49 | } 50 | }; 51 | } 52 | 53 | if (!x || typeof x !== 'object' || !('__link' in x)) { 54 | return x; 55 | } 56 | 57 | let vTree = x; 58 | 59 | if (linker.has(x.__link)) { 60 | vTree = linker.get(x.__link); 61 | return vTree; 62 | } 63 | else if (x.__link === 'mount') { 64 | vTree = transaction.oldTree; 65 | } 66 | else { 67 | link(vTree); 68 | } 69 | 70 | if (((x && x.isSvg) || (vTree && vTree.isSvg)) && vTree) { 71 | transaction.state.svgElements.add(vTree); 72 | } 73 | 74 | return vTree; 75 | }); 76 | }); 77 | }, { 78 | releaseHook: vTree => { 79 | if (vTree && vTree.__link) { 80 | linker.delete(vTree.__link); 81 | } 82 | }, 83 | }); 84 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/lib/to-object-url.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates a virtual URL that can be used to create a Worker. This is useful 3 | * for inlining a script to run, vs using a phsyical file. Since the operations 4 | * are repetitive we offer this feature as a helper function. 5 | * 6 | * @param {Function} fn - Wrapper function containing code to execute in a 7 | * worker 8 | * @returns {String} URL 9 | */ 10 | export function toObjectURL(fn) { 11 | const fnString = String(fn); 12 | const firstBracket = fnString.indexOf('{') + 1; 13 | const lastBracket = fnString.lastIndexOf('}'); 14 | const workerCode = String(fn).slice(firstBracket, lastBracket); 15 | 16 | return URL.createObjectURL( 17 | // Create a new blob and split the workerCode so it becomes an array with a 18 | // single entry. 19 | new Blob(workerCode.split()), 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/lib/util/binding.js: -------------------------------------------------------------------------------- 1 | import globalThis from './global'; 2 | import { DIFF_BINDING } from './types'; 3 | 4 | /** 5 | * @returns {DIFF_BINDING} 6 | */ 7 | export default /** @type {DIFF_BINDING} */ ( 8 | /** @type {any} */ (globalThis)[Symbol.for('diffHTML')] 9 | ); -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/lib/util/global.js: -------------------------------------------------------------------------------- 1 | /** @type {any} */ 2 | export default ( 3 | typeof global === 'object' 4 | ? global 5 | : (typeof window === 'object' ? window : self) || {} 6 | ); -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/lib/util/node-buffer.js: -------------------------------------------------------------------------------- 1 | let nodeBuffer = null; 2 | 3 | try { 4 | nodeBuffer = (await import('buffer')).default; 5 | } 6 | catch {} 7 | 8 | export default nodeBuffer; 9 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/lib/util/node-worker-threads.js: -------------------------------------------------------------------------------- 1 | let nodeWorkerThreads = null; 2 | 3 | try { 4 | nodeWorkerThreads = (await import('worker_threads')).default; 5 | } 6 | catch {} 7 | 8 | export default nodeWorkerThreads; 9 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/lib/util/types.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Finds a more specific type identifier. 3 | * 4 | * @param {any} x 5 | * @returns {string} 6 | */ 7 | export const type = x => toString.call(x).split(' ')[1].slice(0, -1).toLowerCase(); 8 | 9 | /** 10 | * @type {{ [type: string]: any }} 11 | */ 12 | export const EMPTY = { 13 | OBJ: {}, 14 | }; 15 | 16 | /** @typedef {import('diffhtml/dist/typings/index')} DIFF_BINDING */ 17 | export const DIFF_BINDING = EMPTY.OBJ; 18 | 19 | /** @typedef {import('diffhtml/dist/typings/util/types').Mount} Mount */ 20 | export const Mount = EMPTY.OBJ; -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diffhtml-middleware-worker", 3 | "version": "1.0.0-beta.30", 4 | "description": "Provides worker rendering for client and server", 5 | "main": "dist/cjs/index", 6 | "type": "module", 7 | "module": "dist/es/index", 8 | "scripts": { 9 | "prepublishOnly": "npm run build", 10 | "clean": "rm -rf dist/* && mkdir -p dist", 11 | "build": "npm run clean && npm run build-umd && npm run build-cjs && npm run build-esm && npm run build-min", 12 | "build-cjs": "NODE_ENV=cjs babel lib -d dist/cjs && sh ../../post-cjs.sh", 13 | "build-esm": "NODE_ENV=esm babel lib -d dist/es", 14 | "build-umd": "NODE_ENV=umd rollup -c rollup.config.js", 15 | "build-min": "NODE_ENV=min rollup -c rollup.config.js && uglifyjs dist/worker.min.js -o dist/worker.min.js -m -c", 16 | "watch": "NODE_ENV=umd rollup -c rollup.config.js -w" 17 | }, 18 | "keywords": [ 19 | "diffhtml", 20 | "worker", 21 | "middleware" 22 | ], 23 | "author": "Tim Branyen (@tbranyen)", 24 | "license": "MIT", 25 | "devDependencies": { 26 | "@babel/cli": "^7.12.10", 27 | "@babel/core": "^7.12.10", 28 | "babel-preset-diffhtml-imports": "^1.0.0-beta.30", 29 | "diffhtml": "^1.0.0-beta.30", 30 | "rollup": "^1.21.4", 31 | "rollup-plugin-babel": "^4.3.3", 32 | "rollup-plugin-hypothetical": "^2.1.0", 33 | "rollup-plugin-node-resolve": "^5.2.0", 34 | "rollup-plugin-replace": "^2.2.0", 35 | "rollup-plugin-visualizer": "^2.6.0", 36 | "rollup-watch": "^4.3.1", 37 | "uglify-js": "^3.12.4" 38 | }, 39 | "gitHead": "091b497588f6f48221630431e2f1eeb7f2db37cb" 40 | } 41 | -------------------------------------------------------------------------------- /packages/diffhtml-middleware-worker/rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import nodeResolve from 'rollup-plugin-node-resolve'; 3 | import replace from 'rollup-plugin-replace'; 4 | import hypothetical from 'rollup-plugin-hypothetical'; 5 | import Visualizer from 'rollup-plugin-visualizer'; 6 | 7 | const entries = { 8 | min: 'lib/index.js', 9 | umd: 'lib/index.js', 10 | }; 11 | 12 | const dests = { 13 | min: 'dist/worker.min.js', 14 | umd: 'dist/worker.js', 15 | } 16 | 17 | const { NODE_ENV = 'umd' } = process.env; 18 | 19 | export const input = entries[NODE_ENV]; 20 | export const context = 'this'; 21 | export const external = ['diffhtml']; 22 | 23 | export const output = [{ 24 | file: dests[NODE_ENV], 25 | format: 'umd', 26 | exports: 'named', 27 | name: 'worker', 28 | sourcemap: false, 29 | globals: { diffhtml: 'diff' }, 30 | }]; 31 | 32 | const pluginDynamicImports = (options = {}) => ({ 33 | name: 'dynamic-imports', 34 | transform(code, filename) { 35 | const transformedCode = code.replace(/import\(['"`](?![\.\/])(.*?)['"`]\)/gi, (match, request) => { 36 | return 'Promise.resolve(null)'; 37 | if (request in options.globals) { 38 | return `Promise.resolve(global["${options.globals[request]}"])`; 39 | } 40 | 41 | return 'Promise.resolve(null)'; 42 | }); 43 | 44 | return transformedCode; 45 | }, 46 | moduleParsed(moduleInfo) { 47 | console.log('here', moduleInfo); 48 | }, 49 | }); 50 | 51 | export const plugins = [ 52 | pluginDynamicImports({ 53 | globals: { 54 | 'fs': 'NodeFS', 55 | 'path': 'NodePath', 56 | 'worker_threads': 'NodeWorkerThreads', 57 | } 58 | }), 59 | NODE_ENV === 'min' && replace({ 'process.env.NODE_ENV': JSON.stringify('production') }), 60 | babel(), 61 | nodeResolve({ 62 | preferBuiltins: true, 63 | mainFields: ['module'], 64 | }), 65 | hypothetical({ 66 | allowFallthrough: true, 67 | files: { 68 | './lib/util/node-buffer.js': ` 69 | export default undefined; 70 | `, 71 | './lib/util/node-worker-threads.js': ` 72 | export default undefined; 73 | `, 74 | } 75 | }), 76 | NODE_ENV === 'umd' && Visualizer({ filename: './dist/build-size.html' }), 77 | ]; 78 | -------------------------------------------------------------------------------- /packages/diffhtml-react-compat/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["diffhtml-imports"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/diffhtml-react-compat/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/diffhtml-react-compat/index.js: -------------------------------------------------------------------------------- 1 | export * from './dist/es/index.js'; 2 | -------------------------------------------------------------------------------- /packages/diffhtml-react-compat/lib/children.js: -------------------------------------------------------------------------------- 1 | export function map(children, fn, ctx) { 2 | if (children === undefined || children === null) { 3 | return null; 4 | } 5 | 6 | children = toArray(children); 7 | 8 | if (ctx && ctx !== children) { 9 | fn = fn.bind(ctx); 10 | } 11 | 12 | return children.map(fn); 13 | } 14 | 15 | export function forEach(children, fn, ctx) { 16 | if (children === undefined || children === null) { 17 | return null; 18 | } 19 | 20 | children = toArray(children); 21 | 22 | if (ctx && ctx !== children) { 23 | fn = fn.bind(ctx); 24 | } 25 | 26 | children.forEach(fn); 27 | } 28 | 29 | export function count(children) { 30 | return children ? children.length : 0; 31 | } 32 | 33 | export function only(children) { 34 | children = toArray(children).filter(({ nodeType }) => nodeType !== 3); 35 | 36 | return children.length ? children[0] : null; 37 | } 38 | 39 | export function toArray(children) { 40 | return Array.isArray(children) ? children : [].concat(children); 41 | } 42 | 43 | export default { map, forEach, count, only, toArray }; 44 | -------------------------------------------------------------------------------- /packages/diffhtml-react-compat/lib/fake-prop-types.js: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /packages/diffhtml-react-compat/lib/pure-component.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'diffhtml-components'; 2 | 3 | export default class PureComponent extends Component { 4 | shouldComponentUpdate(nextProps, nextState) { 5 | const nextPropKeys = keys(nextProps); 6 | const nextStateKeys = keys(nextState); 7 | 8 | if (nextPropKeys.length !== keys(this.props).length) { 9 | return true; 10 | } 11 | else if (nextStateKeys.length !== keys(this.state).length) { 12 | return true; 13 | } 14 | 15 | let isDirty = false; 16 | 17 | nextPropsKeys.forEach(keyName => { 18 | if (isDirty) { return; } 19 | 20 | if (this.props[keyName] !== nextProps[keyName]) { 21 | isDirty = true; 22 | } 23 | }); 24 | 25 | if (isDirty) { 26 | return true; 27 | } 28 | 29 | nextStateKeys.forEach(keyName => { 30 | if (isDirty) { return; } 31 | 32 | if (this.state[keyName] !== nextState[keyName]) { 33 | isDirty = true; 34 | } 35 | }); 36 | 37 | return isDirty; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/diffhtml-react-compat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diffhtml-react-compat", 3 | "version": "1.0.0-beta.30", 4 | "description": "Provides a strong layer of compatibility with React", 5 | "type": "module", 6 | "main": "dist/react-compat.js", 7 | "module": "dist/es/index", 8 | "scripts": { 9 | "prepublishOnly": "npm run build", 10 | "clean": "rm -rf dist/* && mkdir -p dist", 11 | "build": "npm run clean && npm run build-umd && npm run build-cjs && npm run build-esm && npm run build-min", 12 | "build-cjs": "NODE_ENV=cjs babel lib -d dist/cjs && sh ../../post-cjs.sh", 13 | "build-esm": "NODE_ENV=esm babel lib -d dist/es", 14 | "build-umd": "NODE_ENV=umd rollup -c rollup.config.js", 15 | "build-min": "NODE_ENV=min rollup -c rollup.config.js && uglifyjs dist/react-compat.min.js -o dist/react-compat.min.js -m -c", 16 | "watch": "NODE_ENV=umd rollup -c rollup.config.js -w" 17 | }, 18 | "author": "Tim Branyen (@tbranyen)", 19 | "license": "MIT", 20 | "devDependencies": { 21 | "@babel/cli": "^7.12.10", 22 | "@babel/core": "^7.12.10", 23 | "babel-preset-diffhtml-imports": "^1.0.0-beta.30", 24 | "diffhtml": "^1.0.0-beta.30", 25 | "prop-types": "^15.7.2", 26 | "rollup": "^1.21.4", 27 | "rollup-plugin-babel": "^4.3.3", 28 | "rollup-plugin-commonjs": "^10.1.0", 29 | "rollup-plugin-node-resolve": "^5.2.0", 30 | "rollup-plugin-replace": "^2.2.0", 31 | "rollup-plugin-visualizer": "^2.6.0", 32 | "rollup-watch": "^4.3.1", 33 | "uglify-js": "^3.12.4" 34 | }, 35 | "dependencies": { 36 | "diffhtml-components": "^1.0.0-beta.30", 37 | "diffhtml-middleware-synthetic-events": "^1.0.0-beta.30", 38 | "prop-types": "^15.7.2" 39 | }, 40 | "gitHead": "091b497588f6f48221630431e2f1eeb7f2db37cb" 41 | } 42 | -------------------------------------------------------------------------------- /packages/diffhtml-react-compat/rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import nodeResolve from 'rollup-plugin-node-resolve'; 3 | import commonjs from 'rollup-plugin-commonjs'; 4 | import replace from 'rollup-plugin-replace'; 5 | import Visualizer from 'rollup-plugin-visualizer'; 6 | 7 | const entries = { 8 | min: 'lib/index.js', 9 | umd: 'lib/index.js', 10 | }; 11 | 12 | const dests = { 13 | min: 'dist/react-compat.min.js', 14 | umd: 'dist/react-compat.js', 15 | } 16 | 17 | const { NODE_ENV = 'umd' } = process.env; 18 | 19 | export const context = 'this'; 20 | export const input = entries[NODE_ENV]; 21 | export const external = ['diffhtml', 'prop-types']; 22 | 23 | export const output = { 24 | file: dests[NODE_ENV], 25 | format: 'umd', 26 | exports: 'named', 27 | globals: { diffhtml: 'diff', 'prop-types': 'PropTypes' }, 28 | sourcemap: false, 29 | name: 'React', 30 | }; 31 | 32 | export const plugins = [ 33 | NODE_ENV === 'min' && replace({ 'process.env.NODE_ENV': JSON.stringify('production') }), 34 | babel({ runtimeHelpers: true }), 35 | (NODE_ENV !== 'umd' && NODE_ENV !== 'min') && nodeResolve({ jsnext: true, main: true, skip: external }), 36 | commonjs({ include: 'node_modules/**', }), 37 | NODE_ENV === 'umd' && Visualizer({ filename: './dist/build-size.html' }), 38 | ]; 39 | -------------------------------------------------------------------------------- /packages/diffhtml-react-compat/server.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/diffhtml-react-compat/test-utils.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tbranyen/diffhtml/f25d1c4acf8da35debb1e1f3f4af868f011ae0ab/packages/diffhtml-react-compat/test-utils.js -------------------------------------------------------------------------------- /packages/diffhtml-rust-parser/.cargo/config: -------------------------------------------------------------------------------- 1 | build = { target = "wasm32-unknown-unknown" } 2 | -------------------------------------------------------------------------------- /packages/diffhtml-rust-parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "diffhtml-rust-parser" 3 | version = "1.0.0-beta.29" 4 | edition = "2021" 5 | 6 | [lib] 7 | name = "parser" 8 | path = "src/parser.rs" 9 | test = false 10 | bench = false 11 | crate-type = ["cdylib"] 12 | 13 | [dependencies] 14 | tl = { version = "0.7.7", features = [] } 15 | wasm-bindgen = { version = "0.2.88", features = [] } 16 | serde = { version = "1.0", features = ["derive"] } 17 | js-sys = "0.3.60" 18 | 19 | [profile.release] 20 | strip = "symbols" 21 | lto = true 22 | opt-level = 3 23 | -------------------------------------------------------------------------------- /packages/diffhtml-rust-parser/Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | cargo +nightly build --release 3 | mkdir -p dist 4 | wasm-bindgen ./target/wasm32-unknown-unknown/release/parser.wasm --target=bundler --omit-default-module-path --out-dir dist 5 | -------------------------------------------------------------------------------- /packages/diffhtml-rust-parser/README.md: -------------------------------------------------------------------------------- 1 | # Rust WASM HTML Parser 2 | 3 | Provides [tl a Rust-based zero-copy HTML parser](https://docs.rs/tl/latest/tl/) 4 | compiled to WASM, with a lightweight compatibility layer. This is an 5 | alternative to the regex-based parser found in the core source. You can use 6 | this alternative during a build, in the browser, and when writing server-side 7 | code. 8 | 9 | ## Build 10 | 11 | ``` 12 | make 13 | ``` 14 | 15 | ## Test 16 | 17 | ``` 18 | npm t 19 | ``` 20 | 21 | ## Using with diffHTML 22 | 23 | ```js 24 | import { Internals, innerHTML, html } from 'diffhtml'; 25 | import { parse } from 'diffhtml-rust-parser'; 26 | 27 | // Use the rust parser 28 | Internals.parse = parse; 29 | 30 | // Now the html`` tagged template will be parsed using WASM 31 | innerHTML(document.body, html` 32 |
Parsed with TL using WASM
33 | `); 34 | 35 | // Simple HTML strings are also automatically parsed using WASM now 36 | innerHTML(document.body, '
Also parsed with WASM
'); 37 | ``` 38 | 39 | ## Using with the Babel plugin 40 | 41 | To use the WASM plugin with you need to enable the Node WASM support as part of 42 | your build step. This is done with the `NODE_OPTIONS` env var and the 43 | `--experimental-wasm-modules` flag. 44 | 45 | ```json 46 | { 47 | "scripts": { 48 | "build": "NODE_OPTIONS=--experimental-wasm-modules babel input.js -o output.js" 49 | } 50 | } 51 | ``` 52 | 53 | You need to use the JS version of the Babel config in order to pass the dynamic 54 | value. The JSON `.babelrc` is not compatible with the WASM parser. 55 | 56 | A `babel.config.js` config could look something like: 57 | 58 | ```js 59 | import { parse } from 'diffhtml-rust-parser'; 60 | 61 | export default { 62 | plugins: [ 63 | ['transform-diffhtml', { 64 | parse, 65 | }] 66 | ], 67 | }; 68 | ``` 69 | 70 | ## Using with Webpack 71 | 72 | This module is generated with bundlers and Node in mind. You should be able to 73 | consume it with a bundler that supports loading WASM like webpack. A sample 74 | example of how you could integrate the WASM parser in both development and 75 | production is available. 76 | 77 | - [Sample webpack example](./examples/webpack) 78 | -------------------------------------------------------------------------------- /packages/diffhtml-rust-parser/examples/webpack/README.md: -------------------------------------------------------------------------------- 1 | # Webpack WASM Example 2 | 3 | This example demonstrates how to build the WASM parser into a web bundle using 4 | webpack. 5 | 6 | In development `npm run start` or `npm run build-dev`: 7 | 8 | - Build output is ~240KB 9 | - WASM is loaded asynchronously 10 | - Uses dev-server with `start`, outputs to the filesystem with `build-dev` 11 | 12 | In production `npm run build`: 13 | 14 | - Build output is ~20KB (7KB gzip) 15 | - Removes the parser resulting in a significantly smaller filesize 16 | - Uses Babel to pre-compile using the WASM parser to maintain consistency 17 | -------------------------------------------------------------------------------- /packages/diffhtml-rust-parser/examples/webpack/babel.config.mjs: -------------------------------------------------------------------------------- 1 | import { parse } from 'diffhtml-rust-parser'; 2 | import transformPlugin from 'babel-plugin-transform-diffhtml'; 3 | 4 | export default { 5 | plugins: [ 6 | [transformPlugin, { createTree: 'html', parse }] 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /packages/diffhtml-rust-parser/examples/webpack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Index 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/diffhtml-rust-parser/examples/webpack/index.js: -------------------------------------------------------------------------------- 1 | import { Internals, innerHTML, html } from 'diffhtml'; 2 | import { parse } from 'diffhtml-rust-parser'; 3 | 4 | // Use Rust WASM parser at runtime during development 5 | Internals.parse = parse; 6 | 7 | function App() { 8 | return html` 9 |
console.log('clicked')}> 10 | This markup was parsed with WASM 11 |
12 | `; 13 | } 14 | 15 | innerHTML(document.body, App()); 16 | -------------------------------------------------------------------------------- /packages/diffhtml-rust-parser/examples/webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack", 3 | "version": "1.0.0", 4 | "description": "Example of using WASM parser with webpack and babel", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack serve", 8 | "build-dev": "webpack", 9 | "build": "NODE_OPTIONS=--experimental-wasm-modules NODE_ENV=production webpack -c webpack.config.prod.mjs" 10 | }, 11 | "devDependencies": { 12 | "@babel/core": "^7.20.2", 13 | "@wasm-tool/wasm-pack-plugin": "^1.6.0", 14 | "babel-loader": "^9.1.0", 15 | "babel-plugin-transform-diffhtml": "file:../../../babel-plugin-transform-diffhtml", 16 | "diffhtml": "^1.0.0-beta.29", 17 | "diffhtml-rust-parser": "file:../../", 18 | "url-loader": "^4.1.1", 19 | "webpack": "^5.75.0", 20 | "webpack-cli": "^4.10.0", 21 | "webpack-dev-server": "^4.11.1", 22 | "webpack-virtual-modules": "^0.4.6" 23 | }, 24 | "keywords": [], 25 | "author": "Tim Branyen (@tbranyen)", 26 | "license": "MIT" 27 | } 28 | -------------------------------------------------------------------------------- /packages/diffhtml-rust-parser/examples/webpack/webpack.config.mjs: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { fileURLToPath } from 'url'; 3 | 4 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 5 | const dist = path.resolve(__dirname, 'dist'); 6 | 7 | export default { 8 | mode: 'development', 9 | entry: { 10 | index: './index.js' 11 | }, 12 | output: { 13 | path: dist, 14 | filename: 'build.js', 15 | publicPath: '/dist/', 16 | clean: true, 17 | }, 18 | experiments: { 19 | futureDefaults: true, 20 | }, 21 | devServer: { 22 | static: { 23 | directory: '.', 24 | } 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /packages/diffhtml-rust-parser/examples/webpack/webpack.config.prod.mjs: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | import config from './webpack.config.mjs'; 3 | import VirtualModulesPlugin from 'webpack-virtual-modules'; 4 | 5 | const virtualModules = new VirtualModulesPlugin({ 6 | './empty.js': ` 7 | export const parse = () => {}; 8 | `, 9 | }); 10 | 11 | export default { 12 | ...config, 13 | 14 | mode: 'production', 15 | 16 | optimization: { 17 | minimize: true, 18 | }, 19 | 20 | module: { 21 | rules: [ 22 | { 23 | test: /\.js$/, 24 | exclude: /(node_modules)|(\.wasm$)/, 25 | use: { 26 | loader: 'babel-loader', 27 | } 28 | } 29 | ] 30 | }, 31 | 32 | resolve: { 33 | alias: { 34 | 'diffhtml': 'diffhtml/dist/es/lite.js', 35 | 'diffhtml-rust-parser': './empty.js', 36 | }, 37 | }, 38 | 39 | plugins: [ virtualModules ], 40 | }; 41 | -------------------------------------------------------------------------------- /packages/diffhtml-rust-parser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diffhtml-rust-parser", 3 | "version": "1.0.0-beta.30", 4 | "description": "", 5 | "main": "dist/parser.js", 6 | "type": "module", 7 | "author": "Tim Branyen (@tbranyen)", 8 | "license": "MIT", 9 | "scripts": { 10 | "build": "make", 11 | "test": "node --experimental-wasm-modules test/index.js", 12 | "test-cov": "npm run test" 13 | }, 14 | "devDependencies": { 15 | "diffhtml": "^1.0.0-beta.30" 16 | }, 17 | "gitHead": "091b497588f6f48221630431e2f1eeb7f2db37cb" 18 | } 19 | -------------------------------------------------------------------------------- /packages/diffhtml-static-sync/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-transform-modules-commonjs" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/diffhtml-static-sync/README.md: -------------------------------------------------------------------------------- 1 | # <±/> diffHTML Static Sync 2 | 3 | *Static server that livereloads using VDOM on the entire page along with CSS.* 4 | 5 | Stable Version: 1.0.0-beta.30 6 | 7 | Provides a static HTTP server that monitors the folder it was executed in for 8 | changes. Whenever a file changes, it is read from disk and sent to the page 9 | using WebSockets. This server injects a client-side handler which responds to 10 | this WebSocket event, and using diffHTML, diffs the contents seamlessly without 11 | needing to reload. This includes all ``, ``, and `` tag 12 | changes. Provides smart CSS reloading, that instantly updates. 13 | 14 | Takes a "just works" approach by automatically injecting the synchronization 15 | code. 16 | 17 | ## Installation 18 | 19 | You can install via `yarn` or `npm`, below you will see commands to install 20 | globally which is what is most commonly used for command line tools. 21 | 22 | With npm: 23 | 24 | ``` sh 25 | npm install -g diffhtml-static-sync 26 | ``` 27 | 28 | With yarn: 29 | 30 | ``` 31 | yarn global add diffhtml-static-sync 32 | ``` 33 | 34 | ## Usage 35 | 36 | If all goes well, you should now have a `diffhtml-static-sync` command in your 37 | `PATH` which means you can execute it in the command line. Open an existing 38 | folder with HTML files or make a new one and run this command in it. You 39 | shouldn't see any output just yet, but the command should appear to hang. 40 | 41 | Once you open your browser to: http://localhost:8000/ you should see the 42 | `index.html` file or a directory listing to chose from. 43 | 44 | Basic usage: 45 | 46 | ``` sh 47 | λ diffhtml-static-sync . 48 | Open http://localhost:8000 49 | 50 | Waiting for changes ⣻ Socket connection established 51 | ``` 52 | 53 | Pass `--quiet` to prevent verbose logging in the browser console and terminal. 54 | 55 | ## Markdown 56 | 57 | Markdown is supported out-of-the-box, browse them as you would normal HTML 58 | files. Name your markdown files with `index.markdown` or `index.md` to be 59 | picked up automatically in folders. 60 | 61 | ## LiveReload 62 | 63 | By default any other file types are treated as a full page reload. 64 | 65 | ## Static Handler 66 | 67 | You can define your own handlers to respond to file changes. These are set up 68 | as a global `Set`. Like so: 69 | 70 | ``` js 71 | window.staticSyncHandlers = new Set(); 72 | ``` 73 | 74 | You can add your own function hooks into the Set, but calling `add`: 75 | 76 | ``` js 77 | staticSyncHandlers.add(({ file, markup /*, quiet */ }) => { 78 | if (file === 'some/file') { 79 | // Do something with the contents 80 | } 81 | }); 82 | ``` 83 | 84 | There is an optional argument quiet, shown above, that you can use to silence 85 | logging output to prevent clutter in the console. This is toggled from the 86 | `--quiet` CLI flag. 87 | -------------------------------------------------------------------------------- /packages/diffhtml-static-sync/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | if (require.main === module) { 4 | return require('./lib/watch'); 5 | } 6 | 7 | const { watch } = require('chokidar'); 8 | const getSocket = require('./lib/socket'); 9 | 10 | exports.clientScript = require('./lib/util/client-script'); 11 | 12 | // Not an official API yet... only testing. 13 | exports.watch = (path, file, cb) => { 14 | const watcher = watch(path, { ignored: /[\/\\]\./, persistent: true, }); 15 | 16 | getSocket.then(socket => { 17 | watcher.on('change', () => cb(markup => socket.send( 18 | JSON.stringify({ 19 | file, 20 | markup, 21 | }) 22 | ))); 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /packages/diffhtml-static-sync/lib/socket.js: -------------------------------------------------------------------------------- 1 | const engine = require('engine.io'); 2 | const server = engine.listen(process.env.WS_PORT || 54321); 3 | 4 | const sockets = new Set(); 5 | const yellow = '\x1B[33m'; 6 | const reset = '\x1B[m'; 7 | const quiet = process.argv.includes('--quiet'); 8 | 9 | module.exports = new Promise(resolve => { 10 | server.on('close', socket => sockets.delete(socket)); 11 | server.on('error', socket => sockets.delete(socket)); 12 | 13 | server.on('connection', socket => { 14 | socket.on('close', () => sockets.delete(socket)); 15 | 16 | if (!quiet) { 17 | console.log(`${yellow}Socket connection established${reset}`); 18 | } 19 | 20 | // Broadcast client messages. 21 | socket.on('message', msg => { 22 | [...sockets].filter(x => socket !== x).forEach(socket => socket.send(msg)); 23 | }); 24 | 25 | sockets.add(socket); 26 | resolve(sockets); 27 | }) 28 | }); 29 | 30 | process.on('exit', () => server.close()); 31 | -------------------------------------------------------------------------------- /packages/diffhtml-static-sync/lib/util/client-script.js: -------------------------------------------------------------------------------- 1 | const { readFileSync } = require('fs'); 2 | const { join } = require('path'); 3 | const escape = require('./escape'); 4 | 5 | module.exports = escape(readFileSync(join(__dirname, '../../dist/sync.js'))); -------------------------------------------------------------------------------- /packages/diffhtml-static-sync/lib/util/escape.js: -------------------------------------------------------------------------------- 1 | module.exports = script => { 2 | return String(script) 3 | .replace(/\