subay 0.0.13 → 0.1.1

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/src/h.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  // deno-lint-ignore-file no-explicit-any no-sloppy-imports
2
+ import { arr } from './arr.js';
2
3
  import { cleanup, isReactive, root, S, sample } from './s.js';
3
4
 
4
5
  const enum InterNodeType {
@@ -28,88 +29,61 @@ type InterAttributeNode = {
28
29
  };
29
30
 
30
31
  const cache = new Map<TemplateStringsArray, InterTreeNode[]>();
31
- function random6LetterString() {
32
- const BASE = 36;
33
- const power5 = BASE ** 5;
34
- const min = 10 * power5;
35
- const length = 26 * power5;
36
- return Math.floor(Math.random() * length + min).toString(BASE);
37
- }
38
- const MarkStart = random6LetterString();
39
- const MarkEnd = random6LetterString();
32
+ const rnds = () => String.fromCharCode((97 + Math.random() * 26) | 0) + Math.random().toString(36).substring(2, 5);
33
+ const MarkStart = rnds();
34
+ const MarkEnd = rnds();
40
35
  const MarkReg = new RegExp(`([\\s\\S]*?)${MarkStart}(\\d+)${MarkEnd}([\\s\\S]*?)(?=${MarkStart}\\d+${MarkEnd}|$)`, 'g');
41
36
  const BlankText = /\s*\n\s*/g;
42
37
  const domParser = new DOMParser();
38
+ const isNum = (d: any): d is number => typeof d === 'number';
39
+ const isFun = (d: any): d is (...args: any[]) => any => typeof d === 'function';
40
+ const isStr = (d: any): d is string => typeof d === 'string';
41
+ const deferRemove = <T extends Element | Comment | Text>(d: T) => (cleanup(() => d.remove()), d);
42
+ const callif = <T>(fun: (() => T) | T): T => (isFun(fun) ? fun() : fun);
43
43
 
44
44
  const parseText = (data: string) => {
45
- const result: Array<string | number> = [];
46
45
  data = data.replace(BlankText, '');
46
+ if (!data) return [];
47
47
  if (data.includes(MarkStart) && data.includes(MarkEnd)) {
48
+ const result: Array<string | number> = [];
48
49
  for (const res of data.matchAll(MarkReg)) {
49
50
  const [, prefix, index, suffix] = res;
50
51
  if (prefix) {
51
52
  result.push(prefix);
52
53
  }
53
54
  if (index) {
54
- result.push(Number(index));
55
+ result.push(+index);
55
56
  }
56
57
  if (suffix) {
57
58
  result.push(suffix);
58
59
  }
59
60
  }
60
- } else if (data) {
61
- result.push(data);
61
+ return result;
62
62
  }
63
- return result;
63
+ return [data];
64
64
  };
65
65
 
66
66
  const unparseText = (data: Array<string | number | boolean>, values: any[]) => {
67
67
  if (data.length === 1) {
68
- if (typeof data[0] === 'number') {
69
- return values[data[0]!];
70
- }
71
- return data[0];
68
+ const head = data[0];
69
+ return isNum(head) ? values[head] : head;
72
70
  }
73
- return S(() => {
74
- return data
75
- .map((it) => {
76
- if (typeof it === 'number') {
77
- const value = values[it];
78
- if (typeof value === 'function') {
79
- return value();
80
- }
81
- return value;
82
- }
83
- return it;
84
- })
85
- .join('');
86
- });
71
+ return S(() => data.map((it) => (isNum(it) ? callif(values[it]) : it)).join(''));
87
72
  };
88
73
 
89
74
  const parse = (
90
- docType: DOMParserSupportedType,
91
- wrapSource: (source: string) => string,
92
- unwrapNode: (node: Document) => NodeList,
75
+ markup2nodeList: (markup: string) => NodeList,
93
76
  segments: TemplateStringsArray,
94
77
  indexMap: number[],
95
78
  ): InterTreeNode[] => {
96
79
  if (cache.has(segments)) {
97
80
  return cache.get(segments)!;
98
81
  }
99
-
100
82
  const htmlStr = segments
101
- .flatMap((segment, index, { length }) => {
102
- if (index === length - 1) {
103
- return [segment];
104
- }
105
- return [segment, MarkStart, indexMap[index], MarkEnd];
106
- })
83
+ .flatMap((segment, index, { length }) =>
84
+ index === length - 1 ? [segment] : [segment, MarkStart, indexMap[index], MarkEnd],
85
+ )
107
86
  .join('');
108
-
109
- const wrappedHtmlStr = wrapSource(htmlStr);
110
-
111
- const document = domParser.parseFromString(wrappedHtmlStr, docType);
112
-
113
87
  const dom2treenode = (domNodeList: NodeList) => {
114
88
  const nodeList: InterTreeNode[] = [];
115
89
  for (
@@ -117,115 +91,79 @@ const parse = (
117
91
  item != null;
118
92
  item = nextSibling, nextSibling = nextSibling?.nextSibling, index++
119
93
  ) {
120
- do
121
- remove_and_dec_index: {
122
- if (item.nodeType === 1 /* Node.ELEMENT_NODE*/) {
123
- const el = item as Element;
124
- const tag = parseText(el.tagName.toLowerCase());
125
- const isVariableElem = tag.length > 1 || typeof tag[0] === 'number';
126
-
127
- nodeList.push({
128
- _ns: el.namespaceURI,
129
- _type: InterNodeType.Element,
130
- _tag: tag,
131
- _attrs: Array.from(el.attributes).flatMap((attr) => {
132
- const name = parseText(attr.nodeName);
133
- const value = parseText(attr.value);
134
- const isVariableAttr =
135
- name.length > 1 ||
136
- typeof name[0] === 'number' ||
137
- value.length > 1 ||
138
- typeof value[0] === 'number';
139
-
140
- if (!isVariableElem && !isVariableAttr) {
141
- return [];
142
- } else {
143
- el.removeAttribute(attr.nodeName);
144
- }
145
- return [
146
- {
147
- _name: name,
148
- _value: value.length === 0 ? [true] : value,
149
- } satisfies InterAttributeNode,
150
- ];
151
- }),
152
- _content: el.childNodes.length > 0 ? dom2treenode(el.childNodes) : [],
153
- _sourceNode: isVariableElem ? null : el,
154
- _sourceIndex: index,
155
- });
156
-
157
- if (!isVariableElem) {
158
- break remove_and_dec_index;
159
- }
160
- } else if (item.nodeType === 3 /*Node.TEXT_NODE*/) {
161
- const data = (item as Text).data;
162
- const result = parseText(data);
163
- if (result.length > 0) {
164
- nodeList.push({
165
- _type: InterNodeType.Text,
166
- _data: result,
167
- });
94
+ if (item.nodeType === 1 /* Node.ELEMENT_NODE*/) {
95
+ const el = item as Element;
96
+ const tag = parseText(el.tagName.toLowerCase());
97
+ const isVariableElem = tag.length > 1 || isNum(tag[0]);
98
+
99
+ nodeList.push({
100
+ _ns: el.namespaceURI,
101
+ _type: InterNodeType.Element,
102
+ _tag: tag,
103
+ _attrs: Array.from(el.attributes).flatMap((attr) => {
104
+ const name = parseText(attr.nodeName);
105
+ const value = parseText(attr.value);
106
+ const isVariableAttr = name.length > 1 || isNum(name[0]) || value.length > 1 || isNum(value[0]);
107
+
108
+ if (!isVariableElem && !isVariableAttr) {
109
+ return [];
168
110
  }
169
- }
170
- (item as Comment | Text | Element).remove();
171
- index--;
111
+ el.removeAttribute(attr.nodeName);
112
+ return [
113
+ {
114
+ _name: name,
115
+ _value: value.length === 0 ? [true] : value,
116
+ } satisfies InterAttributeNode,
117
+ ];
118
+ }),
119
+ _content: el.childNodes.length > 0 ? dom2treenode(el.childNodes) : [],
120
+ _sourceNode: isVariableElem ? null : el,
121
+ _sourceIndex: index,
122
+ });
123
+
124
+ if (!isVariableElem) {
125
+ continue;
126
+ }
127
+ } else if (item.nodeType === 3 /*Node.TEXT_NODE*/) {
128
+ const result = parseText((item as Text).data);
129
+ if (result.length > 0) {
130
+ nodeList.push({
131
+ _type: InterNodeType.Text,
132
+ _data: result,
133
+ });
172
134
  }
173
- while (false);
135
+ }
136
+ (item as Comment | Text | Element).remove();
137
+ index--;
174
138
  }
175
139
  return nodeList;
176
140
  };
177
-
178
- const rootList = unwrapNode(document);
179
-
180
- const treeNode = dom2treenode(rootList);
181
-
141
+ const treeNode = dom2treenode(markup2nodeList(htmlStr));
182
142
  cache.set(segments, treeNode);
183
-
184
143
  return treeNode;
185
144
  };
186
145
 
187
146
  const element = (
188
147
  ns: string | null,
189
148
  sourceNode: Element | null,
190
- tagName: string | (() => string) | ((...props: any[]) => Node[]),
191
- attrs: InterAttributeNode[],
192
- values: any[],
193
- child: () => Node[],
149
+ tagName: string | (() => string),
150
+ child: (el: Element) => Node[],
194
151
  ): Node[] => {
195
- if (typeof tagName === 'function') {
196
- if (isReactive(tagName)) {
197
- return fragment(() => {
198
- const realTagName = tagName();
199
- return element(ns, null, realTagName, attrs, values, child);
200
- });
201
- } else {
202
- return fragment(() => {
203
- return tagName(
204
- ...attrs.flatMap(({ _name: name, _value: value }) => {
205
- if (name[0] === '...' && typeof name[1] === 'number') {
206
- return unparseText(name.slice(1), values);
207
- } else {
208
- return unparseText(value, values);
209
- }
210
- }),
211
- child,
212
- ) as Node[];
213
- });
214
- }
152
+ if (isFun(tagName)) {
153
+ return fragment(() => {
154
+ const realTagName = tagName();
155
+ if (!isStr(realTagName)) {
156
+ throw new Error(`Expecting string returned from ${tagName} but get ${realTagName}`, { cause: tagName });
157
+ }
158
+ return element(ns, null, realTagName, child);
159
+ });
215
160
  }
216
- if (typeof tagName !== 'string') {
161
+ if (!isStr(tagName)) {
217
162
  return [];
218
163
  }
219
- const el = sourceNode || (ns ? document.createElementNS(ns, tagName) : document.createElement(tagName));
164
+ const el = deferRemove(sourceNode || (ns ? document.createElementNS(ns, tagName) : document.createElement(tagName)));
220
165
  S(() => {
221
- for (const { _name: name, _value: value } of attrs) {
222
- if (name[0] === '...' && typeof name[1] === 'number') {
223
- spreadAttrs(el, unparseText(name.slice(1), values));
224
- } else {
225
- attribute(el, unparseText(name, values), unparseText(value, values));
226
- }
227
- }
228
- const subnodes = child();
166
+ const subnodes = child(el);
229
167
  if (subnodes.length === 0) {
230
168
  return;
231
169
  }
@@ -242,28 +180,21 @@ const element = (
242
180
  }
243
181
  }
244
182
  });
245
- cleanup(() => el.remove());
246
183
  return [el];
247
184
  };
248
185
  let fragmentHeadNode: null | Comment = null;
249
186
  const fragment = (child: () => Node[]): Node[] => {
250
- const firstNode = new Comment('');
251
- cleanup(() => firstNode.remove());
187
+ const firstNode = deferRemove(new Text(''));
252
188
  const tempArray: Node[] = [firstNode];
253
189
  S(() => {
254
190
  fragmentHeadNode = firstNode;
255
191
  const c = child();
256
192
  const carr = Array.isArray(c) ? c : [c];
257
-
258
- const nodified = carr.map((c) => {
259
- if (c instanceof Node) {
260
- return c;
261
- }
262
- const node = new Text(c);
263
- cleanup(() => node.remove());
264
- return node;
265
- });
266
-
193
+ const nodified = carr.flatMap((c) =>
194
+ c == null ? []
195
+ : c instanceof Node ? c
196
+ : deferRemove(new Text(c)),
197
+ );
267
198
  if (firstNode.isConnected) {
268
199
  firstNode.after(...nodified);
269
200
  } else {
@@ -273,218 +204,82 @@ const fragment = (child: () => Node[]): Node[] => {
273
204
 
274
205
  return tempArray;
275
206
  };
276
- function arrTrans<T>(a: readonly T[], b: readonly T[], a2b: (a: T) => void, dela: (a: T) => void, keep: (a: T) => void): void {
277
- const aLen = a.length;
278
- const bLen = b.length;
279
-
280
- if (a === b || (aLen === 0 && bLen === 0)) {
281
- return;
282
- }
283
-
284
- let start = 0;
285
- const minLen = Math.min(aLen, bLen);
286
- while (start < minLen && a[start] === b[start]) {
287
- start++;
288
- }
289
-
290
- let end = 0;
291
- while (end < minLen - start && a[aLen - 1 - end] === b[bLen - 1 - end]) {
292
- end++;
293
- }
294
-
295
- const aMidLen = aLen - start - end;
296
- const bMidLen = bLen - start - end;
297
-
298
- if (aMidLen === 0 && bMidLen === 0) {
299
- for (let i = 0; i < start; i++) {
300
- keep(a[i]!);
301
- }
302
- for (let i = 0; i < end; i++) {
303
- keep(a[aLen - end + i]!);
304
- }
305
- return;
306
- }
307
-
308
- const bIndexMap = new Map<T, number>();
309
- for (let i = 0; i < bMidLen; i++) {
310
- bIndexMap.set(b[start + i]!, i);
311
- }
312
-
313
- const indices: number[] = [];
314
- for (let i = 0; i < aMidLen; i++) {
315
- const val = a[start + i]!;
316
- const bIdx = bIndexMap.get(val);
317
- if (bIdx !== undefined) {
318
- indices.push(bIdx);
319
- }
320
- }
321
-
322
- const tails: number[] = [];
323
- const parents: number[] = new Array(indices.length).fill(-1);
324
- const tailIndices: number[] = [];
325
-
326
- for (let i = 0; i < indices.length; i++) {
327
- const idx = indices[i]!;
328
-
329
- let left = 0;
330
- let right = tails.length;
331
- while (left < right) {
332
- const mid = (left + right) >> 1;
333
- if (tails[mid]! < idx) {
334
- left = mid + 1;
335
- } else {
336
- right = mid;
337
- }
338
- }
339
-
340
- const pos = left;
341
- if (pos === tails.length) {
342
- tails.push(idx);
343
- tailIndices.push(i);
344
- } else {
345
- tails[pos] = idx;
346
- tailIndices[pos] = i;
347
- }
348
-
349
- if (pos > 0) {
350
- parents[i] = tailIndices[pos - 1]!;
351
- }
352
- }
353
-
354
- const keepSet = new Set<T>();
355
- let curr = tailIndices.length > 0 ? tailIndices[tailIndices.length - 1]! : -1;
356
- while (curr !== -1) {
357
- const bIdx = indices[curr]!;
358
- keepSet.add(b[start + bIdx]!);
359
- curr = parents[curr]!;
360
- }
361
-
362
- for (let i = 0; i < start; i++) {
363
- keep(a[i]!);
364
- }
365
-
366
- for (let i = 0; i < aMidLen; i++) {
367
- const val = a[start + i]!;
368
- if (!keepSet.has(val)) {
369
- dela(val);
370
- }
371
- }
372
-
373
- for (let i = 0; i < bMidLen; i++) {
374
- const val = b[start + i]!;
375
- if (keepSet.has(val)) {
376
- keep(val);
377
- } else {
378
- a2b(val);
379
- }
380
- }
381
-
382
- for (let i = 0; i < end; i++) {
383
- keep(a[aLen - end + i]!);
384
- }
385
- }
386
207
  interface MapState<T> {
387
- _itemDestroyer: Array<() => void>;
388
- _domNodesOfItem: Node[][];
389
- _arrayValue: T[];
208
+ _killer: Array<() => void>;
209
+ _nodes: Node[][];
210
+ _arr: T[];
390
211
  }
391
212
  export const map = <T>(array: () => T[], f: (item: T) => Node[]): Node[] => {
392
213
  return fragment(() => {
393
214
  const headNode = fragmentHeadNode;
394
- cleanup(() => {
395
- mapState()._itemDestroyer.forEach((f) => f());
396
- });
397
- const createItem = (item: T) => {
398
- return root((destroyItem) => {
399
- const domNodes = f(item);
400
- return [destroyItem, domNodes] as [() => void, Node[]];
401
- });
402
- };
215
+ cleanup(() => mapState()._killer.forEach((f) => f()));
216
+ const createItem = (item: T) => root((destroyItem) => [destroyItem, f(item)] as [() => void, Node[]]);
403
217
  const mapState = S<MapState<T>>(
404
218
  (
405
219
  preState = {
406
- _arrayValue: [] as T[],
407
- _domNodesOfItem: [],
408
- _itemDestroyer: [],
220
+ _arr: [] as T[],
221
+ _nodes: [],
222
+ _killer: [],
409
223
  },
410
224
  ) => {
411
225
  const currentArrayValue = array();
412
- const preArrayValue = preState._arrayValue;
226
+ const preArrayValue = preState._arr;
413
227
  const newState: MapState<T> = {
414
- _arrayValue: currentArrayValue,
415
- _domNodesOfItem: [],
416
- _itemDestroyer: [],
228
+ _arr: currentArrayValue,
229
+ _nodes: [],
230
+ _killer: [],
417
231
  };
418
- arrTrans(
232
+ arr(
419
233
  preArrayValue,
420
234
  currentArrayValue,
235
+ (oldItem) => preState._killer[preArrayValue.indexOf(oldItem)]!(),
421
236
  (newItem) => {
422
237
  const [destroyer, domNodes] = createItem(newItem);
423
- const preNode = newState._domNodesOfItem.at(-1)?.at(-1) ?? headNode;
424
- (preNode as Element | Comment).after(...domNodes);
425
- newState._domNodesOfItem.push(domNodes);
426
- newState._itemDestroyer.push(destroyer);
238
+ const preNode = (newState._nodes.at(-1)?.at(-1) ?? headNode) as Element | Comment;
239
+ preNode.after(...domNodes);
240
+ newState._nodes.push(domNodes);
241
+ newState._killer.push(destroyer);
427
242
  },
428
243
  (oldItem) => {
429
244
  const i = preArrayValue.indexOf(oldItem);
430
- preState._itemDestroyer[i]!();
245
+ newState._nodes.push(preState._nodes[i]!);
246
+ newState._killer.push(preState._killer[i]!);
431
247
  },
432
248
  (oldItem) => {
433
249
  const i = preArrayValue.indexOf(oldItem);
434
- newState._domNodesOfItem.push(preState._domNodesOfItem[i]!);
435
- newState._itemDestroyer.push(preState._itemDestroyer[i]!);
250
+ const preNode = (newState._nodes.at(-1)?.at(-1) ?? headNode) as Element | Comment;
251
+ const domNodes = preState._nodes[i]!;
252
+ preNode.after(...domNodes);
253
+ newState._nodes.push(domNodes);
254
+ newState._killer.push(preState._killer[i]!);
436
255
  },
437
256
  );
438
257
  return newState;
439
258
  },
440
259
  );
441
- return sample(() => mapState()._domNodesOfItem.flat());
260
+ return sample(() => mapState()._nodes.flat());
442
261
  });
443
262
  };
444
263
 
445
- const isPropertyOf = (domEl: HTMLElement | SVGElement, traitName: string) => {
446
- let descriptor;
447
- do {
448
- descriptor = Object.getOwnPropertyDescriptor(domEl, traitName);
449
- domEl = Object.getPrototypeOf(domEl);
450
- } while (domEl && !descriptor);
451
- if (!descriptor) {
452
- return false;
453
- }
454
- if (false === descriptor.writable) {
455
- return false;
456
- }
457
- if (descriptor.get && !descriptor.set) {
458
- return false;
459
- }
460
- return true;
264
+ const isPropertyOf = (el: HTMLElement | SVGElement, key: string) => {
265
+ if (!(key in el)) return false;
266
+ let d;
267
+ while (el && !(d = Object.getOwnPropertyDescriptor(el, key))) el = Object.getPrototypeOf(el);
268
+ return d && d.writable !== false && !!(!d.get || d.set);
461
269
  };
462
270
 
463
271
  const attribute = (node: Element, key: string | (() => string), val: string | (() => any)) => {
464
- if (typeof key !== 'function' && !isReactive(val)) {
465
- if (!node) {
466
- return;
467
- }
468
- if (!(node instanceof Element)) {
469
- return;
470
- }
471
- const isProp = isPropertyOf(node as any, key);
472
- if (isProp) {
473
- const preValue = (node as any)[key];
272
+ if (!(node instanceof Element)) {
273
+ return;
274
+ }
275
+ if (!isFun(key) && !isReactive(val)) {
276
+ if (isPropertyOf(node as any, key)) {
474
277
  (node as any)[key] = val;
475
- cleanup(() => {
476
- (node as any)[key] = preValue;
477
- });
478
278
  } else {
479
279
  node.setAttribute(key, String(val));
480
- cleanup(() => {
481
- node.removeAttribute(key);
482
- });
483
280
  }
484
281
  } else {
485
- S(() => {
486
- attribute(node, typeof key === 'function' ? key() : key, isReactive(val) ? val() : val);
487
- });
282
+ S(() => attribute(node, callif(key), isReactive(val) ? val() : val));
488
283
  }
489
284
  };
490
285
 
@@ -500,31 +295,31 @@ const spreadAttrs = (node: Element, value: () => any) => {
500
295
  const tree2dom = (ns: string | null, node: InterTreeNode, values: any[]): Node[] => {
501
296
  if (node._type === InterNodeType.Text) {
502
297
  return node._data.flatMap((part) => {
503
- if (typeof part === 'string') {
504
- const textNode = new Text(part);
505
- cleanup(() => textNode.remove());
506
- return textNode;
507
- } else if (typeof part === 'number') {
508
- const value = values[part];
509
- if (typeof value === 'function') {
510
- if (isReactive(value)) {
511
- return fragment(() => value());
512
- } else {
513
- return fragment(value);
514
- }
515
- } else if (typeof value !== 'object') {
516
- const textNode = new Text(String(value));
517
- cleanup(() => textNode.remove());
518
- return textNode;
519
- }
520
- return value;
298
+ if (isStr(part)) {
299
+ return deferRemove(new Text(part));
300
+ }
301
+ const value = values[part];
302
+ if (isFun(value)) {
303
+ return fragment(value);
304
+ } else if (null == value) {
305
+ return [];
306
+ } else if (typeof value !== 'object') {
307
+ return deferRemove(new Text(String(value)));
521
308
  }
309
+ return value;
522
310
  });
523
311
  } else {
524
312
  const { _attrs: attrs, _content: content, _tag: tag, _sourceNode: sourceNode } = node;
525
313
  const tagValue = unparseText(tag, values);
526
314
  const currentNs = node._ns ?? ns;
527
- return element(currentNs, sourceNode, tagValue, attrs, values, () => {
315
+ return element(currentNs, sourceNode, tagValue, (el) => {
316
+ for (const { _name: name, _value: value } of attrs) {
317
+ if (name[0] === '...' && isNum(name[1])) {
318
+ spreadAttrs(el, unparseText(name.slice(1), values));
319
+ } else {
320
+ attribute(el, unparseText(name, values), unparseText(value, values));
321
+ }
322
+ }
528
323
  return content.flatMap((t) => tree2dom(currentNs, t, values));
529
324
  });
530
325
  }
@@ -550,35 +345,19 @@ const cloneTreeNode = (treeNode: InterTreeNode, parentNode: Element | null): Int
550
345
  };
551
346
 
552
347
  const tag =
553
- (
554
- ns: string | null,
555
- docType: DOMParserSupportedType,
556
- wrapSource: (source: string) => string,
557
- unwrapNode: (node: Document) => NodeList,
558
- ) =>
348
+ (ns: string | null, markup2nodeList: (markup: string) => NodeList) =>
559
349
  (segments: TemplateStringsArray, ...values: any[]): Node[] => {
560
350
  const tree = parse(
561
- docType,
562
- wrapSource,
563
- unwrapNode,
351
+ markup2nodeList,
564
352
  segments,
565
353
  values.map((it) => values.indexOf(it)),
566
354
  );
567
355
  const clonedTree = tree.map((tree) => cloneTreeNode(tree, null));
568
- return fragment(() => {
569
- return clonedTree.flatMap((item) => tree2dom(ns, item, values));
570
- });
356
+ return fragment(() => clonedTree.flatMap((item) => tree2dom(ns, item, values)));
571
357
  };
572
358
  const svgns = 'http://www.w3.org/2000/svg';
573
359
  export const svg = tag(
574
360
  svgns,
575
- 'text/xml',
576
- (svg) => `<svg xmlns="${svgns}">${svg}</svg>`,
577
- (doc) => doc.firstChild!.childNodes,
578
- );
579
- export const html = tag(
580
- null,
581
- 'text/html',
582
- (html) => html,
583
- (doc) => doc.body.childNodes,
361
+ (svgStr) => domParser.parseFromString(`<svg xmlns="${svgns}">${svgStr}</svg>`, 'text/xml').firstChild!.childNodes,
584
362
  );
363
+ export const html = tag(null, (htmlStr) => domParser.parseFromString(htmlStr, 'text/html').body.childNodes);