ripple 0.3.9 → 0.3.11
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/package.json +2 -2
- package/src/compiler/errors.js +1 -1
- package/src/compiler/index.d.ts +3 -1
- package/src/compiler/phases/1-parse/index.js +195 -23
- package/src/compiler/phases/2-analyze/index.js +266 -108
- package/src/compiler/phases/2-analyze/prune.js +13 -5
- package/src/compiler/phases/3-transform/client/index.js +304 -80
- package/src/compiler/phases/3-transform/server/index.js +108 -43
- package/src/compiler/types/index.d.ts +28 -3
- package/src/compiler/types/parse.d.ts +3 -1
- package/src/compiler/utils.js +275 -1
- package/src/runtime/element.js +39 -0
- package/src/runtime/index-client.js +14 -4
- package/src/runtime/internal/client/composite.js +10 -6
- package/src/runtime/internal/client/expression.js +280 -0
- package/src/runtime/internal/client/index.js +4 -0
- package/src/runtime/internal/client/portal.js +12 -6
- package/src/runtime/internal/server/index.js +26 -1
- package/src/utils/builders.js +30 -0
- package/tests/client/basic/__snapshots__/basic.rendering.test.ripple.snap +1 -0
- package/tests/client/basic/basic.components.test.ripple +85 -87
- package/tests/client/basic/basic.errors.test.ripple +4 -8
- package/tests/client/basic/basic.rendering.test.ripple +27 -10
- package/tests/client/capture-error.js +12 -0
- package/tests/client/compiler/compiler.basic.test.ripple +76 -6
- package/tests/client/composite/composite.props.test.ripple +1 -3
- package/tests/client/composite/composite.render.test.ripple +91 -13
- package/tests/client/css/global-additional-cases.test.ripple +3 -3
- package/tests/client/return.test.ripple +101 -0
- package/tests/client/svg.test.ripple +4 -4
- package/tests/client/tsx.test.ripple +486 -0
- package/tests/hydration/basic.test.js +23 -0
- package/tests/hydration/compiled/client/basic.js +111 -75
- package/tests/hydration/compiled/client/composite.js +81 -46
- package/tests/hydration/compiled/client/events.js +18 -63
- package/tests/hydration/compiled/client/for.js +90 -183
- package/tests/hydration/compiled/client/head.js +10 -25
- package/tests/hydration/compiled/client/hmr.js +10 -13
- package/tests/hydration/compiled/client/html.js +251 -380
- package/tests/hydration/compiled/client/if-children.js +35 -45
- package/tests/hydration/compiled/client/if.js +2 -2
- package/tests/hydration/compiled/client/mixed-control-flow.js +24 -72
- package/tests/hydration/compiled/client/nested-control-flow.js +115 -391
- package/tests/hydration/compiled/client/portal.js +8 -20
- package/tests/hydration/compiled/client/reactivity.js +14 -47
- package/tests/hydration/compiled/client/return.js +2 -5
- package/tests/hydration/compiled/client/try.js +4 -4
- package/tests/hydration/compiled/server/basic.js +64 -31
- package/tests/hydration/compiled/server/composite.js +62 -29
- package/tests/hydration/compiled/server/hmr.js +24 -37
- package/tests/hydration/compiled/server/html.js +472 -611
- package/tests/hydration/compiled/server/if-children.js +77 -103
- package/tests/hydration/compiled/server/portal.js +8 -8
- package/tests/hydration/components/basic.ripple +15 -5
- package/tests/hydration/components/composite.ripple +13 -1
- package/tests/hydration/components/hmr.ripple +1 -3
- package/tests/hydration/components/html.ripple +13 -35
- package/tests/hydration/components/if-children.ripple +4 -8
- package/tests/hydration/composite.test.js +11 -0
- package/tests/server/basic.attributes.test.ripple +50 -0
- package/tests/server/basic.components.test.ripple +22 -28
- package/tests/server/basic.test.ripple +12 -0
- package/tests/server/compiler.test.ripple +25 -8
- package/tests/server/composite.props.test.ripple +1 -3
- package/tests/server/style-identifier.test.ripple +2 -4
- package/tests/utils/compiler-compat-config.test.js +38 -0
- package/tests/utils/vite-plugin-config.test.js +113 -0
- package/tsconfig.typecheck.json +2 -1
- package/types/index.d.ts +8 -11
|
@@ -22,15 +22,24 @@ import { COMMENT_NODE, HYDRATION_START } from '../constants.js';
|
|
|
22
22
|
// Re-export JSX runtime functions for jsxImportSource: "ripple"
|
|
23
23
|
export { jsx, jsxs, Fragment } from '../jsx-runtime.js';
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @returns {CompatOptions | undefined}
|
|
27
|
+
*/
|
|
28
|
+
function get_default_compat() {
|
|
29
|
+
return /** @type {typeof globalThis & { __RIPPLE_COMPAT__?: CompatOptions }} */ (globalThis)
|
|
30
|
+
.__RIPPLE_COMPAT__;
|
|
31
|
+
}
|
|
32
|
+
|
|
25
33
|
/**
|
|
26
34
|
* @param {(anchor: Node, props: Record<string, any>, active_block: Block | null) => void} component
|
|
27
|
-
* @param {{ props?: Record<string, any>, target: HTMLElement
|
|
35
|
+
* @param {{ props?: Record<string, any>, target: HTMLElement }} options
|
|
28
36
|
* @returns {() => void}
|
|
29
37
|
*/
|
|
30
38
|
export function mount(component, options) {
|
|
31
39
|
init_operations();
|
|
32
40
|
remove_ssr_css();
|
|
33
41
|
|
|
42
|
+
const compat = get_default_compat();
|
|
34
43
|
const props = options.props || {};
|
|
35
44
|
const target = options.target;
|
|
36
45
|
const anchor = create_anchor();
|
|
@@ -46,7 +55,7 @@ export function mount(component, options) {
|
|
|
46
55
|
|
|
47
56
|
const _root = root(() => {
|
|
48
57
|
component(anchor, props, active_block);
|
|
49
|
-
},
|
|
58
|
+
}, compat);
|
|
50
59
|
|
|
51
60
|
return () => {
|
|
52
61
|
cleanup_events();
|
|
@@ -56,13 +65,14 @@ export function mount(component, options) {
|
|
|
56
65
|
|
|
57
66
|
/**
|
|
58
67
|
* @param {(anchor: Node, props: Record<string, any>, active_block: Block | null) => void} component
|
|
59
|
-
* @param {{ props?: Record<string, any>, target: HTMLElement
|
|
68
|
+
* @param {{ props?: Record<string, any>, target: HTMLElement }} options
|
|
60
69
|
* @returns {() => void}
|
|
61
70
|
*/
|
|
62
71
|
export function hydrate(component, options) {
|
|
63
72
|
init_operations();
|
|
64
73
|
remove_ssr_css();
|
|
65
74
|
|
|
75
|
+
const compat = get_default_compat();
|
|
66
76
|
const props = options.props || {};
|
|
67
77
|
const target = options.target;
|
|
68
78
|
const was_hydrating = hydrating;
|
|
@@ -86,7 +96,7 @@ export function hydrate(component, options) {
|
|
|
86
96
|
|
|
87
97
|
_root = root(() => {
|
|
88
98
|
component(/** @type {Comment} */ (anchor), props, active_block);
|
|
89
|
-
},
|
|
99
|
+
}, compat);
|
|
90
100
|
} catch (e) {
|
|
91
101
|
throw e;
|
|
92
102
|
} finally {
|
|
@@ -5,6 +5,7 @@ import { COMPOSITE_BLOCK, DEFAULT_NAMESPACE, NAMESPACE_URI } from './constants.j
|
|
|
5
5
|
import { hydrate_next, hydrating } from './hydration.js';
|
|
6
6
|
import { active_block, active_namespace, get, with_ns } from './runtime.js';
|
|
7
7
|
import { top_element_to_ns } from './utils.js';
|
|
8
|
+
import { is_ripple_element } from '../../element.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* @typedef {((anchor: Node, props: Record<string, any>, block: Block | null) => void)} ComponentFunction
|
|
@@ -45,13 +46,14 @@ export function composite(get_component, node, props) {
|
|
|
45
46
|
});
|
|
46
47
|
} else if (component != null) {
|
|
47
48
|
// Custom element - only create if component is not null/undefined
|
|
49
|
+
const ns = top_element_to_ns(component, active_namespace);
|
|
48
50
|
var run = () => {
|
|
49
51
|
var block = /** @type {Block} */ (active_block);
|
|
50
52
|
|
|
51
53
|
var element =
|
|
52
|
-
|
|
54
|
+
ns !== DEFAULT_NAMESPACE
|
|
53
55
|
? document.createElementNS(
|
|
54
|
-
NAMESPACE_URI[
|
|
56
|
+
NAMESPACE_URI[ns],
|
|
55
57
|
/** @type {keyof HTMLElementTagNameMap} */ (component),
|
|
56
58
|
)
|
|
57
59
|
: document.createElement(/** @type {keyof HTMLElementTagNameMap} */ (component));
|
|
@@ -67,16 +69,18 @@ export function composite(get_component, node, props) {
|
|
|
67
69
|
|
|
68
70
|
render_spread(element, () => props || {});
|
|
69
71
|
|
|
70
|
-
if (
|
|
72
|
+
if (is_ripple_element(props?.children)) {
|
|
71
73
|
var child_anchor = document.createComment('');
|
|
72
74
|
element.appendChild(child_anchor);
|
|
73
75
|
|
|
74
|
-
|
|
76
|
+
if (ns !== DEFAULT_NAMESPACE) {
|
|
77
|
+
with_ns(ns, () => props.children.render(child_anchor, block));
|
|
78
|
+
} else {
|
|
79
|
+
props.children.render(child_anchor, block);
|
|
80
|
+
}
|
|
75
81
|
}
|
|
76
82
|
};
|
|
77
83
|
|
|
78
|
-
const ns = top_element_to_ns(component, active_namespace);
|
|
79
|
-
|
|
80
84
|
if (ns !== active_namespace) {
|
|
81
85
|
// support top-level dynamic element svg/math <@tag />
|
|
82
86
|
b = branch(() => with_ns(ns, run));
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/** @import { Block } from '#client' */
|
|
2
|
+
|
|
3
|
+
import { branch, destroy_block, render } from './blocks.js';
|
|
4
|
+
import { BRANCH_BLOCK, UNINITIALIZED } from './constants.js';
|
|
5
|
+
import { create_text, get_next_sibling } from './operations.js';
|
|
6
|
+
import { active_block } from './runtime.js';
|
|
7
|
+
import { hydrating, set_hydrate_node } from './hydration.js';
|
|
8
|
+
import { COMMENT_NODE, HYDRATION_END, HYDRATION_START, TEXT_NODE } from '../../../constants.js';
|
|
9
|
+
import { is_ripple_element } from '../../element.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Finds the nearest enclosing BRANCH_BLOCK in the block hierarchy.
|
|
13
|
+
* @param {Block | null} block
|
|
14
|
+
* @returns {Block | null}
|
|
15
|
+
*/
|
|
16
|
+
function find_enclosing_branch(block) {
|
|
17
|
+
while (block !== null) {
|
|
18
|
+
if ((block.f & BRANCH_BLOCK) !== 0) {
|
|
19
|
+
return block;
|
|
20
|
+
}
|
|
21
|
+
block = block.p;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {Node} node
|
|
28
|
+
* @param {() => any} get_value
|
|
29
|
+
* @returns {void}
|
|
30
|
+
*/
|
|
31
|
+
export function expression(node, get_value) {
|
|
32
|
+
var anchor = /** @type {ChildNode} */ (node);
|
|
33
|
+
/** @type {Block | null} */
|
|
34
|
+
var child_block = null;
|
|
35
|
+
/** @type {Comment | null} */
|
|
36
|
+
var end = null;
|
|
37
|
+
/** @type {Text | null} */
|
|
38
|
+
var text = null;
|
|
39
|
+
/** @type {string | import('../../element.js').RippleElement | typeof UNINITIALIZED} */
|
|
40
|
+
var value = UNINITIALIZED;
|
|
41
|
+
var is_element = false;
|
|
42
|
+
var initialized = false;
|
|
43
|
+
/** @type {Block | null} */
|
|
44
|
+
var modified_parent_branch = null;
|
|
45
|
+
/** @type {Node | null} */
|
|
46
|
+
var original_parent_start = null;
|
|
47
|
+
|
|
48
|
+
render(() => {
|
|
49
|
+
var next_value = get_value();
|
|
50
|
+
var next_is_element = is_ripple_element(next_value);
|
|
51
|
+
var is_hydration_marker = hydrating && anchor.nodeType === COMMENT_NODE;
|
|
52
|
+
|
|
53
|
+
if (is_hydration_marker) {
|
|
54
|
+
end ??= ensure_expression_end(anchor);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (next_is_element) {
|
|
58
|
+
if (initialized && is_element && value === next_value) {
|
|
59
|
+
if (end !== null) {
|
|
60
|
+
advance_hydration(end);
|
|
61
|
+
}
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (anchor.nodeType === TEXT_NODE) {
|
|
66
|
+
/** @type {Text} */ (anchor).nodeValue = '';
|
|
67
|
+
} else if (text !== null) {
|
|
68
|
+
text.remove();
|
|
69
|
+
text = null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (child_block !== null) {
|
|
73
|
+
destroy_block(child_block);
|
|
74
|
+
child_block = null;
|
|
75
|
+
// Restore parent branch's start since we may update it again below
|
|
76
|
+
if (modified_parent_branch !== null && modified_parent_branch.s !== null) {
|
|
77
|
+
modified_parent_branch.s.start = original_parent_start;
|
|
78
|
+
modified_parent_branch = null;
|
|
79
|
+
original_parent_start = null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (end !== null && (initialized || !hydrating)) {
|
|
84
|
+
clear_expression_range(anchor, end);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (is_hydration_marker) {
|
|
88
|
+
set_hydrate_node(get_next_sibling(anchor) ?? end);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Find the enclosing branch block BEFORE creating child_block
|
|
92
|
+
// so we can update its s.start to include content inserted before anchor
|
|
93
|
+
var parent_branch = find_enclosing_branch(active_block);
|
|
94
|
+
|
|
95
|
+
child_block = branch(() => {
|
|
96
|
+
var block = active_block;
|
|
97
|
+
next_value.render(end ?? anchor, block);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Update parent branch's s.start to include content inserted before anchor.
|
|
101
|
+
// This ensures that when the parent branch is destroyed, the full DOM range
|
|
102
|
+
// (including RippleElement content) is removed.
|
|
103
|
+
if (
|
|
104
|
+
parent_branch !== null &&
|
|
105
|
+
parent_branch.s !== null &&
|
|
106
|
+
child_block.s !== null &&
|
|
107
|
+
child_block.s.start !== null
|
|
108
|
+
) {
|
|
109
|
+
// The child inserted content before the anchor. Update parent's start
|
|
110
|
+
// to encompass this content.
|
|
111
|
+
var child_start = child_block.s.start;
|
|
112
|
+
var parent_start = parent_branch.s.start;
|
|
113
|
+
|
|
114
|
+
// If parent's start is the anchor (or comes after child's start),
|
|
115
|
+
// update it to include the child's content
|
|
116
|
+
if (parent_start === anchor || parent_start === end) {
|
|
117
|
+
// Save original so we can restore it when switching to non-RippleElement
|
|
118
|
+
if (modified_parent_branch === null) {
|
|
119
|
+
modified_parent_branch = parent_branch;
|
|
120
|
+
original_parent_start = parent_start;
|
|
121
|
+
}
|
|
122
|
+
parent_branch.s.start = child_start;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
value = next_value;
|
|
127
|
+
is_element = true;
|
|
128
|
+
initialized = true;
|
|
129
|
+
if (end !== null) {
|
|
130
|
+
advance_hydration(end);
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
var next_text = (next_value ?? '') + '';
|
|
136
|
+
|
|
137
|
+
if (initialized && !is_element && value === next_text) {
|
|
138
|
+
if (end !== null) {
|
|
139
|
+
advance_hydration(end);
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (child_block !== null) {
|
|
145
|
+
destroy_block(child_block);
|
|
146
|
+
child_block = null;
|
|
147
|
+
// Restore parent branch's start to original value since the child's DOM nodes
|
|
148
|
+
// have been removed and the old start reference would be stale
|
|
149
|
+
if (modified_parent_branch !== null && modified_parent_branch.s !== null) {
|
|
150
|
+
modified_parent_branch.s.start = original_parent_start;
|
|
151
|
+
modified_parent_branch = null;
|
|
152
|
+
original_parent_start = null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (is_hydration_marker) {
|
|
157
|
+
text = get_hydrated_text(anchor, /** @type {Comment} */ (end));
|
|
158
|
+
|
|
159
|
+
if (next_text === '') {
|
|
160
|
+
if (text !== null) {
|
|
161
|
+
text.remove();
|
|
162
|
+
text = null;
|
|
163
|
+
}
|
|
164
|
+
} else if (text === null) {
|
|
165
|
+
text = create_text(next_text);
|
|
166
|
+
/** @type {Comment} */ (end).before(text);
|
|
167
|
+
} else if (text.nodeValue !== next_text) {
|
|
168
|
+
text.nodeValue = next_text;
|
|
169
|
+
}
|
|
170
|
+
} else if (anchor.nodeType === COMMENT_NODE) {
|
|
171
|
+
if (next_text === '') {
|
|
172
|
+
if (text !== null) {
|
|
173
|
+
text.remove();
|
|
174
|
+
text = null;
|
|
175
|
+
}
|
|
176
|
+
} else if (text === null) {
|
|
177
|
+
text = create_text(next_text);
|
|
178
|
+
(end ?? anchor).before(text);
|
|
179
|
+
} else if (text.nodeValue !== next_text) {
|
|
180
|
+
text.nodeValue = next_text;
|
|
181
|
+
}
|
|
182
|
+
} else if (anchor.nodeType === TEXT_NODE) {
|
|
183
|
+
/** @type {Text} */ (anchor).nodeValue = next_text;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
value = next_text;
|
|
187
|
+
is_element = false;
|
|
188
|
+
initialized = true;
|
|
189
|
+
if (end !== null) {
|
|
190
|
+
advance_hydration(end);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* @param {Node} anchor
|
|
197
|
+
* @returns {Comment}
|
|
198
|
+
*/
|
|
199
|
+
function ensure_expression_end(anchor) {
|
|
200
|
+
if (hydrating) {
|
|
201
|
+
/** @type {Node | null} */
|
|
202
|
+
var current = get_next_sibling(anchor);
|
|
203
|
+
var depth = 0;
|
|
204
|
+
|
|
205
|
+
while (current !== null) {
|
|
206
|
+
if (current.nodeType === COMMENT_NODE) {
|
|
207
|
+
var data = /** @type {Comment} */ (current).data;
|
|
208
|
+
|
|
209
|
+
if (data === HYDRATION_START) {
|
|
210
|
+
depth += 1;
|
|
211
|
+
} else if (data === HYDRATION_END) {
|
|
212
|
+
if (depth === 0) {
|
|
213
|
+
return /** @type {Comment} */ (current);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
depth -= 1;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
current = get_next_sibling(current);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
throw new Error('Hydration mismatch: expected end marker for expression block');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
var end = document.createComment(HYDRATION_END);
|
|
227
|
+
/** @type {ChildNode} */ (anchor).after(end);
|
|
228
|
+
return end;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @param {Node} anchor
|
|
233
|
+
* @param {Node} end
|
|
234
|
+
* @returns {Text | null}
|
|
235
|
+
*/
|
|
236
|
+
function get_hydrated_text(anchor, end) {
|
|
237
|
+
var first = get_next_sibling(anchor);
|
|
238
|
+
|
|
239
|
+
if (first === end) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (first?.nodeType === TEXT_NODE && get_next_sibling(first) === end) {
|
|
244
|
+
return /** @type {Text} */ (first);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
clear_expression_range(anchor, end);
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* @param {Node} anchor
|
|
253
|
+
* @param {Node} end
|
|
254
|
+
* @returns {void}
|
|
255
|
+
*/
|
|
256
|
+
function clear_expression_range(anchor, end) {
|
|
257
|
+
var current = get_next_sibling(anchor);
|
|
258
|
+
|
|
259
|
+
while (current !== null && current !== end) {
|
|
260
|
+
var next = get_next_sibling(current);
|
|
261
|
+
/** @type {ChildNode} */ (current).remove();
|
|
262
|
+
current = next;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* @param {Comment} end
|
|
268
|
+
* @returns {void}
|
|
269
|
+
*/
|
|
270
|
+
function advance_hydration(end) {
|
|
271
|
+
if (!hydrating) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
var next = get_next_sibling(end);
|
|
276
|
+
|
|
277
|
+
if (next !== null) {
|
|
278
|
+
set_hydrate_node(next);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -105,6 +105,8 @@ export { script } from './script.js';
|
|
|
105
105
|
|
|
106
106
|
export { html } from './html.js';
|
|
107
107
|
|
|
108
|
+
export { expression } from './expression.js';
|
|
109
|
+
|
|
108
110
|
export { rpc } from './rpc.js';
|
|
109
111
|
|
|
110
112
|
export { tsx_compat } from './compat.js';
|
|
@@ -114,3 +116,5 @@ export { TRY_BLOCK, HMR } from './constants.js';
|
|
|
114
116
|
export { hmr } from './hmr.js';
|
|
115
117
|
|
|
116
118
|
export { pop, next } from './hydration.js';
|
|
119
|
+
|
|
120
|
+
export { ripple_element, normalize_children } from '../../element.js';
|
|
@@ -12,10 +12,11 @@ import {
|
|
|
12
12
|
set_hydrating,
|
|
13
13
|
set_hydrate_node,
|
|
14
14
|
} from './hydration.js';
|
|
15
|
+
import { is_ripple_element } from '../../element.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* @param {any} _
|
|
18
|
-
* @param {{ target: Element, children: (
|
|
19
|
+
* @param {{ target: Element, children: import('../../element.js').RippleElement }} props
|
|
19
20
|
* @returns {void}
|
|
20
21
|
*/
|
|
21
22
|
export function Portal(_, props) {
|
|
@@ -26,7 +27,7 @@ export function Portal(_, props) {
|
|
|
26
27
|
|
|
27
28
|
/** @type {Element | symbol} */
|
|
28
29
|
let target = UNINITIALIZED;
|
|
29
|
-
/** @type {(
|
|
30
|
+
/** @type {import('../../element.js').RippleElement | symbol} */
|
|
30
31
|
let children = UNINITIALIZED;
|
|
31
32
|
/** @type {Block | null} */
|
|
32
33
|
var b = null;
|
|
@@ -44,8 +45,13 @@ export function Portal(_, props) {
|
|
|
44
45
|
|
|
45
46
|
try {
|
|
46
47
|
render(() => {
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
const next_target = props.target;
|
|
49
|
+
const next_children = props.children;
|
|
50
|
+
|
|
51
|
+
if (target === next_target && children === next_children) return;
|
|
52
|
+
|
|
53
|
+
target = next_target;
|
|
54
|
+
children = next_children;
|
|
49
55
|
|
|
50
56
|
if (b !== null) {
|
|
51
57
|
destroy_block(b);
|
|
@@ -65,8 +71,8 @@ export function Portal(_, props) {
|
|
|
65
71
|
var block = /** @type {Block} */ (active_block);
|
|
66
72
|
|
|
67
73
|
b = branch(() => {
|
|
68
|
-
if (
|
|
69
|
-
children(/** @type {Text} */ (anchor),
|
|
74
|
+
if (is_ripple_element(children)) {
|
|
75
|
+
children.render(/** @type {Text} */ (anchor), block);
|
|
70
76
|
}
|
|
71
77
|
});
|
|
72
78
|
|
|
@@ -17,6 +17,7 @@ import { is_boolean_attribute } from '../../../compiler/utils.js';
|
|
|
17
17
|
import { clsx } from 'clsx';
|
|
18
18
|
import { normalize_css_property_name } from '../../../utils/normalize_css_property_name.js';
|
|
19
19
|
import { BLOCK_CLOSE, BLOCK_OPEN } from '../../../constants.js';
|
|
20
|
+
import { is_ripple_element, normalize_children, ripple_element } from '../../element.js';
|
|
20
21
|
import {
|
|
21
22
|
is_tag_valid_with_parent,
|
|
22
23
|
is_tag_valid_with_ancestor,
|
|
@@ -27,6 +28,30 @@ export { register_component_css as register_css } from './css-registry.js';
|
|
|
27
28
|
export { hash } from '../../../utils/hashing.js';
|
|
28
29
|
export { context } from './context.js';
|
|
29
30
|
export { array_slice };
|
|
31
|
+
export { ripple_element, normalize_children };
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {Output} output
|
|
35
|
+
* @param {any} value
|
|
36
|
+
* @returns {void}
|
|
37
|
+
*/
|
|
38
|
+
export function render_expression(output, value) {
|
|
39
|
+
output.push(BLOCK_OPEN);
|
|
40
|
+
|
|
41
|
+
if (is_ripple_element(value)) {
|
|
42
|
+
var result = value.render(output, {});
|
|
43
|
+
|
|
44
|
+
if (result && typeof result.then === 'function') {
|
|
45
|
+
return result.then(() => {
|
|
46
|
+
output.push(BLOCK_CLOSE);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
output.push(escape(value ?? ''));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
output.push(BLOCK_CLOSE);
|
|
54
|
+
}
|
|
30
55
|
|
|
31
56
|
/** @type {null | Component} */
|
|
32
57
|
export let active_component = null;
|
|
@@ -596,7 +621,7 @@ export function spread_attrs(attrs, css_hash) {
|
|
|
596
621
|
for (name in attrs) {
|
|
597
622
|
var value = attrs[name];
|
|
598
623
|
|
|
599
|
-
if (typeof value === 'function') continue;
|
|
624
|
+
if (name === 'children' || typeof value === 'function' || is_ripple_element(value)) continue;
|
|
600
625
|
|
|
601
626
|
if (is_ripple_object(value)) {
|
|
602
627
|
value = get(value);
|
package/src/utils/builders.js
CHANGED
|
@@ -513,6 +513,36 @@ export function ts_intersection_type(types, loc_info) {
|
|
|
513
513
|
return set_location(node, loc_info);
|
|
514
514
|
}
|
|
515
515
|
|
|
516
|
+
/**
|
|
517
|
+
* @param {'string' | 'number' | 'boolean' | 'any' | 'void' | 'null' | 'undefined' | 'never' | 'unknown' | 'bigint' | 'symbol' | 'object'} keyword
|
|
518
|
+
* @param {AST.NodeWithLocation} [loc_info]
|
|
519
|
+
* @returns {AST.TypeNode}
|
|
520
|
+
*/
|
|
521
|
+
export function ts_keyword_type(keyword, loc_info) {
|
|
522
|
+
/** @type {Record<string, string>} */
|
|
523
|
+
const keyword_to_type = {
|
|
524
|
+
string: 'TSStringKeyword',
|
|
525
|
+
number: 'TSNumberKeyword',
|
|
526
|
+
boolean: 'TSBooleanKeyword',
|
|
527
|
+
any: 'TSAnyKeyword',
|
|
528
|
+
void: 'TSVoidKeyword',
|
|
529
|
+
null: 'TSNullKeyword',
|
|
530
|
+
undefined: 'TSUndefinedKeyword',
|
|
531
|
+
never: 'TSNeverKeyword',
|
|
532
|
+
unknown: 'TSUnknownKeyword',
|
|
533
|
+
bigint: 'TSBigIntKeyword',
|
|
534
|
+
symbol: 'TSSymbolKeyword',
|
|
535
|
+
object: 'TSObjectKeyword',
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
const node = /** @type {AST.TypeNode} */ ({
|
|
539
|
+
type: keyword_to_type[keyword],
|
|
540
|
+
metadata: { path: [] },
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
return set_location(node, loc_info);
|
|
544
|
+
}
|
|
545
|
+
|
|
516
546
|
/**
|
|
517
547
|
* @param {AST.Node} type_annotation
|
|
518
548
|
* @param {AST.NodeWithLocation} [loc_info]
|