vaderjs 1.4.1-lv56aadeg5 → 1.4.2-bml56
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/.editorconfig +11 -0
- package/.vscode/c_cpp_properties.json +21 -0
- package/.vscode/settings.json +12 -0
- package/README.md +35 -198
- package/binaries/Kalix/index.js +668 -0
- package/binaries/compiler/main.js +461 -0
- package/binaries/vader.js +74 -0
- package/binaries/watcher/hmr.js +40 -0
- package/client/index.d.ts +226 -0
- package/client/runtime/index.js +417 -0
- package/client/runtime/router.js +235 -0
- package/config/index.ts +68 -0
- package/index.ts +344 -0
- package/package.json +14 -25
- package/plugins/cloudflare/functions/index.js +98 -0
- package/plugins/cloudflare/toCopy/@server/Kalix/index.js +625 -0
- package/plugins/cloudflare/toCopy/@server/cloudflare_ssr/index.js +85 -0
- package/plugins/cloudflare/toCopy/node_modules/vaderjs/server/index.js +99 -0
- package/plugins/cloudflare/toCopy/src/client.js +432 -0
- package/plugins/cloudflare/toCopy/src/router.js +235 -0
- package/plugins/ssg/index.js +127 -0
- package/plugins/vercel/functions/index.ts +8 -0
- package/router/index.ts +181 -0
- package/server/index.js +129 -0
- package/vader_dev.js +177 -0
- package/@integrations/ssg.js +0 -163
- package/LICENSE +0 -21
- package/binaries/IPC/index.js +0 -277
- package/binaries/main.js +0 -1328
- package/binaries/readme.md +0 -4
- package/binaries/watcher.js +0 -74
- package/binaries/win32/check.ps1 +0 -7
- package/client/index.js +0 -441
- package/config/index.js +0 -36
- package/logo.png +0 -0
- package/runtime/router.js +0 -1
- package/runtime/vader.js +0 -1
- package/vader.js +0 -230
|
@@ -0,0 +1,668 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module DOMParser
|
|
3
|
+
* @description The DOMParser interface provides the ability to parse HTML source code from a string into a DOM Document.
|
|
4
|
+
*/
|
|
5
|
+
export class DOMParser {
|
|
6
|
+
/**
|
|
7
|
+
* @decription - parse html to a document
|
|
8
|
+
* @param {string} html
|
|
9
|
+
* @returns {Document}
|
|
10
|
+
*/
|
|
11
|
+
parseFromString(html) {
|
|
12
|
+
let doc = new Document();
|
|
13
|
+
let t = new Bun.Transpiler({
|
|
14
|
+
loader: "tsx", // "js | "jsx" | "ts" | "tsx",
|
|
15
|
+
target: "browser",
|
|
16
|
+
define: {
|
|
17
|
+
"jsxDEV": "Element",
|
|
18
|
+
"jsx": "Element"
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
let el = t.transformSync(`
|
|
23
|
+
|
|
24
|
+
const html = ${html}
|
|
25
|
+
function Doc() {
|
|
26
|
+
return (
|
|
27
|
+
<html>
|
|
28
|
+
<body>${html}</body>
|
|
29
|
+
</html>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
return Doc()
|
|
33
|
+
` )
|
|
34
|
+
el = el.replaceAll(`jsxDEV`, `Element`)
|
|
35
|
+
let evaluated = eval(`(function(){${el}})()`)
|
|
36
|
+
evaluated.children.forEach((child) => {
|
|
37
|
+
child.outerHTML = child.toString()
|
|
38
|
+
})
|
|
39
|
+
doc.tree = evaluated.children
|
|
40
|
+
doc.body = evaluated.children[0]
|
|
41
|
+
doc.body.outerHTML = evaluated.children[0].toString()
|
|
42
|
+
doc.body.firstChild = evaluated.children[0].children[0]
|
|
43
|
+
doc.documentElement = evaluated
|
|
44
|
+
doc.documentElement.outerHTML = evaluated.children[0].toString()
|
|
45
|
+
this.tree = evaluated.children
|
|
46
|
+
return doc
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* @description - Returns a string containing the HTML serialization of the element's descendants.
|
|
50
|
+
* @returns {string}
|
|
51
|
+
*/
|
|
52
|
+
toString() {
|
|
53
|
+
return this.tree.toString();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
export class HTMLTextNode {
|
|
58
|
+
constructor(text) {
|
|
59
|
+
this.nodeValue = text;
|
|
60
|
+
this.nodeType = 3;
|
|
61
|
+
this.tagName = "TEXT_ELEMENT";
|
|
62
|
+
this.props = { nodeValue: text };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
toString() {
|
|
66
|
+
return this.nodeValue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
insertBefore(node) {
|
|
70
|
+
this.nodeValue = `${node.toString()}${this.nodeValue}`;
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export class HTMLElement {
|
|
75
|
+
constructor(tagName, props, children) {
|
|
76
|
+
this.tagName = tagName;
|
|
77
|
+
this.props = props;
|
|
78
|
+
this.children = children;
|
|
79
|
+
this.outerHTML = this.toString("outerHTML");
|
|
80
|
+
this.innerHTML = this.toString("innerHTML");
|
|
81
|
+
this.textContent = this.toString("innerText");
|
|
82
|
+
/**
|
|
83
|
+
* @type {HTMLElement | HTMLTextNode}
|
|
84
|
+
*/
|
|
85
|
+
this.firstChild = this.children[0];
|
|
86
|
+
this.style = props?.style || {};
|
|
87
|
+
|
|
88
|
+
this.attributes = props;
|
|
89
|
+
this.events = [];
|
|
90
|
+
/**
|
|
91
|
+
* @type {string | null}
|
|
92
|
+
*/
|
|
93
|
+
this.id = props?.id || null;
|
|
94
|
+
this.nodeType = 1;
|
|
95
|
+
this.accessKey = null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @description - Returns a string containing the HTML serialization of the element's descendants.
|
|
100
|
+
* @param {string} type - outerHTML, innerHTML, innerText
|
|
101
|
+
* @returns {string}
|
|
102
|
+
*/
|
|
103
|
+
toString(type = "outerHTML") {
|
|
104
|
+
switch (type) {
|
|
105
|
+
case "outerHTML":
|
|
106
|
+
if (this.tagName === "TEXT_ELEMENT") {
|
|
107
|
+
return this.props.nodeValue;
|
|
108
|
+
}
|
|
109
|
+
let props = "";
|
|
110
|
+
for (let key in this.props) {
|
|
111
|
+
if (key !== 'style' && key !== 'ref' && !key.startsWith('on')) {
|
|
112
|
+
props += `${key}="${this.props[key]}" `
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
let children = this.children
|
|
116
|
+
.map((child) => {
|
|
117
|
+
return child.toString();
|
|
118
|
+
}).join("");
|
|
119
|
+
if (this.attributes?.style) {
|
|
120
|
+
for (let key in this.attributes.style) {
|
|
121
|
+
this.style[key] = this.attributes.style[key]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
if (this.attributes && Object.keys(this.attributes).length > 0) {
|
|
127
|
+
props += ` ${Object.keys(this.attributes).map((key) =>{
|
|
128
|
+
if(key !== 'style' && !props.includes(key) && key !== 'ref' && !key.startsWith('on')){
|
|
129
|
+
return `${key}="${this.attributes[key]}"`
|
|
130
|
+
}
|
|
131
|
+
}).join(' ')}`
|
|
132
|
+
}
|
|
133
|
+
if (this.style && Object.keys(this.style).length > 0) {
|
|
134
|
+
props += ` style="${handleStyles(this.style, this)}"`
|
|
135
|
+
}
|
|
136
|
+
if (this.props?.id) {
|
|
137
|
+
this.id = this.props.id
|
|
138
|
+
}
|
|
139
|
+
return `<${this.tagName} ${props}>${children}</${this.tagName}>`;
|
|
140
|
+
case "innerHTML":
|
|
141
|
+
return this.children.map((child) => {
|
|
142
|
+
child.toString("outerHTML");
|
|
143
|
+
child.toString("innerHTML");
|
|
144
|
+
child.toString("innerText");
|
|
145
|
+
return child.toString("outerHTML");
|
|
146
|
+
}).join("");
|
|
147
|
+
case "innerText":
|
|
148
|
+
let string = ''
|
|
149
|
+
this.children
|
|
150
|
+
.map((child) => {
|
|
151
|
+
if(child instanceof HTMLTextNode){
|
|
152
|
+
string += child.nodeValue
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
.join("");
|
|
156
|
+
return string;
|
|
157
|
+
|
|
158
|
+
case "innerText":
|
|
159
|
+
return this.children
|
|
160
|
+
.map((child) => {
|
|
161
|
+
return child.toString();
|
|
162
|
+
})
|
|
163
|
+
.join("");
|
|
164
|
+
default:
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* @description - Appends a node as the last child of a node.
|
|
170
|
+
* @param {HTMLElement|HTMLTextNode} child
|
|
171
|
+
* @returns
|
|
172
|
+
*/
|
|
173
|
+
appendChild(child) {
|
|
174
|
+
this.outerHTML = this.toString("outerHTML");
|
|
175
|
+
this.innerHTML = this.toString("innerHTML");
|
|
176
|
+
this.textContent = this.toString("innerText");
|
|
177
|
+
if (!this.children.includes(child)) {
|
|
178
|
+
this.children.push(child);
|
|
179
|
+
this.outerHTML = this.toString("outerHTML");
|
|
180
|
+
this.innerHTML = this.toString("innerHTML");
|
|
181
|
+
}
|
|
182
|
+
return this;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* @description - set the content of the element
|
|
186
|
+
* @param {string} content
|
|
187
|
+
* @returns {HTMLElement}
|
|
188
|
+
*/
|
|
189
|
+
setContent(content) {
|
|
190
|
+
let textNode = new HTMLTextNode(content);
|
|
191
|
+
this.children = [textNode];
|
|
192
|
+
this.outerHTML = this.toString("outerHTML");
|
|
193
|
+
this.innerHTML = this.toString("innerHTML");
|
|
194
|
+
return this;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* @description - Appends a set of Node objects or DOMString objects after the last child of the ParentNode.
|
|
199
|
+
* @param {...any} children
|
|
200
|
+
* @returns {HTMLElement}
|
|
201
|
+
*/
|
|
202
|
+
append(...children) {
|
|
203
|
+
this.outerHTML = this.toString("outerHTML");
|
|
204
|
+
this.innerHTML = this.toString("innerHTML");
|
|
205
|
+
this.textContent = this.toString("innerText");
|
|
206
|
+
this.children = [...this.children, ...children];
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* @description - Inserts a set of Node objects or DOMString objects after the last child of the ParentNode.
|
|
211
|
+
* @param {HTMLElement | HTMLTextNode} node1
|
|
212
|
+
* @param {HTMLElement | HTMLTextNode} node2
|
|
213
|
+
* @returns {HTMLElement}
|
|
214
|
+
*/
|
|
215
|
+
insertBefore(node1, node2) {
|
|
216
|
+
this.outerHTML = this.toString("outerHTML");
|
|
217
|
+
this.innerHTML = this.toString("innerHTML");
|
|
218
|
+
this.textContent = this.toString("innerText");
|
|
219
|
+
this.children = this.children.map((child) => {
|
|
220
|
+
if (child === node2) {
|
|
221
|
+
return node1;
|
|
222
|
+
}
|
|
223
|
+
return child;
|
|
224
|
+
});
|
|
225
|
+
return this;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* @description - Inserts a set of Node objects or DOMString objects after the last child of the ParentNode.
|
|
229
|
+
* @param {HTMLElement|HTMLTextNode} child
|
|
230
|
+
* @returns {HTMLElement}
|
|
231
|
+
*/
|
|
232
|
+
prepend(child) {
|
|
233
|
+
this.outerHTML = this.toString("outerHTML");
|
|
234
|
+
this.innerHTML = this.toString("innerHTML");
|
|
235
|
+
this.textContent = this.toString("innerText");
|
|
236
|
+
this.children = [child, ...this.children];
|
|
237
|
+
return this;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
*
|
|
242
|
+
*/
|
|
243
|
+
remove() {
|
|
244
|
+
this.outerHTML = "";
|
|
245
|
+
this.innerHTML = "";
|
|
246
|
+
this.textContent = "";
|
|
247
|
+
this.children = [];
|
|
248
|
+
return this;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* @description - Removes a child node from the DOM
|
|
252
|
+
* @param {Object} child
|
|
253
|
+
* @returns {HTMLElement}
|
|
254
|
+
*/
|
|
255
|
+
removeChild(child) {
|
|
256
|
+
this.children = this.children.filter((c) => c !== child);
|
|
257
|
+
this.innerHTML = this.toString("innerHTML");
|
|
258
|
+
this.outerHTML = this.toString("outerHTML");
|
|
259
|
+
this.textContent = this.toString("innerText");
|
|
260
|
+
return this;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* @description - Search for a specific attribute and returns the value of the attribute.
|
|
265
|
+
* @param {string} name
|
|
266
|
+
* @returns {string | null}
|
|
267
|
+
*/
|
|
268
|
+
getAttribute(name) {
|
|
269
|
+
switch (true){
|
|
270
|
+
case Object.keys(this.props).includes(name):
|
|
271
|
+
return this.props[name]
|
|
272
|
+
case Object.keys(this.attributes).includes(name):
|
|
273
|
+
return this.attributes[name]
|
|
274
|
+
}
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* @description - Sets the value of an attribute on the specified element. If the attribute already exists, the value is updated; otherwise a new attribute is added with the specified name and value.
|
|
279
|
+
* @param {string} name
|
|
280
|
+
* @param {string} value
|
|
281
|
+
* @returns {HTMLElement}
|
|
282
|
+
*/
|
|
283
|
+
|
|
284
|
+
setAttribute(name, value) {
|
|
285
|
+
this.props[name] = value;
|
|
286
|
+
this.attributes[name] = value;
|
|
287
|
+
this.outerHTML = this.toString("outerHTML");
|
|
288
|
+
this.innerHTML = this.toString("innerHTML");
|
|
289
|
+
this.textContent = this.toString("innerText");
|
|
290
|
+
return this;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* @method classList
|
|
295
|
+
* @description - add, remove, toggle, or check the presence of a class in the class attribute of an element
|
|
296
|
+
* @returns {(add: (className: string) => HTMLElement | HTMLTextNode | null, remove: (className: string) => HTMLElement | HTMLTextNode | null, toggle: (className: string) => HTMLElement | HTMLTextNode | null, contains: (className: string) => boolean) => HTMLElement | HTMLTextNode | null}
|
|
297
|
+
*/
|
|
298
|
+
classList = {
|
|
299
|
+
add: (className) => {
|
|
300
|
+
this.props.className = `${this.props.className} ${className}`;
|
|
301
|
+
return this;
|
|
302
|
+
},
|
|
303
|
+
remove: (className) => {
|
|
304
|
+
this.props.className = this.props.className.replace(className, "");
|
|
305
|
+
return this;
|
|
306
|
+
},
|
|
307
|
+
toggle: (className) => {
|
|
308
|
+
if (this.props.className.includes(className)) {
|
|
309
|
+
this.props.className = this.props.className.replace(className, "");
|
|
310
|
+
} else {
|
|
311
|
+
this.props.className = `${this.props.className} ${className}`;
|
|
312
|
+
}
|
|
313
|
+
return this;
|
|
314
|
+
},
|
|
315
|
+
contains: (className) => {
|
|
316
|
+
return this.attributes["class" || "className"].includes(className);
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
*
|
|
322
|
+
* @param {string} selector
|
|
323
|
+
* @returns {HTMLElement}
|
|
324
|
+
*/
|
|
325
|
+
querySelector(selector) {
|
|
326
|
+
switch (true) {
|
|
327
|
+
case selector.startsWith("."):
|
|
328
|
+
this.innerHTML = this.toString("innerHTML");
|
|
329
|
+
this.outerHTML = this.toString("outerHTML");
|
|
330
|
+
this.textContent = this.toString("innerText");
|
|
331
|
+
return this.children.find((child) => {
|
|
332
|
+
child.outerHTML = child.toString();
|
|
333
|
+
return child.props.className.includes(selector.substring(1));
|
|
334
|
+
});
|
|
335
|
+
case selector.startsWith("#"):
|
|
336
|
+
this.innerHTML = this.toString("innerHTML");
|
|
337
|
+
this.outerHTML = this.toString("outerHTML");
|
|
338
|
+
this.textContent = this.toString("innerText");
|
|
339
|
+
return this.children.find((child) => {
|
|
340
|
+
child.outerHTML = child.toString();
|
|
341
|
+
return child.props.id === selector.substring(1);
|
|
342
|
+
});
|
|
343
|
+
default:
|
|
344
|
+
this.innerHTML = this.toString("innerHTML");
|
|
345
|
+
this.outerHTML = this.toString("outerHTML");
|
|
346
|
+
this.textContent = this.toString("innerText");
|
|
347
|
+
let child = this.children.find((child) => {
|
|
348
|
+
child.outerHTML = child.toString();
|
|
349
|
+
return child.tagName === selector;
|
|
350
|
+
}
|
|
351
|
+
);
|
|
352
|
+
if (!child) {
|
|
353
|
+
// check if children of children have the selector
|
|
354
|
+
this.innerHTML = this.toString("innerHTML");
|
|
355
|
+
this.outerHTML = this.toString("outerHTML");
|
|
356
|
+
this.textContent = this.toString("innerText");
|
|
357
|
+
this.children.forEach((c) => {
|
|
358
|
+
if (c.children) {
|
|
359
|
+
child = c.children.find((child) => {
|
|
360
|
+
child.outerHTML = child.toString("outerHTML");
|
|
361
|
+
child.innerHTML = child.toString("innerHTML");
|
|
362
|
+
return child.tagName === selector;
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
})
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return child;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* @description - Returns a list of elements with the given tag name. The subtree underneath the specified element is searched, excluding the element itself.
|
|
374
|
+
* @param {string} selector
|
|
375
|
+
* @returns {Array<HTMLElement | HTMLTextNode>}
|
|
376
|
+
*/
|
|
377
|
+
querySelectorAll(selector) {
|
|
378
|
+
switch (true) {
|
|
379
|
+
case selector.startsWith("."):
|
|
380
|
+
return this.children.filter((child) => {
|
|
381
|
+
return child.props.className.includes(selector.substring(1));
|
|
382
|
+
});
|
|
383
|
+
case selector === '*':
|
|
384
|
+
return this.children;
|
|
385
|
+
case selector.startsWith("#"):
|
|
386
|
+
return this.children.filter((child) => {
|
|
387
|
+
return child.props.id === selector.substring(1);
|
|
388
|
+
});
|
|
389
|
+
default:
|
|
390
|
+
return this.children.filter((child) => {
|
|
391
|
+
return child.tagName === selector;
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
parseHTML(html) {
|
|
396
|
+
const parser = new DOMParser();
|
|
397
|
+
const parsed = parser.parseFromString(html);
|
|
398
|
+
return parsed;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* @module Document
|
|
403
|
+
* @description - The Document interface represents any web page loaded in the browser and serves as an entry point into the web page's content, which is the DOM tree.
|
|
404
|
+
*/
|
|
405
|
+
export class Document {
|
|
406
|
+
constructor() {
|
|
407
|
+
this.tree = [];
|
|
408
|
+
/**
|
|
409
|
+
* @description - Returns the <body> or <frameset> node of the current document.
|
|
410
|
+
* @type {HTMLElement}
|
|
411
|
+
*/
|
|
412
|
+
this.body = new HTMLElement("body", {}, []);
|
|
413
|
+
/**
|
|
414
|
+
* @description - Document.documentElement returns the Element that is the root element of the document
|
|
415
|
+
* @type {HTMLElement}
|
|
416
|
+
* @returns {{outerHTML: string, innerHTML: string}}
|
|
417
|
+
*/
|
|
418
|
+
this.documentElement = new HTMLElement("html", {}, [this.body]);
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* @description - Document.head returns the <head> element of the current document.
|
|
423
|
+
* @type {HTMLElement}
|
|
424
|
+
*/
|
|
425
|
+
this.head = new HTMLElement("head", {}, []);
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* @description - Returns the first child of a node, or the first child that is an element, and null if there are no child elements.
|
|
429
|
+
* **/
|
|
430
|
+
this.firstElementChild = null;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* @description - Creates a new Text node. This method can be used to escape HTML characters.
|
|
435
|
+
* @param {sring} text
|
|
436
|
+
* @returns {HTMLTextNode}
|
|
437
|
+
*/
|
|
438
|
+
createTextNode(text) {
|
|
439
|
+
return new HTMLTextNode(text);
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* @description - Creates a new element with the provided tag name or node object.
|
|
443
|
+
* @param {Object | string} nodeData
|
|
444
|
+
* @returns {HTMLElement}
|
|
445
|
+
*/
|
|
446
|
+
createElement(nodeData) {
|
|
447
|
+
if(!nodeData){
|
|
448
|
+
return new HTMLElement("div", {}, [])
|
|
449
|
+
}
|
|
450
|
+
if (typeof nodeData === 'string') {
|
|
451
|
+
return new HTMLElement(nodeData, {}, [])
|
|
452
|
+
}
|
|
453
|
+
let { tagName, props, children } = nodeData;
|
|
454
|
+
let node = new HTMLElement(tagName, props, children);
|
|
455
|
+
children = children.filter((child) => child !== null || child !== undefined)
|
|
456
|
+
node.children = children.map((child) => {
|
|
457
|
+
if (child.tagName === "TEXT_ELEMENT") {
|
|
458
|
+
return new HTMLTextNode(child);
|
|
459
|
+
}
|
|
460
|
+
if (child instanceof HTMLElement) {
|
|
461
|
+
return child;
|
|
462
|
+
}
|
|
463
|
+
return this.createElement(child);
|
|
464
|
+
});
|
|
465
|
+
return node;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* @description - Returns the first element that is a descendant of the element on which it is invoked that matches the specified group of selectors.
|
|
472
|
+
* @param {string} selector
|
|
473
|
+
* @returns {HTMLElement | HTMLTextNode | null}
|
|
474
|
+
*/
|
|
475
|
+
querySelector(selector) {
|
|
476
|
+
switch (true) {
|
|
477
|
+
case selector.startsWith("."):
|
|
478
|
+
return this.tree.find((child) => {
|
|
479
|
+
child.outerHTML = child.toString();
|
|
480
|
+
return child.props.className.includes(selector.substring(1));
|
|
481
|
+
});
|
|
482
|
+
case selector.startsWith("#"):
|
|
483
|
+
return this.tree.find((child) => {
|
|
484
|
+
return child.props.id === selector.substring(1);
|
|
485
|
+
});
|
|
486
|
+
default:
|
|
487
|
+
let child = this.tree.find((child) => {
|
|
488
|
+
child.outerHTML = child.toString();
|
|
489
|
+
return child.tagName === selector;
|
|
490
|
+
})
|
|
491
|
+
if (!child) {
|
|
492
|
+
// check if children of children have the selector
|
|
493
|
+
this.tree.forEach((c) => {
|
|
494
|
+
if (c.children) {
|
|
495
|
+
child = c.children.find((child) => {
|
|
496
|
+
child.outerHTML = child.toString();
|
|
497
|
+
return child.tagName === selector;
|
|
498
|
+
}
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
})
|
|
502
|
+
}
|
|
503
|
+
return child;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* @description - Returns a list of elements with the given tag name. The subtree underneath the specified element is searched, excluding the element itself.
|
|
509
|
+
* @param {string} selector
|
|
510
|
+
* @returns {Array<HTMLElement | HTMLTextNode>}
|
|
511
|
+
*/
|
|
512
|
+
querySelectorAll(selector) {
|
|
513
|
+
switch (true) {
|
|
514
|
+
case selector.startsWith("."):
|
|
515
|
+
return this.tree.filter((child) => {
|
|
516
|
+
return child.props.className.includes(selector.substring(1));
|
|
517
|
+
});
|
|
518
|
+
case selector.startsWith("#"):
|
|
519
|
+
return this.tree.filter((child) => {
|
|
520
|
+
return child.props.id === selector.substring(1);
|
|
521
|
+
});
|
|
522
|
+
default:
|
|
523
|
+
return this.tree.filter((child) => {
|
|
524
|
+
return child.tagName === selector;
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* @description - Returns a string containing the HTML serialization of the element's descendants.
|
|
532
|
+
* @returns {string}
|
|
533
|
+
*/
|
|
534
|
+
|
|
535
|
+
toString() {
|
|
536
|
+
this.tree.push(this.documentElement)
|
|
537
|
+
this.tree.push(this.head)
|
|
538
|
+
this.tree.push(this.body)
|
|
539
|
+
return this.tree.map((child) => {
|
|
540
|
+
return child.toString();
|
|
541
|
+
}).join("");
|
|
542
|
+
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function handleStyles(styles, nodeEl) {
|
|
548
|
+
let style = "";
|
|
549
|
+
for (let key in styles) {
|
|
550
|
+
let lower = key.replace(/([A-Z])/g, (g) => `-${g[0].toLowerCase()}`);
|
|
551
|
+
if (typeof styles[key] === "object") {
|
|
552
|
+
style += handleStyles(styles[key], nodeEl);
|
|
553
|
+
}
|
|
554
|
+
style += `${lower}:${styles[key]};`;
|
|
555
|
+
}
|
|
556
|
+
return style;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* @method Element
|
|
561
|
+
* @description - Create a virtual jsx DOM element
|
|
562
|
+
* @param {string} tag
|
|
563
|
+
* @param {Object} props
|
|
564
|
+
* @param {...any} children
|
|
565
|
+
* @returns
|
|
566
|
+
*/
|
|
567
|
+
export function Element(tag, props = {}, ...children) {
|
|
568
|
+
if(typeof tag === 'function'){
|
|
569
|
+
let el = tag(props, children)
|
|
570
|
+
return el
|
|
571
|
+
}
|
|
572
|
+
if(props === null){
|
|
573
|
+
props = {}
|
|
574
|
+
}
|
|
575
|
+
let node = {
|
|
576
|
+
tagName: tag,
|
|
577
|
+
props: props || {},
|
|
578
|
+
children: children,
|
|
579
|
+
_key: null,
|
|
580
|
+
innerHTML: "",
|
|
581
|
+
outerHTML: "",
|
|
582
|
+
textContent: "",
|
|
583
|
+
events: [],
|
|
584
|
+
parentNode: null,
|
|
585
|
+
appendChild: (child) => {
|
|
586
|
+
children.push(child);
|
|
587
|
+
return node;
|
|
588
|
+
},
|
|
589
|
+
querySelector: (selector) => {
|
|
590
|
+
switch (true) {
|
|
591
|
+
case selector.startsWith("."):
|
|
592
|
+
return children.find((child) => {
|
|
593
|
+
child.outerHTML = child.toString();
|
|
594
|
+
return child.props.className.includes(selector.substring(1));
|
|
595
|
+
});
|
|
596
|
+
case selector.startsWith("#"):
|
|
597
|
+
return children.find((child) => {
|
|
598
|
+
return child.props.id === selector.substring(1);
|
|
599
|
+
});
|
|
600
|
+
default:
|
|
601
|
+
let child = children.find((child) => {
|
|
602
|
+
return child.tagName === selector;
|
|
603
|
+
})
|
|
604
|
+
if (!child) {
|
|
605
|
+
// check if children of children have the selector
|
|
606
|
+
children.forEach((c) => {
|
|
607
|
+
if (c.children) {
|
|
608
|
+
child = c.children.find((child) => {
|
|
609
|
+
child.outerHTML = child.toString();
|
|
610
|
+
return child.tagName === selector;
|
|
611
|
+
}
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
})
|
|
615
|
+
}
|
|
616
|
+
return child;
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
if (props?.children) {
|
|
622
|
+
switch (true) {
|
|
623
|
+
case typeof props.children === 'string':
|
|
624
|
+
children = [props.children]
|
|
625
|
+
break;
|
|
626
|
+
case Array.isArray(props.children):
|
|
627
|
+
children = props.children
|
|
628
|
+
break;
|
|
629
|
+
default:
|
|
630
|
+
children = [props.children]
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
node.children = children
|
|
634
|
+
delete props.children
|
|
635
|
+
}
|
|
636
|
+
for (var i = 0; i < children.length; i++) {
|
|
637
|
+
if(typeof children[i] === 'undefined'){
|
|
638
|
+
delete children[i]
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
if (typeof children[i] === "string" || typeof children[i] === "number") {
|
|
642
|
+
children[i] = {
|
|
643
|
+
tagName: "TEXT_ELEMENT",
|
|
644
|
+
props: { nodeValue: children[i] },
|
|
645
|
+
_key: null,
|
|
646
|
+
parentNode: { tagName: tag, props: props, children: children, _key: null},
|
|
647
|
+
children: [],
|
|
648
|
+
};
|
|
649
|
+
children[i] = new HTMLTextNode(children[i].props.nodeValue);
|
|
650
|
+
} else {
|
|
651
|
+
if (children[i]) {
|
|
652
|
+
children[i].parentNode = { tagName: tag, props: props, children: children };
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
children[i] = new HTMLElement(children[i].tagName, children[i].props, children[i].children)
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
return new HTMLElement(tag, props, children);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
export default {
|
|
663
|
+
Document,
|
|
664
|
+
Element,
|
|
665
|
+
DOMParser,
|
|
666
|
+
HTMLTextNode,
|
|
667
|
+
HTMLElement
|
|
668
|
+
};
|