ripple 0.4.2 → 0.4.4
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 +164 -0
- package/package.json +10 -4
- package/src/constants.js +6 -0
- package/src/jsx-runtime.d.ts +2 -1
- package/src/runtime/array.js +15 -9
- package/src/runtime/index-client.js +25 -7
- package/src/runtime/index-server.js +1 -0
- package/src/runtime/internal/client/attributes.js +439 -0
- package/src/runtime/internal/client/blocks.js +20 -20
- package/src/runtime/internal/client/component.js +2 -5
- package/src/runtime/internal/client/composite.js +295 -95
- package/src/runtime/internal/client/constants.js +14 -6
- package/src/runtime/internal/client/context.js +3 -2
- package/src/runtime/internal/client/css.js +11 -26
- package/src/runtime/internal/client/errors.js +188 -0
- package/src/runtime/internal/client/events.js +79 -34
- package/src/runtime/internal/client/expression.js +25 -120
- package/src/runtime/internal/client/for.js +113 -262
- package/src/runtime/internal/client/head.js +4 -3
- package/src/runtime/internal/client/html.js +5 -3
- package/src/runtime/internal/client/hydrate.js +484 -0
- package/src/runtime/internal/client/hydration-enabled.js +7 -0
- package/src/runtime/internal/client/hydration.js +16 -81
- package/src/runtime/internal/client/if.js +69 -10
- package/src/runtime/internal/client/index.js +18 -10
- package/src/runtime/internal/client/operations.js +11 -7
- package/src/runtime/internal/client/portal.js +3 -2
- package/src/runtime/internal/client/render.js +42 -325
- package/src/runtime/internal/client/root-boundary-enabled.js +7 -0
- package/src/runtime/internal/client/runtime.js +157 -371
- package/src/runtime/internal/client/template-ns.js +88 -0
- package/src/runtime/internal/client/template.js +118 -139
- package/src/runtime/internal/client/track-async.js +356 -0
- package/src/runtime/internal/client/try.js +133 -319
- package/src/runtime/internal/client/types.d.ts +48 -24
- package/src/runtime/internal/server/index.js +31 -29
- package/src/runtime/internal/server/prerender.js +27 -0
- package/src/server/index.js +1 -0
- package/src/utils/class-name.js +28 -0
- package/tests/client/basic/basic.errors.test.tsrx +3 -3
- package/tests/client/compiler/__snapshots__/compiler.typescript.test.tsrx.snap +0 -2
- package/tests/client/compiler/compiler.basic.test.tsrx +2 -2
- package/tests/client/compiler/compiler.tracked-access.test.tsrx +3 -3
- package/tests/client/composite/composite.dynamic-components.test.tsrx +40 -0
- package/tests/client/composite/composite.props.test.tsrx +10 -10
- package/tests/client/css/scoped-styles.test.tsrx +3 -1
- package/tests/client/derived-release.test.tsrx +3 -3
- package/tests/client/for-item.test.tsrx +39 -0
- package/tests/client/for-selector.test.tsrx +95 -0
- package/tests/client/svg.test.tsrx +273 -1
- package/tests/hydration/compiled/client/basic.js +88 -87
- package/tests/hydration/compiled/client/composite.js +15 -6
- package/tests/hydration/compiled/client/events.js +40 -48
- package/tests/hydration/compiled/client/for.js +191 -199
- package/tests/hydration/compiled/client/fragment-cursor.js +633 -334
- package/tests/hydration/compiled/client/head.js +22 -22
- package/tests/hydration/compiled/client/hmr.js +14 -5
- package/tests/hydration/compiled/client/html-in-template.js +5 -5
- package/tests/hydration/compiled/client/html.js +195 -177
- package/tests/hydration/compiled/client/if-children.js +136 -63
- package/tests/hydration/compiled/client/if-fragment-controlflow.js +63 -43
- package/tests/hydration/compiled/client/if.js +78 -30
- package/tests/hydration/compiled/client/mixed-control-flow.js +90 -72
- package/tests/hydration/compiled/client/nested-control-flow.js +266 -307
- package/tests/hydration/compiled/client/portal.js +20 -11
- package/tests/hydration/compiled/client/reactivity.js +29 -29
- package/tests/hydration/compiled/client/return.js +2 -2
- package/tests/hydration/compiled/client/streaming.js +33 -35
- package/tests/hydration/compiled/client/switch.js +25 -25
- package/tests/hydration/compiled/client/track-async-serialization.js +41 -41
- package/tests/hydration/compiled/client/try.js +15 -15
- package/tests/hydration/compiled/client/typed-text.js +8 -7
- package/tests/hydration/compiled/server/basic.js +6 -6
- package/tests/hydration/compiled/server/events.js +8 -8
- package/tests/hydration/compiled/server/for.js +22 -22
- package/tests/hydration/compiled/server/fragment-cursor.js +188 -52
- package/tests/hydration/compiled/server/head.js +6 -6
- package/tests/hydration/compiled/server/hmr.js +1 -1
- package/tests/hydration/compiled/server/html.js +1 -1
- package/tests/hydration/compiled/server/if-children.js +9 -9
- package/tests/hydration/compiled/server/if.js +5 -5
- package/tests/hydration/compiled/server/mixed-control-flow.js +4 -4
- package/tests/hydration/compiled/server/portal.js +1 -1
- package/tests/hydration/compiled/server/reactivity.js +9 -9
- package/tests/hydration/compiled/server/streaming.js +9 -9
- package/tests/hydration/compiled/server/switch.js +4 -4
- package/tests/hydration/compiled/server/track-async-serialization.js +16 -16
- package/tests/hydration/compiled/server/try.js +4 -4
- package/tests/hydration/compiled/server/typed-text.js +1 -1
- package/tests/hydration/components/fragment-cursor.tsrx +60 -0
- package/tests/hydration/components/track-async-serialization.tsrx +2 -2
- package/tests/hydration/fragment-cursor.test.js +34 -1
- package/tests/hydration/track-async-serialization.test.js +1 -1
- package/tests/server/basic.components.test.tsrx +3 -3
- package/tests/server/track-async-serialization.test.tsrx +6 -6
- package/tests/utils/tracked-types.test.js +3 -3
- package/types/index.d.ts +8 -8
- package/types/server.d.ts +17 -0
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
set_hydrating,
|
|
10
10
|
set_hydration,
|
|
11
11
|
} from './hydration.js';
|
|
12
|
+
import { HYDRATION } from 'ripple/internal/client/hydration-enabled';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* @param {string} hash
|
|
@@ -19,12 +20,12 @@ export function head(hash, render_fn) {
|
|
|
19
20
|
// The head function may be called after the first hydration pass and ssr comment nodes may still be present,
|
|
20
21
|
// therefore we need to skip that when we detect that we're not in hydration mode.
|
|
21
22
|
let previous_hydrate_node = null;
|
|
22
|
-
let was_hydrating = hydrating;
|
|
23
|
+
let was_hydrating = HYDRATION && hydrating;
|
|
23
24
|
|
|
24
25
|
/** @type {Comment | Text} */
|
|
25
26
|
var anchor;
|
|
26
27
|
|
|
27
|
-
if (hydrating) {
|
|
28
|
+
if (HYDRATION && hydrating) {
|
|
28
29
|
previous_hydrate_node = hydrate_node;
|
|
29
30
|
|
|
30
31
|
var head_anchor = get_first_child(document.head);
|
|
@@ -50,7 +51,7 @@ export function head(hash, render_fn) {
|
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
if (!hydrating) {
|
|
54
|
+
if (!(HYDRATION && hydrating)) {
|
|
54
55
|
anchor = document.head.appendChild(create_text());
|
|
55
56
|
}
|
|
56
57
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/** @import { Block } from '#client' */
|
|
2
|
+
import { hydration_mismatch } from './errors.js';
|
|
2
3
|
|
|
3
4
|
import { remove_block_dom, render } from './blocks.js';
|
|
4
5
|
import { get_first_child, get_next_sibling } from './operations.js';
|
|
5
6
|
import { active_block } from './runtime.js';
|
|
6
7
|
import { assign_nodes, create_fragment_from_html } from './template.js';
|
|
7
8
|
import { hydrate_next, hydrating, set_hydrate_node } from './hydration.js';
|
|
9
|
+
import { HYDRATION } from 'ripple/internal/client/hydration-enabled';
|
|
8
10
|
import { COMMENT_NODE } from '../../../constants.js';
|
|
9
11
|
|
|
10
12
|
/**
|
|
@@ -25,7 +27,7 @@ export function html(node, get_html, svg = false, mathml = false) {
|
|
|
25
27
|
// If the HTML hasn't changed, skip the update (but still hydrate on first run)
|
|
26
28
|
if (html === new_html) {
|
|
27
29
|
// During hydration, we need to skip past the content and end marker even if value hasn't changed
|
|
28
|
-
if (hydrating) {
|
|
30
|
+
if (HYDRATION && hydrating) {
|
|
29
31
|
// The anchor is the hash comment - we need to skip past it and its content
|
|
30
32
|
set_hydrate_node(anchor);
|
|
31
33
|
/** @type {Node | null} */
|
|
@@ -60,7 +62,7 @@ export function html(node, get_html, svg = false, mathml = false) {
|
|
|
60
62
|
block.s.start = block.s.end = null;
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
if (hydrating) {
|
|
65
|
+
if (HYDRATION && hydrating) {
|
|
64
66
|
set_hydrate_node(anchor);
|
|
65
67
|
|
|
66
68
|
/** @type {Node | null} */
|
|
@@ -75,7 +77,7 @@ export function html(node, get_html, svg = false, mathml = false) {
|
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
if (next === null) {
|
|
78
|
-
|
|
80
|
+
hydration_mismatch('HTML');
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
// Include the hash comment and end marker in the assigned nodes
|
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/** @import { AppendIntoAnchor, Block, TryPendingFunction, TryState } from '#client' */
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
COMMENT_NODE,
|
|
5
|
+
HYDRATION_END,
|
|
6
|
+
HYDRATION_START,
|
|
7
|
+
HYDRATION_START_ERRORED,
|
|
8
|
+
HYDRATION_START_PENDING,
|
|
9
|
+
STREAM_ERROR_SCRIPT_PREFIX,
|
|
10
|
+
TEXT_NODE,
|
|
11
|
+
} from '../../../constants.js';
|
|
12
|
+
import { is_destroyed } from './blocks.js';
|
|
13
|
+
import { hydration_mismatch } from './errors.js';
|
|
14
|
+
import { clear_expression_range } from './expression.js';
|
|
15
|
+
import {
|
|
16
|
+
hydrate_next,
|
|
17
|
+
hydrate_node,
|
|
18
|
+
hydrating,
|
|
19
|
+
set_hydrate_node,
|
|
20
|
+
set_hydration,
|
|
21
|
+
set_hydration_runtime,
|
|
22
|
+
} from './hydration.js';
|
|
23
|
+
import { create_text, get_next_sibling, next_sibling_getter } from './operations.js';
|
|
24
|
+
import { active_block, queue_microtask } from './runtime.js';
|
|
25
|
+
import { assign_nodes } from './template.js';
|
|
26
|
+
import {
|
|
27
|
+
boundary_branch,
|
|
28
|
+
catch_error,
|
|
29
|
+
destroy_pending,
|
|
30
|
+
get_boundary_with_catch,
|
|
31
|
+
render_resolved,
|
|
32
|
+
} from './try.js';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The hydration paths of the DOM runtime. `hydrate()` installs them on the
|
|
36
|
+
* hydration module (see `H` there); the runtime's insert, template, expression
|
|
37
|
+
* and boundary code calls through that object only while hydrating, so a
|
|
38
|
+
* client-only mount compiles and ships none of this.
|
|
39
|
+
* @typedef {{
|
|
40
|
+
* a: typeof hydrate_append;
|
|
41
|
+
* t: typeof hydrate_template;
|
|
42
|
+
* x: typeof hydrate_text;
|
|
43
|
+
* e: typeof expression_end;
|
|
44
|
+
* h: typeof hydrated_text;
|
|
45
|
+
* m: typeof try_marker;
|
|
46
|
+
* s: typeof try_slot;
|
|
47
|
+
* f: typeof try_fallback;
|
|
48
|
+
* }} HydrationRuntime
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/** @type {HydrationRuntime} */
|
|
52
|
+
var runtime = {
|
|
53
|
+
a: hydrate_append,
|
|
54
|
+
t: hydrate_template,
|
|
55
|
+
x: hydrate_text,
|
|
56
|
+
e: expression_end,
|
|
57
|
+
h: hydrated_text,
|
|
58
|
+
m: try_marker,
|
|
59
|
+
s: try_slot,
|
|
60
|
+
f: try_fallback,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export function install_hydration() {
|
|
64
|
+
set_hydration_runtime(runtime);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The hydration path of `append`: repositions the hydration cursor instead
|
|
69
|
+
* of inserting.
|
|
70
|
+
*
|
|
71
|
+
* Every hydrated node, block, and component leaves the cursor on its last
|
|
72
|
+
* DOM node, and whoever owns the next node steps past it: a parent element
|
|
73
|
+
* with its sibling traversal, a control-flow block by reaching its end
|
|
74
|
+
* marker, an append-into sentinel by adopting the cursor as the next
|
|
75
|
+
* component's first node.
|
|
76
|
+
* @param {ChildNode | AppendIntoAnchor} anchor
|
|
77
|
+
* @param {Node} dom
|
|
78
|
+
*/
|
|
79
|
+
function hydrate_append(anchor, dom) {
|
|
80
|
+
var node = /** @type {Node} */ (hydrate_node);
|
|
81
|
+
|
|
82
|
+
// The cursor descended into dom's children (child()/sibling() traversal
|
|
83
|
+
// inside a single-node template) without a compiler-emitted pop(): bring it
|
|
84
|
+
// back up to dom before deciding where to leave it.
|
|
85
|
+
if (node !== dom && dom.contains(node)) {
|
|
86
|
+
node = dom;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// A child component renders into the anchor it was handed, which is its
|
|
90
|
+
// own first node. Its content is hydrated, so leave the cursor on the
|
|
91
|
+
// content's last node for the parent's sibling traversal.
|
|
92
|
+
if (anchor === dom) {
|
|
93
|
+
set_hydrate_node(node);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (node !== dom) {
|
|
98
|
+
// A fragment's cursor sits on its last top-level node, past the
|
|
99
|
+
// template's first node: widen the block's end to cover the whole
|
|
100
|
+
// fragment rather than only the node assign_nodes saw.
|
|
101
|
+
var s = /** @type {Block} */ (active_block).s;
|
|
102
|
+
if (s !== null) {
|
|
103
|
+
s.end = node;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Step past the content: a branch lands on its block's end marker, the
|
|
108
|
+
// root on the boundary's end marker, an append-into sentinel on the next
|
|
109
|
+
// component's first node.
|
|
110
|
+
set_hydrate_node(next_sibling_getter.call(node), true);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Scans forward from the current hydrate_node to find the matching HYDRATION_END
|
|
115
|
+
* comment, handling nested blocks by tracking depth.
|
|
116
|
+
* Should be called after hydrate_next() has consumed the opening HYDRATION_START.
|
|
117
|
+
* Any `[`-prefixed comment opens a nested region — this includes the plain
|
|
118
|
+
* `<!--[-->` markers as well as streaming slot markers (`<!--[?N-->`,
|
|
119
|
+
* `<!--[!N-->`), which always pair with a `<!--]-->`.
|
|
120
|
+
* @returns {Node} The HYDRATION_END comment node.
|
|
121
|
+
*/
|
|
122
|
+
function skip_to_hydration_end() {
|
|
123
|
+
var depth = 0;
|
|
124
|
+
var node = /** @type {Node} */ (hydrate_node);
|
|
125
|
+
while (true) {
|
|
126
|
+
if (node.nodeType === COMMENT_NODE) {
|
|
127
|
+
var data = /** @type {Comment} */ (node).data;
|
|
128
|
+
if (data === HYDRATION_END) {
|
|
129
|
+
if (depth === 0) return node;
|
|
130
|
+
depth -= 1;
|
|
131
|
+
} else if (data.startsWith(HYDRATION_START)) {
|
|
132
|
+
depth += 1;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
node = /** @type {Node} */ (next_sibling_getter.call(node));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The hydration path of a `template()` instance: adopts the server node(s)
|
|
141
|
+
* at the cursor instead of cloning.
|
|
142
|
+
* @param {boolean} is_fragment
|
|
143
|
+
* @param {number} count
|
|
144
|
+
* @returns {Node}
|
|
145
|
+
*/
|
|
146
|
+
function hydrate_template(is_fragment, count) {
|
|
147
|
+
var node = /** @type {Node} */ (hydrate_node);
|
|
148
|
+
assign_nodes(node, is_fragment ? hydrate_fragment_end(node, count) : node);
|
|
149
|
+
return node;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The last top-level node of a hydrated fragment template. Walks using the
|
|
154
|
+
* compiler-provided hop count so hydration never parses template HTML.
|
|
155
|
+
* @param {Node} start
|
|
156
|
+
* @param {number} count
|
|
157
|
+
* @returns {Node}
|
|
158
|
+
*/
|
|
159
|
+
function hydrate_fragment_end(start, count) {
|
|
160
|
+
var end = start;
|
|
161
|
+
|
|
162
|
+
for (var i = 1; i < count; i++) {
|
|
163
|
+
var next = get_next_sibling(end);
|
|
164
|
+
|
|
165
|
+
while (next !== null && next.nodeType === COMMENT_NODE) {
|
|
166
|
+
next = get_next_sibling(next);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (next === null) {
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
end = next;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return end;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* The hydration path of an expression's text: adopts the server text node at
|
|
181
|
+
* the cursor, splitting it when the server rendered adjacent text into one.
|
|
182
|
+
* @param {string} value
|
|
183
|
+
* @param {ChildNode} anchor
|
|
184
|
+
* @param {boolean} assign
|
|
185
|
+
* @returns {void}
|
|
186
|
+
*/
|
|
187
|
+
function hydrate_text(value, anchor, assign) {
|
|
188
|
+
var node = hydrate_node;
|
|
189
|
+
|
|
190
|
+
if (node?.nodeType === COMMENT_NODE && /** @type {Comment} */ (node).data === HYDRATION_START) {
|
|
191
|
+
node = get_next_sibling(node);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (node?.nodeType === TEXT_NODE) {
|
|
195
|
+
var current_value = /** @type {Text} */ (node).nodeValue ?? '';
|
|
196
|
+
|
|
197
|
+
if (current_value !== value) {
|
|
198
|
+
/** @type {Text} */ (node).nodeValue = value;
|
|
199
|
+
|
|
200
|
+
if (current_value.startsWith(value)) {
|
|
201
|
+
var remaining = current_value.slice(value.length);
|
|
202
|
+
|
|
203
|
+
if (remaining !== '') {
|
|
204
|
+
var remaining_text = create_text(remaining);
|
|
205
|
+
/** @type {ChildNode} */ (node).after(remaining_text);
|
|
206
|
+
if (assign) {
|
|
207
|
+
assign_nodes(node, node);
|
|
208
|
+
}
|
|
209
|
+
set_hydrate_node(remaining_text);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (assign) {
|
|
216
|
+
assign_nodes(node, node);
|
|
217
|
+
}
|
|
218
|
+
set_hydrate_node(get_next_sibling(node) ?? anchor);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
var new_text = create_text(value);
|
|
223
|
+
|
|
224
|
+
if (node !== null && node !== anchor) {
|
|
225
|
+
/** @type {ChildNode} */ (node).before(new_text);
|
|
226
|
+
} else {
|
|
227
|
+
anchor.before(new_text);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (assign) {
|
|
231
|
+
assign_nodes(new_text, new_text);
|
|
232
|
+
}
|
|
233
|
+
set_hydrate_node(node ?? anchor);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* The end marker of a server-rendered expression: the `<!--]-->` that pairs
|
|
238
|
+
* with the `<!--[-->` at `anchor`.
|
|
239
|
+
* @param {Node} anchor
|
|
240
|
+
* @returns {Comment}
|
|
241
|
+
*/
|
|
242
|
+
function expression_end(anchor) {
|
|
243
|
+
/** @type {Node | null} */
|
|
244
|
+
var current = get_next_sibling(anchor);
|
|
245
|
+
var depth = 0;
|
|
246
|
+
|
|
247
|
+
while (current !== null) {
|
|
248
|
+
if (current.nodeType === COMMENT_NODE) {
|
|
249
|
+
var data = /** @type {Comment} */ (current).data;
|
|
250
|
+
|
|
251
|
+
if (data === HYDRATION_START) {
|
|
252
|
+
depth += 1;
|
|
253
|
+
} else if (data === HYDRATION_END) {
|
|
254
|
+
if (depth === 0) {
|
|
255
|
+
return /** @type {Comment} */ (current);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
depth -= 1;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
current = get_next_sibling(current);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
hydration_mismatch('expression');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* The single text node a server-rendered expression holds between its
|
|
270
|
+
* markers, or null once whatever else was there is cleared.
|
|
271
|
+
* @param {Node} anchor
|
|
272
|
+
* @param {Node} end
|
|
273
|
+
* @returns {Text | null}
|
|
274
|
+
*/
|
|
275
|
+
function hydrated_text(anchor, end) {
|
|
276
|
+
var first = get_next_sibling(anchor);
|
|
277
|
+
|
|
278
|
+
if (first === end) {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (first?.nodeType === TEXT_NODE && get_next_sibling(first) === end) {
|
|
283
|
+
return /** @type {Text} */ (first);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
clear_expression_range(anchor, end);
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* A boundary's hydration walk begins: consumes its `<!--[-->` marker, or
|
|
292
|
+
* adopts the streamed slot that stands in for it (see `try_slot`).
|
|
293
|
+
* @param {TryState} state
|
|
294
|
+
*/
|
|
295
|
+
function try_marker(state) {
|
|
296
|
+
var marker = /** @type {Comment} */ (hydrate_node);
|
|
297
|
+
var data = marker.nodeType === COMMENT_NODE ? marker.data : '';
|
|
298
|
+
|
|
299
|
+
// A slot at this anchor belongs to this boundary, including the root.
|
|
300
|
+
if (data.startsWith(HYDRATION_START_PENDING) || data.startsWith(HYDRATION_START_ERRORED)) {
|
|
301
|
+
hydrate_streamed_slot(state, marker, data);
|
|
302
|
+
} else {
|
|
303
|
+
// Settled SSR content must not transition back to pending.
|
|
304
|
+
if (state.p !== null) {
|
|
305
|
+
state.h = true;
|
|
306
|
+
}
|
|
307
|
+
hydrate_next(); // consume <!--[-->
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* @param {TryState} state
|
|
313
|
+
* @param {Comment} marker
|
|
314
|
+
* @param {string} data
|
|
315
|
+
*/
|
|
316
|
+
function hydrate_streamed_slot(state, marker, data) {
|
|
317
|
+
// live streamed slot
|
|
318
|
+
state.si = data.slice(HYDRATION_START_PENDING.length);
|
|
319
|
+
state.se = data.startsWith(HYDRATION_START_ERRORED);
|
|
320
|
+
state.so = marker;
|
|
321
|
+
hydrate_next(); // consume the slot wrapper open
|
|
322
|
+
state.sc = /** @type {Comment} */ (skip_to_hydration_end());
|
|
323
|
+
var fallback_start = /** @type {Comment} */ (hydrate_node);
|
|
324
|
+
state.sf =
|
|
325
|
+
!state.se &&
|
|
326
|
+
state.p !== null &&
|
|
327
|
+
fallback_start.nodeType === COMMENT_NODE &&
|
|
328
|
+
fallback_start.data === HYDRATION_START;
|
|
329
|
+
if (state.sf) {
|
|
330
|
+
hydrate_next(); // consume the fallback's <!--[-->
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* A boundary that hydrated a still-pending streamed slot: the outer walk
|
|
336
|
+
* continues after the slot, and the slot's chunk activates the boundary when
|
|
337
|
+
* it arrives.
|
|
338
|
+
* @param {TryState} state
|
|
339
|
+
*/
|
|
340
|
+
function try_slot(state) {
|
|
341
|
+
// continue the outer hydration walk after the slot
|
|
342
|
+
set_hydrate_node(state.sc);
|
|
343
|
+
|
|
344
|
+
var registry = (window.__RIPPLE_B__ ??= {});
|
|
345
|
+
var unit_id = /** @type {string} */ (state.si);
|
|
346
|
+
if (state.se) {
|
|
347
|
+
// the inline runtime already emptied the slot and marked it errored
|
|
348
|
+
// — route the error once the surrounding hydration has finished
|
|
349
|
+
queue_microtask(() => route_streamed_error(state, unit_id));
|
|
350
|
+
} else {
|
|
351
|
+
registry[unit_id] = {
|
|
352
|
+
a: (template, errored) => activate_streamed_chunk(state, template, errored),
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* The first run of a boundary whose chunk has not arrived: hydrates its
|
|
359
|
+
* fallback until activation.
|
|
360
|
+
* @param {TryState} state
|
|
361
|
+
*/
|
|
362
|
+
function try_fallback(state) {
|
|
363
|
+
if (state.sf) {
|
|
364
|
+
state.m = 1;
|
|
365
|
+
state.pb = boundary_branch(() => {
|
|
366
|
+
/** @type {TryPendingFunction} */ (state.p)(state.a);
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Retires the slot wrapper markers once the slot's fate is decided: the
|
|
373
|
+
* open comment loses its marker data (it may still serve as the boundary
|
|
374
|
+
* anchor, so it must stay in the DOM) and the close comment is dropped —
|
|
375
|
+
* keeping `[`/`]` depth balanced for scans over surrounding slots.
|
|
376
|
+
* @param {TryState} state
|
|
377
|
+
* @returns {void}
|
|
378
|
+
*/
|
|
379
|
+
function neutralize_slot_markers(state) {
|
|
380
|
+
/** @type {Comment} */ (state.so).data = '';
|
|
381
|
+
/** @type {ChildNode} */ (state.sc).remove();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Reads the streamed unit error envelope for this slot and routes the
|
|
386
|
+
* error into this boundary, or the nearest catch boundary above it.
|
|
387
|
+
* @param {TryState} state
|
|
388
|
+
* @param {string} id
|
|
389
|
+
* @returns {void}
|
|
390
|
+
*/
|
|
391
|
+
function route_streamed_error(state, id) {
|
|
392
|
+
if (state.b === null || is_destroyed(state.b)) {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
neutralize_slot_markers(state);
|
|
396
|
+
var message = 'An error occurred during server rendering';
|
|
397
|
+
var script = document.getElementById(STREAM_ERROR_SCRIPT_PREFIX + id);
|
|
398
|
+
if (script !== null) {
|
|
399
|
+
try {
|
|
400
|
+
message = JSON.parse(/** @type {string} */ (script.textContent)).message ?? message;
|
|
401
|
+
} catch {
|
|
402
|
+
// keep the generic message
|
|
403
|
+
}
|
|
404
|
+
script.remove();
|
|
405
|
+
}
|
|
406
|
+
var error = new Error(message);
|
|
407
|
+
if (state.c !== null) {
|
|
408
|
+
catch_error(state, error);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
var outer = get_boundary_with_catch(/** @type {Block} */ (state.b));
|
|
412
|
+
if (outer === null) {
|
|
413
|
+
throw error;
|
|
414
|
+
}
|
|
415
|
+
catch_error(outer.s, error);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Empties the streamed slot: destroys the hydrated fallback branch and
|
|
420
|
+
* sweeps whatever remains between the wrapper comments (leftover marker
|
|
421
|
+
* comments included).
|
|
422
|
+
* @param {TryState} state
|
|
423
|
+
* @returns {void}
|
|
424
|
+
*/
|
|
425
|
+
function clear_streamed_slot(state) {
|
|
426
|
+
destroy_pending(state);
|
|
427
|
+
var node = /** @type {ChildNode} */ (state.so).nextSibling;
|
|
428
|
+
while (node !== null && node !== state.sc) {
|
|
429
|
+
var next = node.nextSibling;
|
|
430
|
+
/** @type {ChildNode} */ (node).remove();
|
|
431
|
+
node = next;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Called by the inline stream runtime when this slot's chunk arrives after
|
|
437
|
+
* hydration: swaps the fallback for the streamed content and claims the
|
|
438
|
+
* new DOM through a boundary-scoped hydration walk (trackAsync inside the
|
|
439
|
+
* body picks its serialized envelope up from the same chunk).
|
|
440
|
+
* @param {TryState} state
|
|
441
|
+
* @param {HTMLTemplateElement | null} template
|
|
442
|
+
* @param {number | undefined} [errored]
|
|
443
|
+
* @returns {void}
|
|
444
|
+
*/
|
|
445
|
+
function activate_streamed_chunk(state, template, errored) {
|
|
446
|
+
if (state.b === null || is_destroyed(state.b)) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
var id = /** @type {string} */ (state.si);
|
|
450
|
+
state.si = null;
|
|
451
|
+
if (errored) {
|
|
452
|
+
clear_streamed_slot(state);
|
|
453
|
+
route_streamed_error(state, id);
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
clear_streamed_slot(state);
|
|
457
|
+
if (template !== null) {
|
|
458
|
+
/** @type {ChildNode} */ (state.sc).before(template.content);
|
|
459
|
+
}
|
|
460
|
+
var first = /** @type {ChildNode} */ (state.so).nextSibling;
|
|
461
|
+
if (first === null || first === state.sc) {
|
|
462
|
+
neutralize_slot_markers(state);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
// adopt the streamed body's own <!--[--> as the boundary anchor (the
|
|
466
|
+
// node the buffered-SSR hydration path would have used) and drop the
|
|
467
|
+
// slot wrapper comments, so the resulting DOM matches buffered SSR
|
|
468
|
+
// exactly like the pre-hydration swap path does
|
|
469
|
+
if (state.a === state.so) {
|
|
470
|
+
state.a = first;
|
|
471
|
+
}
|
|
472
|
+
/** @type {ChildNode} */ (state.so).remove();
|
|
473
|
+
/** @type {ChildNode} */ (state.sc).remove();
|
|
474
|
+
var previous_hydrating = hydrating;
|
|
475
|
+
var previous_hydrate_node = hydrate_node;
|
|
476
|
+
set_hydration(true, first);
|
|
477
|
+
hydrate_next(); // consume the streamed body's <!--[-->
|
|
478
|
+
try {
|
|
479
|
+
state.h = true;
|
|
480
|
+
render_resolved(state);
|
|
481
|
+
} finally {
|
|
482
|
+
set_hydration(previous_hydrating, previous_hydrate_node);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether this build can hydrate server-rendered HTML. The Vite plugin's
|
|
3
|
+
* `ssr: false` build aliases this module to one exporting `false`, and the
|
|
4
|
+
* bundler then drops every hydration path in the runtime along with the
|
|
5
|
+
* hydration cursor the compiled components no longer reference.
|
|
6
|
+
*/
|
|
7
|
+
export const HYDRATION = true;
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
/** @import { AppendIntoAnchor, Block, Derived, Tracked } from '#client' */
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
COMMENT_NODE,
|
|
5
|
-
HYDRATION_END,
|
|
6
|
-
HYDRATION_ERROR,
|
|
7
|
-
HYDRATION_START,
|
|
8
|
-
TEXT_NODE,
|
|
9
|
-
} from '../../../constants.js';
|
|
3
|
+
import { HYDRATION_ERROR, TEXT_NODE } from '../../../constants.js';
|
|
10
4
|
import { create_text, first_child_getter, next_sibling_getter } from './operations.js';
|
|
11
|
-
import { active_block } from './runtime.js';
|
|
12
5
|
|
|
13
6
|
export let hydrating = false;
|
|
14
7
|
|
|
@@ -23,6 +16,21 @@ export let hydrate_node = null;
|
|
|
23
16
|
*/
|
|
24
17
|
export const track_hash_reference = new Map();
|
|
25
18
|
|
|
19
|
+
/**
|
|
20
|
+
* The hydration paths of the DOM runtime (see `hydrate.js`), installed by
|
|
21
|
+
* `hydrate()`: the runtime reaches them through this object, so a client-only
|
|
22
|
+
* mount never loads them.
|
|
23
|
+
* @type {import('./hydrate.js').HydrationRuntime | null}
|
|
24
|
+
*/
|
|
25
|
+
export let H = null;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {import('./hydrate.js').HydrationRuntime} runtime
|
|
29
|
+
*/
|
|
30
|
+
export function set_hydration_runtime(runtime) {
|
|
31
|
+
H = runtime;
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
/**
|
|
27
35
|
* @param {boolean} value
|
|
28
36
|
*/
|
|
@@ -130,53 +138,6 @@ function repair_next_sibling(next_sibling) {
|
|
|
130
138
|
return text;
|
|
131
139
|
}
|
|
132
140
|
|
|
133
|
-
/**
|
|
134
|
-
* The hydration path of `append`: repositions the hydration cursor instead
|
|
135
|
-
* of inserting. Kept out of the insert path so a client-only mount never
|
|
136
|
-
* compiles it.
|
|
137
|
-
*
|
|
138
|
-
* Every hydrated node, block, and component leaves the cursor on its last
|
|
139
|
-
* DOM node, and whoever owns the next node steps past it: a parent element
|
|
140
|
-
* with its sibling traversal, a control-flow block by reaching its end
|
|
141
|
-
* marker, an append-into sentinel by adopting the cursor as the next
|
|
142
|
-
* component's first node.
|
|
143
|
-
* @param {ChildNode | AppendIntoAnchor} anchor
|
|
144
|
-
* @param {Node} dom
|
|
145
|
-
*/
|
|
146
|
-
export function hydrate_append(anchor, dom) {
|
|
147
|
-
var node = /** @type {Node} */ (hydrate_node);
|
|
148
|
-
|
|
149
|
-
// The cursor descended into dom's children (child()/sibling() traversal
|
|
150
|
-
// inside a single-node template) without a compiler-emitted pop(): bring it
|
|
151
|
-
// back up to dom before deciding where to leave it.
|
|
152
|
-
if (node !== dom && dom.contains(node)) {
|
|
153
|
-
node = dom;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// A child component renders into the anchor it was handed, which is its
|
|
157
|
-
// own first node. Its content is hydrated, so leave the cursor on the
|
|
158
|
-
// content's last node for the parent's sibling traversal.
|
|
159
|
-
if (anchor === dom) {
|
|
160
|
-
hydrate_node = node;
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (node !== dom) {
|
|
165
|
-
// A fragment's cursor sits on its last top-level node, past the
|
|
166
|
-
// template's first node: widen the block's end to cover the whole
|
|
167
|
-
// fragment rather than only the node assign_nodes saw.
|
|
168
|
-
var s = /** @type {Block} */ (active_block).s;
|
|
169
|
-
if (s !== null) {
|
|
170
|
-
s.end = node;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Step past the content: a branch lands on its block's end marker, the
|
|
175
|
-
// root on the boundary's end marker, an append-into sentinel on the next
|
|
176
|
-
// component's first node.
|
|
177
|
-
hydrate_node = next_sibling_getter.call(node);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
141
|
export function next(n = 1) {
|
|
181
142
|
if (hydrating) {
|
|
182
143
|
var node = hydrate_node;
|
|
@@ -194,29 +155,3 @@ export function pop(node) {
|
|
|
194
155
|
if (!hydrating) return;
|
|
195
156
|
hydrate_node = node;
|
|
196
157
|
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Scans forward from the current hydrate_node to find the matching HYDRATION_END
|
|
200
|
-
* comment, handling nested blocks by tracking depth.
|
|
201
|
-
* Should be called after hydrate_next() has consumed the opening HYDRATION_START.
|
|
202
|
-
* Any `[`-prefixed comment opens a nested region — this includes the plain
|
|
203
|
-
* `<!--[-->` markers as well as streaming slot markers (`<!--[?N-->`,
|
|
204
|
-
* `<!--[!N-->`), which always pair with a `<!--]-->`.
|
|
205
|
-
* @returns {Node} The HYDRATION_END comment node.
|
|
206
|
-
*/
|
|
207
|
-
export function skip_to_hydration_end() {
|
|
208
|
-
var depth = 0;
|
|
209
|
-
var node = /** @type {Node} */ (hydrate_node);
|
|
210
|
-
while (true) {
|
|
211
|
-
if (node.nodeType === COMMENT_NODE) {
|
|
212
|
-
var data = /** @type {Comment} */ (node).data;
|
|
213
|
-
if (data === HYDRATION_END) {
|
|
214
|
-
if (depth === 0) return node;
|
|
215
|
-
depth -= 1;
|
|
216
|
-
} else if (data.startsWith(HYDRATION_START)) {
|
|
217
|
-
depth += 1;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
node = /** @type {Node} */ (next_sibling_getter.call(node));
|
|
221
|
-
}
|
|
222
|
-
}
|