elit 3.6.5 → 3.6.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/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/README.md +6 -0
- package/dist/build.cjs +421 -331
- package/dist/build.d.ts +1 -16
- package/dist/build.js +420 -330
- package/dist/build.mjs +420 -330
- package/dist/chokidar.cjs +219 -182
- package/dist/chokidar.d.ts +25 -10
- package/dist/chokidar.js +217 -182
- package/dist/chokidar.mjs +218 -183
- package/dist/cli.cjs +21608 -20241
- package/dist/cli.d.ts +19 -37
- package/dist/cli.mjs +21262 -19910
- package/dist/config.cjs +357 -350
- package/dist/config.d.ts +19 -240
- package/dist/config.js +520 -515
- package/dist/config.mjs +346 -341
- package/dist/contracts-BeW9k0yZ.d.ts +54 -0
- package/dist/contracts-D7KIS-TK.d.ts +36 -0
- package/dist/coverage.cjs +448 -485
- package/dist/coverage.d.ts +13 -59
- package/dist/coverage.js +447 -484
- package/dist/coverage.mjs +447 -484
- package/dist/database.cjs +819 -828
- package/dist/database.d.ts +8 -24
- package/dist/database.js +818 -829
- package/dist/database.mjs +818 -829
- package/dist/desktop-auto-render.cjs +1700 -1522
- package/dist/desktop-auto-render.d.ts +4 -9
- package/dist/desktop-auto-render.js +1695 -1517
- package/dist/desktop-auto-render.mjs +1696 -1518
- package/dist/desktop.cjs +3 -1
- package/dist/desktop.d.ts +4 -1
- package/dist/desktop.js +1 -1
- package/dist/desktop.mjs +1 -1
- package/dist/dev-build.cjs +830 -0
- package/dist/dev-build.d.ts +53 -0
- package/dist/dev-build.js +3318 -0
- package/dist/dev-build.mjs +797 -0
- package/dist/dom.cjs +717 -590
- package/dist/dom.d.ts +2 -15
- package/dist/dom.js +714 -587
- package/dist/dom.mjs +716 -589
- package/dist/el.cjs +62 -52
- package/dist/el.d.ts +5 -10
- package/dist/el.js +60 -52
- package/dist/el.mjs +60 -52
- package/dist/fs.cjs +72 -63
- package/dist/fs.d.ts +22 -19
- package/dist/fs.js +71 -62
- package/dist/fs.mjs +71 -62
- package/dist/hmr.cjs +40 -14
- package/dist/hmr.d.ts +11 -23
- package/dist/hmr.js +38 -14
- package/dist/hmr.mjs +38 -14
- package/dist/http.cjs +251 -99
- package/dist/http.d.ts +38 -104
- package/dist/http.js +249 -99
- package/dist/http.mjs +249 -99
- package/dist/https.cjs +524 -228
- package/dist/https.d.ts +44 -36
- package/dist/https.js +520 -226
- package/dist/https.mjs +522 -228
- package/dist/index.cjs +7502 -7690
- package/dist/index.d.ts +8 -3
- package/dist/index.js +7486 -7676
- package/dist/index.mjs +7497 -7686
- package/dist/mime-types.cjs +10 -4
- package/dist/mime-types.d.ts +8 -11
- package/dist/mime-types.js +9 -3
- package/dist/mime-types.mjs +9 -3
- package/dist/native.cjs +8616 -8869
- package/dist/native.d.ts +7 -8
- package/dist/native.js +8682 -8935
- package/dist/native.mjs +8615 -8868
- package/dist/path.cjs +83 -77
- package/dist/path.d.ts +29 -29
- package/dist/path.js +82 -76
- package/dist/path.mjs +82 -76
- package/dist/pm.cjs +3300 -0
- package/dist/pm.d.ts +256 -0
- package/dist/pm.js +5638 -0
- package/dist/pm.mjs +3196 -0
- package/dist/preview-build.cjs +712 -0
- package/dist/preview-build.d.ts +59 -0
- package/dist/preview-build.js +3194 -0
- package/dist/preview-build.mjs +676 -0
- package/dist/render-context.cjs +13 -2
- package/dist/render-context.d.ts +9 -31
- package/dist/render-context.js +11 -2
- package/dist/render-context.mjs +11 -2
- package/dist/router.cjs +787 -645
- package/dist/router.d.ts +8 -12
- package/dist/router.js +786 -644
- package/dist/router.mjs +786 -644
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.js +1 -1
- package/dist/runtime.mjs +1 -1
- package/dist/server.cjs +3315 -2603
- package/dist/server.d.ts +49 -4
- package/dist/server.js +7611 -2834
- package/dist/server.mjs +3317 -2607
- package/dist/smtp-server.cjs +128 -0
- package/dist/smtp-server.d.ts +27 -0
- package/dist/smtp-server.js +4199 -0
- package/dist/smtp-server.mjs +100 -0
- package/dist/state-DvEkDehk.d.ts +195 -0
- package/dist/state.cjs +768 -658
- package/dist/state.d.ts +11 -69
- package/dist/state.js +760 -650
- package/dist/state.mjs +767 -657
- package/dist/style.cjs +1011 -968
- package/dist/style.d.ts +13 -127
- package/dist/style.js +1009 -970
- package/dist/style.mjs +1011 -971
- package/dist/test-reporter.cjs +332 -316
- package/dist/test-reporter.d.ts +28 -33
- package/dist/test-reporter.js +328 -312
- package/dist/test-reporter.mjs +328 -312
- package/dist/test-runtime.cjs +927 -968
- package/dist/test-runtime.d.ts +24 -99
- package/dist/test-runtime.js +922 -965
- package/dist/test-runtime.mjs +922 -965
- package/dist/test.cjs +4428 -4273
- package/dist/test.d.ts +2 -8
- package/dist/test.js +4307 -4154
- package/dist/test.mjs +4419 -4267
- package/dist/types-BONVzPtp.d.ts +59 -0
- package/dist/types-BR4wMiVx.d.ts +32 -0
- package/dist/types-C4gKykuG.d.ts +23 -0
- package/dist/types-CIhpN1-K.d.ts +64 -0
- package/dist/types-Ckj8md_j.d.ts +84 -0
- package/dist/types-CpjQTAkX.d.ts +24 -0
- package/dist/types-D0LjrYjS.d.ts +14 -0
- package/dist/types-DAisuVr5.d.ts +75 -0
- package/dist/types-tJn88E1N.d.ts +242 -0
- package/dist/types.d.ts +71 -226
- package/dist/universal.cjs +1 -1
- package/dist/universal.d.ts +1 -5
- package/dist/universal.js +1 -1
- package/dist/universal.mjs +1 -1
- package/dist/websocket-XfyK23zD.d.ts +119 -0
- package/dist/ws.cjs +129 -108
- package/dist/ws.d.ts +21 -131
- package/dist/ws.js +128 -109
- package/dist/ws.mjs +128 -109
- package/dist/wss.cjs +757 -479
- package/dist/wss.d.ts +31 -28
- package/dist/wss.js +755 -479
- package/dist/wss.mjs +758 -482
- package/package.json +16 -1
- package/vendor/epaint-0.31.1/src/image.rs +418 -0
- package/dist/server-CcBFc2F5.d.ts +0 -449
package/dist/router.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
(() => {
|
|
3
|
-
// src/render-context.ts
|
|
3
|
+
// src/desktop/render-context/constants.ts
|
|
4
4
|
var RUNTIME_TARGET_KEY = "__ELIT_RUNTIME_TARGET__";
|
|
5
5
|
var CAPTURED_RENDER_KEY = "__ELIT_CAPTURED_RENDER__";
|
|
6
6
|
var RUNTIME_TARGET_ENV = "ELIT_RUNTIME_TARGET";
|
|
7
|
+
|
|
8
|
+
// src/desktop/render-context/globals.ts
|
|
7
9
|
function getGlobalRenderScope() {
|
|
8
10
|
return globalThis;
|
|
9
11
|
}
|
|
12
|
+
|
|
13
|
+
// src/desktop/render-context/runtime-target.ts
|
|
10
14
|
function isRenderRuntimeTarget(value) {
|
|
11
15
|
return value === "web" || value === "desktop" || value === "mobile" || value === "unknown";
|
|
12
16
|
}
|
|
@@ -22,7 +26,8 @@
|
|
|
22
26
|
if (typeof globalScope.createWindow === "function") {
|
|
23
27
|
return "desktop";
|
|
24
28
|
}
|
|
25
|
-
const
|
|
29
|
+
const argvValues = globalScope.process?.argv;
|
|
30
|
+
const argv = Array.isArray(argvValues) ? argvValues.join(" ") : "";
|
|
26
31
|
if (/\bdesktop\b/i.test(argv)) {
|
|
27
32
|
return "desktop";
|
|
28
33
|
}
|
|
@@ -31,6 +36,8 @@
|
|
|
31
36
|
}
|
|
32
37
|
return "unknown";
|
|
33
38
|
}
|
|
39
|
+
|
|
40
|
+
// src/desktop/render-context/captured-render.ts
|
|
34
41
|
function captureRenderedVNode(rootElement, vNode, target = detectRenderRuntimeTarget()) {
|
|
35
42
|
const globalScope = getGlobalRenderScope();
|
|
36
43
|
globalScope[RUNTIME_TARGET_KEY] = target;
|
|
@@ -41,7 +48,7 @@
|
|
|
41
48
|
};
|
|
42
49
|
}
|
|
43
50
|
|
|
44
|
-
// src/dom.ts
|
|
51
|
+
// src/client/dom/helpers.ts
|
|
45
52
|
function resolveElement(rootElement) {
|
|
46
53
|
return typeof rootElement === "string" ? document.getElementById(rootElement.replace("#", "")) : rootElement;
|
|
47
54
|
}
|
|
@@ -69,668 +76,788 @@
|
|
|
69
76
|
function hasDocumentApi() {
|
|
70
77
|
return typeof document !== "undefined";
|
|
71
78
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
});
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
if (Array.isArray(vNode)) {
|
|
95
|
-
for (const child of vNode) {
|
|
96
|
-
this.renderToDOM(child, parent);
|
|
79
|
+
function isState(value) {
|
|
80
|
+
return value && typeof value === "object" && "value" in value && "subscribe" in value && typeof value.subscribe === "function";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/client/dom/dom-render.ts
|
|
84
|
+
function isSvgElement(tagName, parent) {
|
|
85
|
+
return tagName === "svg" || tagName[0] === "s" && tagName[1] === "v" && tagName[2] === "g" || parent.namespaceURI === "http://www.w3.org/2000/svg";
|
|
86
|
+
}
|
|
87
|
+
function applyProps(el, props, textareaValue) {
|
|
88
|
+
for (const key in props) {
|
|
89
|
+
const value = props[key];
|
|
90
|
+
if (value == null || value === false) continue;
|
|
91
|
+
const c = key.charCodeAt(0);
|
|
92
|
+
if (c === 99 && (key.length < 6 || key[5] === "N")) {
|
|
93
|
+
const classValue = Array.isArray(value) ? value.join(" ") : String(value);
|
|
94
|
+
if (el instanceof SVGElement) {
|
|
95
|
+
el.setAttribute("class", classValue);
|
|
96
|
+
} else {
|
|
97
|
+
el.className = classValue;
|
|
97
98
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (Array.isArray(child)) {
|
|
106
|
-
for (const c of child) {
|
|
107
|
-
!shouldSkipChild(c) && this.renderToDOM(c, parent);
|
|
108
|
-
}
|
|
109
|
-
} else {
|
|
110
|
-
this.renderToDOM(child, parent);
|
|
99
|
+
} else if (c === 115 && key.length === 5) {
|
|
100
|
+
if (typeof value === "string") {
|
|
101
|
+
el.style.cssText = value;
|
|
102
|
+
} else {
|
|
103
|
+
const style = el.style;
|
|
104
|
+
for (const styleKey in value) {
|
|
105
|
+
style[styleKey] = value[styleKey];
|
|
111
106
|
}
|
|
112
107
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (c === 99 && (key.length < 6 || key[5] === "N")) {
|
|
122
|
-
const classValue = Array.isArray(value) ? value.join(" ") : value;
|
|
123
|
-
isSVG ? el.setAttribute("class", classValue) : el.className = classValue;
|
|
124
|
-
} else if (c === 115 && key.length === 5) {
|
|
125
|
-
if (typeof value === "string") {
|
|
126
|
-
el.style.cssText = value;
|
|
108
|
+
} else if (c === 111 && key.charCodeAt(1) === 110) {
|
|
109
|
+
el[key.toLowerCase()] = value;
|
|
110
|
+
} else if (c === 100 && key.length > 20) {
|
|
111
|
+
el.innerHTML = value.__html;
|
|
112
|
+
} else if (c === 114 && key === "ref") {
|
|
113
|
+
setTimeout(() => {
|
|
114
|
+
if (typeof value === "function") {
|
|
115
|
+
value(el);
|
|
127
116
|
} else {
|
|
128
|
-
|
|
129
|
-
for (const k in value) s[k] = value[k];
|
|
117
|
+
value.current = el;
|
|
130
118
|
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
setTimeout(() => {
|
|
137
|
-
typeof value === "function" ? value(el) : value.current = el;
|
|
138
|
-
}, 0);
|
|
139
|
-
} else if (textareaValue !== void 0 && key === "value") {
|
|
140
|
-
continue;
|
|
141
|
-
} else {
|
|
142
|
-
el.setAttribute(key, value === true ? "" : String(value));
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
const renderableChildren = textareaValue === void 0 ? children : [];
|
|
146
|
-
const len = renderableChildren.length;
|
|
147
|
-
if (!len) {
|
|
148
|
-
if (textareaValue !== void 0) {
|
|
149
|
-
el.value = textareaValue;
|
|
150
|
-
}
|
|
151
|
-
parent.appendChild(el);
|
|
152
|
-
return;
|
|
119
|
+
}, 0);
|
|
120
|
+
} else if (textareaValue !== void 0 && key === "value") {
|
|
121
|
+
continue;
|
|
122
|
+
} else {
|
|
123
|
+
el.setAttribute(key, value === true ? "" : String(value));
|
|
153
124
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function renderChildren(children, target) {
|
|
128
|
+
const len = children.length;
|
|
129
|
+
for (let i = 0; i < len; i++) {
|
|
130
|
+
const child = children[i];
|
|
131
|
+
if (shouldSkipChild(child)) continue;
|
|
132
|
+
if (Array.isArray(child)) {
|
|
133
|
+
for (let j = 0, childLen = child.length; j < childLen; j++) {
|
|
134
|
+
const nestedChild = child[j];
|
|
135
|
+
if (!shouldSkipChild(nestedChild)) {
|
|
136
|
+
renderToDOM(nestedChild, target);
|
|
165
137
|
}
|
|
166
138
|
}
|
|
167
|
-
};
|
|
168
|
-
if (len > 30) {
|
|
169
|
-
const fragment = document.createDocumentFragment();
|
|
170
|
-
renderChildren(fragment);
|
|
171
|
-
el.appendChild(fragment);
|
|
172
139
|
} else {
|
|
173
|
-
|
|
140
|
+
renderToDOM(child, target);
|
|
174
141
|
}
|
|
175
|
-
parent.appendChild(el);
|
|
176
142
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
143
|
+
}
|
|
144
|
+
function renderToDOM(vNode, parent) {
|
|
145
|
+
if (vNode == null || vNode === false) return;
|
|
146
|
+
if (typeof vNode !== "object") {
|
|
147
|
+
parent.appendChild(document.createTextNode(String(vNode)));
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (isState(vNode)) {
|
|
151
|
+
const textNode = document.createTextNode(String(vNode.value ?? ""));
|
|
152
|
+
parent.appendChild(textNode);
|
|
153
|
+
vNode.subscribe((newValue) => {
|
|
154
|
+
textNode.textContent = String(newValue ?? "");
|
|
155
|
+
});
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (Array.isArray(vNode)) {
|
|
159
|
+
for (const child of vNode) {
|
|
160
|
+
renderToDOM(child, parent);
|
|
194
161
|
}
|
|
195
|
-
return
|
|
162
|
+
return;
|
|
196
163
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
processed = end;
|
|
210
|
-
if (processed >= len) {
|
|
211
|
-
el.appendChild(fragment);
|
|
212
|
-
} else {
|
|
213
|
-
requestAnimationFrame(processChunk);
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
processChunk();
|
|
217
|
-
} else {
|
|
218
|
-
const fragment = document.createDocumentFragment();
|
|
219
|
-
for (let i = 0; i < len; i++) {
|
|
220
|
-
this.renderToDOM(vNodes[i], fragment);
|
|
221
|
-
}
|
|
222
|
-
el.appendChild(fragment);
|
|
164
|
+
const { tagName, props, children } = vNode;
|
|
165
|
+
const textareaValue = resolveTextareaValue(tagName, props);
|
|
166
|
+
if (!tagName) {
|
|
167
|
+
renderChildren(children, parent);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const el = isSvgElement(tagName, parent) ? document.createElementNS("http://www.w3.org/2000/svg", tagName.replace("svg", "").toLowerCase() || tagName) : document.createElement(tagName);
|
|
171
|
+
applyProps(el, props, textareaValue);
|
|
172
|
+
const renderableChildren = textareaValue === void 0 ? children : [];
|
|
173
|
+
if (!renderableChildren.length) {
|
|
174
|
+
if (textareaValue !== void 0) {
|
|
175
|
+
el.value = textareaValue;
|
|
223
176
|
}
|
|
224
|
-
|
|
177
|
+
parent.appendChild(el);
|
|
178
|
+
return;
|
|
225
179
|
}
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
180
|
+
if (renderableChildren.length > 30) {
|
|
181
|
+
const fragment = document.createDocumentFragment();
|
|
182
|
+
renderChildren(renderableChildren, fragment);
|
|
183
|
+
el.appendChild(fragment);
|
|
184
|
+
} else {
|
|
185
|
+
renderChildren(renderableChildren, el);
|
|
186
|
+
}
|
|
187
|
+
parent.appendChild(el);
|
|
188
|
+
}
|
|
189
|
+
function render(rootElement, vNode) {
|
|
190
|
+
if (!hasDocumentApi()) {
|
|
191
|
+
const runtimeTarget = detectRenderRuntimeTarget();
|
|
192
|
+
if (runtimeTarget === "desktop" || runtimeTarget === "mobile") {
|
|
193
|
+
captureRenderedVNode(rootElement, vNode, runtimeTarget);
|
|
194
|
+
return {};
|
|
195
|
+
}
|
|
196
|
+
throw new Error("render() requires a DOM or an Elit desktop/mobile runtime target.");
|
|
197
|
+
}
|
|
198
|
+
const el = ensureElement(resolveElement(rootElement), rootElement);
|
|
199
|
+
el.innerHTML = "";
|
|
200
|
+
if (vNode.children && vNode.children.length > 500) {
|
|
201
|
+
const fragment = document.createDocumentFragment();
|
|
202
|
+
renderToDOM(vNode, fragment);
|
|
203
|
+
el.appendChild(fragment);
|
|
204
|
+
} else {
|
|
205
|
+
renderToDOM(vNode, el);
|
|
206
|
+
}
|
|
207
|
+
return el;
|
|
208
|
+
}
|
|
209
|
+
function batchRender(rootElement, vNodes) {
|
|
210
|
+
const el = ensureElement(resolveElement(rootElement), rootElement);
|
|
211
|
+
const len = vNodes.length;
|
|
212
|
+
if (len > 3e3) {
|
|
213
|
+
const fragment = document.createDocumentFragment();
|
|
214
|
+
let processed = 0;
|
|
215
|
+
const chunkSize = 1500;
|
|
216
|
+
const processChunk = () => {
|
|
217
|
+
const end = Math.min(processed + chunkSize, len);
|
|
218
|
+
for (let i = processed; i < end; i++) {
|
|
219
|
+
renderToDOM(vNodes[i], fragment);
|
|
235
220
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
requestAnimationFrame(
|
|
221
|
+
processed = end;
|
|
222
|
+
if (processed >= len) {
|
|
223
|
+
el.appendChild(fragment);
|
|
224
|
+
} else {
|
|
225
|
+
requestAnimationFrame(processChunk);
|
|
241
226
|
}
|
|
242
227
|
};
|
|
243
|
-
|
|
244
|
-
|
|
228
|
+
processChunk();
|
|
229
|
+
} else {
|
|
230
|
+
const fragment = document.createDocumentFragment();
|
|
231
|
+
for (let i = 0; i < len; i++) {
|
|
232
|
+
renderToDOM(vNodes[i], fragment);
|
|
233
|
+
}
|
|
234
|
+
el.appendChild(fragment);
|
|
245
235
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
236
|
+
return el;
|
|
237
|
+
}
|
|
238
|
+
function renderChunked(rootElement, vNodes, chunkSize = 5e3, onProgress) {
|
|
239
|
+
const el = ensureElement(resolveElement(rootElement), rootElement);
|
|
240
|
+
const len = vNodes.length;
|
|
241
|
+
let index = 0;
|
|
242
|
+
const renderChunkFrame = () => {
|
|
243
|
+
const end = Math.min(index + chunkSize, len);
|
|
244
|
+
const fragment = document.createDocumentFragment();
|
|
245
|
+
for (let i = index; i < end; i++) {
|
|
246
|
+
renderToDOM(vNodes[i], fragment);
|
|
247
|
+
}
|
|
248
|
+
el.appendChild(fragment);
|
|
249
|
+
index = end;
|
|
250
|
+
if (onProgress) {
|
|
251
|
+
onProgress(index, len);
|
|
252
|
+
}
|
|
253
|
+
if (index < len) {
|
|
254
|
+
requestAnimationFrame(renderChunkFrame);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
requestAnimationFrame(renderChunkFrame);
|
|
258
|
+
return el;
|
|
259
|
+
}
|
|
260
|
+
function renderToHead(...vNodes) {
|
|
261
|
+
const head = document.head;
|
|
262
|
+
if (head) {
|
|
263
|
+
for (const vNode of vNodes.flat()) {
|
|
264
|
+
if (vNode) {
|
|
265
|
+
renderToDOM(vNode, head);
|
|
251
266
|
}
|
|
252
267
|
}
|
|
253
|
-
return head;
|
|
254
|
-
}
|
|
255
|
-
addStyle(cssText) {
|
|
256
|
-
const el = document.createElement("style");
|
|
257
|
-
el.textContent = cssText;
|
|
258
|
-
return document.head.appendChild(el);
|
|
259
268
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
269
|
+
return head;
|
|
270
|
+
}
|
|
271
|
+
function addStyle(cssText) {
|
|
272
|
+
const el = document.createElement("style");
|
|
273
|
+
el.textContent = cssText;
|
|
274
|
+
return document.head.appendChild(el);
|
|
275
|
+
}
|
|
276
|
+
function addMeta(attrs) {
|
|
277
|
+
const el = document.createElement("meta");
|
|
278
|
+
for (const key in attrs) {
|
|
279
|
+
el.setAttribute(key, attrs[key]);
|
|
264
280
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
281
|
+
return document.head.appendChild(el);
|
|
282
|
+
}
|
|
283
|
+
function addLink(attrs) {
|
|
284
|
+
const el = document.createElement("link");
|
|
285
|
+
for (const key in attrs) {
|
|
286
|
+
el.setAttribute(key, attrs[key]);
|
|
269
287
|
}
|
|
270
|
-
|
|
271
|
-
|
|
288
|
+
return document.head.appendChild(el);
|
|
289
|
+
}
|
|
290
|
+
function setTitle(text) {
|
|
291
|
+
return document.title = text;
|
|
292
|
+
}
|
|
293
|
+
function cleanupUnusedElements(root, elementCache) {
|
|
294
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
|
|
295
|
+
const toRemove = [];
|
|
296
|
+
while (walker.nextNode()) {
|
|
297
|
+
const node = walker.currentNode;
|
|
298
|
+
if (node.id && node.id.startsWith("r") && !elementCache.has(node)) {
|
|
299
|
+
toRemove.push(node);
|
|
300
|
+
}
|
|
272
301
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
} else {
|
|
289
|
-
notify();
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
return {
|
|
293
|
-
get value() {
|
|
294
|
-
return value;
|
|
295
|
-
},
|
|
296
|
-
set value(newValue) {
|
|
297
|
-
const changed = deep ? JSON.stringify(value) !== JSON.stringify(newValue) : value !== newValue;
|
|
298
|
-
if (changed) {
|
|
299
|
-
value = newValue;
|
|
300
|
-
scheduleUpdate();
|
|
301
|
-
}
|
|
302
|
-
},
|
|
303
|
-
subscribe(fn) {
|
|
304
|
-
listeners.add(fn);
|
|
305
|
-
return () => listeners.delete(fn);
|
|
306
|
-
},
|
|
307
|
-
destroy() {
|
|
308
|
-
listeners.clear();
|
|
309
|
-
updateTimer && clearTimeout(updateTimer);
|
|
302
|
+
toRemove.forEach((el) => el.remove());
|
|
303
|
+
return toRemove.length;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// src/client/dom/reactive.ts
|
|
307
|
+
function createReactiveChild(state, reactiveNodes, renderFn) {
|
|
308
|
+
const currentValue = renderFn(state.value);
|
|
309
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
310
|
+
const entry = { node: null, renderFn };
|
|
311
|
+
reactiveNodes.set(state, entry);
|
|
312
|
+
state.subscribe(() => {
|
|
313
|
+
if (entry.node && entry.node.parentNode) {
|
|
314
|
+
const newValue = renderFn(state.value);
|
|
315
|
+
entry.node.textContent = String(newValue ?? "");
|
|
310
316
|
}
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
computed(states, computeFn) {
|
|
314
|
-
const values = states.map((s) => s.value);
|
|
315
|
-
const result = this.createState(computeFn(...values));
|
|
316
|
-
states.forEach((state, index) => {
|
|
317
|
-
state.subscribe((newValue) => {
|
|
318
|
-
values[index] = newValue;
|
|
319
|
-
result.value = computeFn(...values);
|
|
320
|
-
});
|
|
321
317
|
});
|
|
322
|
-
return result;
|
|
323
318
|
}
|
|
324
|
-
|
|
325
|
-
|
|
319
|
+
return currentValue;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/client/dom/string-render.ts
|
|
323
|
+
var SELF_CLOSING_TAGS = /* @__PURE__ */ new Set([
|
|
324
|
+
"area",
|
|
325
|
+
"base",
|
|
326
|
+
"br",
|
|
327
|
+
"col",
|
|
328
|
+
"embed",
|
|
329
|
+
"hr",
|
|
330
|
+
"img",
|
|
331
|
+
"input",
|
|
332
|
+
"link",
|
|
333
|
+
"meta",
|
|
334
|
+
"param",
|
|
335
|
+
"source",
|
|
336
|
+
"track",
|
|
337
|
+
"wbr"
|
|
338
|
+
]);
|
|
339
|
+
function resolveStateValue(value) {
|
|
340
|
+
return isState(value) ? value.value : value;
|
|
341
|
+
}
|
|
342
|
+
function isReactiveWrapper(vNode) {
|
|
343
|
+
if (!vNode || typeof vNode !== "object" || !vNode.tagName) {
|
|
344
|
+
return false;
|
|
326
345
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
const getVisibleRange = () => {
|
|
333
|
-
const start = Math.max(0, Math.floor(scrollTop / itemHeight) - bufferSize);
|
|
334
|
-
const end = Math.min(items.length, Math.ceil((scrollTop + viewportHeight) / itemHeight) + bufferSize);
|
|
335
|
-
return { start, end };
|
|
336
|
-
};
|
|
337
|
-
const render2 = () => {
|
|
338
|
-
const { start, end } = getVisibleRange();
|
|
339
|
-
const wrapper = document.createElement("div");
|
|
340
|
-
wrapper.style.cssText = `height:${totalHeight}px;position:relative`;
|
|
341
|
-
for (let i = start; i < end; i++) {
|
|
342
|
-
const itemEl = document.createElement("div");
|
|
343
|
-
itemEl.style.cssText = `position:absolute;top:${i * itemHeight}px;height:${itemHeight}px;width:100%`;
|
|
344
|
-
this.renderToDOM(renderItem(items[i], i), itemEl);
|
|
345
|
-
wrapper.appendChild(itemEl);
|
|
346
|
-
}
|
|
347
|
-
container.innerHTML = "";
|
|
348
|
-
container.appendChild(wrapper);
|
|
349
|
-
};
|
|
350
|
-
const scrollHandler = () => {
|
|
351
|
-
scrollTop = container.scrollTop;
|
|
352
|
-
requestAnimationFrame(render2);
|
|
353
|
-
};
|
|
354
|
-
container.addEventListener("scroll", scrollHandler);
|
|
355
|
-
render2();
|
|
356
|
-
return {
|
|
357
|
-
render: render2,
|
|
358
|
-
destroy: () => {
|
|
359
|
-
container.removeEventListener("scroll", scrollHandler);
|
|
360
|
-
container.innerHTML = "";
|
|
361
|
-
}
|
|
362
|
-
};
|
|
346
|
+
return vNode.tagName === "span" && vNode.props?.id && typeof vNode.props.id === "string" && /^r[a-z0-9]{9}$/.test(vNode.props.id);
|
|
347
|
+
}
|
|
348
|
+
function unwrapReactive(vNode) {
|
|
349
|
+
if (!isReactiveWrapper(vNode)) {
|
|
350
|
+
return vNode;
|
|
363
351
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
let loading = false;
|
|
368
|
-
return async (...args) => {
|
|
369
|
-
if (!component && !loading) {
|
|
370
|
-
loading = true;
|
|
371
|
-
component = await loadFn();
|
|
372
|
-
loading = false;
|
|
373
|
-
}
|
|
374
|
-
return component ? component(...args) : { tagName: "div", props: { class: "loading" }, children: ["Loading..."] };
|
|
375
|
-
};
|
|
352
|
+
const children = vNode.children;
|
|
353
|
+
if (!children || children.length === 0) {
|
|
354
|
+
return "";
|
|
376
355
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
const
|
|
383
|
-
if (
|
|
384
|
-
|
|
356
|
+
if (children.length === 1) {
|
|
357
|
+
const child = children[0];
|
|
358
|
+
if (child && typeof child === "object" && child.tagName === "span") {
|
|
359
|
+
const props = child.props;
|
|
360
|
+
const hasNoProps = !props || Object.keys(props).length === 0;
|
|
361
|
+
const hasSingleStringChild = child.children && child.children.length === 1 && typeof child.children[0] === "string";
|
|
362
|
+
if (hasNoProps && hasSingleStringChild) {
|
|
363
|
+
return child.children[0];
|
|
385
364
|
}
|
|
386
365
|
}
|
|
387
|
-
|
|
388
|
-
return toRemove.length;
|
|
366
|
+
return unwrapReactive(child);
|
|
389
367
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
368
|
+
return children.map((child) => unwrapReactive(child));
|
|
369
|
+
}
|
|
370
|
+
function escapeHtml(text) {
|
|
371
|
+
const htmlEscapes = {
|
|
372
|
+
"&": "&",
|
|
373
|
+
"<": "<",
|
|
374
|
+
">": ">",
|
|
375
|
+
'"': """,
|
|
376
|
+
"'": "'"
|
|
377
|
+
};
|
|
378
|
+
return text.replace(/[&<>"']/g, (char) => htmlEscapes[char]);
|
|
379
|
+
}
|
|
380
|
+
function isSelfClosingTag(tagName) {
|
|
381
|
+
return SELF_CLOSING_TAGS.has(tagName.toLowerCase());
|
|
382
|
+
}
|
|
383
|
+
function styleToString(style) {
|
|
384
|
+
if (typeof style === "string") {
|
|
385
|
+
return style;
|
|
386
|
+
}
|
|
387
|
+
if (typeof style === "object" && style !== null) {
|
|
388
|
+
const styles = [];
|
|
389
|
+
for (const key in style) {
|
|
390
|
+
const cssKey = key.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
391
|
+
styles.push(`${cssKey}:${style[key]}`);
|
|
392
|
+
}
|
|
393
|
+
return styles.join(";");
|
|
394
|
+
}
|
|
395
|
+
return "";
|
|
396
|
+
}
|
|
397
|
+
function propsToAttributes(props, tagName) {
|
|
398
|
+
const attrs = [];
|
|
399
|
+
for (const key in props) {
|
|
400
|
+
if (key === "children" || key === "dangerouslySetInnerHTML" || key === "ref" || tagName === "textarea" && key === "value") {
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
let value = props[key];
|
|
404
|
+
value = resolveStateValue(value);
|
|
405
|
+
if (value == null || value === false) continue;
|
|
406
|
+
if (key.startsWith("on") && typeof value === "function") {
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
if (key === "className" || key === "class") {
|
|
410
|
+
const className = Array.isArray(value) ? value.join(" ") : value;
|
|
411
|
+
if (className) {
|
|
412
|
+
attrs.push(`class="${escapeHtml(String(className))}"`);
|
|
403
413
|
}
|
|
404
|
-
|
|
414
|
+
continue;
|
|
405
415
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
html += ` ${attrs}`;
|
|
416
|
+
if (key === "style") {
|
|
417
|
+
const styleStr = styleToString(value);
|
|
418
|
+
if (styleStr) {
|
|
419
|
+
attrs.push(`style="${escapeHtml(styleStr)}"`);
|
|
420
|
+
}
|
|
421
|
+
continue;
|
|
413
422
|
}
|
|
414
|
-
if (
|
|
415
|
-
|
|
416
|
-
|
|
423
|
+
if (value === true) {
|
|
424
|
+
attrs.push(key);
|
|
425
|
+
continue;
|
|
417
426
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
427
|
+
attrs.push(`${key}="${escapeHtml(String(value))}"`);
|
|
428
|
+
}
|
|
429
|
+
return attrs.join(" ");
|
|
430
|
+
}
|
|
431
|
+
function renderToString(vNode, options = {}) {
|
|
432
|
+
const { pretty = false, indent = 0 } = options;
|
|
433
|
+
const indentStr = pretty ? " ".repeat(indent) : "";
|
|
434
|
+
const newLine = pretty ? "\n" : "";
|
|
435
|
+
let resolvedVNode = resolveStateValue(vNode);
|
|
436
|
+
resolvedVNode = unwrapReactive(resolvedVNode);
|
|
437
|
+
if (Array.isArray(resolvedVNode)) {
|
|
438
|
+
return resolvedVNode.map((child) => renderToString(child, options)).join("");
|
|
439
|
+
}
|
|
440
|
+
if (typeof resolvedVNode !== "object" || resolvedVNode === null) {
|
|
441
|
+
if (resolvedVNode === null || resolvedVNode === void 0 || resolvedVNode === false) {
|
|
442
|
+
return "";
|
|
443
|
+
}
|
|
444
|
+
return escapeHtml(String(resolvedVNode));
|
|
445
|
+
}
|
|
446
|
+
const { tagName, props, children } = resolvedVNode;
|
|
447
|
+
const textareaValue = resolveTextareaValue(tagName, props);
|
|
448
|
+
const selfClosing = isSelfClosingTag(tagName);
|
|
449
|
+
let html = `${indentStr}<${tagName}`;
|
|
450
|
+
const attrs = propsToAttributes(props, tagName);
|
|
451
|
+
if (attrs) {
|
|
452
|
+
html += ` ${attrs}`;
|
|
453
|
+
}
|
|
454
|
+
if (selfClosing) {
|
|
455
|
+
html += ` />${newLine}`;
|
|
456
|
+
return html;
|
|
457
|
+
}
|
|
458
|
+
html += ">";
|
|
459
|
+
if (textareaValue !== void 0) {
|
|
460
|
+
html += escapeHtml(textareaValue);
|
|
461
|
+
html += `</${tagName}>${newLine}`;
|
|
462
|
+
return html;
|
|
463
|
+
}
|
|
464
|
+
if (props.dangerouslySetInnerHTML) {
|
|
465
|
+
html += props.dangerouslySetInnerHTML.__html;
|
|
466
|
+
html += `</${tagName}>${newLine}`;
|
|
467
|
+
return html;
|
|
468
|
+
}
|
|
469
|
+
const isRawText = tagName === "script" || tagName === "style";
|
|
470
|
+
if (children && children.length > 0) {
|
|
471
|
+
const resolvedChildren = children.map((child) => unwrapReactive(resolveStateValue(child)));
|
|
472
|
+
const hasComplexChildren = resolvedChildren.some(
|
|
473
|
+
(child) => typeof child === "object" && child !== null && !Array.isArray(child) && "tagName" in child
|
|
474
|
+
);
|
|
475
|
+
if (pretty && hasComplexChildren) {
|
|
476
|
+
html += newLine;
|
|
477
|
+
for (const child of resolvedChildren) {
|
|
478
|
+
if (shouldSkipChild(child)) continue;
|
|
479
|
+
if (Array.isArray(child)) {
|
|
480
|
+
for (const nestedChild of child) {
|
|
481
|
+
if (!shouldSkipChild(nestedChild)) {
|
|
482
|
+
html += isRawText && typeof nestedChild === "string" ? nestedChild : renderToString(nestedChild, { pretty, indent: indent + 1 });
|
|
446
483
|
}
|
|
447
|
-
} else {
|
|
448
|
-
html += this.renderToString(child, { pretty, indent: indent + 1 });
|
|
449
484
|
}
|
|
485
|
+
} else {
|
|
486
|
+
html += isRawText && typeof child === "string" ? child : renderToString(child, { pretty, indent: indent + 1 });
|
|
450
487
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
}
|
|
488
|
+
}
|
|
489
|
+
html += indentStr;
|
|
490
|
+
} else {
|
|
491
|
+
for (const child of resolvedChildren) {
|
|
492
|
+
if (shouldSkipChild(child)) continue;
|
|
493
|
+
if (Array.isArray(child)) {
|
|
494
|
+
for (const nestedChild of child) {
|
|
495
|
+
if (!shouldSkipChild(nestedChild)) {
|
|
496
|
+
html += isRawText && typeof nestedChild === "string" ? nestedChild : renderToString(nestedChild, { pretty: false, indent: 0 });
|
|
460
497
|
}
|
|
461
|
-
} else {
|
|
462
|
-
html += this.renderToString(child, { pretty: false, indent: 0 });
|
|
463
498
|
}
|
|
499
|
+
} else {
|
|
500
|
+
html += isRawText && typeof child === "string" ? child : renderToString(child, { pretty: false, indent: 0 });
|
|
464
501
|
}
|
|
465
502
|
}
|
|
466
503
|
}
|
|
467
|
-
html += `</${tagName}>${newLine}`;
|
|
468
|
-
return html;
|
|
469
504
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
505
|
+
html += `</${tagName}>${newLine}`;
|
|
506
|
+
return html;
|
|
507
|
+
}
|
|
508
|
+
function renderToHTMLDocument(vNode, options = {}) {
|
|
509
|
+
const {
|
|
510
|
+
title = "",
|
|
511
|
+
meta = [],
|
|
512
|
+
links = [],
|
|
513
|
+
scripts = [],
|
|
514
|
+
styles = [],
|
|
515
|
+
lang = "en",
|
|
516
|
+
head = "",
|
|
517
|
+
bodyAttrs = {},
|
|
518
|
+
pretty = false
|
|
519
|
+
} = options;
|
|
520
|
+
const nl = pretty ? "\n" : "";
|
|
521
|
+
const indent = pretty ? " " : "";
|
|
522
|
+
const indent2 = pretty ? " " : "";
|
|
523
|
+
let html = `<!DOCTYPE html>${nl}<html lang="${lang}">${nl}${indent}<head>${nl}${indent2}<meta charset="UTF-8">${nl}${indent2}<meta name="viewport" content="width=device-width, initial-scale=1.0">${nl}`;
|
|
524
|
+
if (title) {
|
|
525
|
+
html += `${indent2}<title>${escapeHtml(title)}</title>${nl}`;
|
|
526
|
+
}
|
|
527
|
+
for (const metaAttrs of meta) {
|
|
528
|
+
html += `${indent2}<meta`;
|
|
529
|
+
for (const key in metaAttrs) {
|
|
530
|
+
html += ` ${key}="${escapeHtml(metaAttrs[key])}"`;
|
|
473
531
|
}
|
|
474
|
-
|
|
532
|
+
html += `>${nl}`;
|
|
475
533
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
534
|
+
for (const linkAttrs of links) {
|
|
535
|
+
html += `${indent2}<link`;
|
|
536
|
+
for (const key in linkAttrs) {
|
|
537
|
+
html += ` ${key}="${escapeHtml(linkAttrs[key])}"`;
|
|
479
538
|
}
|
|
480
|
-
|
|
539
|
+
html += `>${nl}`;
|
|
481
540
|
}
|
|
482
|
-
|
|
483
|
-
if (
|
|
484
|
-
|
|
541
|
+
for (const style of styles) {
|
|
542
|
+
if (style.href) {
|
|
543
|
+
html += `${indent2}<link rel="stylesheet" href="${escapeHtml(style.href)}">${nl}`;
|
|
544
|
+
} else if (style.content) {
|
|
545
|
+
html += `${indent2}<style>${style.content}</style>${nl}`;
|
|
485
546
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
547
|
+
}
|
|
548
|
+
if (head) {
|
|
549
|
+
html += head + nl;
|
|
550
|
+
}
|
|
551
|
+
html += `${indent}</head>${nl}${indent}<body`;
|
|
552
|
+
for (const key in bodyAttrs) {
|
|
553
|
+
html += ` ${key}="${escapeHtml(bodyAttrs[key])}"`;
|
|
554
|
+
}
|
|
555
|
+
html += `>${nl}`;
|
|
556
|
+
html += renderToString(vNode, { pretty, indent: 2 });
|
|
557
|
+
for (const script of scripts) {
|
|
558
|
+
html += `${indent2}<script`;
|
|
559
|
+
if (script.type) {
|
|
560
|
+
html += ` type="${escapeHtml(script.type)}"`;
|
|
489
561
|
}
|
|
490
|
-
if (
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
|
|
562
|
+
if (script.async) {
|
|
563
|
+
html += " async";
|
|
564
|
+
}
|
|
565
|
+
if (script.defer) {
|
|
566
|
+
html += " defer";
|
|
567
|
+
}
|
|
568
|
+
if (script.src) {
|
|
569
|
+
html += ` src="${escapeHtml(script.src)}"></script>${nl}`;
|
|
570
|
+
} else if (script.content) {
|
|
571
|
+
html += `>${script.content}</script>${nl}`;
|
|
572
|
+
} else {
|
|
573
|
+
html += `></script>${nl}`;
|
|
501
574
|
}
|
|
502
|
-
return children.map((c) => this.unwrapReactive(c));
|
|
503
575
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
continue;
|
|
538
|
-
}
|
|
539
|
-
let value = props[key];
|
|
540
|
-
value = this.resolveStateValue(value);
|
|
541
|
-
if (value == null || value === false) continue;
|
|
542
|
-
if (key.startsWith("on") && typeof value === "function") {
|
|
543
|
-
continue;
|
|
544
|
-
}
|
|
545
|
-
if (key === "className" || key === "class") {
|
|
546
|
-
const className = Array.isArray(value) ? value.join(" ") : value;
|
|
547
|
-
if (className) {
|
|
548
|
-
attrs.push(`class="${this.escapeHtml(String(className))}"`);
|
|
549
|
-
}
|
|
550
|
-
continue;
|
|
551
|
-
}
|
|
552
|
-
if (key === "style") {
|
|
553
|
-
const styleStr = this.styleToString(value);
|
|
554
|
-
if (styleStr) {
|
|
555
|
-
attrs.push(`style="${this.escapeHtml(styleStr)}"`);
|
|
576
|
+
html += `${indent}</body>${nl}</html>`;
|
|
577
|
+
return html;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// src/client/dom/json.ts
|
|
581
|
+
function jsonToVNode(json, reactiveNodes) {
|
|
582
|
+
if (isState(json)) {
|
|
583
|
+
return createReactiveChild(json, reactiveNodes, (value) => value);
|
|
584
|
+
}
|
|
585
|
+
if (isPrimitiveJson(json)) {
|
|
586
|
+
return json;
|
|
587
|
+
}
|
|
588
|
+
const { tag, attributes = {}, children } = json;
|
|
589
|
+
const props = {};
|
|
590
|
+
for (const key in attributes) {
|
|
591
|
+
const value = attributes[key];
|
|
592
|
+
if (key === "class") {
|
|
593
|
+
props.className = isState(value) ? value.value : value;
|
|
594
|
+
} else {
|
|
595
|
+
props[key] = isState(value) ? value.value : value;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
const childrenArray = [];
|
|
599
|
+
if (children != null) {
|
|
600
|
+
if (Array.isArray(children)) {
|
|
601
|
+
for (const child of children) {
|
|
602
|
+
if (isState(child)) {
|
|
603
|
+
childrenArray.push(createReactiveChild(child, reactiveNodes, (value) => value));
|
|
604
|
+
} else {
|
|
605
|
+
const converted = jsonToVNode(child, reactiveNodes);
|
|
606
|
+
if (converted != null && converted !== false) {
|
|
607
|
+
childrenArray.push(converted);
|
|
608
|
+
}
|
|
556
609
|
}
|
|
557
|
-
continue;
|
|
558
610
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
611
|
+
} else if (isState(children)) {
|
|
612
|
+
childrenArray.push(createReactiveChild(children, reactiveNodes, (value) => value));
|
|
613
|
+
} else if (typeof children === "object" && children !== null && "tag" in children) {
|
|
614
|
+
const converted = jsonToVNode(children, reactiveNodes);
|
|
615
|
+
if (converted != null && converted !== false) {
|
|
616
|
+
childrenArray.push(converted);
|
|
562
617
|
}
|
|
563
|
-
|
|
618
|
+
} else {
|
|
619
|
+
childrenArray.push(children);
|
|
564
620
|
}
|
|
565
|
-
return attrs.join(" ");
|
|
566
621
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
622
|
+
return { tagName: tag, props, children: childrenArray };
|
|
623
|
+
}
|
|
624
|
+
function vNodeJsonToVNode(json, reactiveNodes) {
|
|
625
|
+
if (isState(json)) {
|
|
626
|
+
return createReactiveChild(json, reactiveNodes, (value) => value);
|
|
627
|
+
}
|
|
628
|
+
if (isPrimitiveJson(json)) {
|
|
629
|
+
return json;
|
|
630
|
+
}
|
|
631
|
+
const { tagName, props = {}, children = [] } = json;
|
|
632
|
+
const resolvedProps = {};
|
|
633
|
+
for (const key in props) {
|
|
634
|
+
const value = props[key];
|
|
635
|
+
resolvedProps[key] = isState(value) ? value.value : value;
|
|
636
|
+
}
|
|
637
|
+
const childrenArray = [];
|
|
638
|
+
for (const child of children) {
|
|
639
|
+
if (isState(child)) {
|
|
640
|
+
childrenArray.push(createReactiveChild(child, reactiveNodes, (value) => value));
|
|
641
|
+
} else {
|
|
642
|
+
const converted = vNodeJsonToVNode(child, reactiveNodes);
|
|
643
|
+
if (converted != null && converted !== false) {
|
|
644
|
+
childrenArray.push(converted);
|
|
576
645
|
}
|
|
577
|
-
return styles.join(";");
|
|
578
646
|
}
|
|
579
|
-
return "";
|
|
580
647
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
const entry = { node: null, renderFn };
|
|
588
|
-
this.reactiveNodes.set(state, entry);
|
|
589
|
-
state.subscribe(() => {
|
|
590
|
-
if (entry.node && entry.node.parentNode) {
|
|
591
|
-
const newValue = renderFn(state.value);
|
|
592
|
-
entry.node.textContent = String(newValue ?? "");
|
|
593
|
-
}
|
|
594
|
-
});
|
|
595
|
-
}
|
|
596
|
-
return currentValue;
|
|
648
|
+
return { tagName, props: resolvedProps, children: childrenArray };
|
|
649
|
+
}
|
|
650
|
+
function renderJson(rootElement, json, reactiveNodes) {
|
|
651
|
+
const vNode = jsonToVNode(json, reactiveNodes);
|
|
652
|
+
if (!vNode || typeof vNode !== "object" || !("tagName" in vNode)) {
|
|
653
|
+
throw new Error("Invalid JSON structure");
|
|
597
654
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
655
|
+
return render(rootElement, vNode);
|
|
656
|
+
}
|
|
657
|
+
function renderVNode(rootElement, json, reactiveNodes) {
|
|
658
|
+
const vNode = vNodeJsonToVNode(json, reactiveNodes);
|
|
659
|
+
if (!vNode || typeof vNode !== "object" || !("tagName" in vNode)) {
|
|
660
|
+
throw new Error("Invalid VNode JSON structure");
|
|
661
|
+
}
|
|
662
|
+
return render(rootElement, vNode);
|
|
663
|
+
}
|
|
664
|
+
function renderJsonToString(json, reactiveNodes, options = {}) {
|
|
665
|
+
const vNode = jsonToVNode(json, reactiveNodes);
|
|
666
|
+
return renderToString(vNode, options);
|
|
667
|
+
}
|
|
668
|
+
function renderVNodeToString(json, reactiveNodes, options = {}) {
|
|
669
|
+
const vNode = vNodeJsonToVNode(json, reactiveNodes);
|
|
670
|
+
return renderToString(vNode, options);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// src/client/dom/state-utils.ts
|
|
674
|
+
function createState(initialValue, options = {}) {
|
|
675
|
+
let value = initialValue;
|
|
676
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
677
|
+
let updateTimer = null;
|
|
678
|
+
const { throttle = 0, deep = false } = options;
|
|
679
|
+
const notify = () => listeners.forEach((listener) => listener(value));
|
|
680
|
+
const scheduleUpdate = () => {
|
|
681
|
+
if (throttle > 0) {
|
|
682
|
+
if (!updateTimer) {
|
|
683
|
+
updateTimer = setTimeout(() => {
|
|
684
|
+
updateTimer = null;
|
|
685
|
+
notify();
|
|
686
|
+
}, throttle);
|
|
613
687
|
}
|
|
688
|
+
} else {
|
|
689
|
+
notify();
|
|
614
690
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
} else {
|
|
636
|
-
childrenArray.push(children);
|
|
691
|
+
};
|
|
692
|
+
return {
|
|
693
|
+
get value() {
|
|
694
|
+
return value;
|
|
695
|
+
},
|
|
696
|
+
set value(newValue) {
|
|
697
|
+
const changed = deep ? JSON.stringify(value) !== JSON.stringify(newValue) : value !== newValue;
|
|
698
|
+
if (changed) {
|
|
699
|
+
value = newValue;
|
|
700
|
+
scheduleUpdate();
|
|
701
|
+
}
|
|
702
|
+
},
|
|
703
|
+
subscribe(fn) {
|
|
704
|
+
listeners.add(fn);
|
|
705
|
+
return () => listeners.delete(fn);
|
|
706
|
+
},
|
|
707
|
+
destroy() {
|
|
708
|
+
listeners.clear();
|
|
709
|
+
if (updateTimer) {
|
|
710
|
+
clearTimeout(updateTimer);
|
|
637
711
|
}
|
|
638
712
|
}
|
|
639
|
-
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
function computed(states, computeFn) {
|
|
716
|
+
const values = states.map((state) => state.value);
|
|
717
|
+
const result = createState(computeFn(...values));
|
|
718
|
+
states.forEach((state, index) => {
|
|
719
|
+
state.subscribe((newValue) => {
|
|
720
|
+
values[index] = newValue;
|
|
721
|
+
result.value = computeFn(...values);
|
|
722
|
+
});
|
|
723
|
+
});
|
|
724
|
+
return result;
|
|
725
|
+
}
|
|
726
|
+
function effect(stateFn) {
|
|
727
|
+
stateFn();
|
|
728
|
+
}
|
|
729
|
+
function createVirtualList(container, items, renderItem, itemHeight = 50, bufferSize = 5) {
|
|
730
|
+
const viewportHeight = container.clientHeight;
|
|
731
|
+
const totalHeight = items.length * itemHeight;
|
|
732
|
+
let scrollTop = 0;
|
|
733
|
+
const getVisibleRange = () => {
|
|
734
|
+
const start = Math.max(0, Math.floor(scrollTop / itemHeight) - bufferSize);
|
|
735
|
+
const end = Math.min(items.length, Math.ceil((scrollTop + viewportHeight) / itemHeight) + bufferSize);
|
|
736
|
+
return { start, end };
|
|
737
|
+
};
|
|
738
|
+
const render3 = () => {
|
|
739
|
+
const { start, end } = getVisibleRange();
|
|
740
|
+
const wrapper = document.createElement("div");
|
|
741
|
+
wrapper.style.cssText = `height:${totalHeight}px;position:relative`;
|
|
742
|
+
for (let i = start; i < end; i++) {
|
|
743
|
+
const itemEl = document.createElement("div");
|
|
744
|
+
itemEl.style.cssText = `position:absolute;top:${i * itemHeight}px;height:${itemHeight}px;width:100%`;
|
|
745
|
+
renderToDOM(renderItem(items[i], i), itemEl);
|
|
746
|
+
wrapper.appendChild(itemEl);
|
|
747
|
+
}
|
|
748
|
+
container.innerHTML = "";
|
|
749
|
+
container.appendChild(wrapper);
|
|
750
|
+
};
|
|
751
|
+
const scrollHandler = () => {
|
|
752
|
+
scrollTop = container.scrollTop;
|
|
753
|
+
requestAnimationFrame(render3);
|
|
754
|
+
};
|
|
755
|
+
container.addEventListener("scroll", scrollHandler);
|
|
756
|
+
render3();
|
|
757
|
+
return {
|
|
758
|
+
render: render3,
|
|
759
|
+
destroy: () => {
|
|
760
|
+
container.removeEventListener("scroll", scrollHandler);
|
|
761
|
+
container.innerHTML = "";
|
|
762
|
+
}
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
function lazy(loadFn) {
|
|
766
|
+
let component = null;
|
|
767
|
+
let loading = false;
|
|
768
|
+
return async (...args) => {
|
|
769
|
+
if (!component && !loading) {
|
|
770
|
+
loading = true;
|
|
771
|
+
component = await loadFn();
|
|
772
|
+
loading = false;
|
|
773
|
+
}
|
|
774
|
+
return component ? component(...args) : { tagName: "div", props: { class: "loading" }, children: ["Loading..."] };
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// src/client/dom/index.ts
|
|
779
|
+
var DomNode = class {
|
|
780
|
+
constructor() {
|
|
781
|
+
this.elementCache = /* @__PURE__ */ new WeakMap();
|
|
782
|
+
this.reactiveNodes = /* @__PURE__ */ new Map();
|
|
783
|
+
}
|
|
784
|
+
createElement(tagName, props = {}, children = []) {
|
|
785
|
+
return { tagName, props, children };
|
|
786
|
+
}
|
|
787
|
+
renderToDOM(vNode, parent) {
|
|
788
|
+
return renderToDOM(vNode, parent);
|
|
789
|
+
}
|
|
790
|
+
render(rootElement, vNode) {
|
|
791
|
+
return render(rootElement, vNode);
|
|
792
|
+
}
|
|
793
|
+
batchRender(rootElement, vNodes) {
|
|
794
|
+
return batchRender(rootElement, vNodes);
|
|
795
|
+
}
|
|
796
|
+
renderChunked(rootElement, vNodes, chunkSize = 5e3, onProgress) {
|
|
797
|
+
return renderChunked(rootElement, vNodes, chunkSize, onProgress);
|
|
798
|
+
}
|
|
799
|
+
renderToHead(...vNodes) {
|
|
800
|
+
return renderToHead(...vNodes);
|
|
801
|
+
}
|
|
802
|
+
addStyle(cssText) {
|
|
803
|
+
return addStyle(cssText);
|
|
804
|
+
}
|
|
805
|
+
addMeta(attrs) {
|
|
806
|
+
return addMeta(attrs);
|
|
807
|
+
}
|
|
808
|
+
addLink(attrs) {
|
|
809
|
+
return addLink(attrs);
|
|
810
|
+
}
|
|
811
|
+
setTitle(text) {
|
|
812
|
+
return setTitle(text);
|
|
813
|
+
}
|
|
814
|
+
// Reactive State Management
|
|
815
|
+
createState(initialValue, options = {}) {
|
|
816
|
+
return createState(initialValue, options);
|
|
817
|
+
}
|
|
818
|
+
computed(states, computeFn) {
|
|
819
|
+
return computed(states, computeFn);
|
|
820
|
+
}
|
|
821
|
+
effect(stateFn) {
|
|
822
|
+
effect(stateFn);
|
|
823
|
+
}
|
|
824
|
+
// Virtual scrolling helper for large lists
|
|
825
|
+
createVirtualList(container, items, renderItem, itemHeight = 50, bufferSize = 5) {
|
|
826
|
+
return createVirtualList(container, items, renderItem, itemHeight, bufferSize);
|
|
827
|
+
}
|
|
828
|
+
// Lazy load components
|
|
829
|
+
lazy(loadFn) {
|
|
830
|
+
return lazy(loadFn);
|
|
831
|
+
}
|
|
832
|
+
// Memory management - cleanup unused elements
|
|
833
|
+
cleanupUnusedElements(root) {
|
|
834
|
+
return cleanupUnusedElements(root, this.elementCache);
|
|
835
|
+
}
|
|
836
|
+
// Server-Side Rendering - convert VNode to HTML string
|
|
837
|
+
renderToString(vNode, options = {}) {
|
|
838
|
+
return renderToString(vNode, options);
|
|
839
|
+
}
|
|
840
|
+
jsonToVNode(json) {
|
|
841
|
+
return jsonToVNode(json, this.reactiveNodes);
|
|
640
842
|
}
|
|
641
843
|
vNodeJsonToVNode(json) {
|
|
642
|
-
|
|
643
|
-
return this.createReactiveChild(json, (v) => v);
|
|
644
|
-
}
|
|
645
|
-
if (isPrimitiveJson(json)) {
|
|
646
|
-
return json;
|
|
647
|
-
}
|
|
648
|
-
const { tagName, props = {}, children = [] } = json;
|
|
649
|
-
const resolvedProps = {};
|
|
650
|
-
for (const key in props) {
|
|
651
|
-
const value = props[key];
|
|
652
|
-
resolvedProps[key] = this.isState(value) ? value.value : value;
|
|
653
|
-
}
|
|
654
|
-
const childrenArray = [];
|
|
655
|
-
for (const child of children) {
|
|
656
|
-
if (this.isState(child)) {
|
|
657
|
-
childrenArray.push(this.createReactiveChild(child, (v) => v));
|
|
658
|
-
} else {
|
|
659
|
-
const converted = this.vNodeJsonToVNode(child);
|
|
660
|
-
if (converted != null && converted !== false) {
|
|
661
|
-
childrenArray.push(converted);
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
return { tagName, props: resolvedProps, children: childrenArray };
|
|
844
|
+
return vNodeJsonToVNode(json, this.reactiveNodes);
|
|
666
845
|
}
|
|
667
846
|
renderJson(rootElement, json) {
|
|
668
|
-
|
|
669
|
-
if (!vNode || typeof vNode !== "object" || !("tagName" in vNode)) {
|
|
670
|
-
throw new Error("Invalid JSON structure");
|
|
671
|
-
}
|
|
672
|
-
return this.render(rootElement, vNode);
|
|
847
|
+
return renderJson(rootElement, json, this.reactiveNodes);
|
|
673
848
|
}
|
|
674
849
|
renderVNode(rootElement, json) {
|
|
675
|
-
|
|
676
|
-
if (!vNode || typeof vNode !== "object" || !("tagName" in vNode)) {
|
|
677
|
-
throw new Error("Invalid VNode JSON structure");
|
|
678
|
-
}
|
|
679
|
-
return this.render(rootElement, vNode);
|
|
850
|
+
return renderVNode(rootElement, json, this.reactiveNodes);
|
|
680
851
|
}
|
|
681
852
|
renderJsonToString(json, options = {}) {
|
|
682
|
-
|
|
683
|
-
return this.renderToString(vNode, options);
|
|
853
|
+
return renderJsonToString(json, this.reactiveNodes, options);
|
|
684
854
|
}
|
|
685
855
|
renderVNodeToString(json, options = {}) {
|
|
686
|
-
|
|
687
|
-
return this.renderToString(vNode, options);
|
|
856
|
+
return renderVNodeToString(json, this.reactiveNodes, options);
|
|
688
857
|
}
|
|
689
858
|
// Generate complete HTML document as string (for SSR)
|
|
690
859
|
renderToHTMLDocument(vNode, options = {}) {
|
|
691
|
-
|
|
692
|
-
const nl = pretty ? "\n" : "";
|
|
693
|
-
const indent = pretty ? " " : "";
|
|
694
|
-
const indent2 = pretty ? " " : "";
|
|
695
|
-
let html = `<!DOCTYPE html>${nl}<html lang="${lang}">${nl}${indent}<head>${nl}${indent2}<meta charset="UTF-8">${nl}${indent2}<meta name="viewport" content="width=device-width, initial-scale=1.0">${nl}`;
|
|
696
|
-
if (title) html += `${indent2}<title>${this.escapeHtml(title)}</title>${nl}`;
|
|
697
|
-
for (const m of meta) {
|
|
698
|
-
html += `${indent2}<meta`;
|
|
699
|
-
for (const k in m) html += ` ${k}="${this.escapeHtml(m[k])}"`;
|
|
700
|
-
html += `>${nl}`;
|
|
701
|
-
}
|
|
702
|
-
for (const l of links) {
|
|
703
|
-
html += `${indent2}<link`;
|
|
704
|
-
for (const k in l) html += ` ${k}="${this.escapeHtml(l[k])}"`;
|
|
705
|
-
html += `>${nl}`;
|
|
706
|
-
}
|
|
707
|
-
for (const s of styles) {
|
|
708
|
-
if (s.href) {
|
|
709
|
-
html += `${indent2}<link rel="stylesheet" href="${this.escapeHtml(s.href)}">${nl}`;
|
|
710
|
-
} else if (s.content) {
|
|
711
|
-
html += `${indent2}<style>${s.content}</style>${nl}`;
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
if (head) html += head + nl;
|
|
715
|
-
html += `${indent}</head>${nl}${indent}<body`;
|
|
716
|
-
for (const k in bodyAttrs) html += ` ${k}="${this.escapeHtml(bodyAttrs[k])}"`;
|
|
717
|
-
html += `>${nl}`;
|
|
718
|
-
html += this.renderToString(vNode, { pretty, indent: 2 });
|
|
719
|
-
for (const script of scripts) {
|
|
720
|
-
html += `${indent2}<script`;
|
|
721
|
-
if (script.type) html += ` type="${this.escapeHtml(script.type)}"`;
|
|
722
|
-
if (script.async) html += ` async`;
|
|
723
|
-
if (script.defer) html += ` defer`;
|
|
724
|
-
if (script.src) {
|
|
725
|
-
html += ` src="${this.escapeHtml(script.src)}"></script>${nl}`;
|
|
726
|
-
} else if (script.content) {
|
|
727
|
-
html += `>${script.content}</script>${nl}`;
|
|
728
|
-
} else {
|
|
729
|
-
html += `></script>${nl}`;
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
html += `${indent}</body>${nl}</html>`;
|
|
733
|
-
return html;
|
|
860
|
+
return renderToHTMLDocument(vNode, options);
|
|
734
861
|
}
|
|
735
862
|
// Expose elementCache for reactive updates
|
|
736
863
|
getElementCache() {
|
|
@@ -738,10 +865,10 @@
|
|
|
738
865
|
}
|
|
739
866
|
};
|
|
740
867
|
var dom = new DomNode();
|
|
741
|
-
var
|
|
742
|
-
var
|
|
868
|
+
var render2 = dom.render.bind(dom);
|
|
869
|
+
var renderToString2 = dom.renderToString.bind(dom);
|
|
743
870
|
|
|
744
|
-
// src/router.ts
|
|
871
|
+
// src/client/router/helpers.ts
|
|
745
872
|
function matchRoute(pattern, path) {
|
|
746
873
|
const patternParts = pattern.split("/").filter(Boolean);
|
|
747
874
|
const pathParts = path.split("/").filter(Boolean);
|
|
@@ -751,7 +878,9 @@
|
|
|
751
878
|
return { "*": path.slice(basePattern.length) };
|
|
752
879
|
}
|
|
753
880
|
}
|
|
754
|
-
if (patternParts.length !== pathParts.length)
|
|
881
|
+
if (patternParts.length !== pathParts.length) {
|
|
882
|
+
return null;
|
|
883
|
+
}
|
|
755
884
|
const params = {};
|
|
756
885
|
for (let i = 0; i < patternParts.length; i++) {
|
|
757
886
|
const patternPart = patternParts[i];
|
|
@@ -766,7 +895,9 @@
|
|
|
766
895
|
}
|
|
767
896
|
function executeGuard(guard, to, from, navigate, replace = false) {
|
|
768
897
|
const result = guard(to, from);
|
|
769
|
-
if (result === false)
|
|
898
|
+
if (result === false) {
|
|
899
|
+
return false;
|
|
900
|
+
}
|
|
770
901
|
if (typeof result === "string") {
|
|
771
902
|
navigate(result, replace);
|
|
772
903
|
return false;
|
|
@@ -779,69 +910,76 @@
|
|
|
779
910
|
}
|
|
780
911
|
return { tagName: "span", props: {}, children: [component] };
|
|
781
912
|
}
|
|
913
|
+
function parseQuery(search) {
|
|
914
|
+
const query = {};
|
|
915
|
+
const params = new URLSearchParams(search);
|
|
916
|
+
params.forEach((value, key) => {
|
|
917
|
+
query[key] = value;
|
|
918
|
+
});
|
|
919
|
+
return query;
|
|
920
|
+
}
|
|
921
|
+
function parseLocation(path) {
|
|
922
|
+
const [pathPart, queryPart = ""] = path.split("?");
|
|
923
|
+
const [cleanPath, hash = ""] = pathPart.split("#");
|
|
924
|
+
return {
|
|
925
|
+
path: cleanPath || "/",
|
|
926
|
+
params: {},
|
|
927
|
+
query: parseQuery(queryPart),
|
|
928
|
+
hash: hash ? `#${hash}` : ""
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
function findRoute(routes, path) {
|
|
932
|
+
for (const route of routes) {
|
|
933
|
+
const params = matchRoute(route.path, path);
|
|
934
|
+
if (params !== null) {
|
|
935
|
+
return { route, params };
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
return null;
|
|
939
|
+
}
|
|
940
|
+
function getCurrentPath(mode, base) {
|
|
941
|
+
if (mode === "hash") {
|
|
942
|
+
return window.location.hash.slice(1) || "/";
|
|
943
|
+
}
|
|
944
|
+
return window.location.pathname.replace(base, "") || "/";
|
|
945
|
+
}
|
|
946
|
+
function buildNavigationUrl(mode, base, path) {
|
|
947
|
+
if (mode === "hash") {
|
|
948
|
+
return `#${path}`;
|
|
949
|
+
}
|
|
950
|
+
const hasBaseSlash = base.endsWith("/");
|
|
951
|
+
const hasPathSlash = path.startsWith("/");
|
|
952
|
+
let urlPath = path;
|
|
953
|
+
if (hasBaseSlash && hasPathSlash) {
|
|
954
|
+
urlPath = path.slice(1);
|
|
955
|
+
} else if (!hasBaseSlash && !hasPathSlash && path !== "/") {
|
|
956
|
+
urlPath = `/${path}`;
|
|
957
|
+
}
|
|
958
|
+
return base + urlPath;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// src/client/router/router.ts
|
|
782
962
|
function createRouter(options) {
|
|
783
963
|
const { mode = "history", base = "", routes } = options;
|
|
784
964
|
const globalGuards = [];
|
|
785
|
-
const
|
|
786
|
-
const query = {};
|
|
787
|
-
const params = new URLSearchParams(search);
|
|
788
|
-
params.forEach((value, key) => {
|
|
789
|
-
query[key] = value;
|
|
790
|
-
});
|
|
791
|
-
return query;
|
|
792
|
-
};
|
|
793
|
-
const getCurrentPath = () => {
|
|
794
|
-
if (mode === "hash") {
|
|
795
|
-
return window.location.hash.slice(1) || "/";
|
|
796
|
-
}
|
|
797
|
-
return window.location.pathname.replace(base, "") || "/";
|
|
798
|
-
};
|
|
799
|
-
const parseLocation = (path) => {
|
|
800
|
-
const [pathPart, queryPart = ""] = path.split("?");
|
|
801
|
-
const [cleanPath, hash = ""] = pathPart.split("#");
|
|
802
|
-
return {
|
|
803
|
-
path: cleanPath || "/",
|
|
804
|
-
params: {},
|
|
805
|
-
query: parseQuery(queryPart),
|
|
806
|
-
hash: hash ? "#" + hash : ""
|
|
807
|
-
};
|
|
808
|
-
};
|
|
809
|
-
const findRoute = (path) => {
|
|
810
|
-
for (const route of routes) {
|
|
811
|
-
const params = matchRoute(route.path, path);
|
|
812
|
-
if (params !== null) {
|
|
813
|
-
return { route, params };
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
return null;
|
|
817
|
-
};
|
|
818
|
-
const currentRoute = dom.createState(parseLocation(getCurrentPath()));
|
|
965
|
+
const currentRoute = dom.createState(parseLocation(getCurrentPath(mode, base)));
|
|
819
966
|
const navigate = (path, replace = false) => {
|
|
820
967
|
const location = parseLocation(path);
|
|
821
|
-
const match = findRoute(location.path);
|
|
968
|
+
const match = findRoute(routes, location.path);
|
|
822
969
|
if (match) {
|
|
823
970
|
location.params = match.params;
|
|
824
971
|
}
|
|
825
972
|
for (const guard of globalGuards) {
|
|
826
|
-
if (!executeGuard(guard, location, currentRoute.value, navigate, replace))
|
|
973
|
+
if (!executeGuard(guard, location, currentRoute.value, navigate, replace)) {
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
827
976
|
}
|
|
828
977
|
if (match?.route.beforeEnter) {
|
|
829
|
-
if (!executeGuard(match.route.beforeEnter, location, currentRoute.value, navigate, replace))
|
|
830
|
-
|
|
831
|
-
let urlPath = path;
|
|
832
|
-
let url;
|
|
833
|
-
if (mode === "hash") {
|
|
834
|
-
url = "#" + path;
|
|
835
|
-
} else {
|
|
836
|
-
const hasBaseSlash = base.endsWith("/");
|
|
837
|
-
const hasPathSlash = path.startsWith("/");
|
|
838
|
-
if (hasBaseSlash && hasPathSlash) {
|
|
839
|
-
urlPath = path.slice(1);
|
|
840
|
-
} else if (!hasBaseSlash && !hasPathSlash && path !== "/") {
|
|
841
|
-
urlPath = "/" + path;
|
|
978
|
+
if (!executeGuard(match.route.beforeEnter, location, currentRoute.value, navigate, replace)) {
|
|
979
|
+
return;
|
|
842
980
|
}
|
|
843
|
-
url = base + urlPath;
|
|
844
981
|
}
|
|
982
|
+
const url = buildNavigationUrl(mode, base, path);
|
|
845
983
|
if (replace) {
|
|
846
984
|
window.history.replaceState({ path }, "", url);
|
|
847
985
|
} else {
|
|
@@ -850,9 +988,9 @@
|
|
|
850
988
|
currentRoute.value = location;
|
|
851
989
|
};
|
|
852
990
|
const handlePopState = () => {
|
|
853
|
-
const path = getCurrentPath();
|
|
991
|
+
const path = getCurrentPath(mode, base);
|
|
854
992
|
const location = parseLocation(path);
|
|
855
|
-
const match = findRoute(location.path);
|
|
993
|
+
const match = findRoute(routes, location.path);
|
|
856
994
|
if (match) {
|
|
857
995
|
location.params = match.params;
|
|
858
996
|
}
|
|
@@ -881,11 +1019,13 @@
|
|
|
881
1019
|
}
|
|
882
1020
|
};
|
|
883
1021
|
}
|
|
1022
|
+
|
|
1023
|
+
// src/client/router/view.ts
|
|
884
1024
|
function createRouterView(router, options) {
|
|
885
1025
|
const { routes, notFound } = options;
|
|
886
1026
|
return () => {
|
|
887
1027
|
const location = router.currentRoute.value;
|
|
888
|
-
const match = routes.find((
|
|
1028
|
+
const match = routes.find((route) => matchRoute(route.path, location.path) !== null);
|
|
889
1029
|
if (match) {
|
|
890
1030
|
const params = matchRoute(match.path, location.path) || {};
|
|
891
1031
|
const component = match.component({ ...params, ...location.query });
|
|
@@ -898,6 +1038,8 @@
|
|
|
898
1038
|
return { tagName: "div", props: {}, children: ["404 - Not Found"] };
|
|
899
1039
|
};
|
|
900
1040
|
}
|
|
1041
|
+
|
|
1042
|
+
// src/client/router/link.ts
|
|
901
1043
|
var routerLink = (router, props, ...children) => {
|
|
902
1044
|
const href = router.mode === "hash" ? `#${props.to}` : props.to;
|
|
903
1045
|
return {
|
|
@@ -905,8 +1047,8 @@
|
|
|
905
1047
|
props: {
|
|
906
1048
|
...props,
|
|
907
1049
|
href,
|
|
908
|
-
onclick: (
|
|
909
|
-
|
|
1050
|
+
onclick: (event) => {
|
|
1051
|
+
event.preventDefault();
|
|
910
1052
|
router.push(props.to);
|
|
911
1053
|
}
|
|
912
1054
|
},
|