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