├── .npmrc ├── .gitattributes ├── .gitignore ├── media └── logo.png ├── .github ├── funding.yml └── workflows │ ├── ci.yml │ └── esm-lint.yml ├── global.d.ts ├── .editorconfig ├── tsconfig.json ├── tests ├── _fixtures.js └── index.tsx ├── license ├── package.json ├── readme.md └── index.ts /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | index.js 3 | index.d.ts 4 | -------------------------------------------------------------------------------- /media/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vadimdemedes/dom-chef/HEAD/media/logo.png -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | github: fregante 2 | custom: [paypal.me/fregante, www.buymeacoffee.com/fregante] 3 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace JSX { 2 | interface Element extends HTMLElement, DocumentFragment {} 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{package.json,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@sindresorhus/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "target": "es2015", 5 | "module": "es2015", 6 | }, 7 | "files": [ 8 | "tests/index.tsx", 9 | "index.ts", 10 | "global.d.ts" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /tests/_fixtures.js: -------------------------------------------------------------------------------- 1 | import {JSDOM} from 'jsdom'; 2 | 3 | const {window} = new JSDOM('…'); 4 | global.document = window.document; 5 | global.Node = window.Node; 6 | global.Element = window.Element; 7 | global.DocumentFragment = window.DocumentFragment; 8 | global.EventTarget = window.EventTarget; 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | - pull_request 5 | - push 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v1 13 | with: 14 | node-version: 12 15 | - run: npm install 16 | - run: npm test 17 | - run: npm build 18 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Vadim Demedes (github.com/vadimdemedes) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dom-chef", 3 | "version": "5.1.1", 4 | "description": "Build regular DOM elements using JSX", 5 | "keywords": [ 6 | "jsx", 7 | "dom", 8 | "native", 9 | "innerHTML", 10 | "document", 11 | "createElement", 12 | "create", 13 | "element", 14 | "documentFragment", 15 | "typescript" 16 | ], 17 | "repository": "vadimdemedes/dom-chef", 18 | "funding": "https://github.com/sponsors/fregante", 19 | "license": "MIT", 20 | "author": { 21 | "name": "Vadim Demedes", 22 | "email": "vdemedes@gmail.com", 23 | "url": "https://github.com/vadimdemedes" 24 | }, 25 | "maintainers": [ 26 | { 27 | "name": "Federico Brigante", 28 | "email": "me@fregante.com", 29 | "url": "https://fregante.com" 30 | } 31 | ], 32 | "type": "module", 33 | "main": "index.js", 34 | "module": "index.js", 35 | "types": "index.d.ts", 36 | "files": [ 37 | "index.js", 38 | "index.d.ts" 39 | ], 40 | "scripts": { 41 | "build": "tsc", 42 | "prepack": "tsc --sourceMap false", 43 | "pretest": "npm run build", 44 | "test": "xo && ava", 45 | "watch": "npm run build -- --watch" 46 | }, 47 | "xo": { 48 | "env": "browser", 49 | "plugins": [ 50 | "react" 51 | ], 52 | "rules": { 53 | "ava/no-ignored-test-files": "off", 54 | "unicorn/prefer-dom-node-append": "off", 55 | "import/no-extraneous-dependencies": "off", 56 | "import/no-unassigned-import": "off", 57 | "@typescript-eslint/prefer-readonly-parameter-types": "off", 58 | "@typescript-eslint/naming-convention": "off", 59 | "@typescript-eslint/no-namespace": "off", 60 | "@typescript-eslint/no-empty-function": "off", 61 | "@typescript-eslint/no-unsafe-member-access": "off", 62 | "@typescript-eslint/no-unsafe-assignment": "off", 63 | "@typescript-eslint/no-unsafe-return": "off", 64 | "react/jsx-uses-vars": "error", 65 | "react/jsx-uses-react": "error" 66 | } 67 | }, 68 | "dependencies": { 69 | "@types/react": "^17.0.42", 70 | "svg-tag-names": "^3.0.1" 71 | }, 72 | "devDependencies": { 73 | "@sindresorhus/tsconfig": "^2.0.0", 74 | "@types/sinon": "^10.0.11", 75 | "ava": "^4.1.0", 76 | "eslint-plugin-react": "^7.29.4", 77 | "jsdom": "^19.0.0", 78 | "sinon": "^13.0.1", 79 | "typescript": "^4.6.2", 80 | "xo": "^0.48.0" 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /.github/workflows/esm-lint.yml: -------------------------------------------------------------------------------- 1 | env: 2 | IMPORT_TEXT: import React from 3 | NPM_MODULE_NAME: dom-chef 4 | 5 | # FILE GENERATED WITH: npx ghat fregante/ghatemplates/esm-lint 6 | # SOURCE: https://github.com/fregante/ghatemplates 7 | 8 | name: ESM 9 | on: 10 | pull_request: 11 | branches: 12 | - '*' 13 | push: 14 | branches: 15 | - master 16 | - main 17 | jobs: 18 | Pack: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v2 22 | - run: npm install 23 | - run: npm run build --if-present 24 | - run: npm pack --dry-run 25 | - run: npm pack | tail -1 | xargs -n1 tar -xzf 26 | - uses: actions/upload-artifact@v2 27 | with: 28 | path: package 29 | Webpack: 30 | runs-on: ubuntu-latest 31 | needs: Pack 32 | steps: 33 | - uses: actions/download-artifact@v2 34 | - run: npm install ./artifact 35 | - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.js 36 | - run: webpack --entry ./index.js 37 | - run: cat dist/main.js 38 | Parcel: 39 | runs-on: ubuntu-latest 40 | needs: Pack 41 | steps: 42 | - uses: actions/download-artifact@v2 43 | - run: npm install ./artifact 44 | - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.js 45 | - run: npx parcel@2 build index.js 46 | - run: cat dist/index.js 47 | Rollup: 48 | runs-on: ubuntu-latest 49 | needs: Pack 50 | steps: 51 | - uses: actions/download-artifact@v2 52 | - run: npm install ./artifact rollup@2 @rollup/plugin-node-resolve 53 | - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.js 54 | - run: npx rollup -p node-resolve index.js 55 | Snowpack: 56 | runs-on: ubuntu-latest 57 | needs: Pack 58 | steps: 59 | - uses: actions/download-artifact@v2 60 | - run: echo '{}' > package.json 61 | - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.js 62 | - run: npm install ./artifact 63 | - run: npx snowpack@2 build 64 | - run: cat build/web_modules/$NPM_MODULE_NAME.js 65 | TypeScript: 66 | runs-on: ubuntu-latest 67 | needs: Pack 68 | steps: 69 | - uses: actions/download-artifact@v2 70 | - run: npm install ./artifact 71 | - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.ts 72 | - run: tsc index.ts 73 | - run: cat index.js 74 | Node: 75 | runs-on: ubuntu-latest 76 | needs: Pack 77 | steps: 78 | - uses: actions/download-artifact@v2 79 | - uses: actions/setup-node@v1 80 | with: 81 | node-version: 14.x 82 | - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.mjs 83 | - run: npm install ./artifact 84 | - run: node index.mjs 85 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | 4 |
5 |
6 |
7 |

8 | 9 | > Build regular DOM elements using JSX 10 | 11 | With `dom-chef`, you can use Babel or TypeScript to transform [JSX](https://reactjs.org/docs/introducing-jsx.html) into plain old DOM elements, without using the unsafe `innerHTML` or clumsy `document.createElement` calls. 12 | 13 | It supports everything you expect from JSX, including: 14 | 15 | - [SVG elements](#render-svg) 16 | - [Event listeners](#inline-event-listeners) 17 | - [Inline CSS](#inline-styles) 18 | - [Nested elements](#nested-elements) 19 | - [Function elements](#use-functions) 20 | 21 | If something isn't supported (or doesn't work as well as it does in React) please open an issue! 22 | 23 | ## Install 24 | 25 | ``` 26 | $ npm install dom-chef 27 | ``` 28 | 29 | ## Usage 30 | 31 | Make sure to use a JSX transpiler (e.g. [Babel](#babel), [TypeScript compiler](#typescript-compiler), [esbuild](https://esbuild.github.io/content-types/#using-jsx-without-react), you only need one of them). 32 | 33 | ```jsx 34 | import {h} from 'dom-chef'; 35 | 36 | const handleClick = e => { 37 | // 45 | 46 | ); 47 | 48 | document.body.appendChild(el); 49 | ``` 50 | 51 | ### Babel 52 | 53 | `pragma` and `pragmaFrag` must be configured this way. More information on [Babel’s documentation](https://babeljs.io/docs/en/babel-plugin-transform-react-jsx.html#pragma). 54 | 55 | ```js 56 | // babel.config.js 57 | 58 | const plugins = [ 59 | [ 60 | '@babel/plugin-transform-react-jsx', 61 | { 62 | pragma: 'h', 63 | pragmaFrag: 'DocumentFragment', 64 | }, 65 | ], 66 | ]; 67 | 68 | // ... 69 | ``` 70 | 71 | ### TypeScript compiler 72 | 73 | `jsxFactory` and `jsxFragmentFactory` must be configured this way. More information on [TypeScripts’s documentation](https://www.typescriptlang.org/tsconfig#jsxFactory). 74 | 75 | 76 | ```jsonc 77 | // tsconfig.json 78 | 79 | { 80 | "compilerOptions": { 81 | "jsxFactory": "h", 82 | "jsxFragmentFactory": "DocumentFragment" 83 | } 84 | } 85 | ``` 86 | 87 | ### Alternative usage 88 | 89 | You can avoid configuring your JSX compiler by just letting it default to `React` and exporting the `React` object: 90 | 91 | ```js 92 | import React from 'dom-chef'; 93 | ``` 94 | 95 | ## Recipes 96 | 97 | ### Set classes 98 | 99 | ```jsx 100 | const el = Text; 101 | 102 | // or use `className` alias 103 | const el = Text; 104 | ``` 105 | 106 | ### Inline styles 107 | 108 | ```jsx 109 | const el =
; 110 | ``` 111 | 112 | ### Inline event listeners 113 | 114 | ```jsx 115 | const handleClick = e => { 116 | // was clicked 117 | }; 118 | 119 | const el = Text; 120 | ``` 121 | 122 | This is equivalent to: `span.addEventListener('click', handleClick)` 123 | 124 | ### Nested elements 125 | 126 | ```jsx 127 | const title =

Hello World

; 128 | const body =

Post body

; 129 | 130 | const post = ( 131 |
132 | {title} 133 | {body} 134 |
135 | ); 136 | ``` 137 | 138 | ### Set innerHTML 139 | 140 | ```jsx 141 | const dangerousHTML = ''; 142 | 143 | const wannaCry =
; 144 | ``` 145 | 146 | ### Render SVG 147 | 148 | **Note**: Due to the way `dom-chef` works, tags ``, `