@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/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
- * Expands single hiccup element/component into its canonical form:
12
- *
13
- * ```
14
- * [tagname, {attribs}, ...children]
15
- * ```
16
- *
17
- * Emmet-style ID and class names in the original tagname are moved into
18
- * the attribs object, e.g.:
19
- *
20
- * ```
21
- * ["div#foo.bar.baz"] => ["div", {id: "foo", class: "bar baz"}]
22
- * ```
23
- *
24
- * If both Emmet-style classes AND a `class` attrib exists, the former
25
- * are appended to the latter:
26
- *
27
- * ```
28
- * ["div.bar.baz", {class: "foo"}] => ["div", {class: "foo bar baz"}]
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
- * See {@link HDOMImplementation} interface for further details.
60
- *
61
- * @param opts - hdom config options
62
- * @param tree - component tree
63
- */
64
- export const normalizeTree = (opts, tree) => _normalizeTree(tree, opts, opts.ctx, [0], opts.keys !== false, opts.span !== false);
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
- if (tree == null) {
67
- return;
39
+ if (tree == null) {
40
+ return;
41
+ }
42
+ if (isArray(tree)) {
43
+ if (tree.length === 0) {
44
+ return;
68
45
  }
69
- if (isArray(tree)) {
70
- if (tree.length === 0) {
71
- return;
72
- }
73
- let norm, nattribs = tree[1], impl;
74
- // if available, use branch-local normalize implementation
75
- if (nattribs &&
76
- (impl = nattribs.__impl) &&
77
- (impl = impl.normalizeTree)) {
78
- return impl(opts, tree);
79
- }
80
- const tag = tree[0];
81
- // use result of function call
82
- // pass ctx as first arg and remaining array elements as rest args
83
- if (typeof tag === "function") {
84
- return _normalizeTree(tag.apply(null, [ctx, ...tree.slice(1)]), opts, ctx, path, keys, span);
85
- }
86
- // component object w/ life cycle methods
87
- // (render() is the only required hook)
88
- if (typeof tag.render === "function") {
89
- const args = [ctx, ...tree.slice(1)];
90
- norm = _normalizeTree(tag.render.apply(tag, args), opts, ctx, path, keys, span);
91
- if (isArray(norm)) {
92
- norm.__this = tag;
93
- norm.__init = tag.init;
94
- norm.__release = tag.release;
95
- norm.__args = args;
96
- }
97
- return norm;
98
- }
99
- norm = normalizeElement(tree, keys);
100
- nattribs = norm[1];
101
- if (nattribs.__normalize === false) {
102
- return norm;
103
- }
104
- if (keys && nattribs.key === undefined) {
105
- nattribs.key = path.join("-");
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
- return typeof tree === "function"
112
- ? _normalizeTree(tree(ctx), opts, ctx, path, keys, span)
113
- : typeof tree.toHiccup === "function"
114
- ? _normalizeTree(tree.toHiccup(opts.ctx), opts, ctx, path, keys, span)
115
- : typeof tree.deref === "function"
116
- ? _normalizeTree(tree.deref(), opts, ctx, path, keys, span)
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
- const tag = norm[0];
123
- const res = [tag, nattribs];
124
- span = span && !NO_SPANS[tag];
125
- for (let i = 2, j = 2, k = 0, n = norm.length; i < n; i++) {
126
- let el = norm[i];
127
- if (el != null) {
128
- const isarray = isArray(el);
129
- if ((isarray && isArray(el[0])) ||
130
- (!isarray && isNotStringAndIterable(el))) {
131
- for (let c of el) {
132
- c = _normalizeTree(c, opts, ctx, path.concat(k), keys, span);
133
- if (c !== undefined) {
134
- res[j++] = c;
135
- }
136
- k++;
137
- }
138
- }
139
- else {
140
- el = _normalizeTree(el, opts, ctx, path.concat(k), keys, span);
141
- if (el !== undefined) {
142
- res[j++] = el;
143
- }
144
- k++;
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
- return res;
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.30",
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 clean && tsc --declaration",
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.10",
40
- "@thi.ng/checks": "^3.4.10",
41
- "@thi.ng/diff": "^5.1.43",
42
- "@thi.ng/equiv": "^2.1.35",
43
- "@thi.ng/errors": "^2.4.4",
44
- "@thi.ng/hiccup": "^5.0.9",
45
- "@thi.ng/logger": "^2.0.1",
46
- "@thi.ng/prefixes": "^2.2.6"
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.16",
51
- "@thi.ng/testament": "^0.4.3",
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": "04d1de79f256d7a53c6b5fd157b37f49bc88e11d\n"
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
- * One-off hdom tree conversion & target DOM application. Takes same
6
- * options as {@link start}, but performs no diffing and only creates or
7
- * hydrates target once. The given tree is first normalized and if
8
- * result is `null` or `undefined` no further action will be taken.
9
- *
10
- * @param tree - component tree
11
- * @param opts - hdom config options
12
- * @param impl - hdom implementation
13
- */
14
- export const renderOnce = (tree, opts = {}, impl = DEFAULT_IMPL) => {
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
@@ -1,2 +1,5 @@
1
1
  import { isString } from "@thi.ng/checks/is-string";
2
- export const resolveRoot = (root, impl) => isString(root) ? impl.getElementById(root) : root;
2
+ const resolveRoot = (root, impl) => isString(root) ? impl.getElementById(root) : root;
3
+ export {
4
+ resolveRoot
5
+ };
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
- * Takes an hiccup tree (array, function or component object w/ life cycle
6
- * methods) and an optional object of DOM update options. Starts RAF update
7
- * loop, in each iteration first normalizing given tree, then computing diff to
8
- * previous frame's tree and applying any changes to the real DOM. The `ctx`
9
- * option can be used for passing arbitrary config data or state down into the
10
- * hiccup component tree. Any embedded component function in the tree will
11
- * receive this context object (shallow copy) as first argument, as will life
12
- * cycle methods in component objects. If the `autoDerefKeys` option is given,
13
- * attempts to auto-expand/deref the given keys in the user supplied context
14
- * object (`ctx` option) prior to *each* tree normalization. All of these values
15
- * should implement the
16
- * [`IDeref`](https://docs.thi.ng/umbrella/api/interfaces/IDeref.html) interface
17
- * (e.g. atoms, cursors, views, rstreams etc.). This feature can be used to
18
- * define dynamic contexts linked to the main app state, e.g. using derived
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
- requestAnimationFrame(update);
69
- return () => (isActive = false);
20
+ prev = curr;
21
+ }
22
+ isActive && requestAnimationFrame(update);
23
+ }
24
+ };
25
+ requestAnimationFrame(update);
26
+ return () => isActive = false;
27
+ };
28
+ export {
29
+ start
70
30
  };