@praxisjs/runtime 0.5.5 → 0.6.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/CHANGELOG.md +16 -0
- package/dist/__tests__/hydration.test.d.ts +2 -0
- package/dist/__tests__/hydration.test.d.ts.map +1 -0
- package/dist/__tests__/hydration.test.js +299 -0
- package/dist/__tests__/hydration.test.js.map +1 -0
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +5 -0
- package/dist/element.js.map +1 -1
- package/dist/hydration-context.d.ts +21 -0
- package/dist/hydration-context.d.ts.map +1 -0
- package/dist/hydration-context.js +40 -0
- package/dist/hydration-context.js.map +1 -0
- package/dist/hydration.d.ts +18 -0
- package/dist/hydration.d.ts.map +1 -0
- package/dist/hydration.js +67 -0
- package/dist/hydration.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -10
- package/dist/index.js.map +1 -1
- package/dist/portal.d.ts.map +1 -1
- package/dist/portal.js +8 -0
- package/dist/portal.js.map +1 -1
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +10 -0
- package/dist/reactive.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/hydration.test.ts +360 -0
- package/src/element.ts +5 -0
- package/src/hydration-context.ts +53 -0
- package/src/hydration.ts +70 -0
- package/src/index.ts +57 -11
- package/src/portal.ts +8 -0
- package/src/reactive.ts +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @praxisjs/runtime
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 728c90e: `render()` now hydrates prerendered HTML instead of discarding it, when the container carries the marker `@praxisjs/ssg` writes onto its static output.
|
|
8
|
+
|
|
9
|
+
The tree is still built the normal way (same `mountElement`/`mountComponent`/`mountReactive` code path, unchanged for every existing app), just into a detached node first. A new reconciliation pass then walks that fresh tree against the container's existing DOM: matching elements keep the real, already-attached node (with props and listeners replayed onto it) instead of being recreated, while text/comment nodes and any local mismatch are recreated as before. `Portal` is skipped during a server render pass (`isServerRenderPass()` from `@praxisjs/core/internal`) so its content is never baked into static HTML, avoiding duplication once it mounts normally on the client.
|
|
10
|
+
|
|
11
|
+
Apps that never render server-provided HTML are unaffected — `render()`'s create-mode path is unchanged.
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [1ec5b2f]
|
|
16
|
+
- @praxisjs/core@2.1.0
|
|
17
|
+
- @praxisjs/decorators@1.6.1
|
|
18
|
+
|
|
3
19
|
## 0.5.5
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/hydration.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
3
|
+
import { StatefulComponent } from "@praxisjs/core";
|
|
4
|
+
import { isServerRenderPass, setServerRenderPass, signal } from "@praxisjs/core/internal";
|
|
5
|
+
import { mountComponent } from "../component";
|
|
6
|
+
import { getCurrentScope } from "../context";
|
|
7
|
+
import { mountElement } from "../element";
|
|
8
|
+
import { reconcile } from "../hydration";
|
|
9
|
+
import { registerReactiveNode } from "../hydration-context";
|
|
10
|
+
import { render, Portal } from "../index";
|
|
11
|
+
import { Scope } from "../scope";
|
|
12
|
+
const SSG_MARKER = "data-praxis-ssg";
|
|
13
|
+
/** Renders `factory` into a throwaway container and returns the resulting markup, simulating SSR output. */
|
|
14
|
+
function ssrHTML(factory) {
|
|
15
|
+
const serverContainer = document.createElement("div");
|
|
16
|
+
const unmount = render(factory, serverContainer);
|
|
17
|
+
const html = serverContainer.innerHTML;
|
|
18
|
+
unmount();
|
|
19
|
+
return html;
|
|
20
|
+
}
|
|
21
|
+
function hydratable(factory) {
|
|
22
|
+
const container = document.createElement("div");
|
|
23
|
+
container.innerHTML = ssrHTML(factory);
|
|
24
|
+
container.setAttribute(SSG_MARKER, "1");
|
|
25
|
+
document.body.appendChild(container);
|
|
26
|
+
return container;
|
|
27
|
+
}
|
|
28
|
+
afterEach(() => { setServerRenderPass(false); });
|
|
29
|
+
describe("render() — hydration (build + reconcile)", () => {
|
|
30
|
+
it("adopts a flat element instead of recreating it", () => {
|
|
31
|
+
const factory = () => mountElement("p", { id: "greet", children: "hello" }, new Scope());
|
|
32
|
+
const container = hydratable(factory);
|
|
33
|
+
const serverEl = container.querySelector("#greet");
|
|
34
|
+
expect(serverEl).not.toBeNull();
|
|
35
|
+
render(factory, container);
|
|
36
|
+
expect(container.querySelector("#greet")).toBe(serverEl);
|
|
37
|
+
document.body.removeChild(container);
|
|
38
|
+
});
|
|
39
|
+
it("adopts nested elements at every level — the case that broke the ambient-cursor attempt", () => {
|
|
40
|
+
const factory = () => mountElement("div", {
|
|
41
|
+
id: "outer",
|
|
42
|
+
children: mountElement("span", { id: "inner", children: "text" }, new Scope()),
|
|
43
|
+
}, new Scope());
|
|
44
|
+
const container = hydratable(factory);
|
|
45
|
+
const outerBefore = container.querySelector("#outer");
|
|
46
|
+
const innerBefore = container.querySelector("#inner");
|
|
47
|
+
render(factory, container);
|
|
48
|
+
expect(container.querySelector("#outer")).toBe(outerBefore);
|
|
49
|
+
expect(container.querySelector("#inner")).toBe(innerBefore);
|
|
50
|
+
expect(container.textContent).toBe("text");
|
|
51
|
+
document.body.removeChild(container);
|
|
52
|
+
});
|
|
53
|
+
it("adopts three levels deep with siblings at each level", () => {
|
|
54
|
+
const factory = () => mountElement("ul", {
|
|
55
|
+
id: "list",
|
|
56
|
+
children: [
|
|
57
|
+
mountElement("li", { id: "a", children: mountElement("b", { children: "1" }, new Scope()) }, new Scope()),
|
|
58
|
+
mountElement("li", { id: "b", children: "2" }, new Scope()),
|
|
59
|
+
],
|
|
60
|
+
}, new Scope());
|
|
61
|
+
const container = hydratable(factory);
|
|
62
|
+
const before = {
|
|
63
|
+
list: container.querySelector("#list"),
|
|
64
|
+
a: container.querySelector("#a"),
|
|
65
|
+
b: container.querySelector("#b"),
|
|
66
|
+
bold: container.querySelector("b"),
|
|
67
|
+
};
|
|
68
|
+
render(factory, container);
|
|
69
|
+
expect(container.querySelector("#list")).toBe(before.list);
|
|
70
|
+
expect(container.querySelector("#a")).toBe(before.a);
|
|
71
|
+
expect(container.querySelector("#b")).toBe(before.b);
|
|
72
|
+
expect(container.querySelector("b")).toBe(before.bold);
|
|
73
|
+
document.body.removeChild(container);
|
|
74
|
+
});
|
|
75
|
+
it("wires event listeners onto the adopted (real) element via replay", () => {
|
|
76
|
+
let clicks = 0;
|
|
77
|
+
const factory = () => mountElement("div", {
|
|
78
|
+
children: mountElement("button", { onClick: () => { clicks++; }, children: "Click" }, new Scope()),
|
|
79
|
+
}, new Scope());
|
|
80
|
+
const container = hydratable(factory);
|
|
81
|
+
const buttonBefore = container.querySelector("button");
|
|
82
|
+
render(factory, container);
|
|
83
|
+
expect(container.querySelector("button")).toBe(buttonBefore);
|
|
84
|
+
container.querySelector("button")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
85
|
+
expect(clicks).toBe(1);
|
|
86
|
+
document.body.removeChild(container);
|
|
87
|
+
});
|
|
88
|
+
it("hydrates reactive text and keeps reacting to signal changes afterwards", () => {
|
|
89
|
+
const text = signal("initial");
|
|
90
|
+
const factory = () => mountElement("span", { children: () => text() }, new Scope());
|
|
91
|
+
const container = hydratable(factory);
|
|
92
|
+
expect(container.textContent).toBe("initial");
|
|
93
|
+
render(factory, container);
|
|
94
|
+
text.set("updated");
|
|
95
|
+
expect(container.textContent).toBe("updated");
|
|
96
|
+
text.set("initial");
|
|
97
|
+
document.body.removeChild(container);
|
|
98
|
+
});
|
|
99
|
+
it("hydrates a reactive list of elements, keeping real nodes, and stays correct after a later update", () => {
|
|
100
|
+
const items = signal(["a", "b", "c"]);
|
|
101
|
+
const factory = () => mountElement("ul", {
|
|
102
|
+
id: "list",
|
|
103
|
+
children: () => items().map((item) => mountElement("li", { children: item }, new Scope())),
|
|
104
|
+
}, new Scope());
|
|
105
|
+
const container = hydratable(factory);
|
|
106
|
+
const list = container.querySelector("#list");
|
|
107
|
+
const firstLiBefore = list.querySelector("li");
|
|
108
|
+
expect(container.textContent).toBe("abc");
|
|
109
|
+
render(factory, container);
|
|
110
|
+
expect(container.querySelector("#list")?.querySelector("li")).toBe(firstLiBefore);
|
|
111
|
+
expect(container.textContent).toBe("abc");
|
|
112
|
+
// A subsequent reactive update must diff correctly — this only works if
|
|
113
|
+
// reconcile() rewrote mountReactive's `currentNodes` bookkeeping in place
|
|
114
|
+
// when it kept the real <li> elements instead of the fresh ones.
|
|
115
|
+
items.set(["x", "y"]);
|
|
116
|
+
expect(container.textContent).toBe("xy");
|
|
117
|
+
items.set(["z"]);
|
|
118
|
+
expect(container.textContent).toBe("z");
|
|
119
|
+
document.body.removeChild(container);
|
|
120
|
+
});
|
|
121
|
+
it("hydrates a component nested inside another component, firing onMount/onUnmount correctly", async () => {
|
|
122
|
+
class Inner extends StatefulComponent {
|
|
123
|
+
static __isComponent = true;
|
|
124
|
+
static __isStateless = false;
|
|
125
|
+
onMount() { }
|
|
126
|
+
onUnmount() { }
|
|
127
|
+
render() { return mountElement("em", { children: "inner" }, getCurrentScope()); }
|
|
128
|
+
}
|
|
129
|
+
class Outer extends StatefulComponent {
|
|
130
|
+
static __isComponent = true;
|
|
131
|
+
static __isStateless = false;
|
|
132
|
+
onMount() { }
|
|
133
|
+
render() {
|
|
134
|
+
return mountElement("section", {
|
|
135
|
+
children: mountComponent(Inner, {}, getCurrentScope()),
|
|
136
|
+
}, getCurrentScope());
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const factory = () => mountComponent(Outer, {}, getCurrentScope());
|
|
140
|
+
const container = hydratable(factory);
|
|
141
|
+
const emBefore = container.querySelector("em");
|
|
142
|
+
const outerMount = vi.spyOn(Outer.prototype, "onMount");
|
|
143
|
+
const innerMount = vi.spyOn(Inner.prototype, "onMount");
|
|
144
|
+
const innerUnmount = vi.spyOn(Inner.prototype, "onUnmount");
|
|
145
|
+
const dispose = render(factory, container);
|
|
146
|
+
await Promise.resolve();
|
|
147
|
+
expect(outerMount).toHaveBeenCalledOnce();
|
|
148
|
+
expect(innerMount).toHaveBeenCalledOnce();
|
|
149
|
+
expect(container.querySelector("em")).toBe(emBefore);
|
|
150
|
+
expect(container.textContent).toBe("inner");
|
|
151
|
+
dispose();
|
|
152
|
+
expect(innerUnmount).toHaveBeenCalledOnce();
|
|
153
|
+
outerMount.mockRestore();
|
|
154
|
+
innerMount.mockRestore();
|
|
155
|
+
innerUnmount.mockRestore();
|
|
156
|
+
document.body.removeChild(container);
|
|
157
|
+
});
|
|
158
|
+
it("a tag mismatch at one node replaces only that node — siblings stay adopted", () => {
|
|
159
|
+
const factory = () => mountElement("div", {
|
|
160
|
+
id: "wrap",
|
|
161
|
+
children: [
|
|
162
|
+
mountElement("p", { id: "before", children: "before" }, new Scope()),
|
|
163
|
+
mountElement("span", { id: "target", children: "new" }, new Scope()),
|
|
164
|
+
mountElement("p", { id: "after", children: "after" }, new Scope()),
|
|
165
|
+
],
|
|
166
|
+
}, new Scope());
|
|
167
|
+
const container = document.createElement("div");
|
|
168
|
+
container.innerHTML =
|
|
169
|
+
'<div id="wrap"><p id="before">before</p><strong id="target">old</strong><p id="after">after</p></div>';
|
|
170
|
+
container.setAttribute(SSG_MARKER, "1");
|
|
171
|
+
document.body.appendChild(container);
|
|
172
|
+
const beforeEl = container.querySelector("#before");
|
|
173
|
+
const afterEl = container.querySelector("#after");
|
|
174
|
+
render(factory, container);
|
|
175
|
+
expect(container.querySelector("#before")).toBe(beforeEl);
|
|
176
|
+
expect(container.querySelector("#after")).toBe(afterEl);
|
|
177
|
+
expect(container.querySelector("#target")?.tagName.toLowerCase()).toBe("span");
|
|
178
|
+
expect(container.textContent).toBe("beforenewafter");
|
|
179
|
+
document.body.removeChild(container);
|
|
180
|
+
});
|
|
181
|
+
it("appends extra fresh nodes when the real DOM has fewer children than the fresh tree", () => {
|
|
182
|
+
// Client factory produces three items; the server only rendered two —
|
|
183
|
+
// exercises reconcile()'s mismatch branch once realCursor has run out.
|
|
184
|
+
const factory = () => mountElement("ul", {
|
|
185
|
+
id: "list",
|
|
186
|
+
children: [
|
|
187
|
+
mountElement("li", { id: "a", children: "a" }, new Scope()),
|
|
188
|
+
mountElement("li", { id: "b", children: "b" }, new Scope()),
|
|
189
|
+
mountElement("li", { id: "c", children: "c" }, new Scope()),
|
|
190
|
+
],
|
|
191
|
+
}, new Scope());
|
|
192
|
+
const container = document.createElement("div");
|
|
193
|
+
container.innerHTML = '<ul id="list"><li id="a">a</li><li id="b">b</li></ul>';
|
|
194
|
+
container.setAttribute(SSG_MARKER, "1");
|
|
195
|
+
document.body.appendChild(container);
|
|
196
|
+
const aBefore = container.querySelector("#a");
|
|
197
|
+
const bBefore = container.querySelector("#b");
|
|
198
|
+
render(factory, container);
|
|
199
|
+
expect(container.querySelector("#a")).toBe(aBefore);
|
|
200
|
+
expect(container.querySelector("#b")).toBe(bBefore);
|
|
201
|
+
expect(container.querySelector("#c")?.textContent).toBe("c");
|
|
202
|
+
expect(container.querySelectorAll("#list > li")).toHaveLength(3);
|
|
203
|
+
document.body.removeChild(container);
|
|
204
|
+
});
|
|
205
|
+
it("keeps the real element even when its fresh counterpart bypassed mountElement (nothing recorded to replay)", () => {
|
|
206
|
+
class RawComp extends StatefulComponent {
|
|
207
|
+
static __isComponent = true;
|
|
208
|
+
static __isStateless = false;
|
|
209
|
+
render() {
|
|
210
|
+
const el = document.createElement("mark");
|
|
211
|
+
el.id = "raw";
|
|
212
|
+
el.textContent = "raw content";
|
|
213
|
+
return el;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const factory = () => mountComponent(RawComp, {}, getCurrentScope());
|
|
217
|
+
const container = hydratable(factory);
|
|
218
|
+
const markBefore = container.querySelector("#raw");
|
|
219
|
+
render(factory, container);
|
|
220
|
+
expect(container.querySelector("#raw")).toBe(markBefore);
|
|
221
|
+
expect(container.textContent).toBe("raw content");
|
|
222
|
+
document.body.removeChild(container);
|
|
223
|
+
});
|
|
224
|
+
it("removes trailing real nodes the fresh tree no longer produces", () => {
|
|
225
|
+
// Server rendered three items; the client factory only produces two —
|
|
226
|
+
// exercises reconcile()'s cleanup loop for leftover real siblings past
|
|
227
|
+
// the end of freshChildren.
|
|
228
|
+
const factory = () => mountElement("ul", {
|
|
229
|
+
id: "list",
|
|
230
|
+
children: [
|
|
231
|
+
mountElement("li", { id: "a", children: "a" }, new Scope()),
|
|
232
|
+
mountElement("li", { id: "b", children: "b" }, new Scope()),
|
|
233
|
+
],
|
|
234
|
+
}, new Scope());
|
|
235
|
+
const container = document.createElement("div");
|
|
236
|
+
container.innerHTML =
|
|
237
|
+
'<ul id="list"><li id="a">a</li><li id="b">b</li><li id="c">c</li></ul>';
|
|
238
|
+
container.setAttribute(SSG_MARKER, "1");
|
|
239
|
+
document.body.appendChild(container);
|
|
240
|
+
const aBefore = container.querySelector("#a");
|
|
241
|
+
const bBefore = container.querySelector("#b");
|
|
242
|
+
render(factory, container);
|
|
243
|
+
expect(container.querySelector("#a")).toBe(aBefore);
|
|
244
|
+
expect(container.querySelector("#b")).toBe(bBefore);
|
|
245
|
+
expect(container.querySelector("#c")).toBeNull();
|
|
246
|
+
expect(container.querySelectorAll("#list > li")).toHaveLength(2);
|
|
247
|
+
document.body.removeChild(container);
|
|
248
|
+
});
|
|
249
|
+
it("reconcile() is a safe no-op when a fresh node's registered reactive owner doesn't actually contain it", () => {
|
|
250
|
+
// Defensive guard in notifyReplacement(): registerReactiveNode() always
|
|
251
|
+
// registers a node into the exact array collectNodes() just built it
|
|
252
|
+
// into, so indexOf() finding nothing shouldn't happen through normal
|
|
253
|
+
// mounting — this exercises that branch directly, at the unit level, to
|
|
254
|
+
// confirm the guard degrades safely (no throw, no corruption) rather
|
|
255
|
+
// than silently trusting it can never occur.
|
|
256
|
+
const fresh = document.createElement("span");
|
|
257
|
+
const bogusOwner = [];
|
|
258
|
+
registerReactiveNode(fresh, bogusOwner);
|
|
259
|
+
const container = document.createElement("div");
|
|
260
|
+
const real = document.createElement("span");
|
|
261
|
+
container.appendChild(real);
|
|
262
|
+
expect(() => { reconcile(container, [fresh]); }).not.toThrow();
|
|
263
|
+
expect(bogusOwner).toEqual([]);
|
|
264
|
+
expect(container.firstChild).toBe(real);
|
|
265
|
+
});
|
|
266
|
+
it("plain client render (no SSG marker) is completely unaffected", () => {
|
|
267
|
+
const factory = () => mountElement("p", { children: "hello" }, new Scope());
|
|
268
|
+
const container = document.createElement("div");
|
|
269
|
+
document.body.appendChild(container);
|
|
270
|
+
render(factory, container);
|
|
271
|
+
expect(container.textContent).toBe("hello");
|
|
272
|
+
document.body.removeChild(container);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
describe("Portal — server render pass", () => {
|
|
276
|
+
it("renders nothing into the target during a server render pass", () => {
|
|
277
|
+
setServerRenderPass(true);
|
|
278
|
+
expect(isServerRenderPass()).toBe(true);
|
|
279
|
+
const target = document.createElement("div");
|
|
280
|
+
document.body.appendChild(target);
|
|
281
|
+
const scope = new Scope();
|
|
282
|
+
const nodes = mountComponent(Portal, { to: target, children: document.createTextNode("hi") }, scope);
|
|
283
|
+
expect(target.textContent).toBe("");
|
|
284
|
+
expect(nodes.every((n) => n.nodeType === Node.COMMENT_NODE)).toBe(true);
|
|
285
|
+
scope.dispose();
|
|
286
|
+
document.body.removeChild(target);
|
|
287
|
+
});
|
|
288
|
+
it("mounts normally on the client once the server pass is over", () => {
|
|
289
|
+
setServerRenderPass(false);
|
|
290
|
+
const target = document.createElement("div");
|
|
291
|
+
document.body.appendChild(target);
|
|
292
|
+
const scope = new Scope();
|
|
293
|
+
mountComponent(Portal, { to: target, children: document.createTextNode("hi") }, scope);
|
|
294
|
+
expect(target.textContent).toBe("hi");
|
|
295
|
+
scope.dispose();
|
|
296
|
+
document.body.removeChild(target);
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
//# sourceMappingURL=hydration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydration.test.js","sourceRoot":"","sources":["../../src/__tests__/hydration.test.ts"],"names":[],"mappings":"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"}
|
package/dist/element.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"element.d.ts","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"element.d.ts","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,KAAK,EAAE,KAAK,GACX,WAAW,GAAG,UAAU,CAiB1B"}
|
package/dist/element.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { mountChildren } from "./children";
|
|
2
2
|
import { createElement } from "./dom/create";
|
|
3
3
|
import { applyProp } from "./dom/props";
|
|
4
|
+
import { isRecording, recordProps } from "./hydration-context";
|
|
4
5
|
export function mountElement(tag, props, scope) {
|
|
5
6
|
const el = createElement(tag);
|
|
7
|
+
// During a hydration build pass, retain (props, scope) so reconcile() can
|
|
8
|
+
// replay them onto whichever real element it ends up keeping instead.
|
|
9
|
+
if (isRecording())
|
|
10
|
+
recordProps(el, props, scope);
|
|
6
11
|
for (const key in props) {
|
|
7
12
|
if (key === "children")
|
|
8
13
|
continue;
|
package/dist/element.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"element.js","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"element.js","sourceRoot":"","sources":["../src/element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAI/D,MAAM,UAAU,YAAY,CAC1B,GAAW,EACX,KAA8B,EAC9B,KAAY;IAEZ,MAAM,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAE9B,0EAA0E;IAC1E,sEAAsE;IACtE,IAAI,WAAW,EAAE;QAAE,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAEjD,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,IAAI,GAAG,KAAK,UAAU;YAAE,SAAS;QACjC,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;IACxC,CAAC;IAED,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACjC,aAAa,CAAC,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Scope } from "./scope";
|
|
2
|
+
/**
|
|
3
|
+
* Marks the synchronous "build" pass of a hydration attempt: `factory()` runs
|
|
4
|
+
* completely normally (same create-mode code as a plain client render), but
|
|
5
|
+
* every element additionally records its (props, scope) for reconcile()'s
|
|
6
|
+
* replay step, and every mountReactive() region records which of its output
|
|
7
|
+
* nodes may need to be swapped in-place if reconcile() decides to keep a real
|
|
8
|
+
* element instead of the fresh one. No-op / zero cost outside a build pass.
|
|
9
|
+
*/
|
|
10
|
+
export declare function runInRecording<T>(fn: () => T): T;
|
|
11
|
+
export declare function isRecording(): boolean;
|
|
12
|
+
interface PropRecord {
|
|
13
|
+
props: Record<string, unknown>;
|
|
14
|
+
scope: Scope;
|
|
15
|
+
}
|
|
16
|
+
export declare function recordProps(el: Element, props: Record<string, unknown>, scope: Scope): void;
|
|
17
|
+
export declare function getRecordedProps(el: Element): PropRecord | undefined;
|
|
18
|
+
export declare function registerReactiveNode(node: Node, owner: Node[]): void;
|
|
19
|
+
export declare function getReactiveOwner(node: Node): Node[] | undefined;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=hydration-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydration-context.d.ts","sourceRoot":"","sources":["../src/hydration-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAIrC;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAQhD;AAED,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED,UAAU,UAAU;IAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,KAAK,EAAE,KAAK,CAAC;CACd;AAID,wBAAgB,WAAW,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAE3F;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAEpE;AAOD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAEpE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,EAAE,GAAG,SAAS,CAE/D"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
let _recording = false;
|
|
2
|
+
/**
|
|
3
|
+
* Marks the synchronous "build" pass of a hydration attempt: `factory()` runs
|
|
4
|
+
* completely normally (same create-mode code as a plain client render), but
|
|
5
|
+
* every element additionally records its (props, scope) for reconcile()'s
|
|
6
|
+
* replay step, and every mountReactive() region records which of its output
|
|
7
|
+
* nodes may need to be swapped in-place if reconcile() decides to keep a real
|
|
8
|
+
* element instead of the fresh one. No-op / zero cost outside a build pass.
|
|
9
|
+
*/
|
|
10
|
+
export function runInRecording(fn) {
|
|
11
|
+
const prev = _recording;
|
|
12
|
+
_recording = true;
|
|
13
|
+
try {
|
|
14
|
+
return fn();
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
_recording = prev;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function isRecording() {
|
|
21
|
+
return _recording;
|
|
22
|
+
}
|
|
23
|
+
const _propRecords = new WeakMap();
|
|
24
|
+
export function recordProps(el, props, scope) {
|
|
25
|
+
_propRecords.set(el, { props, scope });
|
|
26
|
+
}
|
|
27
|
+
export function getRecordedProps(el) {
|
|
28
|
+
return _propRecords.get(el);
|
|
29
|
+
}
|
|
30
|
+
// mountReactive() keeps a `currentNodes` array (by reference) for diffing its
|
|
31
|
+
// *next* update. If reconcile() replaces one of those fresh nodes with a real
|
|
32
|
+
// one, it needs to overwrite that array in place so the closure sees the swap.
|
|
33
|
+
const _reactiveOwners = new WeakMap();
|
|
34
|
+
export function registerReactiveNode(node, owner) {
|
|
35
|
+
_reactiveOwners.set(node, owner);
|
|
36
|
+
}
|
|
37
|
+
export function getReactiveOwner(node) {
|
|
38
|
+
return _reactiveOwners.get(node);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=hydration-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydration-context.js","sourceRoot":"","sources":["../src/hydration-context.ts"],"names":[],"mappings":"AAEA,IAAI,UAAU,GAAG,KAAK,CAAC;AAEvB;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAI,EAAW;IAC3C,MAAM,IAAI,GAAG,UAAU,CAAC;IACxB,UAAU,GAAG,IAAI,CAAC;IAClB,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,CAAC;IACd,CAAC;YAAS,CAAC;QACT,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,UAAU,CAAC;AACpB,CAAC;AAOD,MAAM,YAAY,GAAG,IAAI,OAAO,EAAuB,CAAC;AAExD,MAAM,UAAU,WAAW,CAAC,EAAW,EAAE,KAA8B,EAAE,KAAY;IACnF,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAW;IAC1C,OAAO,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED,8EAA8E;AAC9E,8EAA8E;AAC9E,+EAA+E;AAC/E,MAAM,eAAe,GAAG,IAAI,OAAO,EAAgB,CAAC;AAEpD,MAAM,UAAU,oBAAoB,CAAC,IAAU,EAAE,KAAa;IAC5D,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAU;IACzC,OAAO,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walks `freshChildren` (the complete, correctly-nested output of a normal
|
|
3
|
+
* create-mode mount into a detached scratch node) against `realParent`'s
|
|
4
|
+
* existing children (the server-rendered DOM already attached to the page),
|
|
5
|
+
* in document order — safe now that both trees are fully built, unlike trying
|
|
6
|
+
* to adopt while `freshChildren` was still being constructed.
|
|
7
|
+
*
|
|
8
|
+
* Matching elements keep the real node (props/listeners replayed onto it,
|
|
9
|
+
* via `applyProp` — the same function normal mounting uses) and recurse.
|
|
10
|
+
* Text/comment nodes are always taken from the fresh side — they carry no
|
|
11
|
+
* listeners or state, so recreating them is free and avoids having to
|
|
12
|
+
* re-point mountComponent/mountReactive's anchor comments. Anything that
|
|
13
|
+
* doesn't line up (different tag, or one side ran out) is a local mismatch:
|
|
14
|
+
* the stale real node is dropped and the fresh one takes its place — no
|
|
15
|
+
* lookahead, no reordering, and no whole-page fallback.
|
|
16
|
+
*/
|
|
17
|
+
export declare function reconcile(realParent: Node, freshChildren: Node[]): void;
|
|
18
|
+
//# sourceMappingURL=hydration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydration.d.ts","sourceRoot":"","sources":["../src/hydration.ts"],"names":[],"mappings":"AAuBA;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,GAAG,IAAI,CA8BvE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { applyProp } from "./dom/props";
|
|
2
|
+
import { getReactiveOwner, getRecordedProps } from "./hydration-context";
|
|
3
|
+
function replayProps(real, fresh) {
|
|
4
|
+
const record = getRecordedProps(fresh);
|
|
5
|
+
if (!record)
|
|
6
|
+
return;
|
|
7
|
+
for (const key in record.props) {
|
|
8
|
+
if (key === "children")
|
|
9
|
+
continue;
|
|
10
|
+
applyProp(real, key, record.props[key], record.scope);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
// mountReactive()'s `currentNodes` array holds fresh node references for its
|
|
14
|
+
// *next* diff. If we're keeping `real` instead of `fresh` here, that array
|
|
15
|
+
// needs to see the swap too — it's registered by reference, so this mutation
|
|
16
|
+
// is visible to whichever effect closure owns it.
|
|
17
|
+
function notifyReplacement(fresh, real) {
|
|
18
|
+
const owner = getReactiveOwner(fresh);
|
|
19
|
+
if (!owner)
|
|
20
|
+
return;
|
|
21
|
+
const idx = owner.indexOf(fresh);
|
|
22
|
+
if (idx !== -1)
|
|
23
|
+
owner[idx] = real;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Walks `freshChildren` (the complete, correctly-nested output of a normal
|
|
27
|
+
* create-mode mount into a detached scratch node) against `realParent`'s
|
|
28
|
+
* existing children (the server-rendered DOM already attached to the page),
|
|
29
|
+
* in document order — safe now that both trees are fully built, unlike trying
|
|
30
|
+
* to adopt while `freshChildren` was still being constructed.
|
|
31
|
+
*
|
|
32
|
+
* Matching elements keep the real node (props/listeners replayed onto it,
|
|
33
|
+
* via `applyProp` — the same function normal mounting uses) and recurse.
|
|
34
|
+
* Text/comment nodes are always taken from the fresh side — they carry no
|
|
35
|
+
* listeners or state, so recreating them is free and avoids having to
|
|
36
|
+
* re-point mountComponent/mountReactive's anchor comments. Anything that
|
|
37
|
+
* doesn't line up (different tag, or one side ran out) is a local mismatch:
|
|
38
|
+
* the stale real node is dropped and the fresh one takes its place — no
|
|
39
|
+
* lookahead, no reordering, and no whole-page fallback.
|
|
40
|
+
*/
|
|
41
|
+
export function reconcile(realParent, freshChildren) {
|
|
42
|
+
let realCursor = realParent.firstChild;
|
|
43
|
+
for (const fresh of freshChildren) {
|
|
44
|
+
if (fresh.nodeType === Node.ELEMENT_NODE &&
|
|
45
|
+
realCursor?.nodeType === Node.ELEMENT_NODE &&
|
|
46
|
+
realCursor.localName === fresh.localName) {
|
|
47
|
+
const real = realCursor;
|
|
48
|
+
realCursor = real.nextSibling;
|
|
49
|
+
replayProps(real, fresh);
|
|
50
|
+
reconcile(real, Array.from(fresh.childNodes));
|
|
51
|
+
notifyReplacement(fresh, real);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (realCursor) {
|
|
55
|
+
const stale = realCursor;
|
|
56
|
+
realCursor = realCursor.nextSibling;
|
|
57
|
+
stale.remove();
|
|
58
|
+
}
|
|
59
|
+
realParent.insertBefore(fresh, realCursor);
|
|
60
|
+
}
|
|
61
|
+
while (realCursor) {
|
|
62
|
+
const stale = realCursor;
|
|
63
|
+
realCursor = realCursor.nextSibling;
|
|
64
|
+
stale.remove();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=hydration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydration.js","sourceRoot":"","sources":["../src/hydration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEzE,SAAS,WAAW,CAAC,IAAa,EAAE,KAAc;IAChD,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM;QAAE,OAAO;IACpB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,GAAG,KAAK,UAAU;YAAE,SAAS;QACjC,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACxD,CAAC;AACH,CAAC;AAED,6EAA6E;AAC7E,2EAA2E;AAC3E,6EAA6E;AAC7E,kDAAkD;AAClD,SAAS,iBAAiB,CAAC,KAAW,EAAE,IAAU;IAChD,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK;QAAE,OAAO;IACnB,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;AACpC,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,SAAS,CAAC,UAAgB,EAAE,aAAqB;IAC/D,IAAI,UAAU,GAAqB,UAAU,CAAC,UAAU,CAAC;IAEzD,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;QAClC,IACE,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC,YAAY;YACpC,UAAU,EAAE,QAAQ,KAAK,IAAI,CAAC,YAAY;YACzC,UAAsB,CAAC,SAAS,KAAM,KAAiB,CAAC,SAAS,EAClE,CAAC;YACD,MAAM,IAAI,GAAG,UAAqB,CAAC;YACnC,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC;YAC9B,WAAW,CAAC,IAAI,EAAE,KAAgB,CAAC,CAAC;YACpC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAE,KAAiB,CAAC,UAAU,CAAC,CAAC,CAAC;YAC3D,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAC/B,SAAS;QACX,CAAC;QAED,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,UAAU,CAAC;YACzB,UAAU,GAAG,UAAU,CAAC,WAAW,CAAC;YACpC,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,CAAC;QACD,UAAU,CAAC,YAAY,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,UAAU,EAAE,CAAC;QAClB,MAAM,KAAK,GAAG,UAAU,CAAC;QACzB,UAAU,GAAG,UAAU,CAAC,WAAW,CAAC;QACpC,KAAK,CAAC,MAAM,EAAE,CAAC;IACjB,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
* The factory function is called once inside the root scope,
|
|
5
5
|
* so `jsx()` can access the current scope via `getCurrentScope()`.
|
|
6
6
|
*
|
|
7
|
+
* If `container` carries the marker `@praxisjs/ssg` stamps onto prerendered
|
|
8
|
+
* HTML, the tree is built normally into a detached scratch node and then
|
|
9
|
+
* reconciled against the existing DOM (real elements are kept and have their
|
|
10
|
+
* props/listeners replayed onto them; text/comments and any mismatched nodes
|
|
11
|
+
* are recreated) instead of clearing the container and mounting fresh.
|
|
12
|
+
*
|
|
7
13
|
* @example
|
|
8
14
|
* render(() => <App />, document.getElementById('app'));
|
|
9
15
|
*/
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAqDA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,MAAM,CACpB,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI,EAAE,GAAG,IAAI,EACnC,SAAS,EAAE,WAAW,GACrB,MAAM,IAAI,CAKZ;AAED,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import { mountChildren } from "./children";
|
|
2
2
|
import { runInScope } from "./context";
|
|
3
|
+
import { reconcile } from "./hydration";
|
|
4
|
+
import { runInRecording } from "./hydration-context";
|
|
3
5
|
import { Scope } from "./scope";
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* The factory function is called once inside the root scope,
|
|
8
|
-
* so `jsx()` can access the current scope via `getCurrentScope()`.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* render(() => <App />, document.getElementById('app'));
|
|
12
|
-
*/
|
|
13
|
-
export function render(factory, container) {
|
|
6
|
+
/** Written by `@praxisjs/ssg` onto the mount container in its prerendered HTML. */
|
|
7
|
+
const SSG_MARKER = "data-praxis-ssg";
|
|
8
|
+
function mountCreate(factory, container) {
|
|
14
9
|
const rootScope = new Scope();
|
|
15
10
|
container.innerHTML = "";
|
|
16
11
|
runInScope(rootScope, () => {
|
|
@@ -22,6 +17,46 @@ export function render(factory, container) {
|
|
|
22
17
|
container.innerHTML = "";
|
|
23
18
|
};
|
|
24
19
|
}
|
|
20
|
+
function hydrate(factory, container) {
|
|
21
|
+
const rootScope = new Scope();
|
|
22
|
+
const scratch = document.createDocumentFragment();
|
|
23
|
+
// Build normally — same code as mountCreate, just into a detached scratch
|
|
24
|
+
// node instead of the real container, so it can be walked in document
|
|
25
|
+
// order afterwards (its own construction order is bottom-up, not useful
|
|
26
|
+
// for matching against the server-rendered DOM directly).
|
|
27
|
+
runInRecording(() => {
|
|
28
|
+
runInScope(rootScope, () => {
|
|
29
|
+
const result = factory();
|
|
30
|
+
mountChildren(scratch, result, rootScope);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
reconcile(container, Array.from(scratch.childNodes));
|
|
34
|
+
return () => {
|
|
35
|
+
rootScope.dispose();
|
|
36
|
+
container.innerHTML = "";
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Renders a component tree into a container element.
|
|
41
|
+
*
|
|
42
|
+
* The factory function is called once inside the root scope,
|
|
43
|
+
* so `jsx()` can access the current scope via `getCurrentScope()`.
|
|
44
|
+
*
|
|
45
|
+
* If `container` carries the marker `@praxisjs/ssg` stamps onto prerendered
|
|
46
|
+
* HTML, the tree is built normally into a detached scratch node and then
|
|
47
|
+
* reconciled against the existing DOM (real elements are kept and have their
|
|
48
|
+
* props/listeners replayed onto them; text/comments and any mismatched nodes
|
|
49
|
+
* are recreated) instead of clearing the container and mounting fresh.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* render(() => <App />, document.getElementById('app'));
|
|
53
|
+
*/
|
|
54
|
+
export function render(factory, container) {
|
|
55
|
+
if (container.hasAttribute(SSG_MARKER)) {
|
|
56
|
+
return hydrate(factory, container);
|
|
57
|
+
}
|
|
58
|
+
return mountCreate(factory, container);
|
|
59
|
+
}
|
|
25
60
|
export { Scope } from "./scope";
|
|
26
61
|
export { runInScope, getCurrentScope } from "./context";
|
|
27
62
|
export { mountElement } from "./element";
|