Skip to content

Commit ea13937

Browse files
committed
WIP
1 parent e6cd426 commit ea13937

File tree

12 files changed

+77
-11
lines changed

12 files changed

+77
-11
lines changed

packages/svelte/src/compiler/phases/2-analyze/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { ArrowFunctionExpression } from './visitors/ArrowFunctionExpression.js';
2020
import { AssignmentExpression } from './visitors/AssignmentExpression.js';
2121
import { Attribute } from './visitors/Attribute.js';
2222
import { AwaitBlock } from './visitors/AwaitBlock.js';
23+
import { AwaitExpression } from './visitors/AwaitExpression.js';
2324
import { BindDirective } from './visitors/BindDirective.js';
2425
import { CallExpression } from './visitors/CallExpression.js';
2526
import { ClassBody } from './visitors/ClassBody.js';
@@ -133,6 +134,7 @@ const visitors = {
133134
AssignmentExpression,
134135
Attribute,
135136
AwaitBlock,
137+
AwaitExpression,
136138
BindDirective,
137139
CallExpression,
138140
ClassBody,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/** @import { AwaitExpression } from 'estree' */
2+
/** @import { Context } from '../types' */
3+
4+
/**
5+
* @param {AwaitExpression} node
6+
* @param {Context} context
7+
*/
8+
export function AwaitExpression(node, context) {
9+
if (context.state.expression) {
10+
context.state.expression.is_async = true;
11+
}
12+
13+
context.next();
14+
}

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

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { ArrowFunctionExpression } from './visitors/ArrowFunctionExpression.js';
1212
import { AssignmentExpression } from './visitors/AssignmentExpression.js';
1313
import { Attribute } from './visitors/Attribute.js';
1414
import { AwaitBlock } from './visitors/AwaitBlock.js';
15+
import { AwaitExpression } from './visitors/AwaitExpression.js';
1516
import { BinaryExpression } from './visitors/BinaryExpression.js';
1617
import { BindDirective } from './visitors/BindDirective.js';
1718
import { BlockStatement } from './visitors/BlockStatement.js';
@@ -87,6 +88,7 @@ const visitors = {
8788
AssignmentExpression,
8889
Attribute,
8990
AwaitBlock,
91+
AwaitExpression,
9092
BinaryExpression,
9193
BindDirective,
9294
BlockStatement,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/** @import { AwaitExpression, Expression } from 'estree' */
2+
/** @import { ComponentContext } from '../types' */
3+
import * as b from '../../../../utils/builders.js';
4+
5+
/**
6+
* @param {AwaitExpression} node
7+
* @param {ComponentContext} context
8+
*/
9+
export function AwaitExpression(node, context) {
10+
return b.await(
11+
b.call(
12+
'$.preserve_context',
13+
node.argument && /** @type {Expression} */ (context.visit(node.argument))
14+
)
15+
);
16+
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export function process_children(nodes, initial, is_element, { visit, state }) {
6969

7070
state.template.push(' ');
7171

72-
const { has_state, has_call, value } = build_template_chunk(sequence, visit, state);
72+
const { has_state, has_call, is_async, value } = build_template_chunk(sequence, visit, state);
7373

7474
// if this is a standalone `{expression}`, make sure we handle the case where
7575
// no text node was created because the expression was empty during SSR
@@ -79,7 +79,7 @@ export function process_children(nodes, initial, is_element, { visit, state }) {
7979
const update = b.stmt(b.call('$.set_text', id, value));
8080

8181
if (has_call && !within_bound_contenteditable) {
82-
state.init.push(build_update(update));
82+
state.init.push(build_update(update, is_async));
8383
} else if (has_state && !within_bound_contenteditable) {
8484
state.update.push(update);
8585
} else {

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

+8-5
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { locator } from '../../../../../state.js';
1414
* @param {Array<AST.Text | AST.ExpressionTag>} values
1515
* @param {(node: AST.SvelteNode, state: any) => any} visit
1616
* @param {ComponentClientTransformState} state
17-
* @returns {{ value: Expression, has_state: boolean, has_call: boolean }}
17+
* @returns {{ value: Expression, has_state: boolean, has_call: boolean, is_async: boolean }}
1818
*/
1919
export function build_template_chunk(values, visit, state) {
2020
/** @type {Expression[]} */
@@ -25,6 +25,7 @@ export function build_template_chunk(values, visit, state) {
2525

2626
let has_call = false;
2727
let has_state = false;
28+
let is_async = false;
2829
let contains_multiple_call_expression = false;
2930

3031
for (const node of values) {
@@ -34,6 +35,7 @@ export function build_template_chunk(values, visit, state) {
3435
contains_multiple_call_expression ||= has_call && metadata.has_call;
3536
has_call ||= metadata.has_call;
3637
has_state ||= metadata.has_state;
38+
is_async ||= metadata.is_async;
3739
}
3840
}
3941

@@ -68,7 +70,7 @@ export function build_template_chunk(values, visit, state) {
6870
} else if (values.length === 1) {
6971
// If we have a single expression, then pass that in directly to possibly avoid doing
7072
// extra work in the template_effect (instead we do the work in set_text).
71-
return { value: visit(node.expression, state), has_state, has_call };
73+
return { value: visit(node.expression, state), has_state, has_call, is_async };
7274
} else {
7375
expressions.push(b.logical('??', visit(node.expression, state), b.literal('')));
7476
}
@@ -84,17 +86,18 @@ export function build_template_chunk(values, visit, state) {
8486

8587
const value = b.template(quasis, expressions);
8688

87-
return { value, has_state, has_call };
89+
return { value, has_state, has_call, is_async };
8890
}
8991

9092
/**
9193
* @param {Statement} statement
94+
* @param {boolean} is_async
9295
*/
93-
export function build_update(statement) {
96+
export function build_update(statement, is_async) {
9497
const body =
9598
statement.type === 'ExpressionStatement' ? statement.expression : b.block([statement]);
9699

97-
return b.stmt(b.call('$.template_effect', b.thunk(body)));
100+
return b.stmt(b.call('$.template_effect', b.thunk(body, is_async)));
98101
}
99102

100103
/**

packages/svelte/src/compiler/phases/nodes.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export function create_expression_metadata() {
5858
return {
5959
dependencies: new Set(),
6060
has_state: false,
61-
has_call: false
61+
has_call: false,
62+
is_async: false
6263
};
6364
}

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

+2
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,8 @@ export interface ExpressionMetadata {
318318
has_state: boolean;
319319
/** True if the expression involves a call expression (often, it will need to be wrapped in a derived) */
320320
has_call: boolean;
321+
/** True if the expression contains `await` */
322+
is_async: boolean;
321323
}
322324

323325
export * from './template.js';

packages/svelte/src/index-client.js

-2
Original file line numberDiff line numberDiff line change
@@ -191,5 +191,3 @@ export {
191191
} from './internal/client/runtime.js';
192192

193193
export { createRawSnippet } from './internal/client/dom/blocks/snippet.js';
194-
195-
export { create_suspense } from './internal/client/dom/blocks/boundary.js';

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

+27
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,30 @@ export function create_suspense() {
248248

249249
return [suspend, unsuspend];
250250
}
251+
252+
/**
253+
* @template T
254+
* @param {Promise<T>} promise
255+
* @returns {Promise<T>}
256+
*/
257+
export async function preserve_context(promise) {
258+
if (!active_effect) {
259+
return promise;
260+
}
261+
262+
var previous_effect = active_effect;
263+
var previous_reaction = active_reaction;
264+
var previous_component_context = component_context;
265+
266+
const [suspend, unsuspend] = create_suspense();
267+
268+
try {
269+
suspend();
270+
return await promise;
271+
} finally {
272+
set_active_effect(previous_effect);
273+
set_active_reaction(previous_reaction);
274+
set_component_context(previous_component_context);
275+
unsuspend();
276+
}
277+
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export {
129129
update_store,
130130
mark_store_binding
131131
} from './reactivity/store.js';
132-
export { boundary } from './dom/blocks/boundary.js';
132+
export { boundary, preserve_context } from './dom/blocks/boundary.js';
133133
export { set_text } from './render.js';
134134
export {
135135
get,

packages/svelte/types/index.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ declare module 'svelte' {
419419
render: () => string;
420420
setup?: (element: Element) => void | (() => void);
421421
}): Snippet<Params>;
422+
export function create_suspense(): (() => void)[];
422423
/** Anything except a function */
423424
type NotFunction<T> = T extends Function ? never : T;
424425
/**

0 commit comments

Comments
 (0)