@opentf/web 0.5.0 → 0.7.0
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 +43 -0
- package/components/Link.jsx +18 -4
- package/core/reactive.js +190 -0
- package/core/signals.js +19 -0
- package/index.js +1 -0
- package/package.json +1 -1
- package/runtime/clipboard.js +47 -0
- package/runtime/code-block.js +47 -0
- package/runtime/context.js +13 -3
- package/runtime/dom.js +181 -24
- package/runtime/error-boundary.js +5 -5
- package/runtime/hydrate.js +268 -0
- package/runtime/index.js +7 -1
- package/runtime/portal.js +4 -4
- package/runtime/raw-html.js +33 -0
- package/runtime/router.js +216 -10
- package/server/builtins.js +8 -3
- package/server/head.js +191 -0
- package/server/index.js +1 -0
- package/server/render.js +42 -13
- package/server/ssg-runtime.js +55 -1
package/server/ssg-runtime.js
CHANGED
|
@@ -20,6 +20,53 @@ export function defineSSG(tag, render) {
|
|
|
20
20
|
registry[tag] = render;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// ── hydration props collector (compiler-driven data hydration) ────────────────
|
|
24
|
+
// The lossy path is passing rich props through string attributes. Instead, each
|
|
25
|
+
// server-rendered island gets a `data-h` id and its JSON-safe props are recorded into a
|
|
26
|
+
// single payload the shell embeds as `<script type="application/json">`. At upgrade the
|
|
27
|
+
// client component initializes its prop *signals* from those real JS values (objects,
|
|
28
|
+
// arrays, numbers) — not from `getAttribute` — so islands resume with correct data, with
|
|
29
|
+
// no flash and no dependence on a parent walk re-applying props. `renderRoute` brackets a
|
|
30
|
+
// render with `beginHydrationCollect()` / `endHydrationCollect()`.
|
|
31
|
+
let _collect = null;
|
|
32
|
+
|
|
33
|
+
/** Start collecting per-island hydration props for one render. */
|
|
34
|
+
export function beginHydrationCollect() {
|
|
35
|
+
_collect = [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Finish collecting and return the payload as a JSON string ("" when nothing needs
|
|
40
|
+
* hydration). `<` is escaped so a prop value can never break out of the surrounding
|
|
41
|
+
* `<script>` (a `</script>` / `<!--` injection).
|
|
42
|
+
*/
|
|
43
|
+
export function endHydrationCollect() {
|
|
44
|
+
const data = _collect;
|
|
45
|
+
_collect = null;
|
|
46
|
+
if (!data || data.length === 0) return "";
|
|
47
|
+
return JSON.stringify(data).replace(/</g, "\\u003c");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Record an island's JSON-safe props, returning its hydration id — or `null` when not
|
|
52
|
+
* collecting, or when nothing serializes. A JSON round-trip drops functions (event
|
|
53
|
+
* callbacks are client-only — applied by the parent walk, invisible so no flash),
|
|
54
|
+
* `undefined`, and anything cyclic/DOM/signal-shaped, so only plain data crosses.
|
|
55
|
+
*/
|
|
56
|
+
function collectHydrationProps(props) {
|
|
57
|
+
if (!_collect || props == null || typeof props !== "object") return null;
|
|
58
|
+
let safe;
|
|
59
|
+
try {
|
|
60
|
+
safe = JSON.parse(JSON.stringify(props));
|
|
61
|
+
} catch {
|
|
62
|
+
return null; // cyclic / non-serializable → client falls back to attributes/defaults
|
|
63
|
+
}
|
|
64
|
+
if (!safe || typeof safe !== "object" || Object.keys(safe).length === 0) return null;
|
|
65
|
+
const id = _collect.length;
|
|
66
|
+
_collect.push(safe);
|
|
67
|
+
return id;
|
|
68
|
+
}
|
|
69
|
+
|
|
23
70
|
/** Escape text content for HTML. */
|
|
24
71
|
export function escapeHtml(s) {
|
|
25
72
|
return String(s).replace(/[&<>]/g, (c) => (c === "&" ? "&" : c === "<" ? "<" : ">"));
|
|
@@ -103,7 +150,14 @@ export function ssgComponent(tag, props, children) {
|
|
|
103
150
|
} catch {
|
|
104
151
|
inner = ""; // fail soft (client renders/handles it)
|
|
105
152
|
}
|
|
106
|
-
|
|
153
|
+
// Stamp the stable styling hook on the host (mirrors CSR's `classList.add`) so
|
|
154
|
+
// tag-hashed components are still styleable by a readable class name, plus the
|
|
155
|
+
// hydration id (`data-h`) keying this island's rich props in the payload (if any).
|
|
156
|
+
const cls = render && render.hostClass;
|
|
157
|
+
const id = collectHydrationProps(props);
|
|
158
|
+
let attrs = cls ? ` class="${cls}"` : "";
|
|
159
|
+
if (id != null) attrs += ` data-h="${id}"`;
|
|
160
|
+
return `<${tag}${attrs}>${inner}</${tag}>`;
|
|
107
161
|
}
|
|
108
162
|
|
|
109
163
|
export { VOID };
|