@vertz/ui-server 0.2.0 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +298 -310
- package/dist/bun-dev-server.d.ts +115 -0
- package/dist/bun-dev-server.js +2032 -0
- package/dist/bun-plugin/fast-refresh-dom-state.d.ts +51 -0
- package/dist/bun-plugin/fast-refresh-dom-state.js +10 -0
- package/dist/bun-plugin/fast-refresh-runtime.d.ts +43 -0
- package/dist/bun-plugin/fast-refresh-runtime.js +150 -0
- package/dist/bun-plugin/index.d.ts +44 -0
- package/dist/bun-plugin/index.js +197 -0
- package/dist/dom-shim/index.d.ts +37 -6
- package/dist/dom-shim/index.js +12 -324
- package/dist/index.d.ts +331 -64
- package/dist/index.js +285 -292
- package/dist/jsx-runtime/index.js +15 -2
- package/dist/shared/chunk-2qsqp9xj.js +150 -0
- package/dist/shared/chunk-32688jav.js +564 -0
- package/dist/shared/chunk-4t0ekdyv.js +513 -0
- package/dist/shared/chunk-eb80r8e8.js +4 -0
- package/dist/ssr/index.d.ts +86 -0
- package/dist/ssr/index.js +11 -0
- package/package.json +35 -18
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
// src/dom-shim/index.ts
|
|
2
|
+
import { setAdapter } from "@vertz/ui/internals";
|
|
3
|
+
|
|
4
|
+
// src/dom-shim/ssr-node.ts
|
|
5
|
+
class SSRNode {
|
|
6
|
+
childNodes = [];
|
|
7
|
+
parentNode = null;
|
|
8
|
+
get firstChild() {
|
|
9
|
+
return this.childNodes[0] ?? null;
|
|
10
|
+
}
|
|
11
|
+
get nextSibling() {
|
|
12
|
+
if (!this.parentNode)
|
|
13
|
+
return null;
|
|
14
|
+
const index = this.parentNode.childNodes.indexOf(this);
|
|
15
|
+
return this.parentNode.childNodes[index + 1] ?? null;
|
|
16
|
+
}
|
|
17
|
+
removeChild(child) {
|
|
18
|
+
const index = this.childNodes.indexOf(child);
|
|
19
|
+
if (index !== -1) {
|
|
20
|
+
this.childNodes.splice(index, 1);
|
|
21
|
+
child.parentNode = null;
|
|
22
|
+
}
|
|
23
|
+
return child;
|
|
24
|
+
}
|
|
25
|
+
insertBefore(newNode, referenceNode) {
|
|
26
|
+
if (!referenceNode) {
|
|
27
|
+
this.childNodes.push(newNode);
|
|
28
|
+
newNode.parentNode = this;
|
|
29
|
+
} else {
|
|
30
|
+
const index = this.childNodes.indexOf(referenceNode);
|
|
31
|
+
if (index !== -1) {
|
|
32
|
+
this.childNodes.splice(index, 0, newNode);
|
|
33
|
+
newNode.parentNode = this;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return newNode;
|
|
37
|
+
}
|
|
38
|
+
replaceChild(newNode, oldNode) {
|
|
39
|
+
const index = this.childNodes.indexOf(oldNode);
|
|
40
|
+
if (index !== -1) {
|
|
41
|
+
this.childNodes[index] = newNode;
|
|
42
|
+
newNode.parentNode = this;
|
|
43
|
+
oldNode.parentNode = null;
|
|
44
|
+
}
|
|
45
|
+
return oldNode;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/dom-shim/ssr-comment.ts
|
|
50
|
+
class SSRComment extends SSRNode {
|
|
51
|
+
text;
|
|
52
|
+
constructor(text) {
|
|
53
|
+
super();
|
|
54
|
+
this.text = text;
|
|
55
|
+
}
|
|
56
|
+
get data() {
|
|
57
|
+
return this.text;
|
|
58
|
+
}
|
|
59
|
+
set data(value) {
|
|
60
|
+
this.text = value;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/types.ts
|
|
65
|
+
function rawHtml(html) {
|
|
66
|
+
return { __raw: true, html };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/dom-shim/ssr-text-node.ts
|
|
70
|
+
class SSRTextNode extends SSRNode {
|
|
71
|
+
text;
|
|
72
|
+
constructor(text) {
|
|
73
|
+
super();
|
|
74
|
+
this.text = text;
|
|
75
|
+
}
|
|
76
|
+
get data() {
|
|
77
|
+
return this.text;
|
|
78
|
+
}
|
|
79
|
+
set data(value) {
|
|
80
|
+
this.text = value;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/dom-shim/ssr-fragment.ts
|
|
85
|
+
class SSRDocumentFragment extends SSRNode {
|
|
86
|
+
children = [];
|
|
87
|
+
appendChild(child) {
|
|
88
|
+
if (child instanceof SSRTextNode) {
|
|
89
|
+
this.children.push(child.text);
|
|
90
|
+
this.childNodes.push(child);
|
|
91
|
+
child.parentNode = this;
|
|
92
|
+
} else if (child instanceof SSRDocumentFragment) {
|
|
93
|
+
this.children.push(...child.children);
|
|
94
|
+
this.childNodes.push(...child.childNodes);
|
|
95
|
+
for (const fragmentChild of child.childNodes) {
|
|
96
|
+
fragmentChild.parentNode = this;
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
this.children.push(child);
|
|
100
|
+
this.childNodes.push(child);
|
|
101
|
+
child.parentNode = this;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/dom-shim/ssr-element.ts
|
|
107
|
+
function createStyleProxy(element) {
|
|
108
|
+
const styles = {};
|
|
109
|
+
return new Proxy(styles, {
|
|
110
|
+
set(_target, prop, value) {
|
|
111
|
+
if (typeof prop === "string") {
|
|
112
|
+
styles[prop] = value;
|
|
113
|
+
const pairs = Object.entries(styles).map(([k, v]) => {
|
|
114
|
+
const key = k.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
115
|
+
return `${key}: ${v}`;
|
|
116
|
+
});
|
|
117
|
+
element.attrs.style = pairs.join("; ");
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
},
|
|
121
|
+
get(_target, prop) {
|
|
122
|
+
if (typeof prop === "string") {
|
|
123
|
+
return styles[prop] ?? "";
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
class SSRElement extends SSRNode {
|
|
131
|
+
tag;
|
|
132
|
+
attrs = {};
|
|
133
|
+
children = [];
|
|
134
|
+
_classList = new Set;
|
|
135
|
+
_textContent = null;
|
|
136
|
+
_innerHTML = null;
|
|
137
|
+
style;
|
|
138
|
+
constructor(tag) {
|
|
139
|
+
super();
|
|
140
|
+
this.tag = tag;
|
|
141
|
+
this.style = createStyleProxy(this);
|
|
142
|
+
}
|
|
143
|
+
setAttribute(name, value) {
|
|
144
|
+
if (name === "class") {
|
|
145
|
+
this._classList = new Set(value.split(/\s+/).filter(Boolean));
|
|
146
|
+
}
|
|
147
|
+
this.attrs[name] = value;
|
|
148
|
+
}
|
|
149
|
+
getAttribute(name) {
|
|
150
|
+
return this.attrs[name] ?? null;
|
|
151
|
+
}
|
|
152
|
+
removeAttribute(name) {
|
|
153
|
+
delete this.attrs[name];
|
|
154
|
+
if (name === "class") {
|
|
155
|
+
this._classList.clear();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
appendChild(child) {
|
|
159
|
+
if (child instanceof SSRComment) {
|
|
160
|
+
this.children.push(child);
|
|
161
|
+
this.childNodes.push(child);
|
|
162
|
+
child.parentNode = this;
|
|
163
|
+
} else if (child instanceof SSRTextNode) {
|
|
164
|
+
this.children.push(child.text);
|
|
165
|
+
this.childNodes.push(child);
|
|
166
|
+
child.parentNode = this;
|
|
167
|
+
} else if (child instanceof SSRDocumentFragment) {
|
|
168
|
+
for (const fragmentChild of child.childNodes) {
|
|
169
|
+
if (fragmentChild instanceof SSRComment) {
|
|
170
|
+
this.children.push(fragmentChild);
|
|
171
|
+
} else if (fragmentChild instanceof SSRTextNode) {
|
|
172
|
+
this.children.push(fragmentChild.text);
|
|
173
|
+
} else if (fragmentChild instanceof SSRElement) {
|
|
174
|
+
this.children.push(fragmentChild);
|
|
175
|
+
}
|
|
176
|
+
this.childNodes.push(fragmentChild);
|
|
177
|
+
fragmentChild.parentNode = this;
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
this.children.push(child);
|
|
181
|
+
this.childNodes.push(child);
|
|
182
|
+
child.parentNode = this;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
insertBefore(newNode, referenceNode) {
|
|
186
|
+
const refIdx = referenceNode ? this._findChildIndex(referenceNode) : -1;
|
|
187
|
+
const result = super.insertBefore(newNode, referenceNode);
|
|
188
|
+
if (newNode instanceof SSRDocumentFragment) {
|
|
189
|
+
const fragmentChildren = [];
|
|
190
|
+
for (const fc of newNode.childNodes) {
|
|
191
|
+
if (fc instanceof SSRComment)
|
|
192
|
+
fragmentChildren.push(fc);
|
|
193
|
+
else if (fc instanceof SSRTextNode)
|
|
194
|
+
fragmentChildren.push(fc.text);
|
|
195
|
+
else if (fc instanceof SSRElement)
|
|
196
|
+
fragmentChildren.push(fc);
|
|
197
|
+
}
|
|
198
|
+
if (!referenceNode || refIdx === -1) {
|
|
199
|
+
this.children.push(...fragmentChildren);
|
|
200
|
+
} else {
|
|
201
|
+
this.children.splice(refIdx, 0, ...fragmentChildren);
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
const child = newNode instanceof SSRComment ? newNode : newNode instanceof SSRTextNode ? newNode.text : newNode instanceof SSRElement ? newNode : null;
|
|
205
|
+
if (child != null) {
|
|
206
|
+
if (!referenceNode || refIdx === -1) {
|
|
207
|
+
this.children.push(child);
|
|
208
|
+
} else {
|
|
209
|
+
this.children.splice(refIdx, 0, child);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
replaceChild(newNode, oldNode) {
|
|
216
|
+
const oldIdx = this._findChildIndex(oldNode);
|
|
217
|
+
const result = super.replaceChild(newNode, oldNode);
|
|
218
|
+
if (oldIdx !== -1) {
|
|
219
|
+
const newChild = newNode instanceof SSRComment ? newNode : newNode instanceof SSRTextNode ? newNode.text : newNode instanceof SSRElement ? newNode : null;
|
|
220
|
+
if (newChild != null) {
|
|
221
|
+
this.children[oldIdx] = newChild;
|
|
222
|
+
} else {
|
|
223
|
+
this.children.splice(oldIdx, 1);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
_findChildIndex(node) {
|
|
229
|
+
return this.childNodes.indexOf(node);
|
|
230
|
+
}
|
|
231
|
+
removeChild(child) {
|
|
232
|
+
const idx = this._findChildIndex(child);
|
|
233
|
+
const result = super.removeChild(child);
|
|
234
|
+
if (idx !== -1) {
|
|
235
|
+
this.children.splice(idx, 1);
|
|
236
|
+
}
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
get classList() {
|
|
240
|
+
const self = this;
|
|
241
|
+
return {
|
|
242
|
+
add(cls) {
|
|
243
|
+
self._classList.add(cls);
|
|
244
|
+
self.attrs.class = [...self._classList].join(" ");
|
|
245
|
+
},
|
|
246
|
+
remove(cls) {
|
|
247
|
+
self._classList.delete(cls);
|
|
248
|
+
const val = [...self._classList].join(" ");
|
|
249
|
+
if (val) {
|
|
250
|
+
self.attrs.class = val;
|
|
251
|
+
} else {
|
|
252
|
+
delete self.attrs.class;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
set className(value) {
|
|
258
|
+
this._classList = new Set(value.split(/\s+/).filter(Boolean));
|
|
259
|
+
if (value) {
|
|
260
|
+
this.attrs.class = value;
|
|
261
|
+
} else {
|
|
262
|
+
delete this.attrs.class;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
get className() {
|
|
266
|
+
return this.attrs.class ?? "";
|
|
267
|
+
}
|
|
268
|
+
set textContent(value) {
|
|
269
|
+
this._textContent = value;
|
|
270
|
+
this.children = value ? [value] : [];
|
|
271
|
+
this.childNodes = [];
|
|
272
|
+
}
|
|
273
|
+
get textContent() {
|
|
274
|
+
return this._textContent;
|
|
275
|
+
}
|
|
276
|
+
set innerHTML(value) {
|
|
277
|
+
this._innerHTML = value;
|
|
278
|
+
this.children = value ? [value] : [];
|
|
279
|
+
this.childNodes = [];
|
|
280
|
+
}
|
|
281
|
+
get innerHTML() {
|
|
282
|
+
return this._innerHTML ?? "";
|
|
283
|
+
}
|
|
284
|
+
addEventListener(_event, _handler) {}
|
|
285
|
+
removeEventListener(_event, _handler) {}
|
|
286
|
+
toVNode() {
|
|
287
|
+
return {
|
|
288
|
+
tag: this.tag,
|
|
289
|
+
attrs: { ...this.attrs },
|
|
290
|
+
children: this.children.map((child) => {
|
|
291
|
+
if (typeof child === "string") {
|
|
292
|
+
return this._innerHTML != null ? rawHtml(child) : child;
|
|
293
|
+
}
|
|
294
|
+
if (child instanceof SSRComment)
|
|
295
|
+
return rawHtml(`<!--${child.text}-->`);
|
|
296
|
+
if (typeof child.toVNode === "function")
|
|
297
|
+
return child.toVNode();
|
|
298
|
+
return String(child);
|
|
299
|
+
})
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// src/ssr-adapter.ts
|
|
305
|
+
var BRAND = Symbol.for("vertz:render-node");
|
|
306
|
+
Object.defineProperty(SSRNode.prototype, BRAND, {
|
|
307
|
+
value: true,
|
|
308
|
+
enumerable: false,
|
|
309
|
+
configurable: false,
|
|
310
|
+
writable: false
|
|
311
|
+
});
|
|
312
|
+
function createSSRAdapter() {
|
|
313
|
+
return {
|
|
314
|
+
createElement: (tag) => new SSRElement(tag),
|
|
315
|
+
createElementNS: (_ns, tag) => new SSRElement(tag),
|
|
316
|
+
createTextNode: (text) => new SSRTextNode(text),
|
|
317
|
+
createComment: (text) => new SSRComment(text),
|
|
318
|
+
createDocumentFragment: () => new SSRDocumentFragment,
|
|
319
|
+
isNode: (value) => value != null && typeof value === "object" && (BRAND in value)
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/dom-shim/index.ts
|
|
324
|
+
var SHIM_GLOBALS = [
|
|
325
|
+
"document",
|
|
326
|
+
"window",
|
|
327
|
+
"Node",
|
|
328
|
+
"HTMLElement",
|
|
329
|
+
"HTMLAnchorElement",
|
|
330
|
+
"HTMLDivElement",
|
|
331
|
+
"HTMLInputElement",
|
|
332
|
+
"HTMLButtonElement",
|
|
333
|
+
"HTMLSelectElement",
|
|
334
|
+
"HTMLTextAreaElement",
|
|
335
|
+
"DocumentFragment",
|
|
336
|
+
"MouseEvent",
|
|
337
|
+
"Event"
|
|
338
|
+
];
|
|
339
|
+
var savedGlobals = null;
|
|
340
|
+
var shimInstalled = false;
|
|
341
|
+
var installedGlobals = [];
|
|
342
|
+
function installGlobal(name, value) {
|
|
343
|
+
if (globalThis[name] === undefined) {
|
|
344
|
+
Object.defineProperty(globalThis, name, {
|
|
345
|
+
value,
|
|
346
|
+
writable: true,
|
|
347
|
+
configurable: true
|
|
348
|
+
});
|
|
349
|
+
installedGlobals.push(name);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function installDomShim() {
|
|
353
|
+
for (const g of installedGlobals) {
|
|
354
|
+
delete globalThis[g];
|
|
355
|
+
}
|
|
356
|
+
installedGlobals = [];
|
|
357
|
+
setAdapter(createSSRAdapter());
|
|
358
|
+
const isSSRContext = typeof globalThis.__SSR_URL__ !== "undefined";
|
|
359
|
+
if (typeof document !== "undefined" && !isSSRContext && !shimInstalled) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (!shimInstalled) {
|
|
363
|
+
savedGlobals = new Map;
|
|
364
|
+
for (const g of SHIM_GLOBALS) {
|
|
365
|
+
if (g in globalThis) {
|
|
366
|
+
savedGlobals.set(g, globalThis[g]);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
shimInstalled = true;
|
|
371
|
+
const fakeDocument = {
|
|
372
|
+
createElement(tag) {
|
|
373
|
+
return new SSRElement(tag);
|
|
374
|
+
},
|
|
375
|
+
createTextNode(text) {
|
|
376
|
+
return new SSRTextNode(text);
|
|
377
|
+
},
|
|
378
|
+
createComment(text) {
|
|
379
|
+
return new SSRComment(text);
|
|
380
|
+
},
|
|
381
|
+
createDocumentFragment() {
|
|
382
|
+
return new SSRDocumentFragment;
|
|
383
|
+
},
|
|
384
|
+
head: new SSRElement("head"),
|
|
385
|
+
body: new SSRElement("body"),
|
|
386
|
+
querySelector: () => null,
|
|
387
|
+
querySelectorAll: () => [],
|
|
388
|
+
getElementById: () => null,
|
|
389
|
+
cookie: ""
|
|
390
|
+
};
|
|
391
|
+
globalThis.document = fakeDocument;
|
|
392
|
+
if (typeof window === "undefined") {
|
|
393
|
+
globalThis.window = {
|
|
394
|
+
location: { pathname: globalThis.__SSR_URL__ || "/", search: "", hash: "" },
|
|
395
|
+
addEventListener: () => {},
|
|
396
|
+
removeEventListener: () => {},
|
|
397
|
+
history: {
|
|
398
|
+
pushState: () => {},
|
|
399
|
+
replaceState: () => {}
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
} else {
|
|
403
|
+
globalThis.window.location = {
|
|
404
|
+
...globalThis.window.location || {},
|
|
405
|
+
pathname: globalThis.__SSR_URL__ || "/"
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
globalThis.Node = SSRNode;
|
|
409
|
+
globalThis.HTMLElement = SSRElement;
|
|
410
|
+
globalThis.HTMLAnchorElement = SSRElement;
|
|
411
|
+
globalThis.HTMLDivElement = SSRElement;
|
|
412
|
+
globalThis.HTMLInputElement = SSRElement;
|
|
413
|
+
globalThis.HTMLButtonElement = SSRElement;
|
|
414
|
+
globalThis.HTMLSelectElement = SSRElement;
|
|
415
|
+
globalThis.HTMLTextAreaElement = SSRElement;
|
|
416
|
+
globalThis.DocumentFragment = SSRDocumentFragment;
|
|
417
|
+
globalThis.MouseEvent = class MockMouseEvent {
|
|
418
|
+
};
|
|
419
|
+
globalThis.Event = class MockEvent {
|
|
420
|
+
};
|
|
421
|
+
const storageStub = {
|
|
422
|
+
getItem: () => null,
|
|
423
|
+
setItem: () => {},
|
|
424
|
+
removeItem: () => {},
|
|
425
|
+
clear: () => {},
|
|
426
|
+
key: () => null,
|
|
427
|
+
length: 0
|
|
428
|
+
};
|
|
429
|
+
installGlobal("localStorage", storageStub);
|
|
430
|
+
installGlobal("sessionStorage", { ...storageStub });
|
|
431
|
+
installGlobal("navigator", {
|
|
432
|
+
userAgent: "",
|
|
433
|
+
language: "en",
|
|
434
|
+
languages: ["en"],
|
|
435
|
+
onLine: true,
|
|
436
|
+
cookieEnabled: false,
|
|
437
|
+
hardwareConcurrency: 1,
|
|
438
|
+
maxTouchPoints: 0,
|
|
439
|
+
platform: "",
|
|
440
|
+
vendor: ""
|
|
441
|
+
});
|
|
442
|
+
const NoopObserver = class {
|
|
443
|
+
observe() {}
|
|
444
|
+
unobserve() {}
|
|
445
|
+
disconnect() {}
|
|
446
|
+
takeRecords() {
|
|
447
|
+
return [];
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
installGlobal("IntersectionObserver", NoopObserver);
|
|
451
|
+
installGlobal("ResizeObserver", NoopObserver);
|
|
452
|
+
installGlobal("MutationObserver", NoopObserver);
|
|
453
|
+
let nextFrameId = 1;
|
|
454
|
+
installGlobal("requestAnimationFrame", () => nextFrameId++);
|
|
455
|
+
installGlobal("cancelAnimationFrame", () => {});
|
|
456
|
+
installGlobal("requestIdleCallback", () => nextFrameId++);
|
|
457
|
+
installGlobal("cancelIdleCallback", () => {});
|
|
458
|
+
installGlobal("CustomEvent", class MockCustomEvent {
|
|
459
|
+
type;
|
|
460
|
+
detail;
|
|
461
|
+
constructor(type, init) {
|
|
462
|
+
this.type = type;
|
|
463
|
+
this.detail = init?.detail ?? null;
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
function removeDomShim() {
|
|
468
|
+
setAdapter(null);
|
|
469
|
+
if (!shimInstalled) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
shimInstalled = false;
|
|
473
|
+
if (savedGlobals) {
|
|
474
|
+
for (const g of SHIM_GLOBALS) {
|
|
475
|
+
if (savedGlobals.has(g)) {
|
|
476
|
+
globalThis[g] = savedGlobals.get(g);
|
|
477
|
+
} else {
|
|
478
|
+
delete globalThis[g];
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
savedGlobals = null;
|
|
482
|
+
} else {
|
|
483
|
+
for (const g of SHIM_GLOBALS) {
|
|
484
|
+
delete globalThis[g];
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
for (const g of installedGlobals) {
|
|
488
|
+
delete globalThis[g];
|
|
489
|
+
}
|
|
490
|
+
installedGlobals = [];
|
|
491
|
+
}
|
|
492
|
+
function toVNode(element) {
|
|
493
|
+
if (element instanceof SSRElement) {
|
|
494
|
+
return element.toVNode();
|
|
495
|
+
}
|
|
496
|
+
if (element instanceof SSRDocumentFragment) {
|
|
497
|
+
return {
|
|
498
|
+
tag: "fragment",
|
|
499
|
+
attrs: {},
|
|
500
|
+
children: element.children.map((child) => {
|
|
501
|
+
if (typeof child === "string")
|
|
502
|
+
return child;
|
|
503
|
+
return child.toVNode();
|
|
504
|
+
})
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
if (typeof element === "object" && "tag" in element) {
|
|
508
|
+
return element;
|
|
509
|
+
}
|
|
510
|
+
return { tag: "span", attrs: {}, children: [String(element)] };
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
export { SSRNode, SSRComment, rawHtml, SSRTextNode, SSRDocumentFragment, SSRElement, createSSRAdapter, installDomShim, removeDomShim, toVNode };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Theme } from "@vertz/ui";
|
|
2
|
+
interface SSRModule {
|
|
3
|
+
default?: () => unknown;
|
|
4
|
+
App?: () => unknown;
|
|
5
|
+
theme?: Theme;
|
|
6
|
+
/** Global CSS strings to include in every SSR response (e.g. resets, body styles). */
|
|
7
|
+
styles?: string[];
|
|
8
|
+
/**
|
|
9
|
+
* Return all CSS tracked by the bundled @vertz/ui instance.
|
|
10
|
+
* The Vite SSR build inlines @vertz/ui into the server bundle, creating
|
|
11
|
+
* a separate module instance from @vertz/ui-server's dependency. Without
|
|
12
|
+
* this, component CSS from module-level css() calls is invisible to the
|
|
13
|
+
* SSR renderer. Export `getInjectedCSS` from @vertz/ui in the app entry.
|
|
14
|
+
*/
|
|
15
|
+
getInjectedCSS?: () => string[];
|
|
16
|
+
}
|
|
17
|
+
interface SSRRenderResult {
|
|
18
|
+
html: string;
|
|
19
|
+
css: string;
|
|
20
|
+
ssrData: Array<{
|
|
21
|
+
key: string;
|
|
22
|
+
data: unknown;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
interface SSRDiscoverResult {
|
|
26
|
+
resolved: Array<{
|
|
27
|
+
key: string;
|
|
28
|
+
data: unknown;
|
|
29
|
+
}>;
|
|
30
|
+
pending: string[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Render an SSR module to an HTML string with CSS and pre-fetched query data.
|
|
34
|
+
*
|
|
35
|
+
* Performs a two-pass render:
|
|
36
|
+
* - Pass 1: Discovery — calls the app to trigger query() registrations, awaits them
|
|
37
|
+
* - Pass 2: Render — calls the app again with data populated, renders to HTML
|
|
38
|
+
*/
|
|
39
|
+
declare function ssrRenderToString(module: SSRModule, url: string, options?: {
|
|
40
|
+
ssrTimeout?: number;
|
|
41
|
+
}): Promise<SSRRenderResult>;
|
|
42
|
+
/**
|
|
43
|
+
* Discover queries for a given URL without rendering.
|
|
44
|
+
* Runs only Pass 1 (query registration + resolution), no Pass 2 render.
|
|
45
|
+
* Used by the production handler to pre-fetch query data for client-side navigations.
|
|
46
|
+
*/
|
|
47
|
+
declare function ssrDiscoverQueries(module: SSRModule, url: string, options?: {
|
|
48
|
+
ssrTimeout?: number;
|
|
49
|
+
}): Promise<SSRDiscoverResult>;
|
|
50
|
+
interface SSRHandlerOptions {
|
|
51
|
+
/** The loaded SSR module (import('./dist/server/index.js')) */
|
|
52
|
+
module: SSRModule;
|
|
53
|
+
/** HTML template string (contents of dist/client/index.html) */
|
|
54
|
+
template: string;
|
|
55
|
+
/** SSR timeout for queries (default: 300ms) */
|
|
56
|
+
ssrTimeout?: number;
|
|
57
|
+
/**
|
|
58
|
+
* Map of CSS asset URLs to their content for inlining.
|
|
59
|
+
* Replaces `<link rel="stylesheet" href="...">` tags with inline `<style>` tags.
|
|
60
|
+
* Eliminates extra network requests, preventing FOUC on slow connections.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* inlineCSS: { '/assets/vertz.css': await Bun.file('./dist/client/assets/vertz.css').text() }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
inlineCSS?: Record<string, string>;
|
|
68
|
+
/**
|
|
69
|
+
* CSP nonce to inject on all inline `<script>` tags emitted during SSR.
|
|
70
|
+
*
|
|
71
|
+
* When set, the SSR data hydration script will include `nonce="<value>"`
|
|
72
|
+
* so that strict Content-Security-Policy headers do not block it.
|
|
73
|
+
*/
|
|
74
|
+
nonce?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create a web-standard SSR request handler.
|
|
78
|
+
*
|
|
79
|
+
* Handles two types of requests:
|
|
80
|
+
* - X-Vertz-Nav: 1 -> SSE Response with pre-fetched query data
|
|
81
|
+
* - Normal HTML request -> SSR-rendered HTML Response
|
|
82
|
+
*
|
|
83
|
+
* Does NOT serve static files — that's the adapter/platform's job.
|
|
84
|
+
*/
|
|
85
|
+
declare function createSSRHandler(options: SSRHandlerOptions): (request: Request) => Promise<Response>;
|
|
86
|
+
export { ssrRenderToString, ssrDiscoverQueries, createSSRHandler, SSRRenderResult, SSRModule, SSRHandlerOptions, SSRDiscoverResult };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertz/ui-server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Vertz UI server-side rendering runtime",
|
|
@@ -9,10 +9,6 @@
|
|
|
9
9
|
"url": "https://github.com/vertz-dev/vertz.git",
|
|
10
10
|
"directory": "packages/ui-server"
|
|
11
11
|
},
|
|
12
|
-
"publishConfig": {
|
|
13
|
-
"access": "public",
|
|
14
|
-
"provenance": true
|
|
15
|
-
},
|
|
16
12
|
"main": "dist/index.js",
|
|
17
13
|
"types": "dist/index.d.ts",
|
|
18
14
|
"exports": {
|
|
@@ -20,6 +16,10 @@
|
|
|
20
16
|
"import": "./dist/index.js",
|
|
21
17
|
"types": "./dist/index.d.ts"
|
|
22
18
|
},
|
|
19
|
+
"./ssr": {
|
|
20
|
+
"import": "./dist/ssr/index.js",
|
|
21
|
+
"types": "./dist/ssr/index.d.ts"
|
|
22
|
+
},
|
|
23
23
|
"./dom-shim": {
|
|
24
24
|
"import": "./dist/dom-shim/index.js",
|
|
25
25
|
"types": "./dist/dom-shim/index.d.ts"
|
|
@@ -27,6 +27,18 @@
|
|
|
27
27
|
"./jsx-runtime": {
|
|
28
28
|
"import": "./dist/jsx-runtime/index.js",
|
|
29
29
|
"types": "./dist/jsx-runtime/index.d.ts"
|
|
30
|
+
},
|
|
31
|
+
"./bun-plugin": {
|
|
32
|
+
"import": "./dist/bun-plugin/index.js",
|
|
33
|
+
"types": "./dist/bun-plugin/index.d.ts"
|
|
34
|
+
},
|
|
35
|
+
"./fast-refresh-runtime": {
|
|
36
|
+
"import": "./dist/bun-plugin/fast-refresh-runtime.js",
|
|
37
|
+
"types": "./dist/bun-plugin/fast-refresh-runtime.d.ts"
|
|
38
|
+
},
|
|
39
|
+
"./bun-dev-server": {
|
|
40
|
+
"import": "./dist/bun-dev-server.js",
|
|
41
|
+
"types": "./dist/bun-dev-server.d.ts"
|
|
30
42
|
}
|
|
31
43
|
},
|
|
32
44
|
"files": [
|
|
@@ -34,26 +46,31 @@
|
|
|
34
46
|
],
|
|
35
47
|
"scripts": {
|
|
36
48
|
"build": "bunup",
|
|
37
|
-
"test": "
|
|
38
|
-
"test:
|
|
49
|
+
"test": "bun test",
|
|
50
|
+
"test:integration": "bun test src/__tests__/bun-dev-server.integration.local.ts",
|
|
39
51
|
"typecheck": "tsc --noEmit"
|
|
40
52
|
},
|
|
41
53
|
"dependencies": {
|
|
42
|
-
"@
|
|
43
|
-
"@
|
|
44
|
-
"@vertz/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"
|
|
54
|
+
"@ampproject/remapping": "^2.3.0",
|
|
55
|
+
"@jridgewell/trace-mapping": "^0.3.31",
|
|
56
|
+
"@vertz/core": "0.2.2",
|
|
57
|
+
"@vertz/ui": "0.2.2",
|
|
58
|
+
"@vertz/ui-compiler": "0.2.2",
|
|
59
|
+
"magic-string": "^0.30.0",
|
|
60
|
+
"ts-morph": "^27.0.2"
|
|
48
61
|
},
|
|
49
62
|
"devDependencies": {
|
|
50
|
-
"
|
|
51
|
-
"bunup": "
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
63
|
+
"bun-types": "^1.3.10",
|
|
64
|
+
"bunup": "^0.16.31",
|
|
65
|
+
"@happy-dom/global-registrator": "^20.8.3",
|
|
66
|
+
"happy-dom": "^18.0.1",
|
|
67
|
+
"typescript": "^5.7.0"
|
|
55
68
|
},
|
|
56
69
|
"engines": {
|
|
57
70
|
"node": ">=22"
|
|
71
|
+
},
|
|
72
|
+
"publishConfig": {
|
|
73
|
+
"access": "public",
|
|
74
|
+
"provenance": true
|
|
58
75
|
}
|
|
59
76
|
}
|