Skip to content

Commit 21811b1

Browse files
committed
WIP implement $effect.pending(...)
1 parent 12e1c81 commit 21811b1

File tree

9 files changed

+72
-5
lines changed

9 files changed

+72
-5
lines changed

packages/svelte/src/compiler/phases/1-parse/utils/create.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ export function create_fragment(transparent = false) {
1111
metadata: {
1212
transparent,
1313
dynamic: false,
14-
has_await: false
14+
has_await: false,
15+
effect_pending_expressions: []
1516
}
1617
};
1718
}

packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,22 @@ export function CallExpression(node, context) {
165165
break;
166166

167167
case '$effect.pending':
168+
if (node.arguments.length > 1) {
169+
e.rune_invalid_arguments_length(node, rune, 'zero or one arguments');
170+
}
171+
168172
if (context.state.expression) {
169173
context.state.expression.has_state = true;
170174
}
171175

176+
if (node.arguments[0]) {
177+
const fragment = /** @type {AST.Fragment} */ (context.state.fragment);
178+
179+
fragment.metadata.effect_pending_expressions.push(
180+
/** @type {Expression} */ (node.arguments[0])
181+
);
182+
}
183+
172184
break;
173185

174186
case '$inspect':

packages/svelte/src/compiler/phases/3-transform/client/types.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export interface ClientTransformState extends TransformState {
2424
/** `true` if we're transforming the contents of `<script>` */
2525
readonly is_instance: boolean;
2626

27+
effect_pending: Map<Expression, Identifier>;
28+
2729
readonly transform: Record<
2830
string,
2931
{

packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,17 @@ export function CallExpression(node, context) {
6363
);
6464

6565
case '$effect.pending':
66+
if (node.arguments[0]) {
67+
const id = b.id(`$$pending_${context.state.effect_pending.size}`);
68+
69+
context.state.effect_pending.set(
70+
/** @type {Expression} */ (context.visit(node.arguments[0])),
71+
id
72+
);
73+
74+
return b.call('$.get', id);
75+
}
76+
6677
return b.call('$.pending');
6778

6879
case '$inspect':

packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export function Fragment(node, context) {
6868
memoizer: new Memoizer(),
6969
template: new Template(),
7070
transform: { ...context.state.transform },
71+
effect_pending: new Map(),
7172
metadata: {
7273
namespace,
7374
bound_contenteditable: context.state.metadata.bound_contenteditable
@@ -152,6 +153,10 @@ export function Fragment(node, context) {
152153

153154
body.push(...state.consts);
154155

156+
for (const [expression, id] of state.effect_pending) {
157+
body.push(b.const(id, b.call('$.pending', b.thunk(expression))));
158+
}
159+
155160
if (has_await) {
156161
body.push(b.if(b.call('$.aborted'), b.return()));
157162
}

packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,8 @@ export function RegularElement(node, context) {
310310
metadata,
311311
scope: /** @type {Scope} */ (context.state.scopes.get(node.fragment)),
312312
preserve_whitespace:
313-
context.state.preserve_whitespace || node.name === 'pre' || node.name === 'textarea'
313+
context.state.preserve_whitespace || node.name === 'pre' || node.name === 'textarea',
314+
effect_pending: new Map()
314315
};
315316

316317
const { hoisted, trimmed } = clean_nodes(
@@ -378,6 +379,10 @@ export function RegularElement(node, context) {
378379
}
379380
}
380381

382+
for (const [expression, id] of state.effect_pending) {
383+
child_state.init.push(b.const(id, b.call('$.pending', b.thunk(expression))));
384+
}
385+
381386
if (node.fragment.nodes.some((node) => node.type === 'SnippetBlock')) {
382387
// Wrap children in `{...}` to avoid declaration conflicts
383388
context.state.init.push(

packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export function CallExpression(node, context) {
2626
}
2727

2828
if (rune === '$effect.pending') {
29-
return b.literal(0);
29+
return node.arguments[0] ?? b.literal(0);
3030
}
3131

3232
if (rune === '$state' || rune === '$state.raw') {

packages/svelte/src/compiler/types/template.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export namespace AST {
5757
*/
5858
dynamic: boolean;
5959
has_await: boolean;
60+
effect_pending_expressions: Expression[];
6061
};
6162
}
6263

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

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ import {
88
import { HYDRATION_START_ELSE } from '../../../../constants.js';
99
import { component_context, set_component_context } from '../../context.js';
1010
import { handle_error, invoke_error_boundary } from '../../error-handling.js';
11-
import { block, branch, destroy_effect, pause_effect } from '../../reactivity/effects.js';
11+
import {
12+
block,
13+
branch,
14+
destroy_effect,
15+
inspect_effect,
16+
pause_effect
17+
} from '../../reactivity/effects.js';
1218
import {
1319
active_effect,
1420
active_reaction,
@@ -33,6 +39,7 @@ import { Batch, current_batch, effect_pending_updates } from '../../reactivity/b
3339
import { internal_set, source } from '../../reactivity/sources.js';
3440
import { tag } from '../../dev/tracing.js';
3541
import { createSubscriber } from '../../../../reactivity/create-subscriber.js';
42+
import { untrack } from 'svelte';
3643

3744
/**
3845
* @typedef {{
@@ -440,7 +447,10 @@ export function get_boundary() {
440447
return /** @type {Boundary} */ (/** @type {Effect} */ (active_effect).b);
441448
}
442449

443-
export function pending() {
450+
/**
451+
* @param {(() => any) | void} fn
452+
*/
453+
export function pending(fn) {
444454
if (active_effect === null) {
445455
e.effect_pending_outside_reaction();
446456
}
@@ -451,5 +461,25 @@ export function pending() {
451461
return 0; // TODO eventually we will need this to be global
452462
}
453463

464+
if (fn) {
465+
const signal = source(untrack(fn));
466+
467+
inspect_effect(() => {
468+
const value = fn();
469+
let stale = false;
470+
471+
queue_micro_task(() => {
472+
if (stale) return;
473+
internal_set(signal, value);
474+
});
475+
476+
return () => {
477+
stale = true;
478+
};
479+
});
480+
481+
return signal;
482+
}
483+
454484
return boundary.get_effect_pending();
455485
}

0 commit comments

Comments
 (0)