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.
Files changed (98) hide show
  1. package/CHANGELOG.md +164 -0
  2. package/package.json +10 -4
  3. package/src/constants.js +6 -0
  4. package/src/jsx-runtime.d.ts +2 -1
  5. package/src/runtime/array.js +15 -9
  6. package/src/runtime/index-client.js +25 -7
  7. package/src/runtime/index-server.js +1 -0
  8. package/src/runtime/internal/client/attributes.js +439 -0
  9. package/src/runtime/internal/client/blocks.js +20 -20
  10. package/src/runtime/internal/client/component.js +2 -5
  11. package/src/runtime/internal/client/composite.js +295 -95
  12. package/src/runtime/internal/client/constants.js +14 -6
  13. package/src/runtime/internal/client/context.js +3 -2
  14. package/src/runtime/internal/client/css.js +11 -26
  15. package/src/runtime/internal/client/errors.js +188 -0
  16. package/src/runtime/internal/client/events.js +79 -34
  17. package/src/runtime/internal/client/expression.js +25 -120
  18. package/src/runtime/internal/client/for.js +113 -262
  19. package/src/runtime/internal/client/head.js +4 -3
  20. package/src/runtime/internal/client/html.js +5 -3
  21. package/src/runtime/internal/client/hydrate.js +484 -0
  22. package/src/runtime/internal/client/hydration-enabled.js +7 -0
  23. package/src/runtime/internal/client/hydration.js +16 -81
  24. package/src/runtime/internal/client/if.js +69 -10
  25. package/src/runtime/internal/client/index.js +18 -10
  26. package/src/runtime/internal/client/operations.js +11 -7
  27. package/src/runtime/internal/client/portal.js +3 -2
  28. package/src/runtime/internal/client/render.js +42 -325
  29. package/src/runtime/internal/client/root-boundary-enabled.js +7 -0
  30. package/src/runtime/internal/client/runtime.js +157 -371
  31. package/src/runtime/internal/client/template-ns.js +88 -0
  32. package/src/runtime/internal/client/template.js +118 -139
  33. package/src/runtime/internal/client/track-async.js +356 -0
  34. package/src/runtime/internal/client/try.js +133 -319
  35. package/src/runtime/internal/client/types.d.ts +48 -24
  36. package/src/runtime/internal/server/index.js +31 -29
  37. package/src/runtime/internal/server/prerender.js +27 -0
  38. package/src/server/index.js +1 -0
  39. package/src/utils/class-name.js +28 -0
  40. package/tests/client/basic/basic.errors.test.tsrx +3 -3
  41. package/tests/client/compiler/__snapshots__/compiler.typescript.test.tsrx.snap +0 -2
  42. package/tests/client/compiler/compiler.basic.test.tsrx +2 -2
  43. package/tests/client/compiler/compiler.tracked-access.test.tsrx +3 -3
  44. package/tests/client/composite/composite.dynamic-components.test.tsrx +40 -0
  45. package/tests/client/composite/composite.props.test.tsrx +10 -10
  46. package/tests/client/css/scoped-styles.test.tsrx +3 -1
  47. package/tests/client/derived-release.test.tsrx +3 -3
  48. package/tests/client/for-item.test.tsrx +39 -0
  49. package/tests/client/for-selector.test.tsrx +95 -0
  50. package/tests/client/svg.test.tsrx +273 -1
  51. package/tests/hydration/compiled/client/basic.js +88 -87
  52. package/tests/hydration/compiled/client/composite.js +15 -6
  53. package/tests/hydration/compiled/client/events.js +40 -48
  54. package/tests/hydration/compiled/client/for.js +191 -199
  55. package/tests/hydration/compiled/client/fragment-cursor.js +633 -334
  56. package/tests/hydration/compiled/client/head.js +22 -22
  57. package/tests/hydration/compiled/client/hmr.js +14 -5
  58. package/tests/hydration/compiled/client/html-in-template.js +5 -5
  59. package/tests/hydration/compiled/client/html.js +195 -177
  60. package/tests/hydration/compiled/client/if-children.js +136 -63
  61. package/tests/hydration/compiled/client/if-fragment-controlflow.js +63 -43
  62. package/tests/hydration/compiled/client/if.js +78 -30
  63. package/tests/hydration/compiled/client/mixed-control-flow.js +90 -72
  64. package/tests/hydration/compiled/client/nested-control-flow.js +266 -307
  65. package/tests/hydration/compiled/client/portal.js +20 -11
  66. package/tests/hydration/compiled/client/reactivity.js +29 -29
  67. package/tests/hydration/compiled/client/return.js +2 -2
  68. package/tests/hydration/compiled/client/streaming.js +33 -35
  69. package/tests/hydration/compiled/client/switch.js +25 -25
  70. package/tests/hydration/compiled/client/track-async-serialization.js +41 -41
  71. package/tests/hydration/compiled/client/try.js +15 -15
  72. package/tests/hydration/compiled/client/typed-text.js +8 -7
  73. package/tests/hydration/compiled/server/basic.js +6 -6
  74. package/tests/hydration/compiled/server/events.js +8 -8
  75. package/tests/hydration/compiled/server/for.js +22 -22
  76. package/tests/hydration/compiled/server/fragment-cursor.js +188 -52
  77. package/tests/hydration/compiled/server/head.js +6 -6
  78. package/tests/hydration/compiled/server/hmr.js +1 -1
  79. package/tests/hydration/compiled/server/html.js +1 -1
  80. package/tests/hydration/compiled/server/if-children.js +9 -9
  81. package/tests/hydration/compiled/server/if.js +5 -5
  82. package/tests/hydration/compiled/server/mixed-control-flow.js +4 -4
  83. package/tests/hydration/compiled/server/portal.js +1 -1
  84. package/tests/hydration/compiled/server/reactivity.js +9 -9
  85. package/tests/hydration/compiled/server/streaming.js +9 -9
  86. package/tests/hydration/compiled/server/switch.js +4 -4
  87. package/tests/hydration/compiled/server/track-async-serialization.js +16 -16
  88. package/tests/hydration/compiled/server/try.js +4 -4
  89. package/tests/hydration/compiled/server/typed-text.js +1 -1
  90. package/tests/hydration/components/fragment-cursor.tsrx +60 -0
  91. package/tests/hydration/components/track-async-serialization.tsrx +2 -2
  92. package/tests/hydration/fragment-cursor.test.js +34 -1
  93. package/tests/hydration/track-async-serialization.test.js +1 -1
  94. package/tests/server/basic.components.test.tsrx +3 -3
  95. package/tests/server/track-async-serialization.test.tsrx +6 -6
  96. package/tests/utils/tracked-types.test.js +3 -3
  97. package/types/index.d.ts +8 -8
  98. package/types/server.d.ts +17 -0
@@ -1,123 +1,323 @@
1
- /** @import { Block } from '#client' */
1
+ /** @import { AppendIntoAnchor, Block } from '#client' */
2
+ /** @import { SpreadState } from './attributes.js' */
3
+ import { component_invalid } from './errors.js';
2
4
 
3
- import { branch, destroy_block, render, render_spread } from './blocks.js';
4
- import { COMPOSITE_BLOCK, DEFAULT_NAMESPACE, NAMESPACE_URI } from './constants.js';
5
+ import { branch, destroy_block, destroy_block_children, render } from './blocks.js';
6
+ import { release_spread, spread } from './attributes.js';
7
+ import { COMPOSITE_BLOCK, DEFAULT_NAMESPACE, NAMESPACE_URI, UNINITIALIZED } from './constants.js';
5
8
  import { hydrate_node, hydrate_next, hydrating, set_hydrate_node } from './hydration.js';
6
9
  import { first_child } from './operations.js';
7
- import { active_block, active_namespace, get, untrack, with_ns } from './runtime.js';
10
+ import { active_block, active_namespace, get, set_ns, set_tracking, untrack } from './runtime.js';
8
11
  import { top_element_to_ns } from './utils.js';
12
+ import { push_ns, with_ns } from './template-ns.js';
13
+ import { append } from './template.js';
9
14
  import { is_tsrx_element } from '../../element.js';
10
15
  import { render_component } from './component.js';
16
+ import { HYDRATION } from 'ripple/internal/client/hydration-enabled';
11
17
 
12
18
  /**
13
19
  * @typedef {Function | string | null | undefined | false} CompositeTarget
20
+ * The state of a composite block. A tag target is the block's own DOM range
21
+ * (`start`/`end`, the element) and its attributes the block's own spread; a
22
+ * component target renders in a branch, a child of the block.
23
+ * @typedef {{
24
+ * start: Element | null;
25
+ * end: Element | null;
26
+ * g: () => CompositeTarget;
27
+ * a: Node | AppendIntoAnchor;
28
+ * p: () => Record<string, any>;
29
+ * c: CompositeTarget | typeof UNINITIALIZED;
30
+ * s: SpreadState | undefined;
31
+ * }} CompositeState
32
+ */
33
+
34
+ /**
35
+ * @param {CompositeState} state
36
+ */
37
+ function run_composite(state) {
38
+ var block = /** @type {Block} */ (active_block);
39
+ // @ts-ignore — get() handles non-tracked values via is_ripple_object() check
40
+ var component = get(state.g());
41
+ var current = state.c;
42
+ var old = state.start;
43
+
44
+ if (component === current && typeof component === 'string') {
45
+ // The same tag keeps its element: only the attributes follow the props.
46
+ state.s = spread(/** @type {Element} */ (old), state.p(), state.s);
47
+ return;
48
+ }
49
+
50
+ if (current !== UNINITIALIZED) {
51
+ // The previous target's blocks: the children rendered into the element,
52
+ // whose DOM goes with it, or a component's branch, which removes its own.
53
+ destroy_block_children(block, old === null);
54
+ state.s = undefined;
55
+ }
56
+ state.c = component;
57
+
58
+ if (typeof component === 'function') {
59
+ if (old !== null) {
60
+ old.remove();
61
+ state.start = state.end = null;
62
+ }
63
+ var anchor = state.a;
64
+ var get_props = state.p;
65
+ // Handle as regular component
66
+ branch(() => {
67
+ var props = untrack(get_props);
68
+ render_component(component, /** @type {Node} */ (anchor), props);
69
+ });
70
+ return;
71
+ }
72
+ if (is_tsrx_element(component)) {
73
+ component_invalid(true);
74
+ }
75
+ if (component == null) {
76
+ if (old !== null) {
77
+ old.remove();
78
+ state.start = state.end = null;
79
+ }
80
+ return;
81
+ }
82
+
83
+ // A tag: the element is created in the namespace the tag resolves to.
84
+ var ns = top_element_to_ns(component, active_namespace);
85
+ /** @type {Element} */
86
+ var element;
87
+ if (HYDRATION && hydrating) {
88
+ // Claim the SSR-rendered element instead of creating a new one.
89
+ element = /** @type {Element} */ (hydrate_node);
90
+ } else {
91
+ element =
92
+ ns !== DEFAULT_NAMESPACE
93
+ ? document.createElementNS(
94
+ NAMESPACE_URI[ns],
95
+ /** @type {keyof HTMLElementTagNameMap} */ (component),
96
+ )
97
+ : document.createElement(/** @type {keyof HTMLElementTagNameMap} */ (component));
98
+ if (old !== null) {
99
+ // A tag change takes the previous element's place.
100
+ old.before(element);
101
+ old.remove();
102
+ } else {
103
+ append(/** @type {ChildNode | AppendIntoAnchor} */ (state.a), element);
104
+ }
105
+ }
106
+ state.start = state.end = element;
107
+
108
+ // The spread reads the props once, tracked: a props change re-runs the
109
+ // block, which diffs the attributes above. The children come from that
110
+ // same read.
111
+ var spread_state = (state.s = spread(element, state.p(), undefined));
112
+ var props = /** @type {Record<string, any>} */ (spread_state.n);
113
+ if (is_tsrx_element(props.children)) {
114
+ /** @type {Node} */
115
+ var child_anchor;
116
+ if (HYDRATION && hydrating) {
117
+ // The server renders children directly inside the element with no
118
+ // extra markers; descend the cursor so they claim those nodes.
119
+ child_anchor = /** @type {Node} */ (first_child(element));
120
+ } else {
121
+ child_anchor = document.createComment('');
122
+ element.appendChild(child_anchor);
123
+ }
124
+
125
+ // The children render untracked, as a branch renders its content. The
126
+ // children of a `foreignObject` are HTML whatever namespace the element
127
+ // itself takes.
128
+ var child_ns = component === 'foreignObject' ? DEFAULT_NAMESPACE : ns;
129
+ set_tracking(false);
130
+ if (child_ns !== active_namespace) {
131
+ with_ns(child_ns, () => props.children.render(child_anchor, block, props.children.p));
132
+ } else {
133
+ props.children.render(child_anchor, block, props.children.p);
134
+ }
135
+ set_tracking(true);
136
+
137
+ if (HYDRATION && hydrating) {
138
+ // Reset the cursor to the claimed element so sibling traversal
139
+ // continues after it.
140
+ set_hydrate_node(element);
141
+ }
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Renders a dynamic element or component (`<{expr}>`): the block re-runs
147
+ * when the expression or the props change. A tag that stays the same keeps
148
+ * its element and diffs the attributes; a new tag or component replaces the
149
+ * content in place.
14
150
  * @param {() => CompositeTarget} get_component
15
- * @param {Node} node
151
+ * @param {Node | AppendIntoAnchor} node
16
152
  * @param {() => Record<string, any>} get_props the props literal of the call
17
153
  * site: read once per rendered component (props are plain values), and
18
154
  * re-read as element attributes when the target is a tag
155
+ * @param {keyof typeof NAMESPACE_URI} [namespace] the namespace of the
156
+ * template the call site sits in, when it is not HTML
19
157
  * @returns {void}
20
158
  */
21
- export function composite(get_component, node, get_props) {
22
- if (hydrating) {
159
+ export function composite(get_component, node, get_props, namespace) {
160
+ if (HYDRATION && hydrating) {
23
161
  // During hydration, `node` may already point at the first real SSR node
24
162
  // (e.g. layout children). Only skip forward when we are on an empty
25
163
  // comment anchor from a client template placeholder.
26
- if (node.nodeType === 8 && /** @type {Comment} */ (node).data === '') {
164
+ if (/** @type {Node} */ (node).nodeType === 8 && /** @type {Comment} */ (node).data === '') {
27
165
  hydrate_next();
28
166
  }
29
167
  }
30
168
 
31
- var anchor = node;
32
- /** @type {Block | null} */
33
- var b = null;
169
+ /** @type {CompositeState} */
170
+ var state = {
171
+ start: null,
172
+ end: null,
173
+ g: get_component,
174
+ a: node,
175
+ p: get_props,
176
+ c: UNINITIALIZED,
177
+ s: undefined,
178
+ };
179
+ if (namespace === undefined) {
180
+ render(run_composite, state, COMPOSITE_BLOCK);
181
+ return;
182
+ }
183
+ var previous = push_ns(namespace);
184
+ try {
185
+ render(run_composite, state, COMPOSITE_BLOCK);
186
+ } finally {
187
+ set_ns(previous);
188
+ }
189
+ }
34
190
 
35
- render(
36
- () => {
37
- // @ts-ignore — get() handles non-tracked values via is_ripple_object() check
38
- var component = get(get_component());
191
+ /**
192
+ * The state of a dynamic element the enclosing render function drives (see
193
+ * `dynamic`): the element rendered for a tag, or the branch rendered for a
194
+ * component, and the tag or component itself.
195
+ * @typedef {{
196
+ * e: Element | null;
197
+ * b: Block | null;
198
+ * t: CompositeTarget | typeof UNINITIALIZED;
199
+ * s: SpreadState | undefined;
200
+ * g: (() => CompositeTarget) | null;
201
+ * p: (() => Record<string, any>) | null;
202
+ * }} DynamicState
203
+ */
39
204
 
40
- if (b !== null) {
41
- destroy_block(b);
42
- b = null;
205
+ /**
206
+ * A dynamic element without children, applied from the render function of
207
+ * the content it sits in instead of a block of its own: `<{tag} {...attrs} />`
208
+ * compiles to `__prev.x = _$_.dynamic(__prev.x, anchor, tag, props)`, so the
209
+ * tag and the props are read by that render function, and a run whose tag is
210
+ * unchanged only diffs the attributes. The element sits in the enclosing
211
+ * template's range, which owns it. A component target is handed to a
212
+ * composite block of its own (in a branch the render function keeps), which
213
+ * reads the tag and the props through the thunks kept since creation and so
214
+ * remounts the component with fresh props whenever the tag expression
215
+ * re-evaluates, exactly as `<{expr}>` did before; from then on the slot is
216
+ * null and the calls are no-ops.
217
+ * @param {DynamicState | null | undefined} state null once a composite block
218
+ * owns the content (a component target, or the hydration claim, see
219
+ * `dynamic_init`)
220
+ * @param {Node | AppendIntoAnchor} anchor
221
+ * @param {CompositeTarget} tag
222
+ * @param {Record<string, any> | null | undefined} props
223
+ * @param {keyof typeof NAMESPACE_URI} [namespace] the namespace of the
224
+ * template the call site sits in, when it is not HTML
225
+ * @returns {DynamicState | null}
226
+ */
227
+ export function dynamic(state, anchor, tag, props, namespace) {
228
+ if (state === null) {
229
+ return null;
230
+ }
231
+ // @ts-ignore — get() handles non-tracked values via is_ripple_object() check
232
+ tag = get(tag);
233
+ /** @type {DynamicState} */
234
+ var s;
235
+ if (state === undefined) {
236
+ s = { e: null, b: null, t: UNINITIALIZED, s: undefined, g: null, p: null };
237
+ } else {
238
+ s = state;
239
+ if (tag === s.t) {
240
+ if (s.e !== null) {
241
+ s.s = spread(s.e, props, s.s);
43
242
  }
243
+ return s;
244
+ }
245
+ }
44
246
 
45
- if (typeof component === 'function') {
46
- // Handle as regular component
47
- b = branch(() => {
48
- var props = untrack(get_props);
49
- render_component(component, anchor, props);
50
- });
51
- } else if (is_tsrx_element(component)) {
52
- throw new TypeError('Invalid component type: received a TSRXElement value.');
53
- } else if (component != null) {
54
- // Custom element - only create if component is not null/undefined
55
- const ns = top_element_to_ns(component, active_namespace);
56
- var run = () => {
57
- var block = /** @type {Block} */ (active_block);
58
-
59
- /** @type {Element} */
60
- var element;
61
- if (hydrating) {
62
- // Claim the SSR-rendered element instead of creating a new one.
63
- element = /** @type {Element} */ (hydrate_node);
64
- } else {
65
- element =
66
- ns !== DEFAULT_NAMESPACE
67
- ? document.createElementNS(
68
- NAMESPACE_URI[ns],
69
- /** @type {keyof HTMLElementTagNameMap} */ (component),
70
- )
71
- : document.createElement(/** @type {keyof HTMLElementTagNameMap} */ (component));
72
-
73
- /** @type {ChildNode} */ (anchor).before(element);
74
- }
75
-
76
- if (block.s === null) {
77
- block.s = {
78
- start: element,
79
- end: element,
80
- };
81
- }
82
-
83
- render_spread(element, get_props, 0);
84
-
85
- var props = untrack(get_props);
86
- if (is_tsrx_element(props.children)) {
87
- /** @type {Node} */
88
- var child_anchor;
89
- if (hydrating) {
90
- // The server renders children directly inside the element with no
91
- // extra markers; descend the cursor so they claim those nodes.
92
- child_anchor = /** @type {Node} */ (first_child(element));
93
- } else {
94
- child_anchor = document.createComment('');
95
- element.appendChild(child_anchor);
96
- }
97
-
98
- if (ns !== DEFAULT_NAMESPACE) {
99
- with_ns(ns, () => props.children.render(child_anchor, block, props.children.p));
100
- } else {
101
- props.children.render(child_anchor, block, props.children.p);
102
- }
103
-
104
- if (hydrating) {
105
- // Reset the cursor to the claimed element so sibling traversal
106
- // continues after it.
107
- set_hydrate_node(element);
108
- }
109
- }
110
- };
111
-
112
- if (ns !== active_namespace) {
113
- // support top-level dynamic element svg/math tags
114
- b = branch(() => with_ns(ns, run));
115
- } else {
116
- b = branch(run);
117
- }
118
- }
119
- },
120
- null,
121
- COMPOSITE_BLOCK,
247
+ var old = s.e;
248
+ if (s.b !== null) {
249
+ destroy_block(s.b);
250
+ s.b = null;
251
+ }
252
+ // The previous element's refs and listeners go with it.
253
+ release_spread(s.s);
254
+ s.t = tag;
255
+ s.s = undefined;
256
+
257
+ if (typeof tag === 'function') {
258
+ if (old !== null) {
259
+ old.remove();
260
+ s.e = null;
261
+ }
262
+ const get_component = s.g;
263
+ const get_props = s.p;
264
+ if (get_component !== null && get_props !== null) {
265
+ branch(() => composite(get_component, anchor, get_props, namespace));
266
+ return null;
267
+ }
268
+ // No thunks (a call site compiled without `dynamic_init`): the
269
+ // component renders once with these props.
270
+ s.b = branch(() => {
271
+ render_component(tag, /** @type {Node} */ (anchor), props ?? {});
272
+ });
273
+ return s;
274
+ }
275
+ if (is_tsrx_element(tag)) {
276
+ component_invalid(true);
277
+ }
278
+ if (tag == null || tag === false) {
279
+ if (old !== null) {
280
+ old.remove();
281
+ s.e = null;
282
+ }
283
+ return s;
284
+ }
285
+
286
+ var ns = top_element_to_ns(
287
+ /** @type {Parameters<typeof top_element_to_ns>[0]} */ (tag),
288
+ namespace === undefined ? active_namespace : namespace,
122
289
  );
290
+ var element =
291
+ ns !== DEFAULT_NAMESPACE
292
+ ? document.createElementNS(NAMESPACE_URI[ns], /** @type {string} */ (tag))
293
+ : document.createElement(/** @type {string} */ (tag));
294
+ if (old !== null) {
295
+ old.before(element);
296
+ old.remove();
297
+ } else {
298
+ append(/** @type {ChildNode | AppendIntoAnchor} */ (anchor), element);
299
+ }
300
+ s.e = element;
301
+ s.s = spread(element, props, undefined);
302
+ return s;
303
+ }
304
+
305
+ /**
306
+ * The initial state of a `dynamic` slot: the tag and props thunks a
307
+ * component target is handed over with. While hydrating, the element is
308
+ * rendered here by a composite block of its own, which claims its server
309
+ * nodes in document order and owns the content from then on, so the render
310
+ * function's `dynamic` calls are no-ops (null).
311
+ * @param {Node | AppendIntoAnchor} node
312
+ * @param {() => CompositeTarget} get_component
313
+ * @param {() => Record<string, any>} get_props
314
+ * @param {keyof typeof NAMESPACE_URI} [namespace]
315
+ * @returns {DynamicState | null}
316
+ */
317
+ export function dynamic_init(node, get_component, get_props, namespace) {
318
+ if (HYDRATION && hydrating) {
319
+ composite(get_component, node, get_props, namespace);
320
+ return null;
321
+ }
322
+ return { e: null, b: null, t: UNINITIALIZED, s: undefined, g: get_component, p: get_props };
123
323
  }
@@ -35,11 +35,19 @@ export var CREATES_DERIVEDS = 1 << 25;
35
35
  * re-runs the body's update function.
36
36
  */
37
37
  export var ITEM_BLOCK = 1 << 26;
38
+ /**
39
+ * The namespace a block was created in, so a rerun outside the `with_ns()`
40
+ * call that established it (a scheduled flush) creates its DOM in the same
41
+ * namespace. Neither bit set means HTML.
42
+ */
43
+ export var SVG_BLOCK = 1 << 27;
44
+ export var MATHML_BLOCK = 1 << 28;
45
+ export var NAMESPACE_BLOCK = SVG_BLOCK | MATHML_BLOCK;
38
46
 
39
47
  export var CONTROL_FLOW_BLOCK = FOR_BLOCK | IF_BLOCK | SWITCH_BLOCK | TRY_BLOCK | COMPOSITE_BLOCK;
40
48
 
41
49
  /** @type {unique symbol} */
42
- export const UNINITIALIZED = Symbol('uninitialized');
50
+ export const UNINITIALIZED = Symbol();
43
51
  /** @type {unique symbol} */
44
52
  export const TRACKED_ARRAY = Symbol();
45
53
  /** @type {unique symbol} */
@@ -58,13 +66,13 @@ export const NAMESPACE_URI = {
58
66
  mathml: 'http://www.w3.org/1998/Math/MathML',
59
67
  };
60
68
  /** @type {unique symbol} */
61
- export const TRACKED_UPDATED = Symbol('TRACKED_UPDATED');
69
+ export const TRACKED_UPDATED = Symbol();
62
70
  /** @type {unique symbol} */
63
- export const SUSPENSE_PENDING = Symbol('suspense_pending');
71
+ export const SUSPENSE_PENDING = Symbol();
64
72
  /** @type {unique symbol} */
65
- export const SUSPENSE_REJECTED = Symbol('suspense_rejected');
73
+ export const SUSPENSE_REJECTED = Symbol();
66
74
  /** @type {unique symbol} */
67
- export const ASYNC_DERIVED_READ_THROWN = Symbol('async_derived_read_thrown');
75
+ export const ASYNC_DERIVED_READ_THROWN = Symbol();
68
76
 
69
77
  /** The hoisted render function a module-level component carries (see `render_component`). */
70
- export const RENDER_ENTRY = Symbol('render');
78
+ export const RENDER_ENTRY = Symbol();
@@ -1,4 +1,5 @@
1
1
  /** @import { Component, ContextEntry } from '#client' */
2
+ import { context_orphan } from './errors.js';
2
3
 
3
4
  import { active_component } from './runtime.js';
4
5
 
@@ -18,7 +19,7 @@ export class Context {
18
19
  const component = active_component;
19
20
 
20
21
  if (component === null) {
21
- throw new Error('No active component found, cannot get context');
22
+ context_orphan(false);
22
23
  }
23
24
 
24
25
  // A component inherits its parent's chain of set entries when it is
@@ -45,7 +46,7 @@ export class Context {
45
46
  const component = active_component;
46
47
 
47
48
  if (component === null) {
48
- throw new Error('No active component found, cannot set context');
49
+ context_orphan(true);
49
50
  }
50
51
 
51
52
  component.c = { k: this, v: value, n: component.c };
@@ -24,49 +24,34 @@ function remove() {
24
24
  }
25
25
 
26
26
  /**
27
- * @param {function} callback
27
+ * Calls back once every stylesheet link has loaded (or failed).
28
+ * @param {() => void} callback
28
29
  * @returns {void}
29
30
  */
30
31
  function remove_when_css_loaded(callback) {
31
- /** @type {HTMLLinkElement[]} */
32
- const links = Array.from(
33
- /** @type {NodeListOf<HTMLLinkElement>} */ (
34
- document.querySelectorAll('link[rel="stylesheet"]')
35
- ),
32
+ var links = /** @type {NodeListOf<HTMLLinkElement>} */ (
33
+ document.querySelectorAll('link[rel="stylesheet"]')
36
34
  );
37
- let remaining = links.length;
35
+ var remaining = links.length;
38
36
 
39
37
  if (remaining === 0) {
40
38
  callback();
41
39
  return;
42
40
  }
43
41
 
44
- const done = () => {
45
- remaining--;
46
- if (remaining === 0) {
47
- // clean up all listeners
48
- links.forEach((link) => {
49
- link.removeEventListener('load', onLoad);
50
- link.removeEventListener('error', onError);
51
- });
42
+ var done = () => {
43
+ if (--remaining === 0) {
52
44
  callback();
53
45
  }
54
46
  };
55
47
 
56
- function onLoad() {
57
- done();
58
- }
59
- function onError() {
60
- done();
61
- }
62
-
63
- links.forEach((link) => {
48
+ for (var link of links) {
64
49
  if (link.sheet) {
65
50
  // already loaded (possibly cached)
66
51
  done();
67
52
  } else {
68
- link.addEventListener('load', onLoad);
69
- link.addEventListener('error', onError);
53
+ link.addEventListener('load', done, { once: true });
54
+ link.addEventListener('error', done, { once: true });
70
55
  }
71
- });
56
+ }
72
57
  }