@htmlplus/element 1.0.0 → 1.1.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.
Files changed (185) hide show
  1. package/README.md +616 -534
  2. package/bundlers/rollup.d.ts +2 -2
  3. package/bundlers/rollup.js +5 -5
  4. package/bundlers/vite.d.ts +2 -2
  5. package/bundlers/vite.js +5 -5
  6. package/client/decorators/bind.d.ts +4 -0
  7. package/client/decorators/bind.js +4 -0
  8. package/client/decorators/direction.d.ts +5 -0
  9. package/client/decorators/direction.js +8 -0
  10. package/client/decorators/element.d.ts +5 -0
  11. package/client/decorators/element.js +7 -2
  12. package/client/decorators/event.d.ts +21 -7
  13. package/client/decorators/event.js +7 -2
  14. package/client/decorators/host.d.ts +4 -2
  15. package/client/decorators/host.js +5 -8
  16. package/client/decorators/index.d.ts +3 -0
  17. package/client/decorators/index.js +3 -0
  18. package/client/decorators/isRTL.d.ts +4 -0
  19. package/client/decorators/isRTL.js +7 -0
  20. package/client/decorators/listen.d.ts +38 -8
  21. package/client/decorators/listen.js +9 -12
  22. package/client/decorators/method.d.ts +4 -0
  23. package/client/decorators/method.js +4 -0
  24. package/client/decorators/property.d.ts +8 -1
  25. package/client/decorators/property.js +4 -0
  26. package/client/decorators/query.d.ts +9 -2
  27. package/client/decorators/query.js +10 -9
  28. package/client/decorators/queryAll.d.ts +12 -2
  29. package/client/decorators/queryAll.js +13 -9
  30. package/client/decorators/slots.d.ts +4 -0
  31. package/client/decorators/slots.js +7 -0
  32. package/client/decorators/state.d.ts +4 -0
  33. package/client/decorators/state.js +4 -0
  34. package/client/decorators/watch.d.ts +5 -4
  35. package/client/decorators/watch.js +5 -4
  36. package/client/index.d.ts +1 -3
  37. package/client/index.js +1 -3
  38. package/client/utils/classes.d.ts +3 -0
  39. package/client/utils/classes.js +11 -28
  40. package/client/utils/config.d.ts +25 -3
  41. package/client/utils/config.js +18 -10
  42. package/client/utils/direction.d.ts +5 -2
  43. package/client/utils/direction.js +5 -1
  44. package/client/utils/event.d.ts +6 -0
  45. package/client/utils/event.js +6 -0
  46. package/client/utils/host.d.ts +3 -0
  47. package/client/utils/host.js +3 -0
  48. package/client/utils/index.d.ts +4 -1
  49. package/client/utils/index.js +4 -1
  50. package/client/utils/isCSSColor.d.ts +5 -0
  51. package/client/utils/isCSSColor.js +9 -0
  52. package/client/utils/isRTL.d.ts +3 -0
  53. package/client/utils/isRTL.js +3 -0
  54. package/client/utils/isServer.d.ts +3 -0
  55. package/client/utils/isServer.js +3 -0
  56. package/client/utils/query.d.ts +5 -0
  57. package/client/utils/query.js +8 -0
  58. package/client/utils/queryAll.d.ts +5 -0
  59. package/client/utils/queryAll.js +8 -0
  60. package/client/utils/request.d.ts +1 -1
  61. package/client/utils/request.js +1 -1
  62. package/client/utils/slots.d.ts +3 -0
  63. package/client/utils/slots.js +6 -1
  64. package/client/utils/styles.d.ts +3 -0
  65. package/client/utils/styles.js +3 -0
  66. package/client/utils/toDecorator.d.ts +2 -0
  67. package/client/utils/toDecorator.js +10 -0
  68. package/client/utils/toUnit.d.ts +4 -1
  69. package/client/utils/toUnit.js +6 -3
  70. package/constants/index.d.ts +1 -0
  71. package/constants/index.js +2 -0
  72. package/package.json +3 -3
  73. package/transformer/index.d.ts +3 -0
  74. package/transformer/index.js +3 -0
  75. package/transformer/plugins/assets.d.ts +8 -0
  76. package/transformer/plugins/assets.js +29 -0
  77. package/{compiler → transformer}/plugins/copy.d.ts +2 -2
  78. package/{compiler → transformer}/plugins/customElement.d.ts +2 -2
  79. package/{compiler → transformer}/plugins/customElement.js +13 -12
  80. package/{compiler → transformer}/plugins/customElementReact/customElementReact.d.ts +4 -4
  81. package/{compiler → transformer}/plugins/customElementReact/customElementReact.js +18 -18
  82. package/{compiler → transformer}/plugins/customElementReact/templates/src/components/{{fileName}}.compact.ts.hbs +1 -1
  83. package/{compiler → transformer}/plugins/customElementReact/templates/src/components/{{fileName}}.ts.hbs +3 -3
  84. package/transformer/plugins/customElementReact/templates/src/index.ts.hbs +1 -0
  85. package/transformer/plugins/document.d.ts +7 -0
  86. package/{compiler → transformer}/plugins/document.js +18 -18
  87. package/transformer/plugins/extract.d.ts +2 -0
  88. package/{compiler → transformer}/plugins/extract.js +1 -18
  89. package/transformer/plugins/parse.d.ts +6 -0
  90. package/{compiler → transformer}/plugins/parse.js +1 -1
  91. package/transformer/plugins/read.d.ts +8 -0
  92. package/transformer/plugins/read.js +20 -0
  93. package/transformer/plugins/readme.d.ts +6 -0
  94. package/{compiler → transformer}/plugins/readme.js +3 -2
  95. package/transformer/plugins/style.d.ts +6 -0
  96. package/{compiler → transformer}/plugins/style.js +4 -2
  97. package/transformer/plugins/validate.d.ts +2 -0
  98. package/transformer/plugins/validate.js +41 -0
  99. package/transformer/plugins/visualStudioCode.d.ts +8 -0
  100. package/{compiler → transformer}/plugins/visualStudioCode.js +10 -8
  101. package/transformer/plugins/webTypes.d.ts +10 -0
  102. package/{compiler → transformer}/plugins/webTypes.js +11 -7
  103. package/transformer/transformer.d.ts +6 -0
  104. package/{compiler/compiler.js → transformer/transformer.js} +17 -17
  105. package/transformer/transformer.types.d.ts +50 -0
  106. package/{compiler → transformer}/utils/printType.js +2 -2
  107. package/{compiler → transformer}/utils/renderTemplate.js +2 -0
  108. package/types/index.d.ts +2 -4
  109. package/types/index.js +1 -4
  110. package/CHANGELOG.md +0 -7
  111. package/bundlers/index.d.ts +0 -2
  112. package/bundlers/index.js +0 -2
  113. package/client/helpers/index.d.ts +0 -1
  114. package/client/helpers/index.js +0 -1
  115. package/client/services/index.d.ts +0 -1
  116. package/client/services/index.js +0 -1
  117. package/client/services/link.d.ts +0 -4
  118. package/client/services/link.js +0 -196
  119. package/client/utils/getNamespace.d.ts +0 -2
  120. package/client/utils/getNamespace.js +0 -4
  121. package/client/vendors/uhtml.d.ts +0 -29
  122. package/client/vendors/uhtml.js +0 -700
  123. package/compiler/compiler.d.ts +0 -6
  124. package/compiler/index.d.ts +0 -2
  125. package/compiler/index.js +0 -2
  126. package/compiler/plugins/assets.d.ts +0 -8
  127. package/compiler/plugins/assets.js +0 -33
  128. package/compiler/plugins/customElementReact/templates/src/index.ts.hbs +0 -1
  129. package/compiler/plugins/document.d.ts +0 -7
  130. package/compiler/plugins/extract.d.ts +0 -2
  131. package/compiler/plugins/parse.d.ts +0 -5
  132. package/compiler/plugins/read.d.ts +0 -8
  133. package/compiler/plugins/read.js +0 -13
  134. package/compiler/plugins/readme.d.ts +0 -6
  135. package/compiler/plugins/style.d.ts +0 -6
  136. package/compiler/plugins/validate.d.ts +0 -2
  137. package/compiler/plugins/validate.js +0 -37
  138. package/compiler/plugins/visualStudioCode.d.ts +0 -8
  139. package/compiler/plugins/webTypes.d.ts +0 -10
  140. package/types/context.d.ts +0 -31
  141. package/types/global.d.ts +0 -4
  142. package/types/global.js +0 -1
  143. package/types/plugin.d.ts +0 -10
  144. package/types/plugin.js +0 -1
  145. package/types/plusElement.d.ts +0 -2
  146. package/types/plusElement.js +0 -1
  147. /package/{compiler → transformer}/plugins/copy.js +0 -0
  148. /package/{compiler → transformer}/plugins/customElementReact/index.d.ts +0 -0
  149. /package/{compiler → transformer}/plugins/customElementReact/index.js +0 -0
  150. /package/{compiler → transformer}/plugins/customElementReact/templates/README.md.hbs +0 -0
  151. /package/{compiler → transformer}/plugins/customElementReact/templates/_.gitignore.hbs +0 -0
  152. /package/{compiler → transformer}/plugins/customElementReact/templates/package.json.hbs +0 -0
  153. /package/{compiler → transformer}/plugins/customElementReact/templates/rollup.config.js.hbs +0 -0
  154. /package/{compiler → transformer}/plugins/customElementReact/templates/src/components/index.ts.hbs +0 -0
  155. /package/{compiler → transformer}/plugins/customElementReact/templates/src/proxy.ts.hbs +0 -0
  156. /package/{compiler → transformer}/plugins/customElementReact/templates/tsconfig.json.hbs +0 -0
  157. /package/{compiler → transformer}/plugins/index.d.ts +0 -0
  158. /package/{compiler → transformer}/plugins/index.js +0 -0
  159. /package/{types/context.js → transformer/transformer.types.js} +0 -0
  160. /package/{compiler → transformer}/utils/__dirname.d.ts +0 -0
  161. /package/{compiler → transformer}/utils/__dirname.js +0 -0
  162. /package/{compiler → transformer}/utils/addDependency.d.ts +0 -0
  163. /package/{compiler → transformer}/utils/addDependency.js +0 -0
  164. /package/{compiler → transformer}/utils/getInitializer.d.ts +0 -0
  165. /package/{compiler → transformer}/utils/getInitializer.js +0 -0
  166. /package/{compiler → transformer}/utils/getType.d.ts +0 -0
  167. /package/{compiler → transformer}/utils/getType.js +0 -0
  168. /package/{compiler → transformer}/utils/getTypeReference.d.ts +0 -0
  169. /package/{compiler → transformer}/utils/getTypeReference.js +0 -0
  170. /package/{compiler → transformer}/utils/hasDecorator.d.ts +0 -0
  171. /package/{compiler → transformer}/utils/hasDecorator.js +0 -0
  172. /package/{compiler → transformer}/utils/index.d.ts +0 -0
  173. /package/{compiler → transformer}/utils/index.js +0 -0
  174. /package/{compiler → transformer}/utils/isDirectoryEmpty.d.ts +0 -0
  175. /package/{compiler → transformer}/utils/isDirectoryEmpty.js +0 -0
  176. /package/{compiler → transformer}/utils/print.d.ts +0 -0
  177. /package/{compiler → transformer}/utils/print.js +0 -0
  178. /package/{compiler → transformer}/utils/printType.d.ts +0 -0
  179. /package/{compiler → transformer}/utils/removeUnusedImport.d.ts +0 -0
  180. /package/{compiler → transformer}/utils/removeUnusedImport.js +0 -0
  181. /package/{compiler → transformer}/utils/renderTemplate.d.ts +0 -0
  182. /package/{compiler → transformer}/utils/tags.d.ts +0 -0
  183. /package/{compiler → transformer}/utils/tags.js +0 -0
  184. /package/{compiler → transformer}/utils/visitor.d.ts +0 -0
  185. /package/{compiler → transformer}/utils/visitor.js +0 -0
@@ -1,700 +0,0 @@
1
- class MapSet extends Map {
2
- set(key, value) {
3
- super.set(key, value);
4
- return value;
5
- }
6
- }
7
- class WeakMapSet extends WeakMap {
8
- set(key, value) {
9
- super.set(key, value);
10
- return value;
11
- }
12
- }
13
- /*! (c) Andrea Giammarchi - ISC */
14
- const empty = /^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i;
15
- const elements = /<([a-z]+[a-z0-9:._-]*)([^>]*?)(\/?)>/g;
16
- const attributes = /([^\s\\>"'=]+)\s*=\s*(['"]?)\x01/g;
17
- const holes = /[\x01\x02]/g;
18
- // \x01 Node.ELEMENT_NODE
19
- // \x02 Node.ATTRIBUTE_NODE
20
- /**
21
- * Given a template, find holes as both nodes and attributes and
22
- * return a string with holes as either comment nodes or named attributes.
23
- * @param {string[]} template a template literal tag array
24
- * @param {string} prefix prefix to use per each comment/attribute
25
- * @param {boolean} svg enforces self-closing tags
26
- * @returns {string} X/HTML with prefixed comments or attributes
27
- */
28
- var instrument = (template, prefix, svg) => {
29
- let i = 0;
30
- return template
31
- .join('\x01')
32
- .trim()
33
- .replace(elements, (_, name, attrs, selfClosing) => {
34
- let ml = name + attrs.replace(attributes, '\x02=$2$1').trimEnd();
35
- if (selfClosing.length)
36
- ml += svg || empty.test(name) ? ' /' : '></' + name;
37
- return '<' + ml + '>';
38
- })
39
- .replace(holes, (hole) => (hole === '\x01' ? '<!--' + prefix + i++ + '-->' : prefix + i++));
40
- };
41
- const ELEMENT_NODE = 1;
42
- const nodeType = 111;
43
- const remove = ({ firstChild, lastChild }) => {
44
- const range = document.createRange();
45
- range.setStartAfter(firstChild);
46
- range.setEndAfter(lastChild);
47
- range.deleteContents();
48
- return firstChild;
49
- };
50
- const diffable = (node, operation) => node.nodeType === nodeType
51
- ? 1 / operation < 0
52
- ? operation
53
- ? remove(node)
54
- : node.lastChild
55
- : operation
56
- ? node.valueOf()
57
- : node.firstChild
58
- : node;
59
- const persistent = (fragment) => {
60
- const { firstChild, lastChild } = fragment;
61
- if (firstChild === lastChild)
62
- return lastChild || fragment;
63
- const { childNodes } = fragment;
64
- const nodes = [...childNodes];
65
- return {
66
- ELEMENT_NODE,
67
- nodeType,
68
- firstChild,
69
- lastChild,
70
- valueOf() {
71
- if (childNodes.length !== nodes.length)
72
- fragment.append(...nodes);
73
- return fragment;
74
- }
75
- };
76
- };
77
- const { isArray: isArray$1 } = Array;
78
- const aria = (node) => (values) => {
79
- for (const key in values) {
80
- const name = key === 'role' ? key : `aria-${key}`;
81
- const value = values[key];
82
- if (value == null)
83
- node.removeAttribute(name);
84
- else
85
- node.setAttribute(name, value);
86
- }
87
- };
88
- const attribute = (node, name) => {
89
- let oldValue, orphan = true;
90
- const attributeNode = document.createAttributeNS(null, name);
91
- return (newValue) => {
92
- if (oldValue !== newValue) {
93
- oldValue = newValue;
94
- if (oldValue == null) {
95
- if (!orphan) {
96
- node.removeAttributeNode(attributeNode);
97
- orphan = true;
98
- }
99
- }
100
- else {
101
- const value = newValue;
102
- if (value == null) {
103
- if (!orphan)
104
- node.removeAttributeNode(attributeNode);
105
- orphan = true;
106
- }
107
- else {
108
- attributeNode.value = value;
109
- if (orphan) {
110
- node.setAttributeNodeNS(attributeNode);
111
- orphan = false;
112
- }
113
- }
114
- }
115
- }
116
- };
117
- };
118
- const boolean = (node, key, oldValue) => (newValue) => {
119
- if (oldValue !== !!newValue) {
120
- // when IE won't be around anymore ...
121
- // node.toggleAttribute(key, oldValue = !!newValue);
122
- if ((oldValue = !!newValue))
123
- node.setAttribute(key, '');
124
- else
125
- node.removeAttribute(key);
126
- }
127
- };
128
- const data = ({ dataset }) => (values) => {
129
- for (const key in values) {
130
- const value = values[key];
131
- if (value == null)
132
- delete dataset[key];
133
- else
134
- dataset[key] = value;
135
- }
136
- };
137
- const event = (node, name) => {
138
- let oldValue, lower, type = name.slice(2);
139
- if (!(name in node) && (lower = name.toLowerCase()) in node)
140
- type = lower.slice(2);
141
- return (newValue) => {
142
- const info = isArray$1(newValue) ? newValue : [newValue, false];
143
- if (oldValue !== info[0]) {
144
- if (oldValue)
145
- node.removeEventListener(type, oldValue, info[1]);
146
- if ((oldValue = info[0]))
147
- node.addEventListener(type, oldValue, info[1]);
148
- }
149
- };
150
- };
151
- const ref = (node) => {
152
- let oldValue;
153
- return (value) => {
154
- if (oldValue !== value) {
155
- oldValue = value;
156
- if (typeof value === 'function')
157
- value(node);
158
- else
159
- value.current = node;
160
- }
161
- };
162
- };
163
- const setter = (node, key) => key === 'dataset'
164
- ? data(node)
165
- : (value) => {
166
- node[key] = value;
167
- };
168
- const text = (node) => {
169
- let oldValue;
170
- return (newValue) => {
171
- if (oldValue != newValue) {
172
- oldValue = newValue;
173
- node.textContent = newValue == null ? '' : newValue;
174
- }
175
- };
176
- };
177
- /**
178
- * ISC License
179
- *
180
- * Copyright (c) 2020, Andrea Giammarchi, @WebReflection
181
- *
182
- * Permission to use, copy, modify, and/or distribute this software for any
183
- * purpose with or without fee is hereby granted, provided that the above
184
- * copyright notice and this permission notice appear in all copies.
185
- *
186
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
187
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
188
- * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
189
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
190
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
191
- * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
192
- * PERFORMANCE OF THIS SOFTWARE.
193
- */
194
- /**
195
- * @param {Node} parentNode The container where children live
196
- * @param {Node[]} a The list of current/live children
197
- * @param {Node[]} b The list of future children
198
- * @param {(entry: Node, action: number) => Node} get
199
- * The callback invoked per each entry related DOM operation.
200
- * @param {Node} [before] The optional node used as anchor to insert before.
201
- * @returns {Node[]} The same list of future children.
202
- */
203
- var udomdiff = (parentNode, a, b, get, before) => {
204
- const bLength = b.length;
205
- let aEnd = a.length;
206
- let bEnd = bLength;
207
- let aStart = 0;
208
- let bStart = 0;
209
- let map = null;
210
- while (aStart < aEnd || bStart < bEnd) {
211
- // append head, tail, or nodes in between: fast path
212
- if (aEnd === aStart) {
213
- // we could be in a situation where the rest of nodes that
214
- // need to be added are not at the end, and in such case
215
- // the node to `insertBefore`, if the index is more than 0
216
- // must be retrieved, otherwise it's gonna be the first item.
217
- const node = bEnd < bLength ? (bStart ? get(b[bStart - 1], -0).nextSibling : get(b[bEnd - bStart], 0)) : before;
218
- while (bStart < bEnd)
219
- parentNode.insertBefore(get(b[bStart++], 1), node);
220
- }
221
- // remove head or tail: fast path
222
- else if (bEnd === bStart) {
223
- while (aStart < aEnd) {
224
- // remove the node only if it's unknown or not live
225
- if (!map || !map.has(a[aStart]))
226
- parentNode.removeChild(get(a[aStart], -1));
227
- aStart++;
228
- }
229
- }
230
- // same node: fast path
231
- else if (a[aStart] === b[bStart]) {
232
- aStart++;
233
- bStart++;
234
- }
235
- // same tail: fast path
236
- else if (a[aEnd - 1] === b[bEnd - 1]) {
237
- aEnd--;
238
- bEnd--;
239
- }
240
- // The once here single last swap "fast path" has been removed in v1.1.0
241
- // https://github.com/WebReflection/udomdiff/blob/single-final-swap/esm/index.js#L69-L85
242
- // reverse swap: also fast path
243
- else if (a[aStart] === b[bEnd - 1] && b[bStart] === a[aEnd - 1]) {
244
- // this is a "shrink" operation that could happen in these cases:
245
- // [1, 2, 3, 4, 5]
246
- // [1, 4, 3, 2, 5]
247
- // or asymmetric too
248
- // [1, 2, 3, 4, 5]
249
- // [1, 2, 3, 5, 6, 4]
250
- const node = get(a[--aEnd], -1).nextSibling;
251
- parentNode.insertBefore(get(b[bStart++], 1), get(a[aStart++], -1).nextSibling);
252
- parentNode.insertBefore(get(b[--bEnd], 1), node);
253
- // mark the future index as identical (yeah, it's dirty, but cheap 👍)
254
- // The main reason to do this, is that when a[aEnd] will be reached,
255
- // the loop will likely be on the fast path, as identical to b[bEnd].
256
- // In the best case scenario, the next loop will skip the tail,
257
- // but in the worst one, this node will be considered as already
258
- // processed, bailing out pretty quickly from the map index check
259
- a[aEnd] = b[bEnd];
260
- }
261
- // map based fallback, "slow" path
262
- else {
263
- // the map requires an O(bEnd - bStart) operation once
264
- // to store all future nodes indexes for later purposes.
265
- // In the worst case scenario, this is a full O(N) cost,
266
- // and such scenario happens at least when all nodes are different,
267
- // but also if both first and last items of the lists are different
268
- if (!map) {
269
- map = new Map();
270
- let i = bStart;
271
- while (i < bEnd)
272
- map.set(b[i], i++);
273
- }
274
- // if it's a future node, hence it needs some handling
275
- if (map.has(a[aStart])) {
276
- // grab the index of such node, 'cause it might have been processed
277
- const index = map.get(a[aStart]);
278
- // if it's not already processed, look on demand for the next LCS
279
- if (bStart < index && index < bEnd) {
280
- let i = aStart;
281
- // counts the amount of nodes that are the same in the future
282
- let sequence = 1;
283
- while (++i < aEnd && i < bEnd && map.get(a[i]) === index + sequence)
284
- sequence++;
285
- // effort decision here: if the sequence is longer than replaces
286
- // needed to reach such sequence, which would brings again this loop
287
- // to the fast path, prepend the difference before a sequence,
288
- // and move only the future list index forward, so that aStart
289
- // and bStart will be aligned again, hence on the fast path.
290
- // An example considering aStart and bStart are both 0:
291
- // a: [1, 2, 3, 4]
292
- // b: [7, 1, 2, 3, 6]
293
- // this would place 7 before 1 and, from that time on, 1, 2, and 3
294
- // will be processed at zero cost
295
- if (sequence > index - bStart) {
296
- const node = get(a[aStart], 0);
297
- while (bStart < index)
298
- parentNode.insertBefore(get(b[bStart++], 1), node);
299
- }
300
- // if the effort wasn't good enough, fallback to a replace,
301
- // moving both source and target indexes forward, hoping that some
302
- // similar node will be found later on, to go back to the fast path
303
- else {
304
- parentNode.replaceChild(get(b[bStart++], 1), get(a[aStart++], -1));
305
- }
306
- }
307
- // otherwise move the source forward, 'cause there's nothing to do
308
- else
309
- aStart++;
310
- }
311
- // this node has no meaning in the future list, so it's more than safe
312
- // to remove it, and check the next live node out instead, meaning
313
- // that only the live list index should be forwarded
314
- else
315
- parentNode.removeChild(get(a[aStart++], -1));
316
- }
317
- }
318
- return b;
319
- };
320
- const { isArray, prototype } = Array;
321
- const { indexOf } = prototype;
322
- const { createDocumentFragment, createElement, createElementNS, createTextNode, createTreeWalker, importNode } = new Proxy(typeof window == 'undefined' ? {} : window.document, {
323
- get: (target, method) => (target[method] || function () { }).bind(target)
324
- });
325
- const createHTML = (html) => {
326
- const template = createElement('template');
327
- template.innerHTML = html;
328
- return template.content;
329
- };
330
- let xml;
331
- const createSVG = (svg) => {
332
- if (!xml)
333
- xml = createElementNS('http://www.w3.org/2000/svg', 'svg');
334
- xml.innerHTML = svg;
335
- const content = createDocumentFragment();
336
- content.append(...xml.childNodes);
337
- return content;
338
- };
339
- const createContent = (text, svg) => (svg ? createSVG(text) : createHTML(text));
340
- // from a generic path, retrieves the exact targeted node
341
- const reducePath = ({ childNodes }, i) => childNodes[i];
342
- // this helper avoid code bloat around handleAnything() callback
343
- const diff = (comment, oldNodes, newNodes) => udomdiff(comment.parentNode,
344
- // TODO: there is a possible edge case where a node has been
345
- // removed manually, or it was a keyed one, attached
346
- // to a shared reference between renders.
347
- // In this case udomdiff might fail at removing such node
348
- // as its parent won't be the expected one.
349
- // The best way to avoid this issue is to filter oldNodes
350
- // in search of those not live, or not in the current parent
351
- // anymore, but this would require both a change to uwire,
352
- // exposing a parentNode from the firstChild, as example,
353
- // but also a filter per each diff that should exclude nodes
354
- // that are not in there, penalizing performance quite a lot.
355
- // As this has been also a potential issue with domdiff,
356
- // and both lighterhtml and hyperHTML might fail with this
357
- // very specific edge case, I might as well document this possible
358
- // "diffing shenanigan" and call it a day.
359
- oldNodes, newNodes, diffable, comment);
360
- // if an interpolation represents a comment, the whole
361
- // diffing will be related to such comment.
362
- // This helper is in charge of understanding how the new
363
- // content for such interpolation/hole should be updated
364
- const handleAnything = (comment) => {
365
- let oldValue, text, nodes = [];
366
- const anyContent = (newValue) => {
367
- switch (typeof newValue) {
368
- // primitives are handled as text content
369
- case 'string':
370
- case 'number':
371
- case 'boolean':
372
- if (oldValue !== newValue) {
373
- oldValue = newValue;
374
- if (!text)
375
- text = createTextNode('');
376
- text.data = newValue;
377
- nodes = diff(comment, nodes, [text]);
378
- }
379
- break;
380
- // null, and undefined are used to cleanup previous content
381
- case 'object':
382
- case 'undefined':
383
- if (newValue == null) {
384
- if (oldValue != newValue) {
385
- oldValue = newValue;
386
- nodes = diff(comment, nodes, []);
387
- }
388
- break;
389
- }
390
- // arrays and nodes have a special treatment
391
- if (isArray(newValue)) {
392
- oldValue = newValue;
393
- // arrays can be used to cleanup, if empty
394
- if (newValue.length === 0)
395
- nodes = diff(comment, nodes, []);
396
- // or diffed, if these contains nodes or "wires"
397
- else if (typeof newValue[0] === 'object')
398
- nodes = diff(comment, nodes, newValue);
399
- // in all other cases the content is stringified as is
400
- else
401
- anyContent(String(newValue));
402
- break;
403
- }
404
- // if the new value is a DOM node, or a wire, and it's
405
- // different from the one already live, then it's diffed.
406
- // if the node is a fragment, it's appended once via its childNodes
407
- // There is no `else` here, meaning if the content
408
- // is not expected one, nothing happens, as easy as that.
409
- if (oldValue !== newValue && 'ELEMENT_NODE' in newValue) {
410
- oldValue = newValue;
411
- nodes = diff(comment, nodes, newValue.nodeType === 11 ? [...newValue.childNodes] : [newValue]);
412
- }
413
- break;
414
- case 'function':
415
- anyContent(newValue(comment));
416
- break;
417
- }
418
- };
419
- return anyContent;
420
- };
421
- // attributes can be:
422
- // * ref=${...} for hooks and other purposes
423
- // * aria=${...} for aria attributes
424
- // * ?boolean=${...} for boolean attributes
425
- // * .dataset=${...} for dataset related attributes
426
- // * .setter=${...} for Custom Elements setters or nodes with setters
427
- // such as buttons, details, options, select, etc
428
- // * @event=${...} to explicitly handle event listeners
429
- // * onevent=${...} to automatically handle event listeners
430
- // * generic=${...} to handle an attribute just like an attribute
431
- const handleAttribute = (node, name /*, svg*/) => {
432
- switch (name[0]) {
433
- case '?':
434
- return boolean(node, name.slice(1), false);
435
- case '.':
436
- return setter(node, name.slice(1));
437
- case '@':
438
- return event(node, 'on' + name.slice(1));
439
- case 'o':
440
- if (name[1] === 'n')
441
- return event(node, name);
442
- }
443
- switch (name) {
444
- case 'ref':
445
- return ref(node);
446
- case 'aria':
447
- return aria(node);
448
- }
449
- return attribute(node, name /*, svg*/);
450
- };
451
- // each mapped update carries the update type and its path
452
- // the type is either node, attribute, or text, while
453
- // the path is how to retrieve the related node to update.
454
- // In the attribute case, the attribute name is also carried along.
455
- function handlers(options) {
456
- const { type, path } = options;
457
- const node = path.reduceRight(reducePath, this);
458
- return type === 'node'
459
- ? handleAnything(node)
460
- : type === 'attr'
461
- ? handleAttribute(node, options.name /*, options.svg*/)
462
- : text(node);
463
- }
464
- // from a fragment container, create an array of indexes
465
- // related to its child nodes, so that it's possible
466
- // to retrieve later on exact node via reducePath
467
- const createPath = (node) => {
468
- const path = [];
469
- let { parentNode } = node;
470
- while (parentNode) {
471
- path.push(indexOf.call(parentNode.childNodes, node));
472
- node = parentNode;
473
- ({ parentNode } = node);
474
- }
475
- return path;
476
- };
477
- // the prefix is used to identify either comments, attributes, or nodes
478
- // that contain the related unique id. In the attribute cases
479
- // isµX="attribute-name" will be used to map current X update to that
480
- // attribute name, while comments will be like <!--isµX-->, to map
481
- // the update to that specific comment node, hence its parent.
482
- // style and textarea will have <!--isµX--> text content, and are handled
483
- // directly through text-only updates.
484
- const prefix = 'isµ';
485
- // Template Literals are unique per scope and static, meaning a template
486
- // should be parsed once, and once only, as it will always represent the same
487
- // content, within the exact same amount of updates each time.
488
- // This cache relates each template to its unique content and updates.
489
- const cache$1 = new WeakMapSet();
490
- // a RegExp that helps checking nodes that cannot contain comments
491
- const textOnly = /^(?:textarea|script|style|title|plaintext|xmp)$/;
492
- const createCache = () => ({
493
- stack: [],
494
- entry: null,
495
- // * the template that is representing
496
- // * the type of node it represents (html or svg)
497
- // * the content fragment with all nodes
498
- // * the list of updates per each node (template holes)
499
- // * the "wired" node or fragment that will get updates
500
- // if the template or type are different from the previous one
501
- // the entry gets re-created each time
502
- wire: null // each rendered node represent some wired content and
503
- // this reference to the latest one. If different, the node
504
- // will be cleaned up and the new "wire" will be appended
505
- });
506
- // the entry stored in the rendered node cache, and per each "hole"
507
- const createEntry = (type, template) => {
508
- const { content, updates } = mapUpdates(type, template);
509
- return { type, template, content, updates, wire: null };
510
- };
511
- // a template is instrumented to be able to retrieve where updates are needed.
512
- // Each unique template becomes a fragment, cloned once per each other
513
- // operation based on the same template, i.e. data => html`<p>${data}</p>`
514
- const mapTemplate = (type, template) => {
515
- const svg = type === 'svg';
516
- const text = instrument(template, prefix, svg);
517
- const content = createContent(text, svg);
518
- // once instrumented and reproduced as fragment, it's crawled
519
- // to find out where each update is in the fragment tree
520
- const tw = createTreeWalker(content, 1 | 128);
521
- const nodes = [];
522
- const length = template.length - 1;
523
- let i = 0;
524
- // updates are searched via unique names, linearly increased across the tree
525
- // <div isµ0="attr" isµ1="other"><!--isµ2--><style><!--isµ3--</style></div>
526
- let search = `${prefix}${i}`;
527
- while (i < length) {
528
- const node = tw.nextNode();
529
- // if not all updates are bound but there's nothing else to crawl
530
- // it means that there is something wrong with the template.
531
- if (!node)
532
- throw `bad template: ${text}`;
533
- // if the current node is a comment, and it contains isµX
534
- // it means the update should take care of any content
535
- if (node.nodeType === 8) {
536
- // The only comments to be considered are those
537
- // which content is exactly the same as the searched one.
538
- if (node.data === search) {
539
- nodes.push({ type: 'node', path: createPath(node) });
540
- search = `${prefix}${++i}`;
541
- }
542
- }
543
- else {
544
- // if the node is not a comment, loop through all its attributes
545
- // named isµX and relate attribute updates to this node and the
546
- // attribute name, retrieved through node.getAttribute("isµX")
547
- // the isµX attribute will be removed as irrelevant for the layout
548
- // let svg = -1;
549
- while (node.hasAttribute(search)) {
550
- nodes.push({
551
- type: 'attr',
552
- path: createPath(node),
553
- name: node.getAttribute(search)
554
- });
555
- node.removeAttribute(search);
556
- search = `${prefix}${++i}`;
557
- }
558
- // if the node was a style, textarea, or others, check its content
559
- // and if it is <!--isµX--> then update tex-only this node
560
- if (textOnly.test(node.localName) && node.textContent.trim() === `<!--${search}-->`) {
561
- node.textContent = '';
562
- nodes.push({ type: 'text', path: createPath(node) });
563
- search = `${prefix}${++i}`;
564
- }
565
- }
566
- }
567
- // once all nodes to update, or their attributes, are known, the content
568
- // will be cloned in the future to represent the template, and all updates
569
- // related to such content retrieved right away without needing to re-crawl
570
- // the exact same template, and its content, more than once.
571
- return { content, nodes };
572
- };
573
- // if a template is unknown, perform the previous mapping, otherwise grab
574
- // its details such as the fragment with all nodes, and updates info.
575
- const mapUpdates = (type, template) => {
576
- const { content, nodes } = cache$1.get(template) || cache$1.set(template, mapTemplate(type, template));
577
- // clone deeply the fragment
578
- const fragment = importNode(content, true);
579
- // and relate an update handler per each node that needs one
580
- const updates = nodes.map(handlers, fragment);
581
- // return the fragment and all updates to use within its nodes
582
- return { content: fragment, updates };
583
- };
584
- // as html and svg can be nested calls, but no parent node is known
585
- // until rendered somewhere, the unroll operation is needed to
586
- // discover what to do with each interpolation, which will result
587
- // into an update operation.
588
- const unroll = (info, { type, template, values }) => {
589
- // interpolations can contain holes and arrays, so these need
590
- // to be recursively discovered
591
- const length = unrollValues(info, values);
592
- let { entry } = info;
593
- // if the cache entry is either null or different from the template
594
- // and the type this unroll should resolve, create a new entry
595
- // assigning a new content fragment and the list of updates.
596
- if (!entry || entry.template !== template || entry.type !== type)
597
- info.entry = entry = createEntry(type, template);
598
- const { content, updates, wire } = entry;
599
- // even if the fragment and its nodes is not live yet,
600
- // it is already possible to update via interpolations values.
601
- for (let i = 0; i < length; i++)
602
- updates[i](values[i]);
603
- // if the entry was new, or representing a different template or type,
604
- // create a new persistent entity to use during diffing.
605
- // This is simply a DOM node, when the template has a single container,
606
- // as in `<p></p>`, or a "wire" in `<p></p><p></p>` and similar cases.
607
- return wire || (entry.wire = persistent(content));
608
- };
609
- // the stack retains, per each interpolation value, the cache
610
- // related to each interpolation value, or null, if the render
611
- // was conditional and the value is not special (Array or Hole)
612
- const unrollValues = ({ stack }, values) => {
613
- const { length } = values;
614
- for (let i = 0; i < length; i++) {
615
- const hole = values[i];
616
- // each Hole gets unrolled and re-assigned as value
617
- // so that domdiff will deal with a node/wire, not with a hole
618
- if (hole instanceof Hole)
619
- values[i] = unroll(stack[i] || (stack[i] = createCache()), hole);
620
- // arrays are recursively resolved so that each entry will contain
621
- // also a DOM node or a wire, hence it can be diffed if/when needed
622
- else if (isArray(hole))
623
- unrollValues(stack[i] || (stack[i] = createCache()), hole);
624
- // if the value is nothing special, the stack doesn't need to retain data
625
- // this is useful also to cleanup previously retained data, if the value
626
- // was a Hole, or an Array, but not anymore, i.e.:
627
- // const update = content => html`<div>${content}</div>`;
628
- // update(listOfItems); update(null); update(html`hole`)
629
- else
630
- stack[i] = null;
631
- }
632
- if (length < stack.length)
633
- stack.splice(length);
634
- return length;
635
- };
636
- /**
637
- * Holds all details wrappers needed to render the content further on.
638
- * @constructor
639
- * @param {string} type The hole type, either `html` or `svg`.
640
- * @param {string[]} template The template literals used to the define the content.
641
- * @param {Array} values Zero, one, or more interpolated values to render.
642
- */
643
- class Hole {
644
- constructor(type, template, values) {
645
- this.type = type;
646
- this.template = template;
647
- this.values = values;
648
- }
649
- }
650
- // both `html` and `svg` template literal tags are polluted
651
- // with a `for(ref[, id])` and a `node` tag too
652
- const tag = (type) => {
653
- // both `html` and `svg` tags have their own cache
654
- const keyed = new WeakMapSet();
655
- // keyed operations always re-use the same cache and unroll
656
- // the template and its interpolations right away
657
- const fixed = (cache) => (template, ...values) => unroll(cache, { type, template, values });
658
- return Object.assign(
659
- // non keyed operations are recognized as instance of Hole
660
- // during the "unroll", recursively resolved and updated
661
- (template, ...values) => new Hole(type, template, values), {
662
- // keyed operations need a reference object, usually the parent node
663
- // which is showing keyed results, and optionally a unique id per each
664
- // related node, handy with JSON results and mutable list of objects
665
- // that usually carry a unique identifier
666
- for(ref, id) {
667
- const memo = keyed.get(ref) || keyed.set(ref, new MapSet());
668
- return memo.get(id) || memo.set(id, fixed(createCache()));
669
- },
670
- // it is possible to create one-off content out of the box via node tag
671
- // this might return the single created node, or a fragment with all
672
- // nodes present at the root level and, of course, their child nodes
673
- node: (template, ...values) => unroll(createCache(), new Hole(type, template, values)).valueOf()
674
- });
675
- };
676
- // each rendered node gets its own cache
677
- const cache = new WeakMapSet();
678
- // rendering means understanding what `html` or `svg` tags returned
679
- // and it relates a specific node to its own unique cache.
680
- // Each time the content to render changes, the node is cleaned up
681
- // and the new new content is appended, and if such content is a Hole
682
- // then it's "unrolled" to resolve all its inner nodes.
683
- const render = (where, what) => {
684
- const hole = typeof what === 'function' ? what() : what;
685
- const info = cache.get(where) || cache.set(where, createCache());
686
- const wire = hole instanceof Hole ? unroll(info, hole) : hole;
687
- if (wire !== info.wire) {
688
- info.wire = wire;
689
- // valueOf() simply returns the node itself, but in case it was a "wire"
690
- // it will eventually re-append all nodes to its fragment so that such
691
- // fragment can be re-appended many times in a meaningful way
692
- // (wires are basically persistent fragments facades with special behavior)
693
- where.replaceChildren(wire.valueOf());
694
- }
695
- return where;
696
- };
697
- const html = tag('html');
698
- const svg = tag('svg');
699
- export { Hole, html, render, svg };
700
- export default { Hole, html, render, svg };