@thi.ng/hdom 9.3.30 → 9.4.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 +7 -1
- package/README.md +1 -1
- package/api.js +0 -1
- package/default.js +39 -28
- package/diff.js +220 -257
- package/dom.js +216 -255
- package/logger.js +6 -2
- package/normalize.js +110 -131
- package/package.json +15 -13
- package/render-once.js +11 -20
- package/resolve.js +4 -1
- package/start.js +25 -65
package/normalize.js
CHANGED
|
@@ -7,143 +7,122 @@ import { mergeEmmetAttribs } from "@thi.ng/hiccup/attribs";
|
|
|
7
7
|
const isArray = isa;
|
|
8
8
|
const isNotStringAndIterable = isi;
|
|
9
9
|
const isPlainObject = iso;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
* ```
|
|
30
|
-
*
|
|
31
|
-
* Elements with `__skip` attrib enabled and no children, will have an
|
|
32
|
-
* empty text child element injected.
|
|
33
|
-
*
|
|
34
|
-
* @param spec - single hdom component
|
|
35
|
-
* @param keys -
|
|
36
|
-
*
|
|
37
|
-
* @internal
|
|
38
|
-
*/
|
|
39
|
-
export const normalizeElement = (spec, keys) => {
|
|
40
|
-
let tag = spec[0];
|
|
41
|
-
let hasAttribs = isPlainObject(spec[1]);
|
|
42
|
-
let match;
|
|
43
|
-
let name;
|
|
44
|
-
let attribs;
|
|
45
|
-
if (typeof tag !== "string" || !(match = RE_TAG.exec(tag))) {
|
|
46
|
-
illegalArgs(`${tag} is not a valid tag name`);
|
|
47
|
-
}
|
|
48
|
-
name = match[1];
|
|
49
|
-
// return orig if already normalized and satisfies key requirement
|
|
50
|
-
if (tag === name && hasAttribs && (!keys || spec[1].key)) {
|
|
51
|
-
return spec;
|
|
52
|
-
}
|
|
53
|
-
attribs = mergeEmmetAttribs(hasAttribs ? { ...spec[1] } : {}, match[2], match[3]);
|
|
54
|
-
return attribs.__skip && spec.length < 3
|
|
55
|
-
? [name, attribs]
|
|
56
|
-
: [name, attribs, ...spec.slice(hasAttribs ? 2 : 1)];
|
|
10
|
+
const normalizeElement = (spec, keys) => {
|
|
11
|
+
let tag = spec[0];
|
|
12
|
+
let hasAttribs = isPlainObject(spec[1]);
|
|
13
|
+
let match;
|
|
14
|
+
let name;
|
|
15
|
+
let attribs;
|
|
16
|
+
if (typeof tag !== "string" || !(match = RE_TAG.exec(tag))) {
|
|
17
|
+
illegalArgs(`${tag} is not a valid tag name`);
|
|
18
|
+
}
|
|
19
|
+
name = match[1];
|
|
20
|
+
if (tag === name && hasAttribs && (!keys || spec[1].key)) {
|
|
21
|
+
return spec;
|
|
22
|
+
}
|
|
23
|
+
attribs = mergeEmmetAttribs(
|
|
24
|
+
hasAttribs ? { ...spec[1] } : {},
|
|
25
|
+
match[2],
|
|
26
|
+
match[3]
|
|
27
|
+
);
|
|
28
|
+
return attribs.__skip && spec.length < 3 ? [name, attribs] : [name, attribs, ...spec.slice(hasAttribs ? 2 : 1)];
|
|
57
29
|
};
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
30
|
+
const normalizeTree = (opts, tree) => _normalizeTree(
|
|
31
|
+
tree,
|
|
32
|
+
opts,
|
|
33
|
+
opts.ctx,
|
|
34
|
+
[0],
|
|
35
|
+
opts.keys !== false,
|
|
36
|
+
opts.span !== false
|
|
37
|
+
);
|
|
65
38
|
const _normalizeTree = (tree, opts, ctx, path, keys, span) => {
|
|
66
|
-
|
|
67
|
-
|
|
39
|
+
if (tree == null) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (isArray(tree)) {
|
|
43
|
+
if (tree.length === 0) {
|
|
44
|
+
return;
|
|
68
45
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
return norm.length > 2
|
|
108
|
-
? normalizeChildren(norm, nattribs, opts, ctx, path, keys, span)
|
|
109
|
-
: norm;
|
|
46
|
+
let norm, nattribs = tree[1], impl;
|
|
47
|
+
if (nattribs && (impl = nattribs.__impl) && (impl = impl.normalizeTree)) {
|
|
48
|
+
return impl(opts, tree);
|
|
49
|
+
}
|
|
50
|
+
const tag = tree[0];
|
|
51
|
+
if (typeof tag === "function") {
|
|
52
|
+
return _normalizeTree(
|
|
53
|
+
tag.apply(null, [ctx, ...tree.slice(1)]),
|
|
54
|
+
opts,
|
|
55
|
+
ctx,
|
|
56
|
+
path,
|
|
57
|
+
keys,
|
|
58
|
+
span
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
if (typeof tag.render === "function") {
|
|
62
|
+
const args = [ctx, ...tree.slice(1)];
|
|
63
|
+
norm = _normalizeTree(
|
|
64
|
+
tag.render.apply(tag, args),
|
|
65
|
+
opts,
|
|
66
|
+
ctx,
|
|
67
|
+
path,
|
|
68
|
+
keys,
|
|
69
|
+
span
|
|
70
|
+
);
|
|
71
|
+
if (isArray(norm)) {
|
|
72
|
+
norm.__this = tag;
|
|
73
|
+
norm.__init = tag.init;
|
|
74
|
+
norm.__release = tag.release;
|
|
75
|
+
norm.__args = args;
|
|
76
|
+
}
|
|
77
|
+
return norm;
|
|
78
|
+
}
|
|
79
|
+
norm = normalizeElement(tree, keys);
|
|
80
|
+
nattribs = norm[1];
|
|
81
|
+
if (nattribs.__normalize === false) {
|
|
82
|
+
return norm;
|
|
110
83
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
: span
|
|
118
|
-
? ["span", keys ? { key: path.join("-") } : {}, tree.toString()]
|
|
119
|
-
: tree.toString();
|
|
84
|
+
if (keys && nattribs.key === void 0) {
|
|
85
|
+
nattribs.key = path.join("-");
|
|
86
|
+
}
|
|
87
|
+
return norm.length > 2 ? normalizeChildren(norm, nattribs, opts, ctx, path, keys, span) : norm;
|
|
88
|
+
}
|
|
89
|
+
return typeof tree === "function" ? _normalizeTree(tree(ctx), opts, ctx, path, keys, span) : typeof tree.toHiccup === "function" ? _normalizeTree(tree.toHiccup(opts.ctx), opts, ctx, path, keys, span) : typeof tree.deref === "function" ? _normalizeTree(tree.deref(), opts, ctx, path, keys, span) : span ? ["span", keys ? { key: path.join("-") } : {}, tree.toString()] : tree.toString();
|
|
120
90
|
};
|
|
121
91
|
const normalizeChildren = (norm, nattribs, opts, ctx, path, keys, span) => {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
92
|
+
const tag = norm[0];
|
|
93
|
+
const res = [tag, nattribs];
|
|
94
|
+
span = span && !NO_SPANS[tag];
|
|
95
|
+
for (let i = 2, j = 2, k = 0, n = norm.length; i < n; i++) {
|
|
96
|
+
let el = norm[i];
|
|
97
|
+
if (el != null) {
|
|
98
|
+
const isarray = isArray(el);
|
|
99
|
+
if (isarray && isArray(el[0]) || !isarray && isNotStringAndIterable(el)) {
|
|
100
|
+
for (let c of el) {
|
|
101
|
+
c = _normalizeTree(
|
|
102
|
+
c,
|
|
103
|
+
opts,
|
|
104
|
+
ctx,
|
|
105
|
+
path.concat(k),
|
|
106
|
+
keys,
|
|
107
|
+
span
|
|
108
|
+
);
|
|
109
|
+
if (c !== void 0) {
|
|
110
|
+
res[j++] = c;
|
|
111
|
+
}
|
|
112
|
+
k++;
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
el = _normalizeTree(el, opts, ctx, path.concat(k), keys, span);
|
|
116
|
+
if (el !== void 0) {
|
|
117
|
+
res[j++] = el;
|
|
146
118
|
}
|
|
119
|
+
k++;
|
|
120
|
+
}
|
|
147
121
|
}
|
|
148
|
-
|
|
122
|
+
}
|
|
123
|
+
return res;
|
|
124
|
+
};
|
|
125
|
+
export {
|
|
126
|
+
normalizeElement,
|
|
127
|
+
normalizeTree
|
|
149
128
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/hdom",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.4.0",
|
|
4
4
|
"description": "Lightweight vanilla ES6 UI component trees with customizable branch-local behaviors",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -27,7 +27,9 @@
|
|
|
27
27
|
],
|
|
28
28
|
"license": "Apache-2.0",
|
|
29
29
|
"scripts": {
|
|
30
|
-
"build": "yarn
|
|
30
|
+
"build": "yarn build:esbuild && yarn build:decl",
|
|
31
|
+
"build:decl": "tsc --declaration --emitDeclarationOnly",
|
|
32
|
+
"build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
|
|
31
33
|
"clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
|
|
32
34
|
"doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
|
|
33
35
|
"doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
|
|
@@ -36,19 +38,19 @@
|
|
|
36
38
|
"test": "bun test"
|
|
37
39
|
},
|
|
38
40
|
"dependencies": {
|
|
39
|
-
"@thi.ng/api": "^8.9.
|
|
40
|
-
"@thi.ng/checks": "^3.4.
|
|
41
|
-
"@thi.ng/diff": "^5.1.
|
|
42
|
-
"@thi.ng/equiv": "^2.1.
|
|
43
|
-
"@thi.ng/errors": "^2.4.
|
|
44
|
-
"@thi.ng/hiccup": "^5.
|
|
45
|
-
"@thi.ng/logger": "^2.0.
|
|
46
|
-
"@thi.ng/prefixes": "^2.2.
|
|
41
|
+
"@thi.ng/api": "^8.9.12",
|
|
42
|
+
"@thi.ng/checks": "^3.4.12",
|
|
43
|
+
"@thi.ng/diff": "^5.1.45",
|
|
44
|
+
"@thi.ng/equiv": "^2.1.37",
|
|
45
|
+
"@thi.ng/errors": "^2.4.6",
|
|
46
|
+
"@thi.ng/hiccup": "^5.1.1",
|
|
47
|
+
"@thi.ng/logger": "^2.0.2",
|
|
48
|
+
"@thi.ng/prefixes": "^2.2.8"
|
|
47
49
|
},
|
|
48
50
|
"devDependencies": {
|
|
49
51
|
"@microsoft/api-extractor": "^7.38.3",
|
|
50
|
-
"@thi.ng/atom": "^5.2.
|
|
51
|
-
"
|
|
52
|
+
"@thi.ng/atom": "^5.2.18",
|
|
53
|
+
"esbuild": "^0.19.8",
|
|
52
54
|
"rimraf": "^5.0.5",
|
|
53
55
|
"tools": "^0.0.1",
|
|
54
56
|
"typedoc": "^0.25.4",
|
|
@@ -130,5 +132,5 @@
|
|
|
130
132
|
"status": "completed",
|
|
131
133
|
"year": 2015
|
|
132
134
|
},
|
|
133
|
-
"gitHead": "
|
|
135
|
+
"gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
|
|
134
136
|
}
|
package/render-once.js
CHANGED
|
@@ -1,24 +1,15 @@
|
|
|
1
1
|
import { derefContext } from "@thi.ng/hiccup/deref";
|
|
2
2
|
import { DEFAULT_IMPL } from "./default.js";
|
|
3
3
|
import { resolveRoot } from "./resolve.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
opts = { root: "app", ...opts };
|
|
16
|
-
opts.ctx = derefContext(opts.ctx, opts.autoDerefKeys);
|
|
17
|
-
const root = resolveRoot(opts.root, impl);
|
|
18
|
-
tree = impl.normalizeTree(opts, tree);
|
|
19
|
-
if (!tree)
|
|
20
|
-
return;
|
|
21
|
-
opts.hydrate
|
|
22
|
-
? impl.hydrateTree(opts, root, tree)
|
|
23
|
-
: impl.createTree(opts, root, tree);
|
|
4
|
+
const renderOnce = (tree, opts = {}, impl = DEFAULT_IMPL) => {
|
|
5
|
+
opts = { root: "app", ...opts };
|
|
6
|
+
opts.ctx = derefContext(opts.ctx, opts.autoDerefKeys);
|
|
7
|
+
const root = resolveRoot(opts.root, impl);
|
|
8
|
+
tree = impl.normalizeTree(opts, tree);
|
|
9
|
+
if (!tree)
|
|
10
|
+
return;
|
|
11
|
+
opts.hydrate ? impl.hydrateTree(opts, root, tree) : impl.createTree(opts, root, tree);
|
|
12
|
+
};
|
|
13
|
+
export {
|
|
14
|
+
renderOnce
|
|
24
15
|
};
|
package/resolve.js
CHANGED
package/start.js
CHANGED
|
@@ -1,70 +1,30 @@
|
|
|
1
1
|
import { derefContext } from "@thi.ng/hiccup/deref";
|
|
2
2
|
import { DEFAULT_IMPL } from "./default.js";
|
|
3
3
|
import { resolveRoot } from "./resolve.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* views provided by [`thi.ng/atom`](https://thi.ng/atom).
|
|
20
|
-
*
|
|
21
|
-
* **Selective updates**: No updates will be applied if the given hiccup tree is
|
|
22
|
-
* `undefined` or `null` or a root component function returns no value. This way
|
|
23
|
-
* a given root function can do some state handling of its own and implement
|
|
24
|
-
* fail-fast checks to determine no DOM updates are necessary, save effort
|
|
25
|
-
* re-creating a new hiccup tree and request skipping DOM updates via this
|
|
26
|
-
* function. In this case, the previous DOM tree is kept around until the root
|
|
27
|
-
* function returns a tree again, which then is diffed and applied against the
|
|
28
|
-
* previous tree kept as usual. Any number of frames may be skipped this way.
|
|
29
|
-
*
|
|
30
|
-
* **Important:** Unless the `hydrate` option is enabled, the parent element
|
|
31
|
-
* given is assumed to have NO children at the time when `start()` is called.
|
|
32
|
-
* Since hdom does NOT track the real DOM, the resulting changes will result in
|
|
33
|
-
* potentially undefined behavior if the parent element wasn't empty. Likewise,
|
|
34
|
-
* if `hydrate` is enabled, it is assumed that an equivalent DOM (minus
|
|
35
|
-
* listeners) already exists (i.e. generated via SSR) when `start()` is called.
|
|
36
|
-
* Any other discrepancies between the pre-existing DOM and the hdom trees will
|
|
37
|
-
* cause undefined behavior.
|
|
38
|
-
*
|
|
39
|
-
* Returns a function, which when called, immediately cancels the update loop.
|
|
40
|
-
*
|
|
41
|
-
* @param tree - hiccup DOM tree
|
|
42
|
-
* @param opts - options
|
|
43
|
-
* @param impl - hdom target implementation
|
|
44
|
-
*/
|
|
45
|
-
export const start = (tree, opts = {}, impl = DEFAULT_IMPL) => {
|
|
46
|
-
const _opts = { root: "app", ...opts };
|
|
47
|
-
let prev = [];
|
|
48
|
-
let isActive = true;
|
|
49
|
-
const root = resolveRoot(_opts.root, impl);
|
|
50
|
-
const update = () => {
|
|
51
|
-
if (isActive) {
|
|
52
|
-
_opts.ctx = derefContext(opts.ctx, _opts.autoDerefKeys);
|
|
53
|
-
const curr = impl.normalizeTree(_opts, tree);
|
|
54
|
-
if (curr != null) {
|
|
55
|
-
if (_opts.hydrate) {
|
|
56
|
-
impl.hydrateTree(_opts, root, curr);
|
|
57
|
-
_opts.hydrate = false;
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
impl.diffTree(_opts, root, prev, curr);
|
|
61
|
-
}
|
|
62
|
-
prev = curr;
|
|
63
|
-
}
|
|
64
|
-
// check again in case one of the components called cancel
|
|
65
|
-
isActive && requestAnimationFrame(update);
|
|
4
|
+
const start = (tree, opts = {}, impl = DEFAULT_IMPL) => {
|
|
5
|
+
const _opts = { root: "app", ...opts };
|
|
6
|
+
let prev = [];
|
|
7
|
+
let isActive = true;
|
|
8
|
+
const root = resolveRoot(_opts.root, impl);
|
|
9
|
+
const update = () => {
|
|
10
|
+
if (isActive) {
|
|
11
|
+
_opts.ctx = derefContext(opts.ctx, _opts.autoDerefKeys);
|
|
12
|
+
const curr = impl.normalizeTree(_opts, tree);
|
|
13
|
+
if (curr != null) {
|
|
14
|
+
if (_opts.hydrate) {
|
|
15
|
+
impl.hydrateTree(_opts, root, curr);
|
|
16
|
+
_opts.hydrate = false;
|
|
17
|
+
} else {
|
|
18
|
+
impl.diffTree(_opts, root, prev, curr);
|
|
66
19
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
20
|
+
prev = curr;
|
|
21
|
+
}
|
|
22
|
+
isActive && requestAnimationFrame(update);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
requestAnimationFrame(update);
|
|
26
|
+
return () => isActive = false;
|
|
27
|
+
};
|
|
28
|
+
export {
|
|
29
|
+
start
|
|
70
30
|
};
|