[d24f17c] | 1 | # hastscript
|
---|
| 2 |
|
---|
| 3 | [![Build][build-badge]][build]
|
---|
| 4 | [![Coverage][coverage-badge]][coverage]
|
---|
| 5 | [![Downloads][downloads-badge]][downloads]
|
---|
| 6 | [![Size][size-badge]][size]
|
---|
| 7 | [![Sponsors][sponsors-badge]][collective]
|
---|
| 8 | [![Backers][backers-badge]][collective]
|
---|
| 9 | [![Chat][chat-badge]][chat]
|
---|
| 10 |
|
---|
| 11 | [**hast**][hast] utility to create [*trees*][tree] in HTML or SVG.
|
---|
| 12 |
|
---|
| 13 | Similar to [`hyperscript`][hyperscript], [`virtual-dom/h`][virtual-hyperscript],
|
---|
| 14 | [`React.createElement`][react], and [Vue’s `createElement`][vue],
|
---|
| 15 | but for [**hast**][hast].
|
---|
| 16 |
|
---|
| 17 | Use [`unist-builder`][u] to create any [**unist**][unist] tree.
|
---|
| 18 |
|
---|
| 19 | ## Install
|
---|
| 20 |
|
---|
| 21 | [npm][]:
|
---|
| 22 |
|
---|
| 23 | ```sh
|
---|
| 24 | npm install hastscript
|
---|
| 25 | ```
|
---|
| 26 |
|
---|
| 27 | ## Use
|
---|
| 28 |
|
---|
| 29 | ```js
|
---|
| 30 | var h = require('hastscript')
|
---|
| 31 | var s = require('hastscript/svg')
|
---|
| 32 |
|
---|
| 33 | // Children as an array:
|
---|
| 34 | console.log(
|
---|
| 35 | h('.foo#some-id', [
|
---|
| 36 | h('span', 'some text'),
|
---|
| 37 | h('input', {type: 'text', value: 'foo'}),
|
---|
| 38 | h('a.alpha', {class: 'bravo charlie', download: 'download'}, [
|
---|
| 39 | 'delta',
|
---|
| 40 | 'echo'
|
---|
| 41 | ])
|
---|
| 42 | ])
|
---|
| 43 | )
|
---|
| 44 |
|
---|
| 45 | // Children as arguments:
|
---|
| 46 | console.log(
|
---|
| 47 | h(
|
---|
| 48 | 'form',
|
---|
| 49 | {method: 'POST'},
|
---|
| 50 | h('input', {type: 'text', name: 'foo'}),
|
---|
| 51 | h('input', {type: 'text', name: 'bar'}),
|
---|
| 52 | h('input', {type: 'submit', value: 'send'})
|
---|
| 53 | )
|
---|
| 54 | )
|
---|
| 55 |
|
---|
| 56 | // SVG:
|
---|
| 57 | console.log(
|
---|
| 58 | s('svg', {xmlns: 'http://www.w3.org/2000/svg', viewbox: '0 0 500 500'}, [
|
---|
| 59 | s('title', 'SVG `<circle>` element'),
|
---|
| 60 | s('circle', {cx: 120, cy: 120, r: 100})
|
---|
| 61 | ])
|
---|
| 62 | )
|
---|
| 63 | ```
|
---|
| 64 |
|
---|
| 65 | Yields:
|
---|
| 66 |
|
---|
| 67 | ```js
|
---|
| 68 | {
|
---|
| 69 | type: 'element',
|
---|
| 70 | tagName: 'div',
|
---|
| 71 | properties: {className: ['foo'], id: 'some-id'},
|
---|
| 72 | children: [
|
---|
| 73 | {
|
---|
| 74 | type: 'element',
|
---|
| 75 | tagName: 'span',
|
---|
| 76 | properties: {},
|
---|
| 77 | children: [{type: 'text', value: 'some text'}]
|
---|
| 78 | },
|
---|
| 79 | {
|
---|
| 80 | type: 'element',
|
---|
| 81 | tagName: 'input',
|
---|
| 82 | properties: {type: 'text', value: 'foo'},
|
---|
| 83 | children: []
|
---|
| 84 | },
|
---|
| 85 | {
|
---|
| 86 | type: 'element',
|
---|
| 87 | tagName: 'a',
|
---|
| 88 | properties: {className: ['alpha', 'bravo', 'charlie'], download: true},
|
---|
| 89 | children: [{type: 'text', value: 'delta'}, {type: 'text', value: 'echo'}]
|
---|
| 90 | }
|
---|
| 91 | ]
|
---|
| 92 | }
|
---|
| 93 | {
|
---|
| 94 | type: 'element',
|
---|
| 95 | tagName: 'form',
|
---|
| 96 | properties: {method: 'POST'},
|
---|
| 97 | children: [
|
---|
| 98 | {
|
---|
| 99 | type: 'element',
|
---|
| 100 | tagName: 'input',
|
---|
| 101 | properties: {type: 'text', name: 'foo'},
|
---|
| 102 | children: []
|
---|
| 103 | },
|
---|
| 104 | {
|
---|
| 105 | type: 'element',
|
---|
| 106 | tagName: 'input',
|
---|
| 107 | properties: {type: 'text', name: 'bar'},
|
---|
| 108 | children: []
|
---|
| 109 | },
|
---|
| 110 | {
|
---|
| 111 | type: 'element',
|
---|
| 112 | tagName: 'input',
|
---|
| 113 | properties: {type: 'submit', value: 'send'},
|
---|
| 114 | children: []
|
---|
| 115 | }
|
---|
| 116 | ]
|
---|
| 117 | }
|
---|
| 118 | {
|
---|
| 119 | type: 'element',
|
---|
| 120 | tagName: 'svg',
|
---|
| 121 | properties: {xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 500 500'},
|
---|
| 122 | children: [
|
---|
| 123 | {
|
---|
| 124 | type: 'element',
|
---|
| 125 | tagName: 'title',
|
---|
| 126 | properties: {},
|
---|
| 127 | children: [{type: 'text', value: 'SVG `<circle>` element'}]
|
---|
| 128 | },
|
---|
| 129 | {
|
---|
| 130 | type: 'element',
|
---|
| 131 | tagName: 'circle',
|
---|
| 132 | properties: {cx: 120, cy: 120, r: 100},
|
---|
| 133 | children: []
|
---|
| 134 | }
|
---|
| 135 | ]
|
---|
| 136 | }
|
---|
| 137 | ```
|
---|
| 138 |
|
---|
| 139 | ## API
|
---|
| 140 |
|
---|
| 141 | ### `h(selector?[, properties][, ...children])`
|
---|
| 142 |
|
---|
| 143 | ### `s(selector?[, properties][, ...children])`
|
---|
| 144 |
|
---|
| 145 | DSL to create virtual [**hast**][hast] [*trees*][tree] for HTML or SVG.
|
---|
| 146 |
|
---|
| 147 | ##### Parameters
|
---|
| 148 |
|
---|
| 149 | ###### `selector`
|
---|
| 150 |
|
---|
| 151 | Simple CSS selector (`string`, optional).
|
---|
| 152 | Can contain a tag name (`foo`), IDs (`#bar`), and classes (`.baz`).
|
---|
| 153 | If there is no tag name in the selector, `h` defaults to a `div` element,
|
---|
| 154 | and `s` to a `g` element.
|
---|
| 155 | `selector` is parsed by [`hast-util-parse-selector`][parse-selector].
|
---|
| 156 |
|
---|
| 157 | ###### `properties`
|
---|
| 158 |
|
---|
| 159 | Map of properties (`Object.<*>`, optional).
|
---|
| 160 |
|
---|
| 161 | ###### `children`
|
---|
| 162 |
|
---|
| 163 | (Lists of) child nodes (`string`, `Node`, `Array.<string|Node>`, optional).
|
---|
| 164 | When strings are encountered, they are mapped to [`text`][text] nodes.
|
---|
| 165 |
|
---|
| 166 | ##### Returns
|
---|
| 167 |
|
---|
| 168 | [`Element`][element].
|
---|
| 169 |
|
---|
| 170 | ## Security
|
---|
| 171 |
|
---|
| 172 | Use of `hastscript` can open you up to a [cross-site scripting (XSS)][xss]
|
---|
| 173 | attack as values are injected into the syntax tree.
|
---|
| 174 | The following example shows how a script is injected that runs when loaded in a
|
---|
| 175 | browser.
|
---|
| 176 |
|
---|
| 177 | ```js
|
---|
| 178 | var tree = {type: 'root', children: []}
|
---|
| 179 |
|
---|
| 180 | tree.children.push(h('script', 'alert(1)'))
|
---|
| 181 | ```
|
---|
| 182 |
|
---|
| 183 | Yields:
|
---|
| 184 |
|
---|
| 185 | ```html
|
---|
| 186 | <script>alert(1)</script>
|
---|
| 187 | ```
|
---|
| 188 |
|
---|
| 189 | The following example shows how an image is injected that fails loading and
|
---|
| 190 | therefore runs code in a browser.
|
---|
| 191 |
|
---|
| 192 | ```js
|
---|
| 193 | var tree = {type: 'root', children: []}
|
---|
| 194 |
|
---|
| 195 | // Somehow someone injected these properties instead of an expected `src` and
|
---|
| 196 | // `alt`:
|
---|
| 197 | var otherProps = {src: 'x', onError: 'alert(2)'}
|
---|
| 198 |
|
---|
| 199 | tree.children.push(h('img', {src: 'default.png', ...otherProps}))
|
---|
| 200 | ```
|
---|
| 201 |
|
---|
| 202 | Yields:
|
---|
| 203 |
|
---|
| 204 | ```html
|
---|
| 205 | <img src="x" onerror="alert(2)">
|
---|
| 206 | ```
|
---|
| 207 |
|
---|
| 208 | The following example shows how code can run in a browser because someone stored
|
---|
| 209 | an object in a database instead of the expected string.
|
---|
| 210 |
|
---|
| 211 | ```js
|
---|
| 212 | var tree = {type: 'root', children: []}
|
---|
| 213 |
|
---|
| 214 | // Somehow this isn’t the expected `'wooorm'`.
|
---|
| 215 | var username = {
|
---|
| 216 | type: 'element',
|
---|
| 217 | tagName: 'script',
|
---|
| 218 | children: [{type: 'text', value: 'alert(3)'}]
|
---|
| 219 | }
|
---|
| 220 |
|
---|
| 221 | tree.children.push(h('span.handle', username))
|
---|
| 222 | ```
|
---|
| 223 |
|
---|
| 224 | Yields:
|
---|
| 225 |
|
---|
| 226 | ```html
|
---|
| 227 | <span class="handle"><script>alert(3)</script></span>
|
---|
| 228 | ```
|
---|
| 229 |
|
---|
| 230 | Either do not use user input in `hastscript` or use
|
---|
| 231 | [`hast-util-santize`][sanitize].
|
---|
| 232 |
|
---|
| 233 | ## Related
|
---|
| 234 |
|
---|
| 235 | * [`unist-builder`](https://github.com/syntax-tree/unist-builder)
|
---|
| 236 | — Create any unist tree
|
---|
| 237 | * [`xastscript`](https://github.com/syntax-tree/xastscript)
|
---|
| 238 | — Create a xast tree
|
---|
| 239 | * [`hast-to-hyperscript`](https://github.com/syntax-tree/hast-to-hyperscript)
|
---|
| 240 | — Convert a Node to React, Virtual DOM, Hyperscript, and more
|
---|
| 241 | * [`hast-util-from-dom`](https://github.com/syntax-tree/hast-util-from-dom)
|
---|
| 242 | — Transform a DOM tree to hast
|
---|
| 243 | * [`hast-util-select`](https://github.com/syntax-tree/hast-util-select)
|
---|
| 244 | — `querySelector`, `querySelectorAll`, and `matches`
|
---|
| 245 | * [`hast-util-to-html`](https://github.com/syntax-tree/hast-util-to-html)
|
---|
| 246 | — Stringify nodes to HTML
|
---|
| 247 | * [`hast-util-to-dom`](https://github.com/syntax-tree/hast-util-to-dom)
|
---|
| 248 | — Transform to a DOM tree
|
---|
| 249 |
|
---|
| 250 | ## Contribute
|
---|
| 251 |
|
---|
| 252 | See [`contributing.md` in `syntax-tree/.github`][contributing] for ways to get
|
---|
| 253 | started.
|
---|
| 254 | See [`support.md`][support] for ways to get help.
|
---|
| 255 |
|
---|
| 256 | This project has a [code of conduct][coc].
|
---|
| 257 | By interacting with this repository, organization, or community you agree to
|
---|
| 258 | abide by its terms.
|
---|
| 259 |
|
---|
| 260 | ## License
|
---|
| 261 |
|
---|
| 262 | [MIT][license] © [Titus Wormer][author]
|
---|
| 263 |
|
---|
| 264 | <!-- Definitions -->
|
---|
| 265 |
|
---|
| 266 | [build-badge]: https://img.shields.io/travis/syntax-tree/hastscript.svg
|
---|
| 267 |
|
---|
| 268 | [build]: https://travis-ci.org/syntax-tree/hastscript
|
---|
| 269 |
|
---|
| 270 | [coverage-badge]: https://img.shields.io/codecov/c/github/syntax-tree/hastscript.svg
|
---|
| 271 |
|
---|
| 272 | [coverage]: https://codecov.io/github/syntax-tree/hastscript
|
---|
| 273 |
|
---|
| 274 | [downloads-badge]: https://img.shields.io/npm/dm/hastscript.svg
|
---|
| 275 |
|
---|
| 276 | [downloads]: https://www.npmjs.com/package/hastscript
|
---|
| 277 |
|
---|
| 278 | [size-badge]: https://img.shields.io/bundlephobia/minzip/hastscript.svg
|
---|
| 279 |
|
---|
| 280 | [size]: https://bundlephobia.com/result?p=hastscript
|
---|
| 281 |
|
---|
| 282 | [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg
|
---|
| 283 |
|
---|
| 284 | [backers-badge]: https://opencollective.com/unified/backers/badge.svg
|
---|
| 285 |
|
---|
| 286 | [collective]: https://opencollective.com/unified
|
---|
| 287 |
|
---|
| 288 | [chat-badge]: https://img.shields.io/badge/chat-spectrum-7b16ff.svg
|
---|
| 289 |
|
---|
| 290 | [chat]: https://spectrum.chat/unified/syntax-tree
|
---|
| 291 |
|
---|
| 292 | [npm]: https://docs.npmjs.com/cli/install
|
---|
| 293 |
|
---|
| 294 | [license]: license
|
---|
| 295 |
|
---|
| 296 | [author]: https://wooorm.com
|
---|
| 297 |
|
---|
| 298 | [contributing]: https://github.com/syntax-tree/.github/blob/HEAD/contributing.md
|
---|
| 299 |
|
---|
| 300 | [support]: https://github.com/syntax-tree/.github/blob/HEAD/support.md
|
---|
| 301 |
|
---|
| 302 | [coc]: https://github.com/syntax-tree/.github/blob/HEAD/code-of-conduct.md
|
---|
| 303 |
|
---|
| 304 | [hyperscript]: https://github.com/dominictarr/hyperscript
|
---|
| 305 |
|
---|
| 306 | [virtual-hyperscript]: https://github.com/Matt-Esch/virtual-dom/tree/HEAD/virtual-hyperscript
|
---|
| 307 |
|
---|
| 308 | [react]: https://reactjs.org/docs/glossary.html#react-elements
|
---|
| 309 |
|
---|
| 310 | [vue]: https://vuejs.org/v2/guide/render-function.html#createElement-Arguments
|
---|
| 311 |
|
---|
| 312 | [unist]: https://github.com/syntax-tree/unist
|
---|
| 313 |
|
---|
| 314 | [tree]: https://github.com/syntax-tree/unist#tree
|
---|
| 315 |
|
---|
| 316 | [hast]: https://github.com/syntax-tree/hast
|
---|
| 317 |
|
---|
| 318 | [element]: https://github.com/syntax-tree/hast#element
|
---|
| 319 |
|
---|
| 320 | [text]: https://github.com/syntax-tree/hast#text
|
---|
| 321 |
|
---|
| 322 | [u]: https://github.com/syntax-tree/unist-builder
|
---|
| 323 |
|
---|
| 324 | [parse-selector]: https://github.com/syntax-tree/hast-util-parse-selector
|
---|
| 325 |
|
---|
| 326 | [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting
|
---|
| 327 |
|
---|
| 328 | [sanitize]: https://github.com/syntax-tree/hast-util-sanitize
|
---|