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