ripple 0.4.1 → 0.4.3
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 +162 -0
- package/package.json +9 -3
- package/src/constants.js +4 -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 +313 -0
- package/src/runtime/internal/client/blocks.js +4 -14
- package/src/runtime/internal/client/component.js +2 -5
- package/src/runtime/internal/client/composite.js +14 -6
- package/src/runtime/internal/client/constants.js +12 -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 +21 -116
- package/src/runtime/internal/client/for.js +272 -222
- 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 -97
- package/src/runtime/internal/client/if.js +13 -9
- package/src/runtime/internal/client/index.js +16 -8
- package/src/runtime/internal/client/operations.js +12 -9
- package/src/runtime/internal/client/portal.js +3 -2
- package/src/runtime/internal/client/render.js +75 -321
- package/src/runtime/internal/client/root-boundary-enabled.js +7 -0
- package/src/runtime/internal/client/runtime.js +129 -362
- package/src/runtime/internal/client/template-ns.js +47 -0
- package/src/runtime/internal/client/template.js +63 -140
- 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 +38 -33
- 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 +4 -4
- package/tests/client/compiler/compiler.tracked-access.test.tsrx +3 -3
- package/tests/client/composite/composite.props.test.tsrx +10 -10
- package/tests/client/derived-release.test.tsrx +3 -3
- package/tests/client/for-item.test.tsrx +443 -0
- package/tests/client/for-selector.test.tsrx +95 -0
- package/tests/hydration/compiled/client/basic.js +97 -123
- package/tests/hydration/compiled/client/composite.js +5 -5
- package/tests/hydration/compiled/client/events.js +84 -79
- package/tests/hydration/compiled/client/for.js +229 -233
- package/tests/hydration/compiled/client/fragment-cursor.js +781 -481
- package/tests/hydration/compiled/client/head.js +23 -26
- package/tests/hydration/compiled/client/hmr.js +4 -4
- package/tests/hydration/compiled/client/html-in-template.js +5 -5
- package/tests/hydration/compiled/client/html.js +174 -208
- package/tests/hydration/compiled/client/if-children.js +71 -70
- package/tests/hydration/compiled/client/if-fragment-controlflow.js +46 -46
- package/tests/hydration/compiled/client/if.js +26 -26
- package/tests/hydration/compiled/client/mixed-control-flow.js +63 -105
- package/tests/hydration/compiled/client/nested-control-flow.js +170 -390
- package/tests/hydration/compiled/client/portal.js +10 -10
- package/tests/hydration/compiled/client/reactivity.js +36 -50
- package/tests/hydration/compiled/client/return.js +2 -2
- package/tests/hydration/compiled/client/streaming.js +34 -40
- 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 -19
- package/tests/hydration/compiled/server/basic.js +6 -6
- package/tests/hydration/compiled/server/events.js +13 -61
- package/tests/hydration/compiled/server/for.js +23 -31
- package/tests/hydration/compiled/server/fragment-cursor.js +214 -366
- 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/basic.test.tsrx +23 -0
- 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
|
@@ -4,13 +4,7 @@
|
|
|
4
4
|
/** @typedef {DerivedValue} Derived */
|
|
5
5
|
|
|
6
6
|
import { DEV } from 'esm-env';
|
|
7
|
-
import {
|
|
8
|
-
destroy_block,
|
|
9
|
-
destroy_non_branch_children,
|
|
10
|
-
effect,
|
|
11
|
-
pause_block,
|
|
12
|
-
pre_effect,
|
|
13
|
-
} from './blocks.js';
|
|
7
|
+
import { destroy_block, destroy_non_branch_children, effect } from './blocks.js';
|
|
14
8
|
import {
|
|
15
9
|
ASYNC_DERIVED_READ_THROWN,
|
|
16
10
|
BLOCK_HAS_RUN,
|
|
@@ -28,29 +22,18 @@ import {
|
|
|
28
22
|
UNINITIALIZED,
|
|
29
23
|
REF_PROP,
|
|
30
24
|
DEFAULT_NAMESPACE,
|
|
31
|
-
TRACKED_UPDATED,
|
|
32
25
|
SUSPENSE_PENDING,
|
|
33
26
|
SUSPENSE_REJECTED,
|
|
34
|
-
TRY_BLOCK,
|
|
35
|
-
DIRECT_CHILD_BLOCK,
|
|
36
27
|
SCHEDULED,
|
|
37
28
|
SELECTOR,
|
|
38
29
|
IF_BLOCK,
|
|
30
|
+
ITEM_BLOCK,
|
|
39
31
|
RELEASED,
|
|
40
32
|
RENDER_ENTRY,
|
|
41
33
|
CREATES_DERIVEDS,
|
|
42
34
|
} from './constants.js';
|
|
43
|
-
import {
|
|
44
|
-
begin_boundary_request,
|
|
45
|
-
complete_boundary_request,
|
|
46
|
-
get_boundary_with_catch,
|
|
47
|
-
get_pending_boundary,
|
|
48
|
-
handle_boundary_error,
|
|
49
|
-
register_boundary_deferred,
|
|
50
|
-
register_boundary_paused_block,
|
|
51
|
-
replace_boundary_request,
|
|
52
|
-
} from './try.js';
|
|
53
35
|
import { is_ripple_object } from './utils.js';
|
|
36
|
+
import { scope_orphan, set_in_derived, track_orphan, update_depth_exceeded } from './errors.js';
|
|
54
37
|
import { render_value } from './expression.js';
|
|
55
38
|
import { throw_invalid_component_type } from './component.js';
|
|
56
39
|
|
|
@@ -60,10 +43,8 @@ import {
|
|
|
60
43
|
is_array,
|
|
61
44
|
object_keys,
|
|
62
45
|
} from '@tsrx/core/runtime/language-helpers';
|
|
63
|
-
import { get_async_track_result } from '../../../utils/async.js';
|
|
64
|
-
import { get_track_async_script_id } from '../../../utils/track-async-serialization.js';
|
|
65
|
-
import { revive } from './transport.js';
|
|
66
46
|
import { hydrating, track_hash_reference } from './hydration.js';
|
|
47
|
+
import { HYDRATION } from 'ripple/internal/client/hydration-enabled';
|
|
67
48
|
import { create_ref_prop as create_core_ref_prop } from '@tsrx/core/runtime/ref';
|
|
68
49
|
|
|
69
50
|
const FLUSH_MICROTASK = 0;
|
|
@@ -129,7 +110,7 @@ let flush_count = 0;
|
|
|
129
110
|
/** @type {(() => void)[]} */
|
|
130
111
|
var queued_post_block_flush = [];
|
|
131
112
|
/** @type {null | Dependency} */
|
|
132
|
-
let active_dependency = null;
|
|
113
|
+
export let active_dependency = null;
|
|
133
114
|
|
|
134
115
|
export let tracking = false;
|
|
135
116
|
export let teardown = false;
|
|
@@ -169,6 +150,28 @@ export function set_tracking(value) {
|
|
|
169
150
|
tracking = value;
|
|
170
151
|
}
|
|
171
152
|
|
|
153
|
+
/**
|
|
154
|
+
* @param {boolean} value
|
|
155
|
+
*/
|
|
156
|
+
export function set_mutating_allowed(value) {
|
|
157
|
+
is_mutating_allowed = value;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Handles a block whose run read a pending async value (see
|
|
162
|
+
* `track-async.js`). Installed by the first `trackAsync()`, so an application
|
|
163
|
+
* without one carries none of the boundary request machinery.
|
|
164
|
+
* @type {((block: Block) => void) | null}
|
|
165
|
+
*/
|
|
166
|
+
let pending_read = null;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @param {(block: Block) => void} fn
|
|
170
|
+
*/
|
|
171
|
+
export function set_pending_read_handler(fn) {
|
|
172
|
+
pending_read = fn;
|
|
173
|
+
}
|
|
174
|
+
|
|
172
175
|
/**
|
|
173
176
|
* @param {Block} block
|
|
174
177
|
*/
|
|
@@ -235,7 +238,7 @@ function update_derived(computed) {
|
|
|
235
238
|
* @param {Tracked} tracked
|
|
236
239
|
* @param {any} value
|
|
237
240
|
*/
|
|
238
|
-
function update_tracked_value_clock(tracked, value) {
|
|
241
|
+
export function update_tracked_value_clock(tracked, value) {
|
|
239
242
|
tracked.__v = value;
|
|
240
243
|
tracked.c = increment_clock();
|
|
241
244
|
mark_subscribers(tracked);
|
|
@@ -305,76 +308,51 @@ function run_derived(computed) {
|
|
|
305
308
|
}
|
|
306
309
|
}
|
|
307
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Routes an error thrown by a block into the nearest boundary with a catch
|
|
313
|
+
* branch (see `try.js`). Installed by the first `try_block`, so a bundle whose
|
|
314
|
+
* mount and components create no boundary carries no catch machinery.
|
|
315
|
+
* @type {((error: unknown, block: Block) => BlockWithTryBoundaryAndCatch | null) | null}
|
|
316
|
+
*/
|
|
317
|
+
let catch_router = null;
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* @param {(error: unknown, block: Block) => BlockWithTryBoundaryAndCatch | null} fn
|
|
321
|
+
*/
|
|
322
|
+
export function set_catch_router(fn) {
|
|
323
|
+
catch_router = fn;
|
|
324
|
+
}
|
|
325
|
+
|
|
308
326
|
/**
|
|
309
327
|
* @param {unknown} error
|
|
310
328
|
* @param {Block} block
|
|
311
329
|
* @returns {BlockWithTryBoundaryAndCatch}
|
|
312
330
|
*/
|
|
313
331
|
export function handle_error(error, block) {
|
|
314
|
-
var boundary_with_catch =
|
|
315
|
-
if (boundary_with_catch
|
|
316
|
-
|
|
317
|
-
return boundary_with_catch;
|
|
332
|
+
var boundary_with_catch = catch_router === null ? null : catch_router(error, block);
|
|
333
|
+
if (boundary_with_catch === null) {
|
|
334
|
+
throw error;
|
|
318
335
|
}
|
|
319
|
-
|
|
320
|
-
throw error;
|
|
336
|
+
return boundary_with_catch;
|
|
321
337
|
}
|
|
322
338
|
|
|
323
339
|
/**
|
|
324
340
|
* The error path of {@link run_block}, kept out of that hot function so a
|
|
325
341
|
* healthy mount never compiles it: routes real errors to the nearest catch
|
|
326
|
-
* boundary and
|
|
327
|
-
*
|
|
342
|
+
* boundary, and a read of a pending async value to the handler `trackAsync`
|
|
343
|
+
* installs (the read is registered as a dependency, so the block re-runs once
|
|
344
|
+
* the value settles).
|
|
328
345
|
* @param {unknown} error
|
|
329
346
|
* @param {Block} block
|
|
330
347
|
*/
|
|
331
348
|
function handle_run_error(error, block) {
|
|
332
|
-
var is_component_direct = false;
|
|
333
|
-
var is_try_fn_block = false;
|
|
334
349
|
finish_dependencies(block, active_dependency);
|
|
335
|
-
// When a derived read throws ASYNC_DERIVED_READ_THROWN, it means the
|
|
336
|
-
// derived is still SUSPENSE_PENDING. The dependency was already registered,
|
|
337
|
-
// so we swallow the throw and let the parent continue processing. When
|
|
338
|
-
// the derived settles, the block will be dirty and rerun automatically.
|
|
339
350
|
if (error !== ASYNC_DERIVED_READ_THROWN) {
|
|
340
351
|
handle_error(error, block);
|
|
341
|
-
} else if (
|
|
342
|
-
|
|
343
|
-
(is_component_direct = active_component?.b === block) ||
|
|
344
|
-
(is_try_fn_block =
|
|
345
|
-
block.p !== null && (block.p.f & TRY_BLOCK) !== 0 && (block.f & DIRECT_CHILD_BLOCK) !== 0)
|
|
346
|
-
) {
|
|
347
|
-
throw new Error(
|
|
348
|
-
`Reads on pending tracked values directly inside ${is_component_direct ? 'component' : 'try/pending/catch'} body are prohibited. Use trackPending() test or peek() for safe access or create another derived instead.`,
|
|
349
|
-
);
|
|
352
|
+
} else if (pending_read !== null) {
|
|
353
|
+
pending_read(block);
|
|
350
354
|
} else {
|
|
351
|
-
|
|
352
|
-
var boundary = get_pending_boundary(block);
|
|
353
|
-
if (boundary !== null) {
|
|
354
|
-
pause_block(block);
|
|
355
|
-
register_boundary_paused_block(boundary, block);
|
|
356
|
-
|
|
357
|
-
// Register deferred boundary completions for async tracked deps.
|
|
358
|
-
// This handles the case where a child boundary reads a tracked value
|
|
359
|
-
// whose resolution is managed by a different (parent) boundary.
|
|
360
|
-
var dep = block.d;
|
|
361
|
-
while (dep !== null) {
|
|
362
|
-
var dep_tracked = /** @type {Tracked} */ (dep.t);
|
|
363
|
-
if (
|
|
364
|
-
(dep_tracked.__v === SUSPENSE_PENDING || dep_tracked.__v === SUSPENSE_REJECTED) &&
|
|
365
|
-
(dep_tracked.f & TRACKED) !== 0
|
|
366
|
-
) {
|
|
367
|
-
var deferred_req = begin_boundary_request(boundary);
|
|
368
|
-
var entry = /** @type {DeferredTrackedEntry} */ ({ b: boundary, r: deferred_req });
|
|
369
|
-
if (dep_tracked.d === null) {
|
|
370
|
-
dep_tracked.d = [entry];
|
|
371
|
-
} else {
|
|
372
|
-
dep_tracked.d.push(entry);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
dep = dep.n;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
355
|
+
throw error;
|
|
378
356
|
}
|
|
379
357
|
}
|
|
380
358
|
|
|
@@ -389,7 +367,9 @@ function handle_run_error(error, block) {
|
|
|
389
367
|
* @returns {Derived[] | null}
|
|
390
368
|
*/
|
|
391
369
|
function prepare_rerun(block) {
|
|
392
|
-
if
|
|
370
|
+
// A list, an if, and a list item re-run only their own logic; their child
|
|
371
|
+
// blocks (items, the branch's blocks, an item body's nested blocks) stay.
|
|
372
|
+
if ((block.f & (FOR_BLOCK | IF_BLOCK | ITEM_BLOCK)) === 0) {
|
|
393
373
|
destroy_non_branch_children(block);
|
|
394
374
|
}
|
|
395
375
|
run_teardown(block);
|
|
@@ -411,6 +391,46 @@ function register_teardown(block, teardown) {
|
|
|
411
391
|
}
|
|
412
392
|
}
|
|
413
393
|
|
|
394
|
+
/**
|
|
395
|
+
* Runs the body of a branch block that was just created, in place of a first
|
|
396
|
+
* `run_block`: a branch renders untracked and never re-runs, so the only
|
|
397
|
+
* bookkeeping its first run needs is the globals the body reads and the
|
|
398
|
+
* dependencies a nested `item` records on it. Errors are handled as
|
|
399
|
+
* `run_block` handles them.
|
|
400
|
+
* @param {Block} block
|
|
401
|
+
* @param {(anchor: any, value: any, index?: any, key?: any) => void} fn
|
|
402
|
+
* @param {any} anchor
|
|
403
|
+
* @param {any} value
|
|
404
|
+
* @param {any} [key] a keyed list item's key (see `create_item`)
|
|
405
|
+
*/
|
|
406
|
+
export function run_branch(block, fn, anchor, value, key) {
|
|
407
|
+
var previous_block = active_block;
|
|
408
|
+
var previous_reaction = active_reaction;
|
|
409
|
+
var previous_tracking = tracking;
|
|
410
|
+
var previous_dependency = active_dependency;
|
|
411
|
+
var previous_component = active_component;
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
active_block = block;
|
|
415
|
+
active_reaction = block;
|
|
416
|
+
active_component = block.co;
|
|
417
|
+
tracking = false;
|
|
418
|
+
active_dependency = null;
|
|
419
|
+
fn(anchor, value, undefined, key);
|
|
420
|
+
if (active_dependency !== null) {
|
|
421
|
+
block.d = active_dependency;
|
|
422
|
+
}
|
|
423
|
+
} catch (error) {
|
|
424
|
+
handle_run_error(error, block);
|
|
425
|
+
} finally {
|
|
426
|
+
active_block = previous_block;
|
|
427
|
+
active_reaction = previous_reaction;
|
|
428
|
+
tracking = previous_tracking;
|
|
429
|
+
active_dependency = previous_dependency;
|
|
430
|
+
active_component = previous_component;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
414
434
|
/**
|
|
415
435
|
* @param {Block} block
|
|
416
436
|
* @param {boolean} [first_run] true when the block has no children, teardown,
|
|
@@ -472,21 +492,6 @@ export function run_block(block, first_run = false) {
|
|
|
472
492
|
|
|
473
493
|
var empty_get_set = { get: undefined, set: undefined };
|
|
474
494
|
|
|
475
|
-
/**
|
|
476
|
-
* Complete all deferred boundary requests registered on a tracked value.
|
|
477
|
-
* @param {Tracked} t
|
|
478
|
-
* @param {boolean} [show_resolved=true]
|
|
479
|
-
*/
|
|
480
|
-
function complete_deferred_boundaries(t, show_resolved = true) {
|
|
481
|
-
if (t.d !== null) {
|
|
482
|
-
for (var i = 0; i < t.d.length; i++) {
|
|
483
|
-
var entry = t.d[i];
|
|
484
|
-
complete_boundary_request(entry.b, entry.r, show_resolved);
|
|
485
|
-
}
|
|
486
|
-
t.d = null;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
495
|
class TrackedValue {
|
|
491
496
|
/**
|
|
492
497
|
* @param {any} v
|
|
@@ -522,17 +527,6 @@ class TrackedValue {
|
|
|
522
527
|
set value(v) {
|
|
523
528
|
set(this, v);
|
|
524
529
|
}
|
|
525
|
-
/**
|
|
526
|
-
* A read-only view: a derived over this value, for a receiver that should
|
|
527
|
-
* read but not write it. Equivalent to `track(() => tracked.value)`. The
|
|
528
|
-
* view is owned by the block that creates it, not by this value's block, so
|
|
529
|
-
* a view made in a component is released with that component even when the
|
|
530
|
-
* value outlives it.
|
|
531
|
-
* @returns {Derived}
|
|
532
|
-
*/
|
|
533
|
-
readOnly() {
|
|
534
|
-
return derived(() => get_tracked(this), /** @type {Block} */ (active_block));
|
|
535
|
-
}
|
|
536
530
|
}
|
|
537
531
|
|
|
538
532
|
class DerivedValue {
|
|
@@ -576,17 +570,6 @@ class DerivedValue {
|
|
|
576
570
|
set value(v) {
|
|
577
571
|
set(this, v);
|
|
578
572
|
}
|
|
579
|
-
/**
|
|
580
|
-
* A read-only view (see `TrackedValue#readOnly`). A derived without a
|
|
581
|
-
* setter is already read-only and is returned as is; a writable one is
|
|
582
|
-
* wrapped in a derived that follows it.
|
|
583
|
-
* @returns {Derived}
|
|
584
|
-
*/
|
|
585
|
-
readOnly() {
|
|
586
|
-
return this.a.set === undefined
|
|
587
|
-
? this
|
|
588
|
-
: derived(() => get_derived(this), /** @type {Block} */ (active_block));
|
|
589
|
-
}
|
|
590
573
|
}
|
|
591
574
|
|
|
592
575
|
if (DEV) {
|
|
@@ -607,7 +590,7 @@ export function tracked(v, block, hash, get, set) {
|
|
|
607
590
|
var t = /** @type {Tracked} */ (
|
|
608
591
|
new TrackedValue(v, block || active_block, get || set ? { get, set } : empty_get_set, hash)
|
|
609
592
|
);
|
|
610
|
-
if (hydrating && hash !== undefined) {
|
|
593
|
+
if (HYDRATION && hydrating && hash !== undefined) {
|
|
611
594
|
track_hash_reference.set(hash, t);
|
|
612
595
|
}
|
|
613
596
|
return t;
|
|
@@ -638,7 +621,7 @@ export function derived(fn, block, hash, get, set) {
|
|
|
638
621
|
created.push(d);
|
|
639
622
|
}
|
|
640
623
|
}
|
|
641
|
-
if (hydrating && hash !== undefined) {
|
|
624
|
+
if (HYDRATION && hydrating && hash !== undefined) {
|
|
642
625
|
track_hash_reference.set(hash, d);
|
|
643
626
|
}
|
|
644
627
|
return d;
|
|
@@ -732,6 +715,30 @@ function keep_deriveds(block, kept) {
|
|
|
732
715
|
block.f |= CREATES_DERIVEDS;
|
|
733
716
|
}
|
|
734
717
|
|
|
718
|
+
/**
|
|
719
|
+
* A read-only view of a tracked or derived value, for a receiver that should
|
|
720
|
+
* read but not write it: a derived over a tracked (or a writable derived),
|
|
721
|
+
* equivalent to `track(() => value.value)`; a read-only derived as it is; a
|
|
722
|
+
* plain value as it is, like `get`. The view is owned by the block that
|
|
723
|
+
* creates it, not by the value's block, so a view made in a component is
|
|
724
|
+
* released with that component even when the value outlives it.
|
|
725
|
+
* @param {any} value
|
|
726
|
+
* @param {Block} [block]
|
|
727
|
+
* @returns {any}
|
|
728
|
+
*/
|
|
729
|
+
export function track_read_only(value, block) {
|
|
730
|
+
if (!is_ripple_object(value)) {
|
|
731
|
+
return value;
|
|
732
|
+
}
|
|
733
|
+
var owner = block || /** @type {Block} */ (active_block);
|
|
734
|
+
if ((value.f & DERIVED) !== 0) {
|
|
735
|
+
var d = /** @type {Derived} */ (value);
|
|
736
|
+
return d.a.set === undefined ? d : derived(() => get_derived(d), owner);
|
|
737
|
+
}
|
|
738
|
+
var t = /** @type {Tracked} */ (value);
|
|
739
|
+
return derived(() => get_tracked(t), owner);
|
|
740
|
+
}
|
|
741
|
+
|
|
735
742
|
/**
|
|
736
743
|
* @param {any} v
|
|
737
744
|
* @param {Block} b
|
|
@@ -745,7 +752,7 @@ export function track(v, b, hash, get, set) {
|
|
|
745
752
|
return v;
|
|
746
753
|
}
|
|
747
754
|
if (b === null) {
|
|
748
|
-
|
|
755
|
+
track_orphan();
|
|
749
756
|
}
|
|
750
757
|
|
|
751
758
|
if (typeof v === 'function') {
|
|
@@ -754,243 +761,6 @@ export function track(v, b, hash, get, set) {
|
|
|
754
761
|
return tracked(v, b, hash, get, set);
|
|
755
762
|
}
|
|
756
763
|
|
|
757
|
-
/**
|
|
758
|
-
* @param {any} fn
|
|
759
|
-
* @param {Block} b
|
|
760
|
-
* @param {string} hash - Unique hash for SSR serialization/hydration
|
|
761
|
-
* @returns {Tracked | void}
|
|
762
|
-
*/
|
|
763
|
-
export function track_async(fn, b, hash) {
|
|
764
|
-
if (is_ripple_object(fn)) {
|
|
765
|
-
return fn;
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
var target_block = b || active_block;
|
|
769
|
-
if (target_block === null) {
|
|
770
|
-
throw new TypeError('trackAsync() requires a valid component context');
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
if (typeof fn !== 'function') {
|
|
774
|
-
throw new TypeError(
|
|
775
|
-
'trackAsync() only accepts function arguments that return a promise or an object with a promise property',
|
|
776
|
-
);
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
// During hydration, attempt to read serialized data from SSR
|
|
780
|
-
var had_hydration_data = false;
|
|
781
|
-
var hydration_value;
|
|
782
|
-
/** @type {string[] | undefined} */
|
|
783
|
-
var hydration_deps;
|
|
784
|
-
|
|
785
|
-
if (hydrating) {
|
|
786
|
-
var script_id = get_track_async_script_id(hash);
|
|
787
|
-
var script_el = document.getElementById(script_id);
|
|
788
|
-
if (script_el) {
|
|
789
|
-
var envelope = JSON.parse(/** @type {string} */ (script_el.textContent));
|
|
790
|
-
script_el.remove();
|
|
791
|
-
|
|
792
|
-
if (envelope.ok) {
|
|
793
|
-
had_hydration_data = true;
|
|
794
|
-
hydration_value =
|
|
795
|
-
envelope.payload === undefined ? envelope.value : revive(envelope.payload);
|
|
796
|
-
hydration_deps = envelope.deps;
|
|
797
|
-
} else {
|
|
798
|
-
// trigger the catch block
|
|
799
|
-
throw new Error(envelope.error?.message ?? 'Unknown server error');
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
var t = tracked(had_hydration_data ? hydration_value : SUSPENSE_PENDING, target_block, hash);
|
|
805
|
-
|
|
806
|
-
// Capture the call-site block for boundary lookups. target_block is the
|
|
807
|
-
// component's block (passed by compiler), but the actual try/pending/catch
|
|
808
|
-
// boundary is an ancestor of active_block (the block executing trackAsync).
|
|
809
|
-
var call_site_block = /** @type {Block} */ (active_block);
|
|
810
|
-
|
|
811
|
-
var version = 0;
|
|
812
|
-
/** @type {AbortController | null} */
|
|
813
|
-
var abort_controller = null;
|
|
814
|
-
var request_id = 0;
|
|
815
|
-
/** @type {Block | null} */
|
|
816
|
-
var boundary = null;
|
|
817
|
-
|
|
818
|
-
// TODO: decide if instead of insisting on pending, we create our own boundary
|
|
819
|
-
// we currently require a pending block upstream but we could also
|
|
820
|
-
// create a try/pending/catch boundary at mount and hydration like
|
|
821
|
-
// we do on the server so that there is always a boundary present.
|
|
822
|
-
// It can handle global pending when none were provided.
|
|
823
|
-
// Not sure about the catch boundary because if none were provided,
|
|
824
|
-
// the whole app for any error will be unmounted with the catch block rendered
|
|
825
|
-
|
|
826
|
-
// Find boundary from the call-site block.
|
|
827
|
-
boundary = get_pending_boundary(active_block);
|
|
828
|
-
if (boundary === null) {
|
|
829
|
-
throw new Error('Missing parent `try { ... } pending { ... }` statement');
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
// If we hydrated with resolved data, the SSR already completed this request.
|
|
833
|
-
// Otherwise mark a pending request on the boundary for the client-side run.
|
|
834
|
-
if (!had_hydration_data) {
|
|
835
|
-
request_id = begin_boundary_request(boundary);
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
pre_effect(() => {
|
|
839
|
-
if (had_hydration_data) {
|
|
840
|
-
// First run after hydration: skip fn() entirely (the SSR already
|
|
841
|
-
// produced the resolved value) and instead register the direct
|
|
842
|
-
// dependencies from the serialized deps list so future dep changes
|
|
843
|
-
// trigger a re-run via the normal async path.
|
|
844
|
-
had_hydration_data = false;
|
|
845
|
-
if (hydration_deps !== undefined) {
|
|
846
|
-
for (var i = 0; i < hydration_deps.length; i++) {
|
|
847
|
-
var dep_ref = track_hash_reference.get(hydration_deps[i]);
|
|
848
|
-
if (dep_ref !== undefined) {
|
|
849
|
-
get(dep_ref);
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
return;
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
var current_version = ++version;
|
|
857
|
-
|
|
858
|
-
// Abort previous in-flight request
|
|
859
|
-
if (abort_controller !== null && abort_controller.signal.aborted === false) {
|
|
860
|
-
abort_controller.abort(TRACKED_UPDATED);
|
|
861
|
-
}
|
|
862
|
-
abort_controller = null;
|
|
863
|
-
|
|
864
|
-
// Manage boundary request: replace if in-flight, or begin new if previous completed
|
|
865
|
-
if (request_id > 0 && boundary !== null) {
|
|
866
|
-
request_id = replace_boundary_request(boundary, request_id);
|
|
867
|
-
} else if (boundary !== null) {
|
|
868
|
-
request_id = begin_boundary_request(boundary);
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
// Set to pending before calling fn() in case it's sync.
|
|
872
|
-
if (t.__v !== SUSPENSE_PENDING) {
|
|
873
|
-
update_tracked_value_clock(t, SUSPENSE_PENDING);
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
// Temporarily allow mutations so set() doesn't throw inside the pre-effect
|
|
877
|
-
var previous_is_mutating_allowed = is_mutating_allowed;
|
|
878
|
-
is_mutating_allowed = true;
|
|
879
|
-
|
|
880
|
-
var result;
|
|
881
|
-
try {
|
|
882
|
-
result = fn();
|
|
883
|
-
} catch (e) {
|
|
884
|
-
is_mutating_allowed = previous_is_mutating_allowed;
|
|
885
|
-
if (e === ASYNC_DERIVED_READ_THROWN) {
|
|
886
|
-
// A dependency is still pending or rejected (e.g. chained trackAsync).
|
|
887
|
-
// Check if any dependency is rejected — if so, propagate rejection.
|
|
888
|
-
var dep = active_dependency;
|
|
889
|
-
while (dep !== null) {
|
|
890
|
-
if (dep.t.__v === SUSPENSE_REJECTED) {
|
|
891
|
-
update_tracked_value_clock(t, SUSPENSE_REJECTED);
|
|
892
|
-
complete_deferred_boundaries(t, false);
|
|
893
|
-
if (request_id > 0 && boundary !== null) {
|
|
894
|
-
complete_boundary_request(boundary, request_id, false);
|
|
895
|
-
request_id = 0;
|
|
896
|
-
}
|
|
897
|
-
return;
|
|
898
|
-
}
|
|
899
|
-
dep = dep.n;
|
|
900
|
-
}
|
|
901
|
-
// Dependencies are pending, not rejected — register deferred
|
|
902
|
-
// rejection so that if the boundary goes to catch mode, this
|
|
903
|
-
// tracked value is also set to REJECTED.
|
|
904
|
-
if (request_id > 0 && boundary !== null) {
|
|
905
|
-
register_boundary_deferred(boundary, request_id, () => {
|
|
906
|
-
update_tracked_value_clock(t, SUSPENSE_REJECTED);
|
|
907
|
-
});
|
|
908
|
-
}
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
throw e;
|
|
912
|
-
}
|
|
913
|
-
is_mutating_allowed = previous_is_mutating_allowed;
|
|
914
|
-
|
|
915
|
-
// Check if the result is async
|
|
916
|
-
var previous_tracking = tracking;
|
|
917
|
-
tracking = false;
|
|
918
|
-
var async_result = get_async_track_result(result);
|
|
919
|
-
tracking = previous_tracking;
|
|
920
|
-
|
|
921
|
-
if (async_result === null) {
|
|
922
|
-
// Sync result
|
|
923
|
-
update_tracked_value_clock(t, result);
|
|
924
|
-
if (request_id > 0 && boundary !== null) {
|
|
925
|
-
complete_boundary_request(boundary, request_id);
|
|
926
|
-
request_id = 0;
|
|
927
|
-
}
|
|
928
|
-
return;
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
// Capture per-invocation so async closures (rejection handler, teardown)
|
|
932
|
-
// have a stable reference. The shared abort_controller is only read
|
|
933
|
-
// synchronously at the top of the pre_effect to abort the previous request.
|
|
934
|
-
var current_abort_controller = async_result.abort_controller;
|
|
935
|
-
abort_controller = current_abort_controller;
|
|
936
|
-
|
|
937
|
-
async_result.promise.then(
|
|
938
|
-
(resolved) => {
|
|
939
|
-
if (current_version !== version) {
|
|
940
|
-
// stale
|
|
941
|
-
return;
|
|
942
|
-
}
|
|
943
|
-
update_tracked_value_clock(t, resolved);
|
|
944
|
-
complete_deferred_boundaries(t);
|
|
945
|
-
if (request_id > 0 && boundary !== null) {
|
|
946
|
-
complete_boundary_request(boundary, request_id);
|
|
947
|
-
request_id = 0;
|
|
948
|
-
}
|
|
949
|
-
},
|
|
950
|
-
(error) => {
|
|
951
|
-
if (current_version !== version) return; // stale
|
|
952
|
-
|
|
953
|
-
var is_internal_abort =
|
|
954
|
-
error === TRACKED_UPDATED || current_abort_controller?.signal?.reason === TRACKED_UPDATED;
|
|
955
|
-
if (is_internal_abort) {
|
|
956
|
-
// Internal abort (superseded by a new request) — don't set rejected
|
|
957
|
-
if (request_id > 0 && boundary !== null) {
|
|
958
|
-
complete_boundary_request(boundary, request_id, false);
|
|
959
|
-
request_id = 0;
|
|
960
|
-
}
|
|
961
|
-
complete_deferred_boundaries(t, false);
|
|
962
|
-
return;
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
update_tracked_value_clock(t, SUSPENSE_REJECTED);
|
|
966
|
-
complete_deferred_boundaries(t, false);
|
|
967
|
-
|
|
968
|
-
// Route error to catch boundary
|
|
969
|
-
var boundary_with_catch = get_boundary_with_catch(call_site_block);
|
|
970
|
-
if (boundary_with_catch !== null) {
|
|
971
|
-
handle_boundary_error(boundary_with_catch, error);
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
if (request_id > 0 && boundary !== null) {
|
|
975
|
-
var should_show_resolved =
|
|
976
|
-
boundary_with_catch === boundary || boundary === null ? false : true;
|
|
977
|
-
complete_boundary_request(boundary, request_id, should_show_resolved);
|
|
978
|
-
request_id = 0;
|
|
979
|
-
}
|
|
980
|
-
},
|
|
981
|
-
);
|
|
982
|
-
|
|
983
|
-
return () => {
|
|
984
|
-
// Teardown: abort in-flight request when block is destroyed
|
|
985
|
-
if (current_abort_controller !== null && current_abort_controller.signal.aborted === false) {
|
|
986
|
-
current_abort_controller.abort(TRACKED_UPDATED);
|
|
987
|
-
}
|
|
988
|
-
};
|
|
989
|
-
});
|
|
990
|
-
|
|
991
|
-
return t;
|
|
992
|
-
}
|
|
993
|
-
|
|
994
764
|
/**
|
|
995
765
|
* @param {(Derived | Tracked) | (() => any)} t
|
|
996
766
|
* @returns {boolean}
|
|
@@ -1258,7 +1028,8 @@ function is_tracking_dirty(tracking) {
|
|
|
1258
1028
|
export function is_block_dirty(block) {
|
|
1259
1029
|
var flags = block.f;
|
|
1260
1030
|
|
|
1261
|
-
|
|
1031
|
+
// A branch never re-runs, except a list item that carries its render block.
|
|
1032
|
+
if ((flags & ITEM_BLOCK) === 0 && (flags & (ROOT_BLOCK | BRANCH_BLOCK)) !== 0) {
|
|
1262
1033
|
return false;
|
|
1263
1034
|
}
|
|
1264
1035
|
if ((flags & BLOCK_HAS_RUN) === 0) {
|
|
@@ -1420,9 +1191,7 @@ function flush_microtasks() {
|
|
|
1420
1191
|
|
|
1421
1192
|
flush_count++;
|
|
1422
1193
|
if (flush_count > 1001) {
|
|
1423
|
-
|
|
1424
|
-
'Maximum update depth exceeded. This typically indicates that an effect reads and writes the same piece of state.',
|
|
1425
|
-
);
|
|
1194
|
+
update_depth_exceeded();
|
|
1426
1195
|
}
|
|
1427
1196
|
var pending = queue;
|
|
1428
1197
|
queue = create_queue();
|
|
@@ -1584,9 +1353,7 @@ export function get_tracked(tracked) {
|
|
|
1584
1353
|
*/
|
|
1585
1354
|
export function set(tracked, value) {
|
|
1586
1355
|
if (!is_mutating_allowed) {
|
|
1587
|
-
|
|
1588
|
-
'Assignments or updates to tracked values are not allowed during computed "track(() => ...)" evaluation',
|
|
1589
|
-
);
|
|
1356
|
+
set_in_derived();
|
|
1590
1357
|
}
|
|
1591
1358
|
|
|
1592
1359
|
var old_value = tracked.__v;
|
|
@@ -1851,9 +1618,9 @@ export function scope() {
|
|
|
1851
1618
|
* @param {string} [err]
|
|
1852
1619
|
* @returns {Block | never}
|
|
1853
1620
|
*/
|
|
1854
|
-
export function safe_scope(err
|
|
1621
|
+
export function safe_scope(err) {
|
|
1855
1622
|
if (active_scope === null) {
|
|
1856
|
-
|
|
1623
|
+
scope_orphan(err);
|
|
1857
1624
|
}
|
|
1858
1625
|
|
|
1859
1626
|
return /** @type {Block} */ (active_scope);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { get_first_child } from './operations.js';
|
|
2
|
+
import { set_ns_parser, template } from './template.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create fragment with proper namespace using Svelte's wrapping approach
|
|
6
|
+
* @param {string} content
|
|
7
|
+
* @param {'svg' | 'math'} ns
|
|
8
|
+
* @returns {DocumentFragment}
|
|
9
|
+
*/
|
|
10
|
+
function from_namespace(content, ns) {
|
|
11
|
+
var wrapped = `<${ns}>${content}</${ns}>`;
|
|
12
|
+
|
|
13
|
+
var elem = document.createElement('template');
|
|
14
|
+
elem.innerHTML = wrapped;
|
|
15
|
+
var fragment = elem.content;
|
|
16
|
+
|
|
17
|
+
var root = /** @type {Element} */ (get_first_child(fragment));
|
|
18
|
+
var result = document.createDocumentFragment();
|
|
19
|
+
|
|
20
|
+
var first;
|
|
21
|
+
while ((first = get_first_child(root))) {
|
|
22
|
+
result.appendChild(/** @type {Node} */ (first));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Makes namespaced template parsing available: called by every path that can
|
|
30
|
+
* clone a template into the SVG or MathML namespace, so an app without one
|
|
31
|
+
* never loads it.
|
|
32
|
+
*/
|
|
33
|
+
export function install_ns_templates() {
|
|
34
|
+
set_ns_parser(from_namespace);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* `template()` for content compiled in the SVG or MathML namespace.
|
|
39
|
+
* @param {string} content
|
|
40
|
+
* @param {number} [flags]
|
|
41
|
+
* @param {number} [count]
|
|
42
|
+
* @returns {() => Node}
|
|
43
|
+
*/
|
|
44
|
+
export function template_ns(content, flags, count) {
|
|
45
|
+
set_ns_parser(from_namespace);
|
|
46
|
+
return template(content, flags, count);
|
|
47
|
+
}
|