svelte 5.42.2 → 5.43.0
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/phases/2-analyze/index.js +201 -2
- package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +6 -6
- package/src/compiler/phases/2-analyze/visitors/BindDirective.js +1 -0
- package/src/compiler/phases/2-analyze/visitors/SnippetBlock.js +6 -1
- package/src/compiler/phases/3-transform/client/transform-client.js +13 -34
- package/src/compiler/phases/3-transform/client/visitors/BindDirective.js +18 -7
- package/src/compiler/phases/3-transform/client/visitors/CallExpression.js +2 -2
- package/src/compiler/phases/3-transform/client/visitors/EachBlock.js +6 -4
- package/src/compiler/phases/3-transform/client/visitors/ExpressionStatement.js +1 -1
- package/src/compiler/phases/3-transform/client/visitors/HtmlTag.js +6 -4
- package/src/compiler/phases/3-transform/client/visitors/IfBlock.js +6 -4
- package/src/compiler/phases/3-transform/client/visitors/KeyBlock.js +5 -4
- package/src/compiler/phases/3-transform/client/visitors/Program.js +15 -3
- package/src/compiler/phases/3-transform/client/visitors/RenderTag.js +6 -4
- package/src/compiler/phases/3-transform/client/visitors/SlotElement.js +3 -1
- package/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +5 -4
- package/src/compiler/phases/3-transform/client/visitors/SvelteHead.js +3 -0
- package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +13 -2
- package/src/compiler/phases/3-transform/client/visitors/shared/component.js +20 -16
- package/src/compiler/phases/3-transform/client/visitors/shared/element.js +1 -0
- package/src/compiler/phases/3-transform/client/visitors/shared/utils.js +15 -1
- package/src/compiler/phases/3-transform/server/transform-server.js +9 -9
- package/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js +6 -2
- package/src/compiler/phases/3-transform/server/visitors/CallExpression.js +8 -1
- package/src/compiler/phases/3-transform/server/visitors/EachBlock.js +14 -3
- package/src/compiler/phases/3-transform/server/visitors/HtmlTag.js +3 -5
- package/src/compiler/phases/3-transform/server/visitors/IfBlock.js +12 -4
- package/src/compiler/phases/3-transform/server/visitors/KeyBlock.js +7 -1
- package/src/compiler/phases/3-transform/server/visitors/Program.js +25 -0
- package/src/compiler/phases/3-transform/server/visitors/RegularElement.js +14 -7
- package/src/compiler/phases/3-transform/server/visitors/RenderTag.js +27 -11
- package/src/compiler/phases/3-transform/server/visitors/SlotElement.js +7 -4
- package/src/compiler/phases/3-transform/server/visitors/SvelteElement.js +24 -4
- package/src/compiler/phases/3-transform/server/visitors/SvelteHead.js +10 -1
- package/src/compiler/phases/3-transform/server/visitors/shared/component.js +10 -4
- package/src/compiler/phases/3-transform/server/visitors/shared/utils.js +79 -8
- package/src/compiler/phases/3-transform/shared/transform-async.js +102 -0
- package/src/compiler/phases/nodes.js +24 -0
- package/src/compiler/phases/scope.js +27 -8
- package/src/compiler/utils/ast.js +1 -1
- package/src/compiler/utils/builders.js +15 -4
- package/src/internal/client/dom/blocks/async.js +7 -6
- package/src/internal/client/dom/blocks/await.js +6 -2
- package/src/internal/client/dom/blocks/boundary.js +5 -8
- package/src/internal/client/dom/blocks/svelte-head.js +10 -19
- package/src/internal/client/dom/elements/attributes.js +3 -1
- package/src/internal/client/index.js +1 -0
- package/src/internal/client/proxy.js +1 -1
- package/src/internal/client/reactivity/async.js +107 -49
- package/src/internal/client/reactivity/batch.js +2 -15
- package/src/internal/client/reactivity/effects.js +3 -2
- package/src/internal/client/render.js +1 -9
- package/src/internal/client/runtime.js +12 -11
- package/src/internal/server/index.js +4 -3
- package/src/internal/server/renderer.js +58 -3
- package/src/version.js +1 -1
- package/types/index.d.ts.map +1 -1
- package/src/compiler/phases/3-transform/client/visitors/ImportDeclaration.js +0 -16
package/package.json
CHANGED
|
@@ -6,7 +6,12 @@ import { walk } from 'zimmerframe';
|
|
|
6
6
|
import { parse } from '../1-parse/acorn.js';
|
|
7
7
|
import * as e from '../../errors.js';
|
|
8
8
|
import * as w from '../../warnings.js';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
extract_identifiers,
|
|
11
|
+
has_await_expression,
|
|
12
|
+
object,
|
|
13
|
+
unwrap_pattern
|
|
14
|
+
} from '../../utils/ast.js';
|
|
10
15
|
import * as b from '#compiler/builders';
|
|
11
16
|
import { Scope, ScopeRoot, create_scopes, get_rune, set_scope } from '../scope.js';
|
|
12
17
|
import check_graph_for_cycles from './utils/check_graph_for_cycles.js';
|
|
@@ -543,7 +548,13 @@ export function analyze_component(root, source, options) {
|
|
|
543
548
|
snippet_renderers: new Map(),
|
|
544
549
|
snippets: new Set(),
|
|
545
550
|
async_deriveds: new Set(),
|
|
546
|
-
pickled_awaits: new Set()
|
|
551
|
+
pickled_awaits: new Set(),
|
|
552
|
+
instance_body: {
|
|
553
|
+
sync: [],
|
|
554
|
+
async: [],
|
|
555
|
+
declarations: [],
|
|
556
|
+
hoisted: []
|
|
557
|
+
}
|
|
547
558
|
};
|
|
548
559
|
|
|
549
560
|
if (!runes) {
|
|
@@ -676,6 +687,194 @@ export function analyze_component(root, source, options) {
|
|
|
676
687
|
}
|
|
677
688
|
}
|
|
678
689
|
|
|
690
|
+
/**
|
|
691
|
+
* @param {ESTree.Node} expression
|
|
692
|
+
* @param {Scope} scope
|
|
693
|
+
* @param {Set<Binding>} touched
|
|
694
|
+
* @param {Set<ESTree.Node>} seen
|
|
695
|
+
*/
|
|
696
|
+
const touch = (expression, scope, touched, seen = new Set()) => {
|
|
697
|
+
if (seen.has(expression)) return;
|
|
698
|
+
seen.add(expression);
|
|
699
|
+
|
|
700
|
+
walk(
|
|
701
|
+
expression,
|
|
702
|
+
{ scope },
|
|
703
|
+
{
|
|
704
|
+
ImportDeclaration(node) {},
|
|
705
|
+
Identifier(node, context) {
|
|
706
|
+
const parent = /** @type {ESTree.Node} */ (context.path.at(-1));
|
|
707
|
+
if (is_reference(node, parent)) {
|
|
708
|
+
const binding = context.state.scope.get(node.name);
|
|
709
|
+
if (binding) {
|
|
710
|
+
touched.add(binding);
|
|
711
|
+
|
|
712
|
+
for (const assignment of binding.assignments) {
|
|
713
|
+
touch(assignment.value, assignment.scope, touched, seen);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
);
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* @param {ESTree.Node} node
|
|
724
|
+
* @param {Set<ESTree.Node>} seen
|
|
725
|
+
* @param {Set<Binding>} reads
|
|
726
|
+
* @param {Set<Binding>} writes
|
|
727
|
+
*/
|
|
728
|
+
const trace_references = (node, reads, writes, seen = new Set()) => {
|
|
729
|
+
if (seen.has(node)) return;
|
|
730
|
+
seen.add(node);
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* @param {ESTree.Pattern} node
|
|
734
|
+
* @param {Scope} scope
|
|
735
|
+
*/
|
|
736
|
+
function update(node, scope) {
|
|
737
|
+
for (const pattern of unwrap_pattern(node)) {
|
|
738
|
+
const node = object(pattern);
|
|
739
|
+
if (!node) return;
|
|
740
|
+
|
|
741
|
+
const binding = scope.get(node.name);
|
|
742
|
+
if (!binding) return;
|
|
743
|
+
|
|
744
|
+
writes.add(binding);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
walk(
|
|
749
|
+
node,
|
|
750
|
+
{ scope: instance.scope },
|
|
751
|
+
{
|
|
752
|
+
_(node, context) {
|
|
753
|
+
const scope = scopes.get(node);
|
|
754
|
+
if (scope) {
|
|
755
|
+
context.next({ scope });
|
|
756
|
+
} else {
|
|
757
|
+
context.next();
|
|
758
|
+
}
|
|
759
|
+
},
|
|
760
|
+
AssignmentExpression(node, context) {
|
|
761
|
+
update(node.left, context.state.scope);
|
|
762
|
+
},
|
|
763
|
+
UpdateExpression(node, context) {
|
|
764
|
+
update(
|
|
765
|
+
/** @type {ESTree.Identifier | ESTree.MemberExpression} */ (node.argument),
|
|
766
|
+
context.state.scope
|
|
767
|
+
);
|
|
768
|
+
},
|
|
769
|
+
CallExpression(node, context) {
|
|
770
|
+
// for now, assume everything touched by the callee ends up mutating the object
|
|
771
|
+
// TODO optimise this better
|
|
772
|
+
|
|
773
|
+
// special case — no need to peek inside effects as they only run once async work has completed
|
|
774
|
+
const rune = get_rune(node, context.state.scope);
|
|
775
|
+
if (rune === '$effect') return;
|
|
776
|
+
|
|
777
|
+
/** @type {Set<Binding>} */
|
|
778
|
+
const touched = new Set();
|
|
779
|
+
touch(node, context.state.scope, touched);
|
|
780
|
+
|
|
781
|
+
for (const b of touched) {
|
|
782
|
+
writes.add(b);
|
|
783
|
+
}
|
|
784
|
+
},
|
|
785
|
+
// don't look inside functions until they are called
|
|
786
|
+
ArrowFunctionExpression(_, context) {},
|
|
787
|
+
FunctionDeclaration(_, context) {},
|
|
788
|
+
FunctionExpression(_, context) {},
|
|
789
|
+
Identifier(node, context) {
|
|
790
|
+
const parent = /** @type {ESTree.Node} */ (context.path.at(-1));
|
|
791
|
+
if (is_reference(node, parent)) {
|
|
792
|
+
const binding = context.state.scope.get(node.name);
|
|
793
|
+
if (binding) {
|
|
794
|
+
reads.add(binding);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
);
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
let awaited = false;
|
|
803
|
+
|
|
804
|
+
// TODO this should probably be attached to the scope?
|
|
805
|
+
var promises = b.id('$$promises');
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* @param {ESTree.Identifier} id
|
|
809
|
+
* @param {ESTree.Expression} blocker
|
|
810
|
+
*/
|
|
811
|
+
function push_declaration(id, blocker) {
|
|
812
|
+
analysis.instance_body.declarations.push(id);
|
|
813
|
+
|
|
814
|
+
const binding = /** @type {Binding} */ (instance.scope.get(id.name));
|
|
815
|
+
binding.blocker = blocker;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
for (let node of instance.ast.body) {
|
|
819
|
+
if (node.type === 'ImportDeclaration') {
|
|
820
|
+
analysis.instance_body.hoisted.push(node);
|
|
821
|
+
continue;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
if (node.type === 'ExportDefaultDeclaration' || node.type === 'ExportAllDeclaration') {
|
|
825
|
+
// these can't exist inside `<script>` but TypeScript doesn't know that
|
|
826
|
+
continue;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
if (node.type === 'ExportNamedDeclaration') {
|
|
830
|
+
if (node.declaration) {
|
|
831
|
+
node = node.declaration;
|
|
832
|
+
} else {
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
const has_await = has_await_expression(node);
|
|
838
|
+
awaited ||= has_await;
|
|
839
|
+
|
|
840
|
+
if (awaited && node.type !== 'FunctionDeclaration') {
|
|
841
|
+
/** @type {Set<Binding>} */
|
|
842
|
+
const reads = new Set(); // TODO we're not actually using this yet
|
|
843
|
+
|
|
844
|
+
/** @type {Set<Binding>} */
|
|
845
|
+
const writes = new Set();
|
|
846
|
+
|
|
847
|
+
trace_references(node, reads, writes);
|
|
848
|
+
|
|
849
|
+
const blocker = b.member(promises, b.literal(analysis.instance_body.async.length), true);
|
|
850
|
+
|
|
851
|
+
for (const binding of writes) {
|
|
852
|
+
binding.blocker = blocker;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
if (node.type === 'VariableDeclaration') {
|
|
856
|
+
for (const declarator of node.declarations) {
|
|
857
|
+
for (const id of extract_identifiers(declarator.id)) {
|
|
858
|
+
push_declaration(id, blocker);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// one declarator per declaration, makes things simpler
|
|
862
|
+
analysis.instance_body.async.push({
|
|
863
|
+
node: declarator,
|
|
864
|
+
has_await
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
} else if (node.type === 'ClassDeclaration') {
|
|
868
|
+
push_declaration(node.id, blocker);
|
|
869
|
+
analysis.instance_body.async.push({ node, has_await });
|
|
870
|
+
} else {
|
|
871
|
+
analysis.instance_body.async.push({ node, has_await });
|
|
872
|
+
}
|
|
873
|
+
} else {
|
|
874
|
+
analysis.instance_body.sync.push(node);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
679
878
|
if (analysis.runes) {
|
|
680
879
|
const props_refs = module.scope.references.get('$$props');
|
|
681
880
|
if (props_refs) {
|
|
@@ -10,16 +10,13 @@ import * as e from '../../../errors.js';
|
|
|
10
10
|
export function AwaitExpression(node, context) {
|
|
11
11
|
const tla = context.state.ast_type === 'instance' && context.state.function_depth === 1;
|
|
12
12
|
|
|
13
|
-
// preserve context for
|
|
14
|
-
// a) top-level await and
|
|
15
|
-
// b) awaits that precede other expressions in template or `$derived(...)`
|
|
13
|
+
// preserve context for awaits that precede other expressions in template or `$derived(...)`
|
|
16
14
|
if (
|
|
17
|
-
|
|
18
|
-
(is_reactive_expression(
|
|
15
|
+
is_reactive_expression(
|
|
19
16
|
context.path,
|
|
20
17
|
context.state.derived_function_depth === context.state.function_depth
|
|
21
18
|
) &&
|
|
22
|
-
|
|
19
|
+
!is_last_evaluated_expression(context.path, node)
|
|
23
20
|
) {
|
|
24
21
|
context.state.analysis.pickled_awaits.add(node);
|
|
25
22
|
}
|
|
@@ -145,6 +142,9 @@ function is_last_evaluated_expression(path, node) {
|
|
|
145
142
|
if (node !== parent.expressions.at(-1)) return false;
|
|
146
143
|
break;
|
|
147
144
|
|
|
145
|
+
case 'VariableDeclarator':
|
|
146
|
+
return true;
|
|
147
|
+
|
|
148
148
|
default:
|
|
149
149
|
return false;
|
|
150
150
|
}
|
|
@@ -81,8 +81,13 @@ export function SnippetBlock(node, context) {
|
|
|
81
81
|
function can_hoist_snippet(scope, scopes, visited = new Set()) {
|
|
82
82
|
for (const [reference] of scope.references) {
|
|
83
83
|
const binding = scope.get(reference);
|
|
84
|
+
if (!binding) continue;
|
|
84
85
|
|
|
85
|
-
if (
|
|
86
|
+
if (binding.blocker) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (binding.scope.function_depth === 0) {
|
|
86
91
|
continue;
|
|
87
92
|
}
|
|
88
93
|
|
|
@@ -33,7 +33,6 @@ import { FunctionExpression } from './visitors/FunctionExpression.js';
|
|
|
33
33
|
import { HtmlTag } from './visitors/HtmlTag.js';
|
|
34
34
|
import { Identifier } from './visitors/Identifier.js';
|
|
35
35
|
import { IfBlock } from './visitors/IfBlock.js';
|
|
36
|
-
import { ImportDeclaration } from './visitors/ImportDeclaration.js';
|
|
37
36
|
import { KeyBlock } from './visitors/KeyBlock.js';
|
|
38
37
|
import { LabeledStatement } from './visitors/LabeledStatement.js';
|
|
39
38
|
import { LetDirective } from './visitors/LetDirective.js';
|
|
@@ -111,7 +110,6 @@ const visitors = {
|
|
|
111
110
|
HtmlTag,
|
|
112
111
|
Identifier,
|
|
113
112
|
IfBlock,
|
|
114
|
-
ImportDeclaration,
|
|
115
113
|
KeyBlock,
|
|
116
114
|
LabeledStatement,
|
|
117
115
|
LetDirective,
|
|
@@ -153,7 +151,7 @@ export function client_component(analysis, options) {
|
|
|
153
151
|
scope: analysis.module.scope,
|
|
154
152
|
scopes: analysis.module.scopes,
|
|
155
153
|
is_instance: false,
|
|
156
|
-
hoisted: [b.import_all('$', 'svelte/internal/client')],
|
|
154
|
+
hoisted: [b.import_all('$', 'svelte/internal/client'), ...analysis.instance_body.hoisted],
|
|
157
155
|
node: /** @type {any} */ (null), // populated by the root node
|
|
158
156
|
legacy_reactive_imports: [],
|
|
159
157
|
legacy_reactive_statements: new Map(),
|
|
@@ -370,41 +368,22 @@ export function client_component(analysis, options) {
|
|
|
370
368
|
analysis.reactive_statements.size > 0 ||
|
|
371
369
|
component_returned_object.length > 0;
|
|
372
370
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
const body = b.block([
|
|
378
|
-
...store_setup,
|
|
379
|
-
...state.instance_level_snippets,
|
|
380
|
-
.../** @type {ESTree.Statement[]} */ (instance.body),
|
|
381
|
-
...(should_inject_context && component_returned_object.length > 0
|
|
382
|
-
? [b.stmt(b.assignment('=', b.id('$$exports'), b.object(component_returned_object)))]
|
|
383
|
-
: []),
|
|
384
|
-
b.if(b.call('$.aborted'), b.return()),
|
|
385
|
-
.../** @type {ESTree.Statement[]} */ (template.body)
|
|
386
|
-
]);
|
|
387
|
-
|
|
388
|
-
component_block.body.push(
|
|
389
|
-
b.stmt(b.call(`$.async_body`, b.id('$$anchor'), b.arrow([b.id('$$anchor')], body, true)))
|
|
390
|
-
);
|
|
391
|
-
} else {
|
|
392
|
-
component_block.body.push(
|
|
393
|
-
...state.instance_level_snippets,
|
|
394
|
-
.../** @type {ESTree.Statement[]} */ (instance.body)
|
|
395
|
-
);
|
|
396
|
-
if (should_inject_context && component_returned_object.length > 0) {
|
|
397
|
-
component_block.body.push(b.var('$$exports', b.object(component_returned_object)));
|
|
398
|
-
}
|
|
399
|
-
component_block.body.unshift(...store_setup);
|
|
371
|
+
component_block.body.push(
|
|
372
|
+
...state.instance_level_snippets,
|
|
373
|
+
.../** @type {ESTree.Statement[]} */ (instance.body)
|
|
374
|
+
);
|
|
400
375
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
376
|
+
if (should_inject_context && component_returned_object.length > 0) {
|
|
377
|
+
component_block.body.push(b.var('$$exports', b.object(component_returned_object)));
|
|
378
|
+
}
|
|
379
|
+
component_block.body.unshift(...store_setup);
|
|
404
380
|
|
|
405
|
-
|
|
381
|
+
if (!analysis.runes && analysis.needs_context) {
|
|
382
|
+
component_block.body.push(b.stmt(b.call('$.init', analysis.immutable ? b.true : undefined)));
|
|
406
383
|
}
|
|
407
384
|
|
|
385
|
+
component_block.body.push(.../** @type {ESTree.Statement[]} */ (template.body));
|
|
386
|
+
|
|
408
387
|
if (analysis.needs_mutation_validation) {
|
|
409
388
|
component_block.body.unshift(
|
|
410
389
|
b.var('$$ownership_validator', b.call('$.create_ownership_validator', b.id('$$props')))
|
|
@@ -243,18 +243,29 @@ export function BindDirective(node, context) {
|
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
+
const defer =
|
|
247
|
+
node.name !== 'this' &&
|
|
248
|
+
parent.type === 'RegularElement' &&
|
|
249
|
+
parent.attributes.find((a) => a.type === 'UseDirective');
|
|
250
|
+
|
|
251
|
+
let statement = defer ? b.stmt(b.call('$.effect', b.thunk(call))) : b.stmt(call);
|
|
252
|
+
|
|
253
|
+
// TODO this doesn't account for function bindings
|
|
254
|
+
if (node.metadata.binding?.blocker) {
|
|
255
|
+
statement = b.stmt(
|
|
256
|
+
b.call(b.member(node.metadata.binding.blocker, b.id('then')), b.thunk(b.block([statement])))
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
246
260
|
// Bindings need to happen after attribute updates, therefore after the render effect, and in order with events/actions.
|
|
247
261
|
// bind:this is a special case as it's one-way and could influence the render effect.
|
|
248
262
|
if (node.name === 'this') {
|
|
249
|
-
context.state.init.push(
|
|
263
|
+
context.state.init.push(statement);
|
|
250
264
|
} else {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (has_use) {
|
|
255
|
-
context.state.init.push(b.stmt(b.call('$.effect', b.thunk(call))));
|
|
265
|
+
if (defer) {
|
|
266
|
+
context.state.init.push(statement);
|
|
256
267
|
} else {
|
|
257
|
-
context.state.after_update.push(
|
|
268
|
+
context.state.after_update.push(statement);
|
|
258
269
|
}
|
|
259
270
|
}
|
|
260
271
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import { CallExpression, Expression
|
|
1
|
+
/** @import { CallExpression, Expression } from 'estree' */
|
|
2
2
|
/** @import { Context } from '../types' */
|
|
3
3
|
import { dev, is_ignored } from '../../../../state.js';
|
|
4
4
|
import * as b from '#compiler/builders';
|
|
@@ -80,7 +80,7 @@ export function CallExpression(node, context) {
|
|
|
80
80
|
);
|
|
81
81
|
|
|
82
82
|
case '$effect.pending':
|
|
83
|
-
return b.call('$.pending');
|
|
83
|
+
return b.call('$.eager', b.thunk(b.call('$.pending')));
|
|
84
84
|
|
|
85
85
|
case '$inspect':
|
|
86
86
|
case '$inspect().with':
|
|
@@ -312,9 +312,10 @@ export function EachBlock(node, context) {
|
|
|
312
312
|
declarations.push(b.let(node.index, index));
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
const
|
|
315
|
+
const is_async = node.metadata.expression.is_async();
|
|
316
|
+
|
|
317
|
+
const get_collection = b.thunk(collection, node.metadata.expression.has_await);
|
|
318
|
+
const thunk = is_async ? b.thunk(b.call('$.get', b.id('$$collection'))) : get_collection;
|
|
318
319
|
|
|
319
320
|
const render_args = [b.id('$$anchor'), item];
|
|
320
321
|
if (uses_index || collection_id) render_args.push(index);
|
|
@@ -341,12 +342,13 @@ export function EachBlock(node, context) {
|
|
|
341
342
|
statements.unshift(b.stmt(b.call('$.validate_each_keys', thunk, key_function)));
|
|
342
343
|
}
|
|
343
344
|
|
|
344
|
-
if (
|
|
345
|
+
if (is_async) {
|
|
345
346
|
context.state.init.push(
|
|
346
347
|
b.stmt(
|
|
347
348
|
b.call(
|
|
348
349
|
'$.async',
|
|
349
350
|
context.state.node,
|
|
351
|
+
node.metadata.expression.blockers(),
|
|
350
352
|
b.array([get_collection]),
|
|
351
353
|
b.arrow([context.state.node, b.id('$$collection')], b.block(statements))
|
|
352
354
|
)
|
|
@@ -11,9 +11,10 @@ import { build_expression } from './shared/utils.js';
|
|
|
11
11
|
export function HtmlTag(node, context) {
|
|
12
12
|
context.state.template.push_comment();
|
|
13
13
|
|
|
14
|
-
const
|
|
14
|
+
const is_async = node.metadata.expression.is_async();
|
|
15
|
+
|
|
15
16
|
const expression = build_expression(context, node.expression, node.metadata.expression);
|
|
16
|
-
const html =
|
|
17
|
+
const html = is_async ? b.call('$.get', b.id('$$html')) : expression;
|
|
17
18
|
|
|
18
19
|
const is_svg = context.state.metadata.namespace === 'svg';
|
|
19
20
|
const is_mathml = context.state.metadata.namespace === 'mathml';
|
|
@@ -30,13 +31,14 @@ export function HtmlTag(node, context) {
|
|
|
30
31
|
);
|
|
31
32
|
|
|
32
33
|
// push into init, so that bindings run afterwards, which might trigger another run and override hydration
|
|
33
|
-
if (
|
|
34
|
+
if (is_async) {
|
|
34
35
|
context.state.init.push(
|
|
35
36
|
b.stmt(
|
|
36
37
|
b.call(
|
|
37
38
|
'$.async',
|
|
38
39
|
context.state.node,
|
|
39
|
-
|
|
40
|
+
node.metadata.expression.blockers(),
|
|
41
|
+
b.array([b.thunk(expression, node.metadata.expression.has_await)]),
|
|
40
42
|
b.arrow([context.state.node, b.id('$$html')], b.block([statement]))
|
|
41
43
|
)
|
|
42
44
|
)
|
|
@@ -25,9 +25,10 @@ export function IfBlock(node, context) {
|
|
|
25
25
|
statements.push(b.var(alternate_id, b.arrow([b.id('$$anchor')], alternate)));
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const
|
|
28
|
+
const is_async = node.metadata.expression.is_async();
|
|
29
|
+
|
|
29
30
|
const expression = build_expression(context, node.test, node.metadata.expression);
|
|
30
|
-
const test =
|
|
31
|
+
const test = is_async ? b.call('$.get', b.id('$$condition')) : expression;
|
|
31
32
|
|
|
32
33
|
/** @type {Expression[]} */
|
|
33
34
|
const args = [
|
|
@@ -71,13 +72,14 @@ export function IfBlock(node, context) {
|
|
|
71
72
|
|
|
72
73
|
statements.push(add_svelte_meta(b.call('$.if', ...args), node, 'if'));
|
|
73
74
|
|
|
74
|
-
if (
|
|
75
|
+
if (is_async) {
|
|
75
76
|
context.state.init.push(
|
|
76
77
|
b.stmt(
|
|
77
78
|
b.call(
|
|
78
79
|
'$.async',
|
|
79
80
|
context.state.node,
|
|
80
|
-
|
|
81
|
+
node.metadata.expression.blockers(),
|
|
82
|
+
b.array([b.thunk(expression, node.metadata.expression.has_await)]),
|
|
81
83
|
b.arrow([context.state.node, b.id('$$condition')], b.block(statements))
|
|
82
84
|
)
|
|
83
85
|
)
|
|
@@ -11,10 +11,10 @@ import { build_expression, add_svelte_meta } from './shared/utils.js';
|
|
|
11
11
|
export function KeyBlock(node, context) {
|
|
12
12
|
context.state.template.push_comment();
|
|
13
13
|
|
|
14
|
-
const
|
|
14
|
+
const is_async = node.metadata.expression.is_async();
|
|
15
15
|
|
|
16
16
|
const expression = build_expression(context, node.expression, node.metadata.expression);
|
|
17
|
-
const key = b.thunk(
|
|
17
|
+
const key = b.thunk(is_async ? b.call('$.get', b.id('$$key')) : expression);
|
|
18
18
|
const body = /** @type {Expression} */ (context.visit(node.fragment));
|
|
19
19
|
|
|
20
20
|
let statement = add_svelte_meta(
|
|
@@ -23,12 +23,13 @@ export function KeyBlock(node, context) {
|
|
|
23
23
|
'key'
|
|
24
24
|
);
|
|
25
25
|
|
|
26
|
-
if (
|
|
26
|
+
if (is_async) {
|
|
27
27
|
statement = b.stmt(
|
|
28
28
|
b.call(
|
|
29
29
|
'$.async',
|
|
30
30
|
context.state.node,
|
|
31
|
-
|
|
31
|
+
node.metadata.expression.blockers(),
|
|
32
|
+
b.array([b.thunk(expression, node.metadata.expression.has_await)]),
|
|
32
33
|
b.arrow([context.state.node, b.id('$$key')], b.block([statement]))
|
|
33
34
|
)
|
|
34
35
|
);
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
/** @import { Expression, ImportDeclaration, MemberExpression, Program } from 'estree' */
|
|
1
|
+
/** @import { Expression, ImportDeclaration, MemberExpression, Node, Program } from 'estree' */
|
|
2
2
|
/** @import { ComponentContext } from '../types' */
|
|
3
3
|
import { build_getter, is_prop_source } from '../utils.js';
|
|
4
4
|
import * as b from '#compiler/builders';
|
|
5
5
|
import { add_state_transformers } from './shared/declarations.js';
|
|
6
|
+
import { transform_body } from '../../shared/transform-async.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
|
-
* @param {Program}
|
|
9
|
+
* @param {Program} node
|
|
9
10
|
* @param {ComponentContext} context
|
|
10
11
|
*/
|
|
11
|
-
export function Program(
|
|
12
|
+
export function Program(node, context) {
|
|
12
13
|
if (!context.state.analysis.runes) {
|
|
13
14
|
context.state.transform['$$props'] = {
|
|
14
15
|
read: (node) => ({ ...node, name: '$$sanitized_props' })
|
|
@@ -137,5 +138,16 @@ export function Program(_, context) {
|
|
|
137
138
|
|
|
138
139
|
add_state_transformers(context);
|
|
139
140
|
|
|
141
|
+
if (context.state.is_instance) {
|
|
142
|
+
return {
|
|
143
|
+
...node,
|
|
144
|
+
body: transform_body(
|
|
145
|
+
context.state.analysis.instance_body,
|
|
146
|
+
b.id('$.run'),
|
|
147
|
+
(node) => /** @type {Node} */ (context.visit(node))
|
|
148
|
+
)
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
140
152
|
context.next();
|
|
141
153
|
}
|
|
@@ -22,11 +22,11 @@ export function RenderTag(node, context) {
|
|
|
22
22
|
for (let i = 0; i < call.arguments.length; i++) {
|
|
23
23
|
const arg = /** @type {Expression} */ (call.arguments[i]);
|
|
24
24
|
const metadata = node.metadata.arguments[i];
|
|
25
|
-
|
|
26
25
|
let expression = build_expression(context, arg, metadata);
|
|
26
|
+
const memoized = memoizer.add(expression, metadata);
|
|
27
27
|
|
|
28
|
-
if (
|
|
29
|
-
expression = b.call('$.get',
|
|
28
|
+
if (expression !== memoized) {
|
|
29
|
+
expression = b.call('$.get', memoized);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
args.push(b.thunk(expression));
|
|
@@ -71,13 +71,15 @@ export function RenderTag(node, context) {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
const async_values = memoizer.async_values();
|
|
74
|
+
const blockers = memoizer.blockers();
|
|
74
75
|
|
|
75
|
-
if (async_values) {
|
|
76
|
+
if (async_values || blockers) {
|
|
76
77
|
context.state.init.push(
|
|
77
78
|
b.stmt(
|
|
78
79
|
b.call(
|
|
79
80
|
'$.async',
|
|
80
81
|
context.state.node,
|
|
82
|
+
blockers,
|
|
81
83
|
memoizer.async_values(),
|
|
82
84
|
b.arrow([context.state.node, ...memoizer.async_ids()], b.block(statements))
|
|
83
85
|
)
|
|
@@ -74,13 +74,15 @@ export function SlotElement(node, context) {
|
|
|
74
74
|
);
|
|
75
75
|
|
|
76
76
|
const async_values = memoizer.async_values();
|
|
77
|
+
const blockers = memoizer.blockers();
|
|
77
78
|
|
|
78
|
-
if (async_values) {
|
|
79
|
+
if (async_values || blockers) {
|
|
79
80
|
context.state.init.push(
|
|
80
81
|
b.stmt(
|
|
81
82
|
b.call(
|
|
82
83
|
'$.async',
|
|
83
84
|
context.state.node,
|
|
85
|
+
blockers,
|
|
84
86
|
async_values,
|
|
85
87
|
b.arrow([context.state.node, ...memoizer.async_ids()], b.block(statements))
|
|
86
88
|
)
|
|
@@ -93,10 +93,10 @@ export function SvelteElement(node, context) {
|
|
|
93
93
|
);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
const
|
|
96
|
+
const is_async = node.metadata.expression.is_async();
|
|
97
97
|
|
|
98
98
|
const expression = /** @type {Expression} */ (context.visit(node.tag));
|
|
99
|
-
const get_tag = b.thunk(
|
|
99
|
+
const get_tag = b.thunk(is_async ? b.call('$.get', b.id('$$tag')) : expression);
|
|
100
100
|
|
|
101
101
|
/** @type {Statement[]} */
|
|
102
102
|
const inner = inner_context.state.init;
|
|
@@ -139,13 +139,14 @@ export function SvelteElement(node, context) {
|
|
|
139
139
|
)
|
|
140
140
|
);
|
|
141
141
|
|
|
142
|
-
if (
|
|
142
|
+
if (is_async) {
|
|
143
143
|
context.state.init.push(
|
|
144
144
|
b.stmt(
|
|
145
145
|
b.call(
|
|
146
146
|
'$.async',
|
|
147
147
|
context.state.node,
|
|
148
|
-
|
|
148
|
+
node.metadata.expression.blockers(),
|
|
149
|
+
b.array([b.thunk(expression, node.metadata.expression.has_await)]),
|
|
149
150
|
b.arrow([context.state.node, b.id('$$tag')], b.block(statements))
|
|
150
151
|
)
|
|
151
152
|
)
|