Skip to content

Commit ffc4f1b

Browse files
committed
mostly working
1 parent 303d738 commit ffc4f1b

File tree

4 files changed

+106
-50
lines changed

4 files changed

+106
-50
lines changed

packages/svelte/src/internal/client/dom/blocks/boundary.js

Lines changed: 80 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
/** @import { Effect, TemplateNode, } from '#client' */
22

3-
import { BOUNDARY_EFFECT, BOUNDARY_SUSPENDED, EFFECT_TRANSPARENT } from '../../constants.js';
3+
import {
4+
BOUNDARY_EFFECT,
5+
BOUNDARY_SUSPENDED,
6+
EFFECT_TRANSPARENT,
7+
RENDER_EFFECT
8+
} from '../../constants.js';
49
import { component_context, set_component_context } from '../../context.js';
510
import { block, branch, destroy_effect, pause_effect } from '../../reactivity/effects.js';
611
import {
@@ -10,7 +15,9 @@ import {
1015
set_active_effect,
1116
set_active_reaction,
1217
reset_is_throwing_error,
13-
schedule_effect
18+
schedule_effect,
19+
check_dirtiness,
20+
update_effect
1421
} from '../../runtime.js';
1522
import {
1623
hydrate_next,
@@ -28,6 +35,9 @@ import { run_all } from '../../../shared/utils.js';
2835
const ASYNC_INCREMENT = Symbol();
2936
const ASYNC_DECREMENT = Symbol();
3037
const ADD_CALLBACK = Symbol();
38+
const ADD_RENDER_EFFECT = Symbol();
39+
const ADD_EFFECT = Symbol();
40+
const RELEASE = Symbol();
3141

3242
/**
3343
* @param {Effect} boundary
@@ -86,6 +96,12 @@ export function boundary(node, props, children) {
8696
/** @type {Array<() => void>} */
8797
var callbacks = [];
8898

99+
/** @type {Effect[]} */
100+
var render_effects = [];
101+
102+
/** @type {Effect[]} */
103+
var effects = [];
104+
89105
/**
90106
* @param {() => void} snippet_fn
91107
* @returns {Effect | null}
@@ -125,7 +141,19 @@ export function boundary(node, props, children) {
125141
}
126142

127143
function unsuspend() {
128-
boundary.f ^= BOUNDARY_SUSPENDED;
144+
if ((boundary.f & BOUNDARY_SUSPENDED) !== 0) {
145+
boundary.f ^= BOUNDARY_SUSPENDED;
146+
}
147+
148+
for (const e of render_effects) {
149+
try {
150+
if (check_dirtiness(e)) {
151+
update_effect(e);
152+
}
153+
} catch (error) {
154+
handle_error(error, e, null, e.ctx);
155+
}
156+
}
129157

130158
run_all(callbacks);
131159
callbacks.length = 0;
@@ -141,14 +169,21 @@ export function boundary(node, props, children) {
141169
offscreen_fragment = null;
142170
}
143171

144-
if (main_effect !== null) {
145-
// TODO do we also need to `resume_effect` here?
146-
schedule_effect(main_effect);
172+
// TODO this timing is wrong, effects need to ~somehow~ end up
173+
// in the right place
174+
for (const e of effects) {
175+
try {
176+
if (check_dirtiness(e)) {
177+
update_effect(e);
178+
}
179+
} catch (error) {
180+
handle_error(error, e, null, e.ctx);
181+
}
147182
}
148183
}
149184

150185
// @ts-ignore We re-use the effect's fn property to avoid allocation of an additional field
151-
boundary.fn = (/** @type {unknown} */ input, /** @type {() => void} */ payload) => {
186+
boundary.fn = (/** @type {unknown} */ input, /** @type {any} */ payload) => {
152187
if (input === ASYNC_INCREMENT) {
153188
boundary.f |= BOUNDARY_SUSPENDED;
154189
async_count++;
@@ -160,7 +195,12 @@ export function boundary(node, props, children) {
160195

161196
if (input === ASYNC_DECREMENT) {
162197
if (--async_count === 0) {
163-
queue_boundary_micro_task(unsuspend);
198+
unsuspend();
199+
200+
if (main_effect !== null) {
201+
// TODO do we also need to `resume_effect` here?
202+
schedule_effect(main_effect);
203+
}
164204
}
165205

166206
return;
@@ -171,6 +211,21 @@ export function boundary(node, props, children) {
171211
return;
172212
}
173213

214+
if (input === ADD_RENDER_EFFECT) {
215+
render_effects.push(payload);
216+
return;
217+
}
218+
219+
if (input === ADD_EFFECT) {
220+
render_effects.push(payload);
221+
return;
222+
}
223+
224+
if (input === RELEASE) {
225+
unsuspend();
226+
return;
227+
}
228+
174229
var error = input;
175230
var onerror = props.onerror;
176231
let failed = props.failed;
@@ -372,3 +427,20 @@ export function add_boundary_callback(boundary, fn) {
372427
// @ts-ignore
373428
boundary.fn(ADD_CALLBACK, fn);
374429
}
430+
431+
/**
432+
* @param {Effect} boundary
433+
* @param {Effect} effect
434+
*/
435+
export function add_boundary_effect(boundary, effect) {
436+
// @ts-ignore
437+
boundary.fn((effect.f & RENDER_EFFECT) !== 0 ? ADD_RENDER_EFFECT : ADD_EFFECT, effect);
438+
}
439+
440+
/**
441+
* @param {Effect} boundary
442+
*/
443+
export function release_boundary(boundary) {
444+
// @ts-ignore
445+
boundary.fn?.(RELEASE);
446+
}

packages/svelte/src/internal/client/dom/blocks/if.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { block, branch, pause_effect, resume_effect } from '../../reactivity/eff
1212
import { HYDRATION_START_ELSE, UNINITIALIZED } from '../../../../constants.js';
1313
import { active_effect, suspended } from '../../runtime.js';
1414
import { add_boundary_callback, find_boundary } from './boundary.js';
15+
import { should_defer_append } from '../operations.js';
1516

1617
/**
1718
* @param {TemplateNode} node
@@ -109,9 +110,10 @@ export function if_block(node, fn, elseif = false) {
109110
}
110111
}
111112

113+
var defer = boundary !== null && should_defer_append();
112114
var target = anchor;
113115

114-
if (suspended) {
116+
if (defer) {
115117
offscreen_fragment = document.createDocumentFragment();
116118
offscreen_fragment.append((target = document.createComment('')));
117119
}
@@ -120,7 +122,7 @@ export function if_block(node, fn, elseif = false) {
120122
pending_effect = fn && branch(() => fn(target));
121123
}
122124

123-
if (suspended) {
125+
if (defer) {
124126
add_boundary_callback(boundary, commit);
125127
} else {
126128
commit();

packages/svelte/src/internal/client/reactivity/sources.js

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -285,22 +285,6 @@ function mark_reactions(signal, status) {
285285
continue;
286286
}
287287

288-
// if we're about to trip an async derived, mark the boundary as
289-
// suspended _before_ we actually process effects
290-
if ((flags & IS_ASYNC) !== 0) {
291-
let boundary = /** @type {Derived} */ (reaction).parent;
292-
293-
while (boundary !== null && (boundary.f & BOUNDARY_EFFECT) === 0) {
294-
boundary = boundary.parent;
295-
}
296-
297-
if (boundary === null) {
298-
// TODO this is presumably an error — throw here?
299-
} else {
300-
boundary.f |= BOUNDARY_SUSPENDED;
301-
}
302-
}
303-
304288
set_signal_status(reaction, status);
305289

306290
// If the signal a) was previously clean or b) is an unowned derived, then mark it

packages/svelte/src/internal/client/runtime.js

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ import {
3131
import {
3232
flush_idle_tasks,
3333
flush_boundary_micro_tasks,
34-
flush_post_micro_tasks
34+
flush_post_micro_tasks,
35+
queue_micro_task
3536
} from './dom/task.js';
3637
import { internal_set } from './reactivity/sources.js';
3738
import {
@@ -51,6 +52,7 @@ import {
5152
set_component_context,
5253
set_dev_current_component_function
5354
} from './context.js';
55+
import { add_boundary_effect, release_boundary } from './dom/blocks/boundary.js';
5456

5557
const FLUSH_MICROTASK = 0;
5658
const FLUSH_SYNC = 1;
@@ -808,12 +810,12 @@ export function schedule_effect(signal) {
808810
*
809811
* @param {Effect} effect
810812
* @param {Effect[]} collected_effects
813+
* @param {Effect} [boundary]
811814
* @returns {void}
812815
*/
813-
function process_effects(effect, collected_effects) {
816+
function process_effects(effect, collected_effects, boundary) {
814817
var current_effect = effect.first;
815818
var effects = [];
816-
suspended = false;
817819

818820
main_loop: while (current_effect !== null) {
819821
var flags = current_effect.f;
@@ -822,22 +824,27 @@ function process_effects(effect, collected_effects) {
822824
var sibling = current_effect.next;
823825

824826
if (!is_skippable_branch && (flags & INERT) === 0) {
825-
// We only want to skip suspended effects if they are not branches or block effects,
826-
// with the exception of template effects, which are technically block effects but also
827-
// have a special flag `TEMPLATE_EFFECT` that we can use to identify them
828-
var skip_suspended =
829-
suspended &&
827+
// Inside a boundary, defer everything except block/branch effects
828+
var defer =
829+
boundary !== undefined &&
830830
(flags & BRANCH_EFFECT) === 0 &&
831831
((flags & BLOCK_EFFECT) === 0 || (flags & TEMPLATE_EFFECT) !== 0);
832832

833-
if ((flags & RENDER_EFFECT) !== 0) {
833+
if (defer) {
834+
add_boundary_effect(/** @type {Effect} */ (boundary), current_effect);
835+
} else if ((flags & BOUNDARY_EFFECT) !== 0) {
836+
process_effects(current_effect, collected_effects, current_effect);
837+
838+
if ((current_effect.f & BOUNDARY_SUSPENDED) === 0) {
839+
// no more async work to happen
840+
release_boundary(current_effect);
841+
}
842+
} else if ((flags & RENDER_EFFECT) !== 0) {
834843
if ((flags & BOUNDARY_EFFECT) !== 0) {
835-
suspended = (flags & BOUNDARY_SUSPENDED) !== 0;
844+
// TODO do we need to do anything here?
836845
} else if (is_branch) {
837-
if (!suspended) {
838-
current_effect.f ^= CLEAN;
839-
}
840-
} else if (!skip_suspended) {
846+
current_effect.f ^= CLEAN;
847+
} else {
841848
// Ensure we set the effect to be the active reaction
842849
// to ensure that unowned deriveds are correctly tracked
843850
// because we're flushing the current effect
@@ -860,7 +867,7 @@ function process_effects(effect, collected_effects) {
860867
current_effect = child;
861868
continue;
862869
}
863-
} else if ((flags & EFFECT) !== 0 && !skip_suspended) {
870+
} else if ((flags & EFFECT) !== 0) {
864871
effects.push(current_effect);
865872
}
866873
}
@@ -873,15 +880,6 @@ function process_effects(effect, collected_effects) {
873880
break main_loop;
874881
}
875882

876-
if ((parent.f & BOUNDARY_EFFECT) !== 0) {
877-
let boundary = parent.parent;
878-
while (boundary !== null && (boundary.f & BOUNDARY_EFFECT) === 0) {
879-
boundary = boundary.parent;
880-
}
881-
882-
suspended = boundary === null ? false : (boundary.f & BOUNDARY_SUSPENDED) !== 0;
883-
}
884-
885883
var parent_sibling = parent.next;
886884
if (parent_sibling !== null) {
887885
current_effect = parent_sibling;

0 commit comments

Comments
 (0)