svelte 5.48.0 → 5.48.2
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/compiler/index.js +1 -1
- package/package.json +1 -1
- package/src/compiler/errors.js +2 -2
- package/src/compiler/phases/3-transform/client/transform-client.js +1 -0
- package/src/compiler/phases/3-transform/client/visitors/ConstTag.js +11 -5
- package/src/compiler/phases/3-transform/client/visitors/Fragment.js +4 -3
- package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js +1 -1
- package/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js +1 -1
- package/src/compiler/phases/3-transform/client/visitors/shared/component.js +1 -1
- package/src/compiler/phases/3-transform/server/visitors/ConstTag.js +4 -2
- package/src/compiler/phases/3-transform/server/visitors/HtmlTag.js +11 -1
- package/src/compiler/phases/3-transform/shared/transform-async.js +15 -12
- package/src/internal/client/dom/blocks/async.js +7 -2
- package/src/internal/client/dom/blocks/boundary.js +13 -7
- package/src/internal/client/dom/elements/attributes.js +2 -2
- package/src/internal/client/index.js +2 -1
- package/src/internal/client/reactivity/async.js +65 -40
- package/src/internal/client/reactivity/batch.js +27 -30
- package/src/internal/client/reactivity/effects.js +3 -3
- package/src/internal/client/reactivity/sources.js +9 -19
- package/src/internal/client/runtime.js +2 -13
- package/src/internal/client/validate.js +2 -1
- package/src/internal/server/dev.js +6 -1
- package/src/version.js +1 -1
- package/types/index.d.ts.map +1 -1
package/package.json
CHANGED
package/src/compiler/errors.js
CHANGED
|
@@ -977,12 +977,12 @@ export function const_tag_invalid_expression(node) {
|
|
|
977
977
|
}
|
|
978
978
|
|
|
979
979
|
/**
|
|
980
|
-
* `{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>`, `<svelte:boundary
|
|
980
|
+
* `{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>`, `<svelte:boundary>` or `<Component>`
|
|
981
981
|
* @param {null | number | NodeLike} node
|
|
982
982
|
* @returns {never}
|
|
983
983
|
*/
|
|
984
984
|
export function const_tag_invalid_placement(node) {
|
|
985
|
-
e(node, 'const_tag_invalid_placement', `\`{@const}\` must be the immediate child of \`{#snippet}\`, \`{#if}\`, \`{:else if}\`, \`{:else}\`, \`{#each}\`, \`{:then}\`, \`{:catch}\`, \`<svelte:fragment>\`, \`<svelte:boundary
|
|
985
|
+
e(node, 'const_tag_invalid_placement', `\`{@const}\` must be the immediate child of \`{#snippet}\`, \`{#if}\`, \`{:else if}\`, \`{:else}\`, \`{#each}\`, \`{:then}\`, \`{:catch}\`, \`<svelte:fragment>\`, \`<svelte:boundary>\` or \`<Component>\`\nhttps://svelte.dev/e/const_tag_invalid_placement`);
|
|
986
986
|
}
|
|
987
987
|
|
|
988
988
|
/**
|
|
@@ -170,6 +170,7 @@ export function client_component(analysis, options) {
|
|
|
170
170
|
// these are set inside the `Fragment` visitor, and cannot be used until then
|
|
171
171
|
init: /** @type {any} */ (null),
|
|
172
172
|
consts: /** @type {any} */ (null),
|
|
173
|
+
snippets: /** @type {any} */ (null),
|
|
173
174
|
let_directives: /** @type {any} */ (null),
|
|
174
175
|
update: /** @type {any} */ (null),
|
|
175
176
|
after_update: /** @type {any} */ (null),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import { Pattern } from 'estree' */
|
|
1
|
+
/** @import { Expression, Identifier, Pattern } from 'estree' */
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentContext } from '../types' */
|
|
4
4
|
/** @import { ExpressionMetadata } from '../../../nodes.js' */
|
|
@@ -88,8 +88,8 @@ export function ConstTag(node, context) {
|
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
90
|
* @param {ComponentContext['state']} state
|
|
91
|
-
* @param {
|
|
92
|
-
* @param {
|
|
91
|
+
* @param {Identifier} id
|
|
92
|
+
* @param {Expression} expression
|
|
93
93
|
* @param {ExpressionMetadata} metadata
|
|
94
94
|
* @param {import('#compiler').Binding[]} bindings
|
|
95
95
|
*/
|
|
@@ -99,7 +99,9 @@ function add_const_declaration(state, id, expression, metadata, bindings) {
|
|
|
99
99
|
const after = dev ? [b.stmt(b.call('$.get', id))] : [];
|
|
100
100
|
|
|
101
101
|
const has_await = metadata.has_await;
|
|
102
|
-
const blockers = [...metadata.dependencies]
|
|
102
|
+
const blockers = [...metadata.dependencies]
|
|
103
|
+
.map((dep) => dep.blocker)
|
|
104
|
+
.filter((b) => b !== null && b.object !== state.async_consts?.id);
|
|
103
105
|
|
|
104
106
|
if (has_await || state.async_consts || blockers.length > 0) {
|
|
105
107
|
const run = (state.async_consts ??= {
|
|
@@ -112,7 +114,11 @@ function add_const_declaration(state, id, expression, metadata, bindings) {
|
|
|
112
114
|
const assignment = b.assignment('=', id, expression);
|
|
113
115
|
const body = after.length === 0 ? assignment : b.block([b.stmt(assignment), ...after]);
|
|
114
116
|
|
|
115
|
-
if (blockers.length
|
|
117
|
+
if (blockers.length === 1) {
|
|
118
|
+
run.thunks.push(b.thunk(b.member(/** @type {Expression} */ (blockers[0]), 'promise')));
|
|
119
|
+
} else if (blockers.length > 0) {
|
|
120
|
+
run.thunks.push(b.thunk(b.call('$.wait', b.array(blockers))));
|
|
121
|
+
}
|
|
116
122
|
|
|
117
123
|
run.thunks.push(b.thunk(body, has_await));
|
|
118
124
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/** @import { Expression, Statement } from 'estree' */
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentClientTransformState, ComponentContext } from '../types' */
|
|
4
|
-
import { TEMPLATE_FRAGMENT, TEMPLATE_USE_IMPORT_NODE } from '../../../../../constants.js';
|
|
5
4
|
import * as b from '#compiler/builders';
|
|
5
|
+
import { TEMPLATE_FRAGMENT, TEMPLATE_USE_IMPORT_NODE } from '../../../../../constants.js';
|
|
6
6
|
import { clean_nodes, infer_namespace } from '../../utils.js';
|
|
7
7
|
import { transform_template } from '../transform-template/index.js';
|
|
8
|
+
import { Template } from '../transform-template/template.js';
|
|
8
9
|
import { process_children } from './shared/fragment.js';
|
|
9
10
|
import { build_render_statement, Memoizer } from './shared/utils.js';
|
|
10
|
-
import { Template } from '../transform-template/template.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* @param {AST.Fragment} node
|
|
@@ -60,6 +60,7 @@ export function Fragment(node, context) {
|
|
|
60
60
|
const state = {
|
|
61
61
|
...context.state,
|
|
62
62
|
init: [],
|
|
63
|
+
snippets: [],
|
|
63
64
|
consts: [],
|
|
64
65
|
let_directives: [],
|
|
65
66
|
update: [],
|
|
@@ -150,7 +151,7 @@ export function Fragment(node, context) {
|
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
153
|
|
|
153
|
-
body.push(...state.let_directives, ...state.consts);
|
|
154
|
+
body.push(...state.snippets, ...state.let_directives, ...state.consts);
|
|
154
155
|
|
|
155
156
|
if (state.async_consts && state.async_consts.thunks.length > 0) {
|
|
156
157
|
body.push(b.var(state.async_consts.id, b.call('$.run', b.array(state.async_consts.thunks))));
|
|
@@ -329,7 +329,7 @@ export function RegularElement(node, context) {
|
|
|
329
329
|
);
|
|
330
330
|
|
|
331
331
|
/** @type {typeof state} */
|
|
332
|
-
const child_state = { ...state, init: [], update: [], after_update: [] };
|
|
332
|
+
const child_state = { ...state, init: [], update: [], after_update: [], snippets: [] };
|
|
333
333
|
|
|
334
334
|
for (const node of hoisted) {
|
|
335
335
|
context.visit(node, child_state);
|
|
@@ -441,6 +441,7 @@ export function RegularElement(node, context) {
|
|
|
441
441
|
// Wrap children in `{...}` to avoid declaration conflicts
|
|
442
442
|
context.state.init.push(
|
|
443
443
|
b.block([
|
|
444
|
+
...child_state.snippets,
|
|
444
445
|
...child_state.init,
|
|
445
446
|
...element_state.init,
|
|
446
447
|
child_state.update.length > 0 ? build_render_statement(child_state) : b.empty,
|
|
@@ -77,7 +77,7 @@ export function SvelteBoundary(node, context) {
|
|
|
77
77
|
/** @type {Statement[]} */
|
|
78
78
|
const statements = [];
|
|
79
79
|
|
|
80
|
-
context.visit(child, { ...context.state,
|
|
80
|
+
context.visit(child, { ...context.state, snippets: statements });
|
|
81
81
|
|
|
82
82
|
const snippet = /** @type {VariableDeclaration} */ (statements[0]);
|
|
83
83
|
|
|
@@ -333,7 +333,7 @@ export function build_component(node, component_name, loc, context) {
|
|
|
333
333
|
// can be used as props without creating conflicts
|
|
334
334
|
context.visit(child, {
|
|
335
335
|
...context.state,
|
|
336
|
-
|
|
336
|
+
snippets: snippet_declarations
|
|
337
337
|
});
|
|
338
338
|
|
|
339
339
|
push_prop(b.prop('init', child.expression, child.expression));
|
|
@@ -15,7 +15,7 @@ export function ConstTag(node, context) {
|
|
|
15
15
|
const has_await = node.metadata.expression.has_await;
|
|
16
16
|
const blockers = [...node.metadata.expression.dependencies]
|
|
17
17
|
.map((dep) => dep.blocker)
|
|
18
|
-
.filter((b) => b !== null);
|
|
18
|
+
.filter((b) => b !== null && b.object !== context.state.async_consts?.id);
|
|
19
19
|
|
|
20
20
|
if (has_await || context.state.async_consts || blockers.length > 0) {
|
|
21
21
|
const run = (context.state.async_consts ??= {
|
|
@@ -30,7 +30,9 @@ export function ConstTag(node, context) {
|
|
|
30
30
|
context.state.init.push(b.let(identifier.name));
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
if (blockers.length
|
|
33
|
+
if (blockers.length === 1) {
|
|
34
|
+
run.thunks.push(b.thunk(/** @type {Expression} */ (blockers[0])));
|
|
35
|
+
} else if (blockers.length > 0) {
|
|
34
36
|
run.thunks.push(b.thunk(b.call('Promise.all', b.array(blockers))));
|
|
35
37
|
}
|
|
36
38
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentContext } from '../types.js' */
|
|
4
4
|
import * as b from '#compiler/builders';
|
|
5
|
-
import { create_push } from './shared/utils.js';
|
|
5
|
+
import { block_close, block_open, create_push } from './shared/utils.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @param {AST.HtmlTag} node
|
|
@@ -12,5 +12,15 @@ export function HtmlTag(node, context) {
|
|
|
12
12
|
const expression = /** @type {Expression} */ (context.visit(node.expression));
|
|
13
13
|
const call = b.call('$.html', expression);
|
|
14
14
|
|
|
15
|
+
const has_await = node.metadata.expression.has_await;
|
|
16
|
+
|
|
17
|
+
if (has_await) {
|
|
18
|
+
context.state.template.push(block_open);
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
context.state.template.push(create_push(call, node.metadata.expression, true));
|
|
22
|
+
|
|
23
|
+
if (has_await) {
|
|
24
|
+
context.state.template.push(block_close);
|
|
25
|
+
}
|
|
16
26
|
}
|
|
@@ -49,22 +49,25 @@ export function transform_body(instance_body, runner, transform) {
|
|
|
49
49
|
if (instance_body.async.length > 0) {
|
|
50
50
|
const thunks = instance_body.async.map((s) => {
|
|
51
51
|
if (s.node.type === 'VariableDeclarator') {
|
|
52
|
-
const visited = /** @type {ESTree.VariableDeclaration} */ (
|
|
52
|
+
const visited = /** @type {ESTree.VariableDeclaration | ESTree.EmptyStatement} */ (
|
|
53
53
|
transform(b.var(s.node.id, s.node.init))
|
|
54
54
|
);
|
|
55
55
|
|
|
56
|
-
const statements =
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
56
|
+
const statements =
|
|
57
|
+
visited.type === 'VariableDeclaration'
|
|
58
|
+
? visited.declarations.map((node) => {
|
|
59
|
+
if (
|
|
60
|
+
node.id.type === 'Identifier' &&
|
|
61
|
+
(node.id.name.startsWith('$$d') || node.id.name.startsWith('$$array'))
|
|
62
|
+
) {
|
|
63
|
+
// this is an intermediate declaration created in VariableDeclaration.js;
|
|
64
|
+
// subsequent statements depend on it
|
|
65
|
+
return b.var(node.id, node.init);
|
|
66
|
+
}
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
return b.stmt(b.assignment('=', node.id, node.init ?? b.void0));
|
|
69
|
+
})
|
|
70
|
+
: [];
|
|
68
71
|
|
|
69
72
|
if (statements.length === 1) {
|
|
70
73
|
const statement = /** @type {ESTree.ExpressionStatement} */ (statements[0]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import { TemplateNode, Value } from '#client' */
|
|
1
|
+
/** @import { Blocker, TemplateNode, Value } from '#client' */
|
|
2
2
|
import { flatten } from '../../reactivity/async.js';
|
|
3
3
|
import { Batch, current_batch } from '../../reactivity/batch.js';
|
|
4
4
|
import { get } from '../../runtime.js';
|
|
@@ -14,11 +14,16 @@ import { get_boundary } from './boundary.js';
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* @param {TemplateNode} node
|
|
17
|
-
* @param {
|
|
17
|
+
* @param {Blocker[]} blockers
|
|
18
18
|
* @param {Array<() => Promise<any>>} expressions
|
|
19
19
|
* @param {(anchor: TemplateNode, ...deriveds: Value[]) => void} fn
|
|
20
20
|
*/
|
|
21
21
|
export function async(node, blockers = [], expressions = [], fn) {
|
|
22
|
+
if (expressions.length === 0 && blockers.every((b) => b.settled)) {
|
|
23
|
+
fn(node);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
22
27
|
var boundary = get_boundary();
|
|
23
28
|
var batch = /** @type {Batch} */ (current_batch);
|
|
24
29
|
var blocking = boundary.is_rendered();
|
|
@@ -102,6 +102,7 @@ export class Boundary {
|
|
|
102
102
|
|
|
103
103
|
#local_pending_count = 0;
|
|
104
104
|
#pending_count = 0;
|
|
105
|
+
#pending_count_update_queued = false;
|
|
105
106
|
|
|
106
107
|
#is_creating_fallback = false;
|
|
107
108
|
|
|
@@ -202,12 +203,11 @@ export class Boundary {
|
|
|
202
203
|
|
|
203
204
|
#hydrate_pending_content() {
|
|
204
205
|
const pending = this.#props.pending;
|
|
205
|
-
if (!pending)
|
|
206
|
-
|
|
207
|
-
}
|
|
206
|
+
if (!pending) return;
|
|
207
|
+
|
|
208
208
|
this.#pending_effect = branch(() => pending(this.#anchor));
|
|
209
209
|
|
|
210
|
-
|
|
210
|
+
queue_micro_task(() => {
|
|
211
211
|
var anchor = this.#get_anchor();
|
|
212
212
|
|
|
213
213
|
this.#main_effect = this.#run(() => {
|
|
@@ -359,9 +359,15 @@ export class Boundary {
|
|
|
359
359
|
|
|
360
360
|
this.#local_pending_count += d;
|
|
361
361
|
|
|
362
|
-
if (this.#effect_pending)
|
|
363
|
-
|
|
364
|
-
|
|
362
|
+
if (!this.#effect_pending || this.#pending_count_update_queued) return;
|
|
363
|
+
this.#pending_count_update_queued = true;
|
|
364
|
+
|
|
365
|
+
queue_micro_task(() => {
|
|
366
|
+
this.#pending_count_update_queued = false;
|
|
367
|
+
if (this.#effect_pending) {
|
|
368
|
+
internal_set(this.#effect_pending, this.#local_pending_count);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
365
371
|
}
|
|
366
372
|
|
|
367
373
|
get_effect_pending() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import { Effect } from '#client' */
|
|
1
|
+
/** @import { Blocker, Effect } from '#client' */
|
|
2
2
|
import { DEV } from 'esm-env';
|
|
3
3
|
import { hydrating, set_hydrating } from '../hydration.js';
|
|
4
4
|
import { get_descriptors, get_prototype_of } from '../../../shared/utils.js';
|
|
@@ -483,7 +483,7 @@ function set_attributes(
|
|
|
483
483
|
* @param {(...expressions: any) => Record<string | symbol, any>} fn
|
|
484
484
|
* @param {Array<() => any>} sync
|
|
485
485
|
* @param {Array<() => Promise<any>>} async
|
|
486
|
-
* @param {
|
|
486
|
+
* @param {Blocker[]} blockers
|
|
487
487
|
* @param {string} [css_hash]
|
|
488
488
|
* @param {boolean} [should_remove_defaults]
|
|
489
489
|
* @param {boolean} [skip_warning]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import {
|
|
1
|
+
/** @import { Blocker, Effect, Value } from '#client' */
|
|
2
2
|
import { DESTROYED, STALE_REACTION } from '#client/constants';
|
|
3
3
|
import { DEV } from 'esm-env';
|
|
4
4
|
import {
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
import { aborted } from './effects.js';
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* @param {
|
|
30
|
+
* @param {Blocker[]} blockers
|
|
31
31
|
* @param {Array<() => any>} sync
|
|
32
32
|
* @param {Array<() => Promise<any>>} async
|
|
33
33
|
* @param {(values: Value[]) => any} fn
|
|
@@ -35,7 +35,10 @@ import { aborted } from './effects.js';
|
|
|
35
35
|
export function flatten(blockers, sync, async, fn) {
|
|
36
36
|
const d = is_runes() ? derived : derived_safe_equal;
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
// Filter out already-settled blockers - no need to wait for them
|
|
39
|
+
var pending = blockers.filter((b) => !b.settled);
|
|
40
|
+
|
|
41
|
+
if (async.length === 0 && pending.length === 0) {
|
|
39
42
|
fn(sync.map(d));
|
|
40
43
|
return;
|
|
41
44
|
}
|
|
@@ -44,47 +47,52 @@ export function flatten(blockers, sync, async, fn) {
|
|
|
44
47
|
var parent = /** @type {Effect} */ (active_effect);
|
|
45
48
|
|
|
46
49
|
var restore = capture();
|
|
50
|
+
var blocker_promise =
|
|
51
|
+
pending.length === 1
|
|
52
|
+
? pending[0].promise
|
|
53
|
+
: pending.length > 1
|
|
54
|
+
? Promise.all(pending.map((b) => b.promise))
|
|
55
|
+
: null;
|
|
56
|
+
|
|
57
|
+
/** @param {Value[]} values */
|
|
58
|
+
function finish(values) {
|
|
59
|
+
restore();
|
|
47
60
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
try {
|
|
62
|
+
fn(values);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
if ((parent.f & DESTROYED) === 0) {
|
|
65
|
+
invoke_error_boundary(error, parent);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
52
68
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// ignore errors in blocks that have already been destroyed
|
|
57
|
-
if ((parent.f & DESTROYED) === 0) {
|
|
58
|
-
invoke_error_boundary(error, parent);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
69
|
+
batch?.deactivate();
|
|
70
|
+
unset_context();
|
|
71
|
+
}
|
|
61
72
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
invoke_error_boundary(error, parent);
|
|
67
|
-
});
|
|
73
|
+
// Fast path: blockers but no async expressions
|
|
74
|
+
if (async.length === 0) {
|
|
75
|
+
/** @type {Promise<any>} */ (blocker_promise).then(() => finish(sync.map(d)));
|
|
76
|
+
return;
|
|
68
77
|
}
|
|
69
78
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
79
|
+
// Full path: has async expressions
|
|
80
|
+
function run() {
|
|
81
|
+
restore();
|
|
82
|
+
Promise.all(async.map((expression) => async_derived(expression)))
|
|
83
|
+
.then((result) => finish([...sync.map(d), ...result]))
|
|
84
|
+
.catch((error) => invoke_error_boundary(error, parent));
|
|
85
|
+
}
|
|
73
86
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
} finally {
|
|
77
|
-
batch?.deactivate();
|
|
78
|
-
unset_context();
|
|
79
|
-
}
|
|
80
|
-
});
|
|
87
|
+
if (blocker_promise) {
|
|
88
|
+
blocker_promise.then(run);
|
|
81
89
|
} else {
|
|
82
90
|
run();
|
|
83
91
|
}
|
|
84
92
|
}
|
|
85
93
|
|
|
86
94
|
/**
|
|
87
|
-
* @param {
|
|
95
|
+
* @param {Blocker[]} blockers
|
|
88
96
|
* @param {(values: Value[]) => any} fn
|
|
89
97
|
*/
|
|
90
98
|
export function run_after_blockers(blockers, fn) {
|
|
@@ -239,7 +247,13 @@ export function run(thunks) {
|
|
|
239
247
|
|
|
240
248
|
var promise = Promise.resolve(thunks[0]()).catch(handle_error);
|
|
241
249
|
|
|
242
|
-
|
|
250
|
+
/** @type {Blocker} */
|
|
251
|
+
var blocker = { promise, settled: false };
|
|
252
|
+
var blockers = [blocker];
|
|
253
|
+
|
|
254
|
+
promise.finally(() => {
|
|
255
|
+
blocker.settled = true;
|
|
256
|
+
});
|
|
243
257
|
|
|
244
258
|
for (const fn of thunks.slice(1)) {
|
|
245
259
|
promise = promise
|
|
@@ -255,13 +269,17 @@ export function run(thunks) {
|
|
|
255
269
|
restore();
|
|
256
270
|
return fn();
|
|
257
271
|
})
|
|
258
|
-
.catch(handle_error)
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
});
|
|
272
|
+
.catch(handle_error);
|
|
273
|
+
|
|
274
|
+
const blocker = { promise, settled: false };
|
|
275
|
+
blockers.push(blocker);
|
|
263
276
|
|
|
264
|
-
|
|
277
|
+
promise.finally(() => {
|
|
278
|
+
blocker.settled = true;
|
|
279
|
+
|
|
280
|
+
unset_context();
|
|
281
|
+
current_batch?.deactivate();
|
|
282
|
+
});
|
|
265
283
|
}
|
|
266
284
|
|
|
267
285
|
promise
|
|
@@ -273,5 +291,12 @@ export function run(thunks) {
|
|
|
273
291
|
batch.decrement(blocking);
|
|
274
292
|
});
|
|
275
293
|
|
|
276
|
-
return
|
|
294
|
+
return blockers;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* @param {Blocker[]} blockers
|
|
299
|
+
*/
|
|
300
|
+
export function wait(blockers) {
|
|
301
|
+
return Promise.all(blockers.map((b) => b.promise));
|
|
277
302
|
}
|
|
@@ -27,8 +27,6 @@ import {
|
|
|
27
27
|
get,
|
|
28
28
|
increment_write_version,
|
|
29
29
|
is_dirty,
|
|
30
|
-
is_updating_effect,
|
|
31
|
-
set_is_updating_effect,
|
|
32
30
|
update_effect
|
|
33
31
|
} from '../runtime.js';
|
|
34
32
|
import * as e from '../errors.js';
|
|
@@ -140,6 +138,8 @@ export class Batch {
|
|
|
140
138
|
|
|
141
139
|
is_fork = false;
|
|
142
140
|
|
|
141
|
+
#decrement_queued = false;
|
|
142
|
+
|
|
143
143
|
is_deferred() {
|
|
144
144
|
return this.is_fork || this.#blocking_pending > 0;
|
|
145
145
|
}
|
|
@@ -151,8 +151,6 @@ export class Batch {
|
|
|
151
151
|
process(root_effects) {
|
|
152
152
|
queued_root_effects = [];
|
|
153
153
|
|
|
154
|
-
previous_batch = null;
|
|
155
|
-
|
|
156
154
|
this.apply();
|
|
157
155
|
|
|
158
156
|
/** @type {Effect[]} */
|
|
@@ -170,14 +168,18 @@ export class Batch {
|
|
|
170
168
|
// log_inconsistent_branches(root);
|
|
171
169
|
}
|
|
172
170
|
|
|
173
|
-
if (!this.is_fork) {
|
|
174
|
-
this.#resolve();
|
|
175
|
-
}
|
|
176
|
-
|
|
177
171
|
if (this.is_deferred()) {
|
|
178
172
|
this.#defer_effects(render_effects);
|
|
179
173
|
this.#defer_effects(effects);
|
|
180
174
|
} else {
|
|
175
|
+
// append/remove branches
|
|
176
|
+
for (const fn of this.#commit_callbacks) fn();
|
|
177
|
+
this.#commit_callbacks.clear();
|
|
178
|
+
|
|
179
|
+
if (this.#pending === 0) {
|
|
180
|
+
this.#commit();
|
|
181
|
+
}
|
|
182
|
+
|
|
181
183
|
// If sources are written to, then work needs to happen in a separate batch, else prior sources would be mixed with
|
|
182
184
|
// newly updated sources, which could lead to infinite loops when effects run over and over again.
|
|
183
185
|
previous_batch = this;
|
|
@@ -330,18 +332,6 @@ export class Batch {
|
|
|
330
332
|
this.#discard_callbacks.clear();
|
|
331
333
|
}
|
|
332
334
|
|
|
333
|
-
#resolve() {
|
|
334
|
-
if (this.#blocking_pending === 0) {
|
|
335
|
-
// append/remove branches
|
|
336
|
-
for (const fn of this.#commit_callbacks) fn();
|
|
337
|
-
this.#commit_callbacks.clear();
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (this.#pending === 0) {
|
|
341
|
-
this.#commit();
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
335
|
#commit() {
|
|
346
336
|
// If there are other pending batches, they now need to be 'rebased' —
|
|
347
337
|
// in other words, we re-run block/async effects with the newly
|
|
@@ -438,7 +428,22 @@ export class Batch {
|
|
|
438
428
|
this.#pending -= 1;
|
|
439
429
|
if (blocking) this.#blocking_pending -= 1;
|
|
440
430
|
|
|
441
|
-
this
|
|
431
|
+
if (this.#decrement_queued) return;
|
|
432
|
+
this.#decrement_queued = true;
|
|
433
|
+
|
|
434
|
+
queue_micro_task(() => {
|
|
435
|
+
this.#decrement_queued = false;
|
|
436
|
+
|
|
437
|
+
if (!this.is_deferred()) {
|
|
438
|
+
// we only reschedule previously-deferred effects if we expect
|
|
439
|
+
// to be able to run them after processing the batch
|
|
440
|
+
this.revive();
|
|
441
|
+
} else if (queued_root_effects.length > 0) {
|
|
442
|
+
// if other effects are scheduled, process the batch _without_
|
|
443
|
+
// rescheduling the previously-deferred effects
|
|
444
|
+
this.flush();
|
|
445
|
+
}
|
|
446
|
+
});
|
|
442
447
|
}
|
|
443
448
|
|
|
444
449
|
revive() {
|
|
@@ -476,7 +481,7 @@ export class Batch {
|
|
|
476
481
|
batches.add(current_batch);
|
|
477
482
|
|
|
478
483
|
if (!is_flushing_sync) {
|
|
479
|
-
|
|
484
|
+
queue_micro_task(() => {
|
|
480
485
|
if (current_batch !== batch) {
|
|
481
486
|
// a flushSync happened in the meantime
|
|
482
487
|
return;
|
|
@@ -490,11 +495,6 @@ export class Batch {
|
|
|
490
495
|
return current_batch;
|
|
491
496
|
}
|
|
492
497
|
|
|
493
|
-
/** @param {() => void} task */
|
|
494
|
-
static enqueue(task) {
|
|
495
|
-
queue_micro_task(task);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
498
|
apply() {
|
|
499
499
|
if (!async_mode_flag || (!this.is_fork && batches.size === 1)) return;
|
|
500
500
|
|
|
@@ -561,14 +561,12 @@ export function flushSync(fn) {
|
|
|
561
561
|
}
|
|
562
562
|
|
|
563
563
|
function flush_effects() {
|
|
564
|
-
var was_updating_effect = is_updating_effect;
|
|
565
564
|
is_flushing = true;
|
|
566
565
|
|
|
567
566
|
var source_stacks = DEV ? new Set() : null;
|
|
568
567
|
|
|
569
568
|
try {
|
|
570
569
|
var flush_count = 0;
|
|
571
|
-
set_is_updating_effect(true);
|
|
572
570
|
|
|
573
571
|
while (queued_root_effects.length > 0) {
|
|
574
572
|
var batch = Batch.ensure();
|
|
@@ -612,7 +610,6 @@ function flush_effects() {
|
|
|
612
610
|
}
|
|
613
611
|
} finally {
|
|
614
612
|
is_flushing = false;
|
|
615
|
-
set_is_updating_effect(was_updating_effect);
|
|
616
613
|
|
|
617
614
|
last_scheduled_effect = null;
|
|
618
615
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import { ComponentContext, ComponentContextLegacy, Derived, Effect, TemplateNode, TransitionManager } from '#client' */
|
|
1
|
+
/** @import { Blocker, ComponentContext, ComponentContextLegacy, Derived, Effect, TemplateNode, TransitionManager } from '#client' */
|
|
2
2
|
import {
|
|
3
3
|
is_dirty,
|
|
4
4
|
active_effect,
|
|
@@ -361,7 +361,7 @@ export function render_effect(fn, flags = 0) {
|
|
|
361
361
|
* @param {(...expressions: any) => void | (() => void)} fn
|
|
362
362
|
* @param {Array<() => any>} sync
|
|
363
363
|
* @param {Array<() => Promise<any>>} async
|
|
364
|
-
* @param {
|
|
364
|
+
* @param {Blocker[]} blockers
|
|
365
365
|
*/
|
|
366
366
|
export function template_effect(fn, sync = [], async = [], blockers = []) {
|
|
367
367
|
flatten(blockers, sync, async, (values) => {
|
|
@@ -374,7 +374,7 @@ export function template_effect(fn, sync = [], async = [], blockers = []) {
|
|
|
374
374
|
* @param {(...expressions: any) => void | (() => void)} fn
|
|
375
375
|
* @param {Array<() => any>} sync
|
|
376
376
|
* @param {Array<() => Promise<any>>} async
|
|
377
|
-
* @param {
|
|
377
|
+
* @param {Blocker[]} blockers
|
|
378
378
|
*/
|
|
379
379
|
export function deferred_template_effect(fn, sync = [], async = [], blockers = []) {
|
|
380
380
|
var batch = /** @type {Batch} */ (current_batch);
|