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
|
---|