@thi.ng/hiccup 4.3.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -1
- package/README.md +89 -81
- package/api.d.ts +2 -0
- package/api.js +3 -1
- package/css.js +4 -2
- package/deref.js +2 -1
- package/package.json +3 -3
- package/serialize.d.ts +58 -30
- package/serialize.js +64 -62
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2023-09-
|
|
3
|
+
- **Last updated**: 2023-09-19T10:42:50Z
|
|
4
4
|
- **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
|
|
5
5
|
|
|
6
6
|
All notable changes to this project will be documented in this file.
|
|
@@ -9,6 +9,27 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
|
|
|
9
9
|
**Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
|
|
10
10
|
and/or version bumps of transitive dependencies.
|
|
11
11
|
|
|
12
|
+
# [5.0.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/hiccup@5.0.0) (2023-09-19)
|
|
13
|
+
|
|
14
|
+
#### 🛑 Breaking changes
|
|
15
|
+
|
|
16
|
+
- add SerializeOpts, update serialize() ([442d777](https://github.com/thi-ng/umbrella/commit/442d777))
|
|
17
|
+
- BREAKING CHANGE: update serialize() args, replace with options object
|
|
18
|
+
- only a breaking change for "advanced" use cases
|
|
19
|
+
- add SerializeOpts to simplify serialize() args
|
|
20
|
+
- add customizable entity escaping (via new opts)
|
|
21
|
+
- add/update tests
|
|
22
|
+
|
|
23
|
+
#### 🩹 Bug fixes
|
|
24
|
+
|
|
25
|
+
- update entity escapes in serialize() ([369d83e](https://github.com/thi-ng/umbrella/commit/369d83e))
|
|
26
|
+
- use [@thi.ng/strings](https://github.com/thi-ng/umbrella/tree/main/packages/strings) escapeEntitiesNum() for better XML/SVG compatibility
|
|
27
|
+
- add tests
|
|
28
|
+
|
|
29
|
+
#### ♻️ Refactoring
|
|
30
|
+
|
|
31
|
+
- minor updates ([94b3de6](https://github.com/thi-ng/umbrella/commit/94b3de6))
|
|
32
|
+
|
|
12
33
|
## [4.3.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/hiccup@4.3.0) (2023-08-27)
|
|
13
34
|
|
|
14
35
|
#### 🚀 Features
|
package/README.md
CHANGED
|
@@ -36,7 +36,6 @@ This project is part of the
|
|
|
36
36
|
- [XML / DTD processing instructions](#xml--dtd-processing-instructions)
|
|
37
37
|
- [API](#api)
|
|
38
38
|
- [serialize()](#serialize)
|
|
39
|
-
- [escape()](#escape)
|
|
40
39
|
- [Authors](#authors)
|
|
41
40
|
- [License](#license)
|
|
42
41
|
|
|
@@ -45,34 +44,35 @@ This project is part of the
|
|
|
45
44
|
HTML/SVG/XML serialization of nested data structures, iterables & closures.
|
|
46
45
|
|
|
47
46
|
Inspired by [Hiccup](https://github.com/weavejester/hiccup) and
|
|
48
|
-
[Reagent](http://reagent-project.github.io/) for Clojure/ClojureScript
|
|
47
|
+
[Reagent](http://reagent-project.github.io/) for Clojure/ClojureScript, this
|
|
48
|
+
package provides key infrastructure for a number of other related libraries.
|
|
49
49
|
|
|
50
|
-
Forget all the custom toy DSLs for templating and instead use the full
|
|
51
|
-
|
|
52
|
-
easily *composable* components for static serialization to HTML &
|
|
53
|
-
friends.
|
|
50
|
+
Forget all the custom toy DSLs for templating and instead use the full power of
|
|
51
|
+
modern JavaScript to directly define fully data-driven, purely functional and
|
|
52
|
+
easily *composable* components for static serialization to HTML & friends.
|
|
54
53
|
|
|
55
|
-
This library is suitable for
|
|
54
|
+
This library is suitable for any SGML-style (HTML/XML/SVG/RSS/Atom etc.)
|
|
55
|
+
serialization, including static website/asset generation, server side rendering
|
|
56
56
|
etc. For interactive use cases, please see companion packages
|
|
57
57
|
[@thi.ng/rdom](https://github.com/thi-ng/umbrella/tree/develop/packages/rdom)
|
|
58
|
-
(or the older
|
|
58
|
+
(or the older, now unmaintained
|
|
59
59
|
[@thi.ng/hdom](https://github.com/thi-ng/umbrella/tree/develop/packages/hdom))
|
|
60
60
|
and their various support packages.
|
|
61
61
|
|
|
62
62
|
### Features
|
|
63
63
|
|
|
64
|
-
- Only uses arrays, functions, ES6 iterables / iterators / generators
|
|
64
|
+
- Only uses JS arrays, plain objects, functions, ES6 iterables / iterators / generators
|
|
65
65
|
- Eager & lazy component composition using embedded functions / closures
|
|
66
66
|
- Support for self-closing tags (incl. validation), boolean attributes
|
|
67
|
-
- Arbitrary user context object injection for component functions
|
|
68
|
-
-
|
|
67
|
+
- Arbitrary user context object injection for embedded component functions
|
|
68
|
+
- Dynamically derived attribute value generation via function values
|
|
69
69
|
- CSS formatting of `style` attribute objects
|
|
70
|
-
- Optional HTML entity encoding
|
|
70
|
+
- Optional HTML/XML entity encoding
|
|
71
71
|
- Support for comments and XML/DTD processing instructions
|
|
72
|
-
- Branch-local behavior control attributes to
|
|
72
|
+
- Branch-local behavior control attributes to customize serialization
|
|
73
73
|
- Small (1.9KB minified) & fast
|
|
74
74
|
|
|
75
|
-
(*) Lazy composition here means that functions are only executed at
|
|
75
|
+
<sup>(*)</sup> Lazy composition here means that functions are only executed at
|
|
76
76
|
serialization time. Examples below...
|
|
77
77
|
|
|
78
78
|
### Use cases
|
|
@@ -86,24 +86,27 @@ serialization time. Examples below...
|
|
|
86
86
|
|
|
87
87
|
### No special sauce needed (or wanted)
|
|
88
88
|
|
|
89
|
-
Using only vanilla language features simplifies the development,
|
|
90
|
-
|
|
91
|
-
custom template parser
|
|
92
|
-
expressiveness of the language /
|
|
93
|
-
engine.
|
|
89
|
+
Using only vanilla language features simplifies the development, removes need
|
|
90
|
+
for extra tooling, improves composability, reusability, transformation and
|
|
91
|
+
testing of components. No custom template parser (a la JSX or Handlebars etc.)
|
|
92
|
+
is required and you're only restricted by the expressiveness of the language /
|
|
93
|
+
environment, not by your template engine.
|
|
94
94
|
|
|
95
|
-
Components can be defined as simple functions returning arrays or
|
|
96
|
-
via JSON
|
|
95
|
+
Components can be defined as simple arrays and/or functions returning arrays or
|
|
96
|
+
can be dynamically generated or loaded via JSON...
|
|
97
97
|
|
|
98
98
|
### What is Hiccup?
|
|
99
99
|
|
|
100
|
-
For many years, [Hiccup](https://github.com/weavejester/hiccup) has been
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
100
|
+
For many years, [Hiccup](https://github.com/weavejester/hiccup) has been the
|
|
101
|
+
de-facto standard to encode HTML/XML datastructures in Clojure (and many years
|
|
102
|
+
before that, [the overall idea was introduced in Scheme by Oleg Kiselyov and
|
|
103
|
+
Kirill Lisovsky in
|
|
104
|
+
1999](https://web.archive.org/web/20011225105556/http://okmij.org/ftp/Scheme/xml.html)).
|
|
105
|
+
This library brings & extends this convention into ES6. A valid Hiccup tree is
|
|
106
|
+
any flat (though, usually nested) array of the following possible structures.
|
|
107
|
+
Any functions embedded in the tree are expected to return values of the same
|
|
108
|
+
structure. Please see [examples](#examples) & [API](#api) further
|
|
109
|
+
explanations...
|
|
107
110
|
|
|
108
111
|
```ts
|
|
109
112
|
["tag", ...]
|
|
@@ -127,6 +130,7 @@ iterable
|
|
|
127
130
|
- [@thi.ng/hiccup-carbon-icons](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup-carbon-icons) - Full set of IBM's Carbon icons in hiccup format
|
|
128
131
|
- [@thi.ng/hiccup-css](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup-css) - CSS from nested JS data structures
|
|
129
132
|
- [@thi.ng/hiccup-html](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup-html) - 100+ type-checked HTML5 element functions for [@thi.ng/hiccup](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup) related infrastructure
|
|
133
|
+
- [@thi.ng/hiccup-html-parse](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup-html-parse) - HTML parsing and transformation to nested JS arrays in hiccup format
|
|
130
134
|
- [@thi.ng/hiccup-markdown](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup-markdown) - Markdown parser & serializer from/to Hiccup format
|
|
131
135
|
- [@thi.ng/hiccup-svg](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup-svg) - SVG element functions for [@thi.ng/hiccup](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup) & related tooling
|
|
132
136
|
|
|
@@ -169,7 +173,7 @@ For Node.js REPL:
|
|
|
169
173
|
const hiccup = await import("@thi.ng/hiccup");
|
|
170
174
|
```
|
|
171
175
|
|
|
172
|
-
Package sizes (brotli'd, pre-treeshake): ESM: 2.
|
|
176
|
+
Package sizes (brotli'd, pre-treeshake): ESM: 2.14 KB
|
|
173
177
|
|
|
174
178
|
## Dependencies
|
|
175
179
|
|
|
@@ -205,7 +209,7 @@ A selection:
|
|
|
205
209
|
### Tags with Zencoding expansion
|
|
206
210
|
|
|
207
211
|
Tag names support
|
|
208
|
-
[Zencoding
|
|
212
|
+
[Emmet/Zencoding](https://docs.emmet.io/abbreviations/syntax/#id-and-class)
|
|
209
213
|
style ID & class attribute expansion:
|
|
210
214
|
|
|
211
215
|
```ts
|
|
@@ -246,13 +250,10 @@ serialize(
|
|
|
246
250
|
<div class="notice" selected style="background:#ff0;border:3px solid black">WARNING</div>
|
|
247
251
|
```
|
|
248
252
|
|
|
249
|
-
If an attribute specifies a function as value, the function is called
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
result MUST be a string.
|
|
253
|
-
|
|
254
|
-
**Function values for event attributes (any attrib name starting with
|
|
255
|
-
"on") WILL BE OMITTED from output.**
|
|
253
|
+
If an attribute specifies a function as value, the function is called with the
|
|
254
|
+
entire attribute object as argument (incl. any `id` or `class` attribs derived
|
|
255
|
+
from an Emmet-style tag name). This allows for the dynamic generation of
|
|
256
|
+
attribute values, based on existing ones. The result MUST be a string.
|
|
256
257
|
|
|
257
258
|
```ts
|
|
258
259
|
["div#foo", { bar: (attribs) => attribs.id + "-bar" }]
|
|
@@ -262,6 +263,9 @@ result MUST be a string.
|
|
|
262
263
|
<div id="foo" bar="foo-bar"></div>
|
|
263
264
|
```
|
|
264
265
|
|
|
266
|
+
Function values for event attributes (any attrib name starting with
|
|
267
|
+
"on") WILL BE OMITTED from output:
|
|
268
|
+
|
|
265
269
|
```ts
|
|
266
270
|
["div#foo", { onclick: () => alert("foo") }, "click me!"]
|
|
267
271
|
```
|
|
@@ -298,9 +302,11 @@ serialize(
|
|
|
298
302
|
|
|
299
303
|
### User context injection
|
|
300
304
|
|
|
301
|
-
Every component function will receive an arbitrary user defined context
|
|
302
|
-
|
|
303
|
-
|
|
305
|
+
Every component function will receive an arbitrary user defined context object
|
|
306
|
+
as first argument. This context object can be passed to
|
|
307
|
+
[`serialize()`](https://docs.thi.ng/umbrella/hiccup/functions/serialize.html)
|
|
308
|
+
via its [options argument]() and is then passed as arg to every component function
|
|
309
|
+
call.
|
|
304
310
|
|
|
305
311
|
The context object should contain any global component configuration,
|
|
306
312
|
e.g. for theming purposes.
|
|
@@ -321,17 +327,18 @@ const theme = {
|
|
|
321
327
|
|
|
322
328
|
serialize(
|
|
323
329
|
[section, "Hello world", "Easy theming"],
|
|
324
|
-
|
|
330
|
+
// pass context object via options
|
|
331
|
+
{ ctx: { theme } }
|
|
325
332
|
);
|
|
326
333
|
// <section class="bg-black moon-gray bt b--dark-gray mt3"><h1 class="white f3">Hello world</h1>Easy theming</section>
|
|
327
334
|
```
|
|
328
335
|
|
|
329
|
-
**Note:** Of course the context is ONLY auto-injected for lazily
|
|
330
|
-
|
|
331
|
-
wrapped in arrays and only called during serialization. If you call a
|
|
332
|
-
component function directly, you MUST pass the context (or `null`) as
|
|
333
|
-
|
|
334
|
-
|
|
336
|
+
**Note:** Of course the context is ONLY auto-injected for lazily embedded
|
|
337
|
+
component functions (like the examples shown above), i.e. if the functions are
|
|
338
|
+
wrapped in arrays and only called during serialization. If you call such a
|
|
339
|
+
component function directly, you MUST pass the context (or `null`) as first arg
|
|
340
|
+
yourself. Likewise, if a component function doesn't make use of the context you
|
|
341
|
+
can use either:
|
|
335
342
|
|
|
336
343
|
```ts
|
|
337
344
|
// skip the context arg and require direct invocation
|
|
@@ -362,13 +369,15 @@ Also see
|
|
|
362
369
|
[@thi.ng/hiccup-svg](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup-svg)
|
|
363
370
|
for related functionality.
|
|
364
371
|
|
|
365
|
-
```ts
|
|
366
|
-
|
|
372
|
+
```ts tangle:export/readme-circles.js
|
|
373
|
+
import { serialize } from "@thi.ng/hiccup";
|
|
374
|
+
import { repeatedly } from "@thi.ng/transducers";
|
|
375
|
+
import { writeFileSync } from "fs";
|
|
367
376
|
|
|
368
377
|
// creates an unstyled SVG circle element
|
|
369
378
|
// we ignore the first arg (an auto-injected context arg)
|
|
370
379
|
// context handling is described further below
|
|
371
|
-
const circle = (_, x, y, r) => ["circle", { cx: x
|
|
380
|
+
const circle = (_, x, y, r) => ["circle", { cx: ~~x, cy: ~~y, r: ~~r }];
|
|
372
381
|
|
|
373
382
|
// note how this next component lazily composes `circle`.
|
|
374
383
|
// This form delays evaluation of the `circle` component
|
|
@@ -382,23 +391,20 @@ const randomCircle = () => [
|
|
|
382
391
|
Math.random() * 100
|
|
383
392
|
];
|
|
384
393
|
|
|
385
|
-
// generator to produce iterable of `n` calls to `fn`
|
|
386
|
-
function* repeatedly(n, fn) {
|
|
387
|
-
while (n-- > 0) yield fn();
|
|
388
|
-
}
|
|
389
|
-
|
|
390
394
|
// generate 100 random circles and write serialized SVG to file
|
|
391
395
|
// `randomCircle` is wrapped
|
|
392
|
-
import {
|
|
396
|
+
import { XML_SVG } from "@thi.ng/prefixes";
|
|
393
397
|
|
|
394
398
|
const doc = [
|
|
395
|
-
"svg", { xmlns:
|
|
399
|
+
"svg", { xmlns: XML_SVG, width: 1000, height: 1000 },
|
|
396
400
|
["g", { fill: "none", stroke: "red" },
|
|
397
|
-
repeatedly(
|
|
401
|
+
repeatedly(randomCircle, 100)]];
|
|
398
402
|
|
|
399
|
-
|
|
403
|
+
writeFileSync("export/circles.svg", serialize(doc));
|
|
400
404
|
```
|
|
401
405
|
|
|
406
|
+
Resulting example output:
|
|
407
|
+
|
|
402
408
|
```xml
|
|
403
409
|
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
|
|
404
410
|
<g fill="none" stroke="red">
|
|
@@ -413,7 +419,9 @@ fs.writeFileSync("circles.svg", serialize(doc));
|
|
|
413
419
|
|
|
414
420
|
### Data-driven component composition
|
|
415
421
|
|
|
416
|
-
```
|
|
422
|
+
```js tangle:export/readme-glossary.js
|
|
423
|
+
import { serialize } from "@thi.ng/hiccup";
|
|
424
|
+
|
|
417
425
|
// data
|
|
418
426
|
const glossary = {
|
|
419
427
|
foo: "widely used placeholder name in computing",
|
|
@@ -438,10 +446,13 @@ const widget = [
|
|
|
438
446
|
["h1", "Glossary"],
|
|
439
447
|
[dlList, { id: "glossary" }, glossary]];
|
|
440
448
|
|
|
441
|
-
//
|
|
442
|
-
serialize(widget,
|
|
449
|
+
// serialize with enforced HTML entity encoding (off by default)
|
|
450
|
+
console.log(serialize(widget, { escape: true }));
|
|
443
451
|
```
|
|
444
452
|
|
|
453
|
+
(Re)formatted output (generated HTML will always be dense, without intermittent
|
|
454
|
+
white space):
|
|
455
|
+
|
|
445
456
|
```html
|
|
446
457
|
<div class="widget">
|
|
447
458
|
<h1>Glossary</h1>
|
|
@@ -460,7 +471,9 @@ serialize(widget, null, true);
|
|
|
460
471
|
|
|
461
472
|
### Stateful component
|
|
462
473
|
|
|
463
|
-
```
|
|
474
|
+
```js tangle:export/readme-toc.js
|
|
475
|
+
import { serialize } from "@thi.ng/hiccup";
|
|
476
|
+
|
|
464
477
|
// stateful component to create hierarchically
|
|
465
478
|
// indexed & referencable section headlines:
|
|
466
479
|
// e.g. "sec-1.1.2.3"
|
|
@@ -490,12 +503,16 @@ const TOC = [
|
|
|
490
503
|
// create new indexer instance
|
|
491
504
|
const section = indexer();
|
|
492
505
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
])
|
|
506
|
+
console.log(
|
|
507
|
+
serialize([
|
|
508
|
+
"div.toc",
|
|
509
|
+
TOC.map(([level, title]) => [section, level, title])
|
|
510
|
+
])
|
|
511
|
+
);
|
|
497
512
|
```
|
|
498
513
|
|
|
514
|
+
Re-formatted HTML output:
|
|
515
|
+
|
|
499
516
|
```html
|
|
500
517
|
<div class="toc">
|
|
501
518
|
<a name="sec-1"></a><h1>Document title</h1>
|
|
@@ -519,7 +536,7 @@ hiccup too supports such components since version 2.0.0. However, for
|
|
|
519
536
|
static serialization only the `render` method is of interest and others
|
|
520
537
|
are ignored.
|
|
521
538
|
|
|
522
|
-
```
|
|
539
|
+
```js
|
|
523
540
|
const component = {
|
|
524
541
|
render: (ctx, title, ...body) => ["section", ["h1", title], ...body]
|
|
525
542
|
};
|
|
@@ -536,8 +553,8 @@ behavior of individual elements / tree branches:
|
|
|
536
553
|
[@thi.ng/hdom](https://github.com/thi-ng/umbrella/tree/develop/packages/hdom))
|
|
537
554
|
- **`__serialize`** - if false, skips serialization (hiccup only)
|
|
538
555
|
|
|
539
|
-
```
|
|
540
|
-
serialize(["div.container", ["div", {__skip: true}, "ignore me"]]);
|
|
556
|
+
```js
|
|
557
|
+
serialize(["div.container", ["div", { __skip: true }, "ignore me"]]);
|
|
541
558
|
// <div class="container"></div>
|
|
542
559
|
```
|
|
543
560
|
|
|
@@ -625,7 +642,7 @@ this are event attributes, i.e. attribute names starting with "on".
|
|
|
625
642
|
The `style` attribute can ONLY be defined as string or object.
|
|
626
643
|
|
|
627
644
|
```ts
|
|
628
|
-
["div", {style: {color: "red", background: "#000"}}]
|
|
645
|
+
["div", { style: { color: "red", background: "#000" } }]
|
|
629
646
|
// <div style="color:red;background:#000;"></div>
|
|
630
647
|
```
|
|
631
648
|
|
|
@@ -645,7 +662,7 @@ new tree (or undefined).
|
|
|
645
662
|
```ts
|
|
646
663
|
const foo = (ctx, a, b) => ["div#" + a, ctx.foo, b];
|
|
647
664
|
|
|
648
|
-
serialize([foo, "id", "body"], { foo: { class: "black" }})
|
|
665
|
+
serialize([foo, "id", "body"], { foo: { class: "black" } })
|
|
649
666
|
// <div id="id" class="black">body</div>
|
|
650
667
|
```
|
|
651
668
|
|
|
@@ -657,15 +674,6 @@ implementation).
|
|
|
657
674
|
Please also see list of supported [behavior control
|
|
658
675
|
attributes](#behavior-control-attributes).
|
|
659
676
|
|
|
660
|
-
### escape()
|
|
661
|
-
|
|
662
|
-
Signature: `escape(str: string): string`
|
|
663
|
-
|
|
664
|
-
Helper function. Applies HTML entity replacement on given string. If
|
|
665
|
-
`serialize()` is called with `true` as 2nd argument, entity encoding is
|
|
666
|
-
done automatically ([list of entities
|
|
667
|
-
considered](https://github.com/thi-ng/umbrella/blob/develop/packages/hiccup/src/api.ts#L11)).
|
|
668
|
-
|
|
669
677
|
## Authors
|
|
670
678
|
|
|
671
679
|
- [Karsten Schmidt](https://thi.ng)
|
package/api.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export declare const RE_TAG: RegExp;
|
|
|
8
8
|
export declare const COMMENT = "__COMMENT__";
|
|
9
9
|
/** @internal */
|
|
10
10
|
export declare const CDATA = "!CDATA";
|
|
11
|
+
/** @internal */
|
|
12
|
+
export declare const DOCTYPE = "!DOCTYPE";
|
|
11
13
|
/**
|
|
12
14
|
* XML processing instruction in hiccup format.
|
|
13
15
|
*
|
package/api.js
CHANGED
|
@@ -12,6 +12,8 @@ export const RE_TAG = /^([^\s\.#]+)(?:#([^\s\.#]+))?(?:\.([^\s#]+))?$/;
|
|
|
12
12
|
export const COMMENT = "__COMMENT__";
|
|
13
13
|
/** @internal */
|
|
14
14
|
export const CDATA = "!CDATA";
|
|
15
|
+
/** @internal */
|
|
16
|
+
export const DOCTYPE = "!DOCTYPE";
|
|
15
17
|
/**
|
|
16
18
|
* XML processing instruction in hiccup format.
|
|
17
19
|
*
|
|
@@ -22,7 +24,7 @@ export const XML_PROC = ["?xml", { version: "1.0", encoding: "UTF-8" }];
|
|
|
22
24
|
/**
|
|
23
25
|
* `<!DOCTYPE html>` in hiccup format
|
|
24
26
|
*/
|
|
25
|
-
export const DOCTYPE_HTML = [
|
|
27
|
+
export const DOCTYPE_HTML = [DOCTYPE, "html"];
|
|
26
28
|
/** @internal */
|
|
27
29
|
export const NO_SPANS = {
|
|
28
30
|
button: 1,
|
package/css.js
CHANGED
|
@@ -5,8 +5,10 @@ export const css = (rules) => {
|
|
|
5
5
|
let v;
|
|
6
6
|
for (let r in rules) {
|
|
7
7
|
v = deref(rules[r]);
|
|
8
|
-
isFunction(v)
|
|
9
|
-
|
|
8
|
+
if (isFunction(v))
|
|
9
|
+
v = v(rules);
|
|
10
|
+
if (v != null)
|
|
11
|
+
css += `${r}:${v};`;
|
|
10
12
|
}
|
|
11
13
|
return css;
|
|
12
14
|
};
|
package/deref.js
CHANGED
|
@@ -16,7 +16,8 @@ export const derefContext = (ctx, keys) => {
|
|
|
16
16
|
const res = { ...ctx };
|
|
17
17
|
for (let k of keys) {
|
|
18
18
|
const v = res[k];
|
|
19
|
-
implementsFunction(v, "deref")
|
|
19
|
+
if (implementsFunction(v, "deref"))
|
|
20
|
+
res[k] = v.deref();
|
|
20
21
|
}
|
|
21
22
|
return res;
|
|
22
23
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/hiccup",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "HTML/SVG/XML serialization of nested data structures, iterables & closures",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@thi.ng/api": "^8.9.5",
|
|
38
38
|
"@thi.ng/checks": "^3.4.5",
|
|
39
39
|
"@thi.ng/errors": "^2.3.5",
|
|
40
|
-
"@thi.ng/strings": "^3.
|
|
40
|
+
"@thi.ng/strings": "^3.6.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@microsoft/api-extractor": "^7.36.4",
|
|
@@ -127,5 +127,5 @@
|
|
|
127
127
|
],
|
|
128
128
|
"year": 2016
|
|
129
129
|
},
|
|
130
|
-
"gitHead": "
|
|
130
|
+
"gitHead": "c22e8996cee284ebe8ea88582beb1ab5fc6ee503\n"
|
|
131
131
|
}
|
package/serialize.d.ts
CHANGED
|
@@ -1,3 +1,42 @@
|
|
|
1
|
+
import type { FnU } from "@thi.ng/api";
|
|
2
|
+
/**
|
|
3
|
+
* Options to customize the behavior of {@link serialize}.
|
|
4
|
+
*/
|
|
5
|
+
export interface SerializeOpts {
|
|
6
|
+
/**
|
|
7
|
+
* Arbitrary user context object
|
|
8
|
+
*/
|
|
9
|
+
ctx?: any;
|
|
10
|
+
/**
|
|
11
|
+
* If true, auto-escape entities via {@link SerializeOpts.escapeFn}.
|
|
12
|
+
*
|
|
13
|
+
* @defaultValue false
|
|
14
|
+
*/
|
|
15
|
+
escape: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Only used if {@link SerializeOpts.escape} is enabled. Function to escape
|
|
18
|
+
* entities. By default uses
|
|
19
|
+
* [`escapeEntitiesNum()`](https://docs.thi.ng/umbrella/strings/functions/escapeEntitiesNum.html).
|
|
20
|
+
*/
|
|
21
|
+
escapeFn: FnU<string>;
|
|
22
|
+
/**
|
|
23
|
+
* If true (default: false), all text content will be wrapped in `<span>`
|
|
24
|
+
* elements (to ensure DOM compatibility with hdom). The only elements for
|
|
25
|
+
* spans are never created are listed in {@link NO_SPANS}.
|
|
26
|
+
*
|
|
27
|
+
* @defaultValue false
|
|
28
|
+
*/
|
|
29
|
+
span: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* If true (default: false), all elements will have an autogenerated `key`
|
|
32
|
+
* attribute injected. If {@link SerializeOpts.span} is enabled, `keys` will
|
|
33
|
+
* be enabled by default too (since in this case we assume the output is
|
|
34
|
+
* meant to be compatible with [`thi.ng/hdom`](https://thi.ng/hdom)).
|
|
35
|
+
*
|
|
36
|
+
* @defaultValue false
|
|
37
|
+
*/
|
|
38
|
+
keys: boolean;
|
|
39
|
+
}
|
|
1
40
|
/**
|
|
2
41
|
* Recursively normalizes and serializes given tree as HTML/SVG/XML string.
|
|
3
42
|
* Expands any embedded component functions with their results.
|
|
@@ -15,7 +54,7 @@
|
|
|
15
54
|
* iterable
|
|
16
55
|
* ```
|
|
17
56
|
*
|
|
18
|
-
* Tags can be defined in "
|
|
57
|
+
* Tags can be defined in "Emmet" convention, e.g.
|
|
19
58
|
*
|
|
20
59
|
* ```js
|
|
21
60
|
* ["div#foo.bar.baz", "hi"] // <div id="foo" class="bar baz">hi</div>
|
|
@@ -37,7 +76,7 @@
|
|
|
37
76
|
* The `style` attribute can ONLY be defined as string or object.
|
|
38
77
|
*
|
|
39
78
|
* ```js
|
|
40
|
-
* ["div", {style: {color: "red", background: "#000"}}]
|
|
79
|
+
* ["div", { style: { color: "red", background: "#000" } }]
|
|
41
80
|
* // <div style="color:red;background:#000;"></div>
|
|
42
81
|
* ```
|
|
43
82
|
*
|
|
@@ -54,7 +93,7 @@
|
|
|
54
93
|
* function is called. The return value the function MUST be a valid new tree
|
|
55
94
|
* (or `undefined`).
|
|
56
95
|
*
|
|
57
|
-
* If the `ctx`
|
|
96
|
+
* If the `ctx` option is given it'll be passed to each embedded component fns.
|
|
58
97
|
* Optionally call {@link derefContext} prior to {@link serialize} to auto-deref
|
|
59
98
|
* context keys with values implementing the
|
|
60
99
|
* [`IDeref`](https://docs.thi.ng/umbrella/api/interfaces/IDeref.html)
|
|
@@ -63,7 +102,7 @@
|
|
|
63
102
|
* ```js
|
|
64
103
|
* const foo = (ctx, a, b) => ["div#" + a, ctx.foo, b];
|
|
65
104
|
*
|
|
66
|
-
* serialize([foo, "id", "body"], { foo: { class: "black" } })
|
|
105
|
+
* serialize([foo, "id", "body"], { ctx: { foo: { class: "black" } } })
|
|
67
106
|
* // <div id="id" class="black">body</div>
|
|
68
107
|
* ```
|
|
69
108
|
*
|
|
@@ -72,33 +111,25 @@
|
|
|
72
111
|
* numbers, iterables or any type with a suitable `.toString()`, `.toHiccup()`
|
|
73
112
|
* or `.deref()` implementation).
|
|
74
113
|
*
|
|
75
|
-
* If the optional `span` flag is true (default: false), all text content will
|
|
76
|
-
* be wrapped in <span> elements (this is to ensure DOM compatibility with
|
|
77
|
-
* hdom). The only elements for spans are never created are listed in `NO_SPANS`
|
|
78
|
-
* in `api.ts`.
|
|
79
|
-
*
|
|
80
|
-
* If the optional `keys` flag is true (default: false), all elements will have
|
|
81
|
-
* an autogenerated `key` attribute injected. If `span` is enabled, `keys` will
|
|
82
|
-
* be enabled by default too (since in this case we assume the output is meant
|
|
83
|
-
* to be compatible with [`thi.ng/hdom`](https://thi.ng/hdom)).
|
|
84
|
-
*
|
|
85
114
|
* hiccup & hdom control attributes (i.e. attrib names prefixed with `__`) will
|
|
86
115
|
* be omitted from the output. The only control attrib supported by this package
|
|
87
|
-
* is `__serialize`. If set to `false`, the entire tree branch
|
|
88
|
-
* from the output.
|
|
116
|
+
* is `__serialize`. If set to `false`, the entire tree branch below (and
|
|
117
|
+
* including) the element with that attrib will be excluded from the output.
|
|
118
|
+
*
|
|
119
|
+
* **See {@link SerializeOpts} for further available options.**
|
|
89
120
|
*
|
|
90
121
|
* Single or multiline comments can be included using the special `COMMENT` tag
|
|
91
|
-
* (`__COMMENT__`) (always WITHOUT attributes!).
|
|
122
|
+
* (`"__COMMENT__"`) (always WITHOUT attributes!).
|
|
92
123
|
*
|
|
93
|
-
* ```
|
|
124
|
+
* ```js
|
|
94
125
|
* [COMMENT, "Hello world"]
|
|
95
126
|
* // <!-- Hello world -->
|
|
96
127
|
*
|
|
97
128
|
* [COMMENT, "Hello", "world"]
|
|
98
|
-
* <!--
|
|
99
|
-
* Hello
|
|
100
|
-
* world
|
|
101
|
-
* -->
|
|
129
|
+
* // <!--
|
|
130
|
+
* // Hello
|
|
131
|
+
* // world
|
|
132
|
+
* // -->
|
|
102
133
|
* ```
|
|
103
134
|
*
|
|
104
135
|
* Currently, the only processing / DTD instructions supported are:
|
|
@@ -112,19 +143,16 @@
|
|
|
112
143
|
* These are used as follows (attribs are only allowed for `?xml`, all others
|
|
113
144
|
* only accept a body string which is taken as is):
|
|
114
145
|
*
|
|
115
|
-
* ```
|
|
116
|
-
* ["?xml", { version: "1.0", standalone: "yes" }]
|
|
146
|
+
* ```js
|
|
147
|
+
* serialize(["?xml", { version: "1.0", standalone: "yes" }])
|
|
117
148
|
* // <?xml version="1.0" standalone="yes"?>
|
|
118
149
|
*
|
|
119
|
-
* ["!DOCTYPE", "html"]
|
|
150
|
+
* ["!DOCTYPE", "html"] // (also available as DOCTYPE_HTML)
|
|
120
151
|
* // <!DOCTYPE html>
|
|
121
152
|
* ```
|
|
122
153
|
*
|
|
123
154
|
* @param tree - hiccup elements / component tree
|
|
124
|
-
* @param
|
|
125
|
-
* @param escape - auto-escape entities
|
|
126
|
-
* @param span - use spans for text content
|
|
127
|
-
* @param keys - attach key attribs
|
|
155
|
+
* @param opts - options
|
|
128
156
|
*/
|
|
129
|
-
export declare const serialize: (tree: any,
|
|
157
|
+
export declare const serialize: (tree: any, opts?: Partial<SerializeOpts>, path?: number[]) => string;
|
|
130
158
|
//# sourceMappingURL=serialize.d.ts.map
|
package/serialize.js
CHANGED
|
@@ -6,7 +6,7 @@ import { isNotStringAndIterable } from "@thi.ng/checks/is-not-string-iterable";
|
|
|
6
6
|
import { isPlainObject } from "@thi.ng/checks/is-plain-object";
|
|
7
7
|
import { isString } from "@thi.ng/checks/is-string";
|
|
8
8
|
import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
|
|
9
|
-
import {
|
|
9
|
+
import { escapeEntitiesNum } from "@thi.ng/strings/entities";
|
|
10
10
|
import { ATTRIB_JOIN_DELIMS, CDATA, COMMENT, NO_CLOSE_EMPTY, NO_SPANS, PROC_TAGS, VOID_TAGS, } from "./api.js";
|
|
11
11
|
import { css } from "./css.js";
|
|
12
12
|
import { normalize } from "./normalize.js";
|
|
@@ -28,7 +28,7 @@ import { formatPrefixes } from "./prefix.js";
|
|
|
28
28
|
* iterable
|
|
29
29
|
* ```
|
|
30
30
|
*
|
|
31
|
-
* Tags can be defined in "
|
|
31
|
+
* Tags can be defined in "Emmet" convention, e.g.
|
|
32
32
|
*
|
|
33
33
|
* ```js
|
|
34
34
|
* ["div#foo.bar.baz", "hi"] // <div id="foo" class="bar baz">hi</div>
|
|
@@ -50,7 +50,7 @@ import { formatPrefixes } from "./prefix.js";
|
|
|
50
50
|
* The `style` attribute can ONLY be defined as string or object.
|
|
51
51
|
*
|
|
52
52
|
* ```js
|
|
53
|
-
* ["div", {style: {color: "red", background: "#000"}}]
|
|
53
|
+
* ["div", { style: { color: "red", background: "#000" } }]
|
|
54
54
|
* // <div style="color:red;background:#000;"></div>
|
|
55
55
|
* ```
|
|
56
56
|
*
|
|
@@ -67,7 +67,7 @@ import { formatPrefixes } from "./prefix.js";
|
|
|
67
67
|
* function is called. The return value the function MUST be a valid new tree
|
|
68
68
|
* (or `undefined`).
|
|
69
69
|
*
|
|
70
|
-
* If the `ctx`
|
|
70
|
+
* If the `ctx` option is given it'll be passed to each embedded component fns.
|
|
71
71
|
* Optionally call {@link derefContext} prior to {@link serialize} to auto-deref
|
|
72
72
|
* context keys with values implementing the
|
|
73
73
|
* [`IDeref`](https://docs.thi.ng/umbrella/api/interfaces/IDeref.html)
|
|
@@ -76,7 +76,7 @@ import { formatPrefixes } from "./prefix.js";
|
|
|
76
76
|
* ```js
|
|
77
77
|
* const foo = (ctx, a, b) => ["div#" + a, ctx.foo, b];
|
|
78
78
|
*
|
|
79
|
-
* serialize([foo, "id", "body"], { foo: { class: "black" } })
|
|
79
|
+
* serialize([foo, "id", "body"], { ctx: { foo: { class: "black" } } })
|
|
80
80
|
* // <div id="id" class="black">body</div>
|
|
81
81
|
* ```
|
|
82
82
|
*
|
|
@@ -85,33 +85,25 @@ import { formatPrefixes } from "./prefix.js";
|
|
|
85
85
|
* numbers, iterables or any type with a suitable `.toString()`, `.toHiccup()`
|
|
86
86
|
* or `.deref()` implementation).
|
|
87
87
|
*
|
|
88
|
-
* If the optional `span` flag is true (default: false), all text content will
|
|
89
|
-
* be wrapped in <span> elements (this is to ensure DOM compatibility with
|
|
90
|
-
* hdom). The only elements for spans are never created are listed in `NO_SPANS`
|
|
91
|
-
* in `api.ts`.
|
|
92
|
-
*
|
|
93
|
-
* If the optional `keys` flag is true (default: false), all elements will have
|
|
94
|
-
* an autogenerated `key` attribute injected. If `span` is enabled, `keys` will
|
|
95
|
-
* be enabled by default too (since in this case we assume the output is meant
|
|
96
|
-
* to be compatible with [`thi.ng/hdom`](https://thi.ng/hdom)).
|
|
97
|
-
*
|
|
98
88
|
* hiccup & hdom control attributes (i.e. attrib names prefixed with `__`) will
|
|
99
89
|
* be omitted from the output. The only control attrib supported by this package
|
|
100
|
-
* is `__serialize`. If set to `false`, the entire tree branch
|
|
101
|
-
* from the output.
|
|
90
|
+
* is `__serialize`. If set to `false`, the entire tree branch below (and
|
|
91
|
+
* including) the element with that attrib will be excluded from the output.
|
|
92
|
+
*
|
|
93
|
+
* **See {@link SerializeOpts} for further available options.**
|
|
102
94
|
*
|
|
103
95
|
* Single or multiline comments can be included using the special `COMMENT` tag
|
|
104
|
-
* (`__COMMENT__`) (always WITHOUT attributes!).
|
|
96
|
+
* (`"__COMMENT__"`) (always WITHOUT attributes!).
|
|
105
97
|
*
|
|
106
|
-
* ```
|
|
98
|
+
* ```js
|
|
107
99
|
* [COMMENT, "Hello world"]
|
|
108
100
|
* // <!-- Hello world -->
|
|
109
101
|
*
|
|
110
102
|
* [COMMENT, "Hello", "world"]
|
|
111
|
-
* <!--
|
|
112
|
-
* Hello
|
|
113
|
-
* world
|
|
114
|
-
* -->
|
|
103
|
+
* // <!--
|
|
104
|
+
* // Hello
|
|
105
|
+
* // world
|
|
106
|
+
* // -->
|
|
115
107
|
* ```
|
|
116
108
|
*
|
|
117
109
|
* Currently, the only processing / DTD instructions supported are:
|
|
@@ -125,79 +117,87 @@ import { formatPrefixes } from "./prefix.js";
|
|
|
125
117
|
* These are used as follows (attribs are only allowed for `?xml`, all others
|
|
126
118
|
* only accept a body string which is taken as is):
|
|
127
119
|
*
|
|
128
|
-
* ```
|
|
129
|
-
* ["?xml", { version: "1.0", standalone: "yes" }]
|
|
120
|
+
* ```js
|
|
121
|
+
* serialize(["?xml", { version: "1.0", standalone: "yes" }])
|
|
130
122
|
* // <?xml version="1.0" standalone="yes"?>
|
|
131
123
|
*
|
|
132
|
-
* ["!DOCTYPE", "html"]
|
|
124
|
+
* ["!DOCTYPE", "html"] // (also available as DOCTYPE_HTML)
|
|
133
125
|
* // <!DOCTYPE html>
|
|
134
126
|
* ```
|
|
135
127
|
*
|
|
136
128
|
* @param tree - hiccup elements / component tree
|
|
137
|
-
* @param
|
|
138
|
-
* @param escape - auto-escape entities
|
|
139
|
-
* @param span - use spans for text content
|
|
140
|
-
* @param keys - attach key attribs
|
|
129
|
+
* @param opts - options
|
|
141
130
|
*/
|
|
142
|
-
export const serialize = (tree,
|
|
143
|
-
const
|
|
131
|
+
export const serialize = (tree, opts, path = [0]) => {
|
|
132
|
+
const $opts = {
|
|
133
|
+
escape: false,
|
|
134
|
+
escapeFn: escapeEntitiesNum,
|
|
135
|
+
span: false,
|
|
136
|
+
keys: false,
|
|
137
|
+
...opts,
|
|
138
|
+
};
|
|
139
|
+
if (opts?.keys == null && $opts.span)
|
|
140
|
+
$opts.keys = true;
|
|
141
|
+
return _serialize(tree, $opts, path);
|
|
142
|
+
};
|
|
143
|
+
const _serialize = (tree, opts, path) => tree == null
|
|
144
144
|
? ""
|
|
145
145
|
: Array.isArray(tree)
|
|
146
|
-
? serializeElement(tree,
|
|
146
|
+
? serializeElement(tree, opts, path)
|
|
147
147
|
: isFunction(tree)
|
|
148
|
-
? _serialize(tree(ctx),
|
|
148
|
+
? _serialize(tree(opts.ctx), opts, path)
|
|
149
149
|
: implementsFunction(tree, "toHiccup")
|
|
150
|
-
? _serialize(tree.toHiccup(ctx),
|
|
150
|
+
? _serialize(tree.toHiccup(opts.ctx), opts, path)
|
|
151
151
|
: isDeref(tree)
|
|
152
|
-
? _serialize(tree.deref(),
|
|
152
|
+
? _serialize(tree.deref(), opts, path)
|
|
153
153
|
: isNotStringAndIterable(tree)
|
|
154
|
-
? serializeIter(tree,
|
|
155
|
-
: ((tree =
|
|
156
|
-
? `<span${keys ? ` key="${path.join("-")}"` : ""}>${tree}</span>`
|
|
154
|
+
? serializeIter(tree, opts, path)
|
|
155
|
+
: ((tree = __escape(String(tree), opts)), opts.span)
|
|
156
|
+
? `<span${opts.keys ? ` key="${path.join("-")}"` : ""}>${tree}</span>`
|
|
157
157
|
: tree;
|
|
158
|
-
const serializeElement = (tree,
|
|
158
|
+
const serializeElement = (tree, opts, path) => {
|
|
159
159
|
let tag = tree[0];
|
|
160
160
|
return !tree.length
|
|
161
161
|
? ""
|
|
162
162
|
: isFunction(tag)
|
|
163
|
-
? _serialize(tag.apply(null, [ctx, ...tree.slice(1)]),
|
|
163
|
+
? _serialize(tag.apply(null, [opts.ctx, ...tree.slice(1)]), opts, path)
|
|
164
164
|
: implementsFunction(tag, "render")
|
|
165
|
-
? _serialize(tag.render.apply(null, [ctx, ...tree.slice(1)]),
|
|
165
|
+
? _serialize(tag.render.apply(null, [opts.ctx, ...tree.slice(1)]), opts, path)
|
|
166
166
|
: tag === COMMENT
|
|
167
167
|
? serializeComment(tree)
|
|
168
168
|
: tag == CDATA
|
|
169
169
|
? serializeCData(tree)
|
|
170
170
|
: isString(tag)
|
|
171
|
-
? serializeTag(tree,
|
|
171
|
+
? serializeTag(tree, opts, path)
|
|
172
172
|
: isNotStringAndIterable(tree)
|
|
173
|
-
? serializeIter(tree,
|
|
173
|
+
? serializeIter(tree, opts, path)
|
|
174
174
|
: illegalArgs(`invalid tree node: ${tree}`);
|
|
175
175
|
};
|
|
176
|
-
const serializeTag = (tree,
|
|
176
|
+
const serializeTag = (tree, opts, path) => {
|
|
177
177
|
tree = normalize(tree);
|
|
178
178
|
const attribs = tree[1];
|
|
179
179
|
if (attribs.__skip || attribs.__serialize === false)
|
|
180
180
|
return "";
|
|
181
|
-
keys && attribs.key === undefined && (attribs.key = path.join("-"));
|
|
181
|
+
opts.keys && attribs.key === undefined && (attribs.key = path.join("-"));
|
|
182
182
|
const tag = tree[0];
|
|
183
183
|
const body = tree[2]
|
|
184
|
-
? serializeBody(tag, tree[2],
|
|
184
|
+
? serializeBody(tag, tree[2], opts, path)
|
|
185
185
|
: !VOID_TAGS[tag] && !NO_CLOSE_EMPTY[tag]
|
|
186
186
|
? `></${tag}>`
|
|
187
187
|
: PROC_TAGS[tag] || "/>";
|
|
188
|
-
return `<${tag}${serializeAttribs(attribs,
|
|
188
|
+
return `<${tag}${serializeAttribs(attribs, opts)}${body}`;
|
|
189
189
|
};
|
|
190
|
-
const serializeAttribs = (attribs,
|
|
190
|
+
const serializeAttribs = (attribs, opts) => {
|
|
191
191
|
let res = "";
|
|
192
192
|
for (let a in attribs) {
|
|
193
193
|
if (a.startsWith("__"))
|
|
194
194
|
continue;
|
|
195
|
-
const v = serializeAttrib(attribs, a, deref(attribs[a]),
|
|
195
|
+
const v = serializeAttrib(attribs, a, deref(attribs[a]), opts);
|
|
196
196
|
v != null && (res += v);
|
|
197
197
|
}
|
|
198
198
|
return res;
|
|
199
199
|
};
|
|
200
|
-
const serializeAttrib = (attribs, a, v,
|
|
200
|
+
const serializeAttrib = (attribs, a, v, opts) => {
|
|
201
201
|
return v == null
|
|
202
202
|
? null
|
|
203
203
|
: isFunction(v) && (/^on\w+/.test(a) || (v = v(attribs)) == null)
|
|
@@ -207,10 +207,10 @@ const serializeAttrib = (attribs, a, v, esc) => {
|
|
|
207
207
|
: v === false
|
|
208
208
|
? null
|
|
209
209
|
: a === "data"
|
|
210
|
-
? serializeDataAttribs(v,
|
|
211
|
-
: attribPair(a, v,
|
|
210
|
+
? serializeDataAttribs(v, opts)
|
|
211
|
+
: attribPair(a, v, opts);
|
|
212
212
|
};
|
|
213
|
-
const attribPair = (a, v,
|
|
213
|
+
const attribPair = (a, v, opts) => {
|
|
214
214
|
v =
|
|
215
215
|
a === "style" && isPlainObject(v)
|
|
216
216
|
? css(v)
|
|
@@ -219,26 +219,27 @@ const attribPair = (a, v, esc) => {
|
|
|
219
219
|
: isArray(v)
|
|
220
220
|
? v.join(ATTRIB_JOIN_DELIMS[a] || " ")
|
|
221
221
|
: v.toString();
|
|
222
|
-
return v.length ? ` ${a}="${
|
|
222
|
+
return v.length ? ` ${a}="${__escape(v, opts)}"` : null;
|
|
223
223
|
};
|
|
224
|
-
const serializeDataAttribs = (data,
|
|
224
|
+
const serializeDataAttribs = (data, opts) => {
|
|
225
225
|
let res = "";
|
|
226
226
|
for (let id in data) {
|
|
227
227
|
let v = deref(data[id]);
|
|
228
228
|
isFunction(v) && (v = v(data));
|
|
229
|
-
v != null && (res += ` data-${id}="${
|
|
229
|
+
v != null && (res += ` data-${id}="${__escape(v, opts)}"`);
|
|
230
230
|
}
|
|
231
231
|
return res;
|
|
232
232
|
};
|
|
233
|
-
const serializeBody = (tag, body,
|
|
233
|
+
const serializeBody = (tag, body, opts, path) => {
|
|
234
234
|
if (VOID_TAGS[tag]) {
|
|
235
235
|
illegalArgs(`No body allowed in tag: ${tag}`);
|
|
236
236
|
}
|
|
237
237
|
const proc = PROC_TAGS[tag];
|
|
238
238
|
let res = proc ? " " : ">";
|
|
239
|
-
|
|
239
|
+
if (opts.span && !proc && !NO_SPANS[tag])
|
|
240
|
+
opts = { ...opts, span: true };
|
|
240
241
|
for (let i = 0, n = body.length; i < n; i++) {
|
|
241
|
-
res += _serialize(body[i],
|
|
242
|
+
res += _serialize(body[i], opts, [...path, i]);
|
|
242
243
|
}
|
|
243
244
|
return res + (proc || `</${tag}>`);
|
|
244
245
|
};
|
|
@@ -249,12 +250,13 @@ const serializeComment = (tree) => tree.length > 2
|
|
|
249
250
|
.join("\n")}\n-->\n`
|
|
250
251
|
: `\n<!-- ${tree[1]} -->\n`;
|
|
251
252
|
const serializeCData = (tree) => `<![CDATA[\n${tree.slice(1).join("\n")}\n]]>`;
|
|
252
|
-
const serializeIter = (iter,
|
|
253
|
+
const serializeIter = (iter, opts, path) => {
|
|
253
254
|
const res = [];
|
|
254
255
|
const p = path.slice(0, path.length - 1);
|
|
255
256
|
let k = 0;
|
|
256
257
|
for (let i of iter) {
|
|
257
|
-
res.push(_serialize(i,
|
|
258
|
+
res.push(_serialize(i, opts, [...p, k++]));
|
|
258
259
|
}
|
|
259
260
|
return res.join("");
|
|
260
261
|
};
|
|
262
|
+
const __escape = (x, opts) => opts.escape ? opts.escapeFn(x) : x;
|