ripple 0.2.208 → 0.2.210
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 +43 -0
- package/README.md +2 -1
- package/package.json +2 -6
- package/shims/rollup-estree-types.d.ts +1 -1
- package/src/compiler/index.d.ts +1 -0
- package/src/compiler/index.js +7 -1
- package/src/compiler/phases/1-parse/index.js +15 -6
- package/src/compiler/phases/2-analyze/css-analyze.js +100 -104
- package/src/compiler/phases/2-analyze/index.js +215 -2
- package/src/compiler/phases/3-transform/client/index.js +388 -50
- package/src/compiler/phases/3-transform/segments.js +123 -39
- package/src/compiler/phases/3-transform/server/index.js +266 -13
- package/src/compiler/types/index.d.ts +16 -3
- package/src/compiler/utils.js +1 -15
- package/src/constants.js +0 -2
- package/src/helpers.d.ts +4 -0
- package/src/html-tree-validation.js +211 -0
- package/src/jsx-runtime.d.ts +260 -259
- package/src/jsx-runtime.js +12 -12
- package/src/runtime/array.js +17 -17
- package/src/runtime/create-subscriber.js +1 -1
- package/src/runtime/index-client.js +1 -5
- package/src/runtime/index-server.js +15 -0
- package/src/runtime/internal/client/compat.js +3 -3
- package/src/runtime/internal/client/composite.js +6 -1
- package/src/runtime/internal/client/head.js +50 -4
- package/src/runtime/internal/client/html.js +73 -12
- package/src/runtime/internal/client/hydration.js +12 -0
- package/src/runtime/internal/client/index.js +1 -1
- package/src/runtime/internal/client/portal.js +54 -29
- package/src/runtime/internal/client/rpc.js +3 -1
- package/src/runtime/internal/client/switch.js +5 -0
- package/src/runtime/internal/client/template.js +117 -11
- package/src/runtime/internal/client/try.js +1 -0
- package/src/runtime/internal/server/index.js +113 -1
- package/src/runtime/internal/server/rpc.js +4 -4
- package/src/runtime/map.js +2 -2
- package/src/runtime/object.js +6 -6
- package/src/runtime/proxy.js +12 -11
- package/src/runtime/reactive-value.js +9 -1
- package/src/runtime/set.js +12 -7
- package/src/runtime/url-search-params.js +0 -1
- package/src/server/index.js +4 -0
- package/src/utils/hashing.js +15 -0
- package/src/utils/normalize_css_property_name.js +1 -1
- package/tests/client/array/array.mutations.test.ripple +8 -8
- package/tests/client/basic/basic.errors.test.ripple +28 -0
- package/tests/client/basic/basic.events.test.ripple +6 -3
- package/tests/client/basic/basic.utilities.test.ripple +1 -1
- package/tests/client/compiler/compiler.regex.test.ripple +10 -8
- package/tests/client/composite/composite.generics.test.ripple +5 -2
- package/tests/client/dynamic-elements.test.ripple +30 -1
- package/tests/client/function-overload-import.ripple +6 -7
- package/tests/client/html.test.ripple +0 -1
- package/tests/client/object.test.ripple +2 -2
- package/tests/client/portal.test.ripple +3 -3
- package/tests/client/return.test.ripple +2500 -0
- package/tests/client/try.test.ripple +69 -0
- package/tests/client/typescript-generics.test.ripple +1 -1
- package/tests/client/url/url.derived.test.ripple +1 -1
- package/tests/client/url/url.parsing.test.ripple +3 -3
- package/tests/client/url/url.partial-removal.test.ripple +7 -7
- package/tests/client/url/url.reactivity.test.ripple +15 -15
- package/tests/client/url/url.serialization.test.ripple +2 -2
- package/tests/hydration/basic.test.js +23 -0
- package/tests/hydration/build-components.js +10 -4
- package/tests/hydration/compiled/client/basic.js +165 -3
- package/tests/hydration/compiled/client/for.js +1140 -23
- package/tests/hydration/compiled/client/head.js +234 -0
- package/tests/hydration/compiled/client/html.js +135 -0
- package/tests/hydration/compiled/client/portal.js +172 -0
- package/tests/hydration/compiled/client/reactivity.js +3 -1
- package/tests/hydration/compiled/client/return.js +1976 -0
- package/tests/hydration/compiled/client/switch.js +162 -0
- package/tests/hydration/compiled/server/basic.js +249 -0
- package/tests/hydration/compiled/server/events.js +1 -1
- package/tests/hydration/compiled/server/for.js +891 -1
- package/tests/hydration/compiled/server/head.js +291 -0
- package/tests/hydration/compiled/server/html.js +133 -0
- package/tests/hydration/compiled/server/if.js +1 -1
- package/tests/hydration/compiled/server/portal.js +250 -0
- package/tests/hydration/compiled/server/reactivity.js +1 -1
- package/tests/hydration/compiled/server/return.js +1969 -0
- package/tests/hydration/compiled/server/switch.js +130 -0
- package/tests/hydration/components/basic.ripple +55 -0
- package/tests/hydration/components/for.ripple +403 -0
- package/tests/hydration/components/head.ripple +111 -0
- package/tests/hydration/components/html.ripple +38 -0
- package/tests/hydration/components/portal.ripple +49 -0
- package/tests/hydration/components/return.ripple +564 -0
- package/tests/hydration/components/switch.ripple +51 -0
- package/tests/hydration/for.test.js +363 -0
- package/tests/hydration/head.test.js +105 -0
- package/tests/hydration/html.test.js +46 -0
- package/tests/hydration/portal.test.js +71 -0
- package/tests/hydration/return.test.js +544 -0
- package/tests/hydration/switch.test.js +42 -0
- package/tests/server/basic.attributes.test.ripple +1 -1
- package/tests/server/compiler.test.ripple +22 -0
- package/tests/server/composite.test.ripple +5 -2
- package/tests/server/html-nesting-validation.test.ripple +237 -0
- package/tests/server/return.test.ripple +1379 -0
- package/tests/setup-hydration.js +6 -1
- package/tests/utils/escaping.test.js +3 -1
- package/tests/utils/normalize_css_property_name.test.js +0 -1
- package/tests/utils/patterns.test.js +6 -2
- package/tests/utils/sanitize_template_string.test.js +3 -2
- package/types/server.d.ts +16 -0
package/src/jsx-runtime.js
CHANGED
|
@@ -13,15 +13,15 @@
|
|
|
13
13
|
* @returns {void} Ripple components don't return anything
|
|
14
14
|
*/
|
|
15
15
|
export function jsx(type, props, key) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
// Ripple components are imperative - they don't return JSX elements
|
|
17
|
+
// This is a placeholder for the actual Ripple rendering logic
|
|
18
|
+
if (typeof type === 'function') {
|
|
19
|
+
// Call the Ripple component function
|
|
20
|
+
type(props);
|
|
21
|
+
} else {
|
|
22
|
+
// Handle DOM elements
|
|
23
|
+
console.warn('DOM element rendering not implemented in jsx runtime:', type, props);
|
|
24
|
+
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/**
|
|
@@ -32,7 +32,7 @@ export function jsx(type, props, key) {
|
|
|
32
32
|
* @returns {void} Ripple components don't return anything
|
|
33
33
|
*/
|
|
34
34
|
export function jsxs(type, props, key) {
|
|
35
|
-
|
|
35
|
+
return jsx(type, props, key);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -41,6 +41,6 @@ export function jsxs(type, props, key) {
|
|
|
41
41
|
* @returns {void} Ripple fragments don't return anything
|
|
42
42
|
*/
|
|
43
43
|
export function Fragment(props) {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
// Ripple fragments are imperative
|
|
45
|
+
console.warn('Fragment rendering not implemented in jsx runtime:', props);
|
|
46
46
|
}
|
package/src/runtime/array.js
CHANGED
|
@@ -9,12 +9,12 @@ import { array_proxy } from './proxy.js';
|
|
|
9
9
|
* @returns {TrackedArray<T>}
|
|
10
10
|
*/
|
|
11
11
|
export function TrackedArray(...elements) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
if (!new.target) {
|
|
13
|
+
throw new Error("TrackedArray must be called with 'new'");
|
|
14
|
+
}
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
var block = safe_scope();
|
|
17
|
+
return array_proxy({ elements, block });
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -25,9 +25,9 @@ export function TrackedArray(...elements) {
|
|
|
25
25
|
* @returns {TrackedArray<T>}
|
|
26
26
|
*/
|
|
27
27
|
TrackedArray.from = function (arrayLike, mapFn, thisArg) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
var block = safe_scope();
|
|
29
|
+
var elements = mapFn ? Array.from(arrayLike, mapFn, thisArg) : Array.from(arrayLike);
|
|
30
|
+
return array_proxy({ elements, block, from_static: true });
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
/**
|
|
@@ -36,9 +36,9 @@ TrackedArray.from = function (arrayLike, mapFn, thisArg) {
|
|
|
36
36
|
* @returns {TrackedArray<T>}
|
|
37
37
|
*/
|
|
38
38
|
TrackedArray.of = function (...items) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
var block = safe_scope();
|
|
40
|
+
var elements = Array.of(...items);
|
|
41
|
+
return array_proxy({ elements, block, from_static: true });
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -49,11 +49,11 @@ TrackedArray.of = function (...items) {
|
|
|
49
49
|
* @returns {Promise<TrackedArray<T>>}
|
|
50
50
|
*/
|
|
51
51
|
TrackedArray.fromAsync = async function (arrayLike, mapFn, thisArg) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
var block = safe_scope();
|
|
53
|
+
var elements = mapFn
|
|
54
|
+
? await Array.fromAsync(arrayLike, mapFn, thisArg)
|
|
55
|
+
: await Array.fromAsync(arrayLike);
|
|
56
|
+
return array_proxy({ elements, block, from_static: true });
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
/**
|
|
@@ -63,5 +63,5 @@ TrackedArray.fromAsync = async function (arrayLike, mapFn, thisArg) {
|
|
|
63
63
|
* @returns {TrackedArray<T>}
|
|
64
64
|
*/
|
|
65
65
|
export function tracked_array(elements, block) {
|
|
66
|
-
|
|
66
|
+
return array_proxy({ elements, block, from_static: true });
|
|
67
67
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @import { createSubscriber } from '#public' */
|
|
2
2
|
import { untrack, queue_microtask } from './internal/client/runtime.js';
|
|
3
|
-
import { effect } from './internal/client/blocks.js'
|
|
3
|
+
import { effect } from './internal/client/blocks.js';
|
|
4
4
|
|
|
5
5
|
/** @type {createSubscriber} */
|
|
6
6
|
export function createSubscriber(start) {
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
set_hydrate_node,
|
|
18
18
|
set_hydrating,
|
|
19
19
|
} from './internal/client/hydration.js';
|
|
20
|
-
import { COMMENT_NODE,
|
|
20
|
+
import { COMMENT_NODE, HYDRATION_START } from '../constants.js';
|
|
21
21
|
|
|
22
22
|
// Re-export JSX runtime functions for jsxImportSource: "ripple"
|
|
23
23
|
export { jsx, jsxs, Fragment } from '../jsx-runtime.js';
|
|
@@ -84,10 +84,6 @@ export function hydrate(component, options) {
|
|
|
84
84
|
set_hydrate_node(/** @type {Comment} */ (anchor));
|
|
85
85
|
hydrate_next();
|
|
86
86
|
|
|
87
|
-
if (!anchor) {
|
|
88
|
-
throw HYDRATION_ERROR;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
87
|
_root = root(() => {
|
|
92
88
|
component(/** @type {Comment} */ (anchor), props, active_block);
|
|
93
89
|
}, options.compat);
|
|
@@ -56,3 +56,18 @@ export const bindTextContent = noop;
|
|
|
56
56
|
export const bindNode = noop;
|
|
57
57
|
export const bindOffsetWidth = noop;
|
|
58
58
|
export const bindOffsetHeight = noop;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Portal component noop for server-side rendering.
|
|
62
|
+
* Portals are client-only and do not render on the server.
|
|
63
|
+
* However, we need to output a marker comment so hydration can work correctly.
|
|
64
|
+
* @param {any} output
|
|
65
|
+
* @param {any} __
|
|
66
|
+
*/
|
|
67
|
+
export function Portal(output, __) {
|
|
68
|
+
// Portals are client-only, but we need to output a marker for hydration
|
|
69
|
+
// Output an empty HTML comment as a placeholder
|
|
70
|
+
if (output && typeof output.push === 'function') {
|
|
71
|
+
output.push('<!--portal-->');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/** @import { CompatApi } from '#client' */
|
|
2
2
|
|
|
3
|
-
import { ROOT_BLOCK } from
|
|
4
|
-
import { active_block } from
|
|
3
|
+
import { ROOT_BLOCK } from './constants.js';
|
|
4
|
+
import { active_block } from './runtime.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @param {string} kind
|
|
8
8
|
* @returns {CompatApi | null}
|
|
9
9
|
*/
|
|
10
|
-
function get_compat_from_root(kind)
|
|
10
|
+
function get_compat_from_root(kind) {
|
|
11
11
|
var current = active_block;
|
|
12
12
|
|
|
13
13
|
while (current !== null) {
|
|
@@ -15,7 +15,12 @@ import { top_element_to_ns } from './utils.js';
|
|
|
15
15
|
*/
|
|
16
16
|
export function composite(get_component, node, props) {
|
|
17
17
|
if (hydrating) {
|
|
18
|
-
|
|
18
|
+
// During hydration, `node` may already point at the first real SSR node
|
|
19
|
+
// (e.g. layout children). Only skip forward when we are on an empty
|
|
20
|
+
// comment anchor from a client template placeholder.
|
|
21
|
+
if (node.nodeType === 8 && /** @type {Comment} */ (node).data === '') {
|
|
22
|
+
hydrate_next();
|
|
23
|
+
}
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
var anchor = node;
|
|
@@ -1,14 +1,60 @@
|
|
|
1
|
+
/** @import { TemplateNode } from '#client' */
|
|
1
2
|
import { render } from './blocks.js';
|
|
2
3
|
import { HEAD_BLOCK } from './constants.js';
|
|
3
|
-
import {
|
|
4
|
+
import { COMMENT_NODE } from '../../../constants.js';
|
|
5
|
+
import { create_text, get_first_child, get_next_sibling } from './operations.js';
|
|
6
|
+
import { hydrate_node, hydrating, set_hydrate_node, set_hydrating } from './hydration.js';
|
|
4
7
|
|
|
5
8
|
/**
|
|
9
|
+
* @param {string} hash
|
|
6
10
|
* @param {(anchor: Node) => void} render_fn
|
|
7
11
|
* @returns {void}
|
|
8
12
|
*/
|
|
9
|
-
export function head(render_fn) {
|
|
13
|
+
export function head(hash, render_fn) {
|
|
14
|
+
// The head function may be called after the first hydration pass and ssr comment nodes may still be present,
|
|
15
|
+
// therefore we need to skip that when we detect that we're not in hydration mode.
|
|
16
|
+
let previous_hydrate_node = null;
|
|
17
|
+
let was_hydrating = hydrating;
|
|
18
|
+
|
|
10
19
|
/** @type {Comment | Text} */
|
|
11
|
-
var anchor
|
|
20
|
+
var anchor;
|
|
21
|
+
|
|
22
|
+
if (hydrating) {
|
|
23
|
+
previous_hydrate_node = hydrate_node;
|
|
24
|
+
|
|
25
|
+
var head_anchor = get_first_child(document.head);
|
|
26
|
+
|
|
27
|
+
// There might be multiple head blocks in our app, and they could have been
|
|
28
|
+
// rendered in an arbitrary order — find one corresponding to this component
|
|
29
|
+
while (
|
|
30
|
+
head_anchor !== null &&
|
|
31
|
+
(head_anchor.nodeType !== COMMENT_NODE || /** @type {Comment} */ (head_anchor).data !== hash)
|
|
32
|
+
) {
|
|
33
|
+
head_anchor = get_next_sibling(head_anchor);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// If we can't find an opening hydration marker, skip hydration (this can happen
|
|
37
|
+
// if a framework rendered body but not head content)
|
|
38
|
+
if (head_anchor === null) {
|
|
39
|
+
set_hydrating(false);
|
|
40
|
+
} else {
|
|
41
|
+
var start = /** @type {TemplateNode} */ (get_next_sibling(head_anchor));
|
|
42
|
+
head_anchor.remove(); // in case this component is repeated
|
|
43
|
+
|
|
44
|
+
set_hydrate_node(start);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!hydrating) {
|
|
49
|
+
anchor = document.head.appendChild(create_text());
|
|
50
|
+
}
|
|
12
51
|
|
|
13
|
-
|
|
52
|
+
try {
|
|
53
|
+
render(() => render_fn(anchor), null, HEAD_BLOCK);
|
|
54
|
+
} finally {
|
|
55
|
+
if (was_hydrating) {
|
|
56
|
+
set_hydrating(true);
|
|
57
|
+
set_hydrate_node(/** @type {TemplateNode} */ (previous_hydrate_node));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
14
60
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/** @import { Block } from '#client' */
|
|
2
2
|
|
|
3
3
|
import { remove_block_dom, render } from './blocks.js';
|
|
4
|
-
import { get_first_child } from './operations.js';
|
|
4
|
+
import { get_first_child, get_next_sibling } from './operations.js';
|
|
5
5
|
import { active_block } from './runtime.js';
|
|
6
6
|
import { assign_nodes, create_fragment_from_html } from './template.js';
|
|
7
|
+
import { hydrate_next, hydrate_node, hydrating, set_hydrate_node } from './hydration.js';
|
|
8
|
+
import { COMMENT_NODE } from '../../../constants.js';
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Renders dynamic HTML content into the DOM by inserting it before the anchor node.
|
|
@@ -13,14 +15,41 @@ import { assign_nodes, create_fragment_from_html } from './template.js';
|
|
|
13
15
|
* @returns {void}
|
|
14
16
|
*/
|
|
15
17
|
export function html(node, get_html, svg = false, mathml = false) {
|
|
16
|
-
/** @type {ChildNode} */
|
|
17
18
|
var anchor = node;
|
|
18
|
-
/** @type {string} */
|
|
19
19
|
var html = '';
|
|
20
20
|
|
|
21
21
|
render(() => {
|
|
22
22
|
var block = /** @type {Block} */ (active_block);
|
|
23
|
-
|
|
23
|
+
var new_html = get_html() + '';
|
|
24
|
+
|
|
25
|
+
// If the HTML hasn't changed, skip the update (but still hydrate on first run)
|
|
26
|
+
if (html === new_html) {
|
|
27
|
+
// During hydration, we need to skip past the content and end marker even if value hasn't changed
|
|
28
|
+
if (hydrating) {
|
|
29
|
+
// The anchor is the hash comment - we need to skip past it and its content
|
|
30
|
+
set_hydrate_node(anchor);
|
|
31
|
+
var next = hydrate_next();
|
|
32
|
+
|
|
33
|
+
// Walk until we find the empty comment end marker
|
|
34
|
+
while (
|
|
35
|
+
next !== null &&
|
|
36
|
+
(next.nodeType !== COMMENT_NODE || /** @type {Comment} */ (next).data !== '')
|
|
37
|
+
) {
|
|
38
|
+
next = get_next_sibling(next);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Move past the end marker (if next sibling exists)
|
|
42
|
+
if (next !== null) {
|
|
43
|
+
var next_node = get_next_sibling(next);
|
|
44
|
+
if (next_node !== null) {
|
|
45
|
+
set_hydrate_node(next_node);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
html = new_html;
|
|
24
53
|
|
|
25
54
|
if (svg) html = `<svg>${html}</svg>`;
|
|
26
55
|
else if (mathml) html = `<math>${html}</math>`;
|
|
@@ -30,25 +59,57 @@ export function html(node, get_html, svg = false, mathml = false) {
|
|
|
30
59
|
block.s.start = block.s.end = null;
|
|
31
60
|
}
|
|
32
61
|
|
|
62
|
+
if (hydrating) {
|
|
63
|
+
set_hydrate_node(anchor);
|
|
64
|
+
|
|
65
|
+
/** @type {Node | null} */
|
|
66
|
+
var next = hydrate_next();
|
|
67
|
+
|
|
68
|
+
// Walk through content nodes until we hit the empty comment end marker
|
|
69
|
+
while (
|
|
70
|
+
next !== null &&
|
|
71
|
+
(next.nodeType !== COMMENT_NODE || /** @type {Comment} */ (next).data !== '')
|
|
72
|
+
) {
|
|
73
|
+
next = get_next_sibling(next);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (next === null) {
|
|
77
|
+
throw new Error('Hydration mismatch: expected end marker for HTML block');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Include the hash comment and end marker in the assigned nodes
|
|
81
|
+
// This follows Svelte's approach where these markers are part of the effect's managed DOM
|
|
82
|
+
// They will be cleaned up when/if the content updates
|
|
83
|
+
assign_nodes(anchor, next);
|
|
84
|
+
|
|
85
|
+
// Move past the end marker for the next operation
|
|
86
|
+
// For empty HTML or end of container, next sibling might be null - that's okay
|
|
87
|
+
var next_node = get_next_sibling(next);
|
|
88
|
+
if (next_node !== null) {
|
|
89
|
+
set_hydrate_node(next_node);
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
33
94
|
if (html === '') return;
|
|
34
|
-
|
|
35
|
-
var
|
|
95
|
+
|
|
96
|
+
var fragment = create_fragment_from_html(html);
|
|
36
97
|
|
|
37
98
|
if (svg || mathml) {
|
|
38
|
-
|
|
99
|
+
fragment = /** @type {DocumentFragment} */ (get_first_child(fragment));
|
|
39
100
|
}
|
|
40
101
|
|
|
41
102
|
assign_nodes(
|
|
42
|
-
/** @type {
|
|
43
|
-
/** @type {
|
|
103
|
+
/** @type {Node} */ (get_first_child(fragment)),
|
|
104
|
+
/** @type {Node} */ (fragment.lastChild),
|
|
44
105
|
);
|
|
45
106
|
|
|
46
107
|
if (svg || mathml) {
|
|
47
|
-
while (get_first_child(
|
|
48
|
-
anchor.before(/** @type {
|
|
108
|
+
while (get_first_child(fragment)) {
|
|
109
|
+
anchor.before(/** @type {Node} */ (get_first_child(fragment)));
|
|
49
110
|
}
|
|
50
111
|
} else {
|
|
51
|
-
anchor.before(
|
|
112
|
+
anchor.before(fragment);
|
|
52
113
|
}
|
|
53
114
|
});
|
|
54
115
|
}
|
|
@@ -28,6 +28,18 @@ export function hydrate_next() {
|
|
|
28
28
|
return set_hydrate_node(get_next_sibling(/** @type {Node} */ (hydrate_node)));
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
export function next(n = 1) {
|
|
32
|
+
if (hydrating) {
|
|
33
|
+
var node = hydrate_node;
|
|
34
|
+
|
|
35
|
+
for (var i = 0; i < n; i++) {
|
|
36
|
+
node = get_next_sibling(/** @type {Node} */ (node));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
hydrate_node = node;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
/** @param {Node} node */
|
|
32
44
|
export function pop(node) {
|
|
33
45
|
if (!hydrating) return;
|
|
@@ -5,6 +5,13 @@ import { UNINITIALIZED } from './constants.js';
|
|
|
5
5
|
import { handle_root_events } from './events.js';
|
|
6
6
|
import { create_text } from './operations.js';
|
|
7
7
|
import { active_block } from './runtime.js';
|
|
8
|
+
import {
|
|
9
|
+
hydrating,
|
|
10
|
+
hydrate_next,
|
|
11
|
+
hydrate_node,
|
|
12
|
+
set_hydrating,
|
|
13
|
+
set_hydrate_node,
|
|
14
|
+
} from './hydration.js';
|
|
8
15
|
|
|
9
16
|
/**
|
|
10
17
|
* @param {any} _
|
|
@@ -12,6 +19,11 @@ import { active_block } from './runtime.js';
|
|
|
12
19
|
* @returns {void}
|
|
13
20
|
*/
|
|
14
21
|
export function Portal(_, props) {
|
|
22
|
+
// Portals are client-only and don't participate in hydration
|
|
23
|
+
// The compiler-generated code already handles getting the node via sibling()
|
|
24
|
+
var was_hydrating = hydrating;
|
|
25
|
+
var previous_hydrate_node = hydrate_node;
|
|
26
|
+
|
|
15
27
|
/** @type {Element | symbol} */
|
|
16
28
|
let target = UNINITIALIZED;
|
|
17
29
|
/** @type {((anchor: Node, props: {}, block: Block) => void) | symbol} */
|
|
@@ -25,42 +37,55 @@ export function Portal(_, props) {
|
|
|
25
37
|
/** @type {Node | null} */
|
|
26
38
|
var dom_end = null;
|
|
27
39
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
40
|
+
// Temporarily disable hydration for portal content
|
|
41
|
+
if (was_hydrating) {
|
|
42
|
+
set_hydrating(false);
|
|
43
|
+
}
|
|
31
44
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
45
|
+
try {
|
|
46
|
+
render(() => {
|
|
47
|
+
if (target === (target = props.target)) return;
|
|
48
|
+
if (children === (children = props.children)) return;
|
|
35
49
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
50
|
+
if (b !== null) {
|
|
51
|
+
destroy_block(b);
|
|
52
|
+
}
|
|
39
53
|
|
|
40
|
-
|
|
54
|
+
if (anchor !== null) {
|
|
55
|
+
anchor.remove();
|
|
56
|
+
}
|
|
41
57
|
|
|
42
|
-
|
|
43
|
-
/** @type {Element} */ (target).append(anchor);
|
|
58
|
+
dom_start = dom_end = null;
|
|
44
59
|
|
|
45
|
-
|
|
60
|
+
anchor = create_text();
|
|
61
|
+
/** @type {Element} */ (target).append(anchor);
|
|
46
62
|
|
|
47
|
-
|
|
63
|
+
const cleanup_events = handle_root_events(/** @type {Element} */ (target));
|
|
48
64
|
|
|
49
|
-
|
|
50
|
-
if (typeof children === 'function') {
|
|
51
|
-
children(/** @type {Text} */ (anchor), {}, block);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
65
|
+
var block = /** @type {Block} */ (active_block);
|
|
54
66
|
|
|
55
|
-
|
|
56
|
-
|
|
67
|
+
b = branch(() => {
|
|
68
|
+
if (typeof children === 'function') {
|
|
69
|
+
children(/** @type {Text} */ (anchor), {}, block);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
57
72
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
dom_start = b?.s?.start;
|
|
74
|
+
dom_end = b?.s?.end;
|
|
75
|
+
|
|
76
|
+
return () => {
|
|
77
|
+
cleanup_events();
|
|
78
|
+
/** @type {Text} */ (anchor).remove();
|
|
79
|
+
if (dom_start && dom_end) {
|
|
80
|
+
remove_block_dom(dom_start, dom_end);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
} finally {
|
|
85
|
+
// Restore hydration state
|
|
86
|
+
if (was_hydrating) {
|
|
87
|
+
set_hydrating(true);
|
|
88
|
+
set_hydrate_node(/** @type {any} */ (previous_hydrate_node));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
66
91
|
}
|
|
@@ -22,7 +22,9 @@ export async function rpc(hash, args) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
if (data === '') {
|
|
25
|
-
throw new Error(
|
|
25
|
+
throw new Error(
|
|
26
|
+
'The server function end-point did not return a response. Are you running a Ripple server?',
|
|
27
|
+
);
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
return devalue.parse(data).value;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { branch, destroy_block, render } from './blocks.js';
|
|
4
4
|
import { SWITCH_BLOCK } from './constants.js';
|
|
5
|
+
import { hydrate_next, hydrating } from './hydration.js';
|
|
5
6
|
import { next_sibling } from './operations.js';
|
|
6
7
|
import { append } from './template.js';
|
|
7
8
|
|
|
@@ -34,6 +35,10 @@ function move(block, anchor) {
|
|
|
34
35
|
* @returns {void}
|
|
35
36
|
*/
|
|
36
37
|
export function switch_block(anchor, fn) {
|
|
38
|
+
if (hydrating) {
|
|
39
|
+
hydrate_next();
|
|
40
|
+
}
|
|
41
|
+
|
|
37
42
|
/** @type {((anchor: ChildNode) => void)[]} */
|
|
38
43
|
var prev = [];
|
|
39
44
|
/** @type {Map<(anchor: ChildNode) => void, Block>} */
|