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