svelte 5.43.8 → 5.43.9
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/1-parse/utils/create.js +1 -2
- package/src/compiler/phases/2-analyze/index.js +269 -187
- package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +0 -4
- package/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js +29 -15
- package/src/compiler/phases/3-transform/client/visitors/ConstTag.js +50 -14
- package/src/compiler/phases/3-transform/client/visitors/Fragment.js +5 -14
- package/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js +2 -8
- package/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js +12 -2
- package/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +1 -1
- package/src/compiler/phases/3-transform/server/visitors/ConstTag.js +25 -1
- package/src/compiler/phases/3-transform/server/visitors/EachBlock.js +2 -6
- package/src/compiler/phases/3-transform/server/visitors/Fragment.js +8 -1
- package/src/compiler/phases/3-transform/server/visitors/IfBlock.js +1 -5
- package/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js +0 -5
- package/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js +3 -10
- package/src/compiler/phases/3-transform/server/visitors/shared/component.js +1 -6
- package/src/compiler/phases/nodes.js +4 -0
- package/src/compiler/phases/scope.js +8 -4
- package/src/compiler/utils/builders.js +1 -1
- package/src/internal/client/dom/blocks/each.js +9 -7
- package/src/internal/client/reactivity/deriveds.js +5 -2
- package/src/internal/client/reactivity/sources.js +17 -9
- package/src/version.js +1 -1
package/package.json
CHANGED
|
@@ -687,193 +687,7 @@ export function analyze_component(root, source, options) {
|
|
|
687
687
|
}
|
|
688
688
|
}
|
|
689
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
|
-
}
|
|
690
|
+
calculate_blockers(instance, scopes, analysis);
|
|
877
691
|
|
|
878
692
|
if (analysis.runes) {
|
|
879
693
|
const props_refs = module.scope.references.get('$$props');
|
|
@@ -1118,6 +932,274 @@ export function analyze_component(root, source, options) {
|
|
|
1118
932
|
return analysis;
|
|
1119
933
|
}
|
|
1120
934
|
|
|
935
|
+
/**
|
|
936
|
+
* Analyzes the instance's top level statements to calculate which bindings need to wait on which
|
|
937
|
+
* top level statements. This includes indirect blockers such as functions referencing async top level statements.
|
|
938
|
+
*
|
|
939
|
+
* @param {Js} instance
|
|
940
|
+
* @param {Map<AST.SvelteNode, Scope>} scopes
|
|
941
|
+
* @param {ComponentAnalysis} analysis
|
|
942
|
+
* @returns {void}
|
|
943
|
+
*/
|
|
944
|
+
function calculate_blockers(instance, scopes, analysis) {
|
|
945
|
+
/**
|
|
946
|
+
* @param {ESTree.Node} expression
|
|
947
|
+
* @param {Scope} scope
|
|
948
|
+
* @param {Set<Binding>} touched
|
|
949
|
+
* @param {Set<ESTree.Node>} seen
|
|
950
|
+
*/
|
|
951
|
+
const touch = (expression, scope, touched, seen = new Set()) => {
|
|
952
|
+
if (seen.has(expression)) return;
|
|
953
|
+
seen.add(expression);
|
|
954
|
+
|
|
955
|
+
walk(
|
|
956
|
+
expression,
|
|
957
|
+
{ scope },
|
|
958
|
+
{
|
|
959
|
+
ImportDeclaration(node) {},
|
|
960
|
+
Identifier(node, context) {
|
|
961
|
+
const parent = /** @type {ESTree.Node} */ (context.path.at(-1));
|
|
962
|
+
if (is_reference(node, parent)) {
|
|
963
|
+
const binding = context.state.scope.get(node.name);
|
|
964
|
+
if (binding) {
|
|
965
|
+
touched.add(binding);
|
|
966
|
+
|
|
967
|
+
for (const assignment of binding.assignments) {
|
|
968
|
+
touch(assignment.value, assignment.scope, touched, seen);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
);
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
/**
|
|
978
|
+
* @param {ESTree.Node} node
|
|
979
|
+
* @param {Set<ESTree.Node>} seen
|
|
980
|
+
* @param {Set<Binding>} reads
|
|
981
|
+
* @param {Set<Binding>} writes
|
|
982
|
+
*/
|
|
983
|
+
const trace_references = (node, reads, writes, seen = new Set()) => {
|
|
984
|
+
if (seen.has(node)) return;
|
|
985
|
+
seen.add(node);
|
|
986
|
+
|
|
987
|
+
/**
|
|
988
|
+
* @param {ESTree.Pattern} node
|
|
989
|
+
* @param {Scope} scope
|
|
990
|
+
*/
|
|
991
|
+
function update(node, scope) {
|
|
992
|
+
for (const pattern of unwrap_pattern(node)) {
|
|
993
|
+
const node = object(pattern);
|
|
994
|
+
if (!node) return;
|
|
995
|
+
|
|
996
|
+
const binding = scope.get(node.name);
|
|
997
|
+
if (!binding) return;
|
|
998
|
+
|
|
999
|
+
writes.add(binding);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
walk(
|
|
1004
|
+
node,
|
|
1005
|
+
{ scope: instance.scope },
|
|
1006
|
+
{
|
|
1007
|
+
_(node, context) {
|
|
1008
|
+
const scope = scopes.get(node);
|
|
1009
|
+
if (scope) {
|
|
1010
|
+
context.next({ scope });
|
|
1011
|
+
} else {
|
|
1012
|
+
context.next();
|
|
1013
|
+
}
|
|
1014
|
+
},
|
|
1015
|
+
AssignmentExpression(node, context) {
|
|
1016
|
+
update(node.left, context.state.scope);
|
|
1017
|
+
},
|
|
1018
|
+
UpdateExpression(node, context) {
|
|
1019
|
+
update(
|
|
1020
|
+
/** @type {ESTree.Identifier | ESTree.MemberExpression} */ (node.argument),
|
|
1021
|
+
context.state.scope
|
|
1022
|
+
);
|
|
1023
|
+
},
|
|
1024
|
+
CallExpression(node, context) {
|
|
1025
|
+
// for now, assume everything touched by the callee ends up mutating the object
|
|
1026
|
+
// TODO optimise this better
|
|
1027
|
+
|
|
1028
|
+
// special case — no need to peek inside effects as they only run once async work has completed
|
|
1029
|
+
const rune = get_rune(node, context.state.scope);
|
|
1030
|
+
if (rune === '$effect') return;
|
|
1031
|
+
|
|
1032
|
+
/** @type {Set<Binding>} */
|
|
1033
|
+
const touched = new Set();
|
|
1034
|
+
touch(node, context.state.scope, touched);
|
|
1035
|
+
|
|
1036
|
+
for (const b of touched) {
|
|
1037
|
+
writes.add(b);
|
|
1038
|
+
}
|
|
1039
|
+
},
|
|
1040
|
+
// don't look inside functions until they are called
|
|
1041
|
+
ArrowFunctionExpression(_, context) {},
|
|
1042
|
+
FunctionDeclaration(_, context) {},
|
|
1043
|
+
FunctionExpression(_, context) {},
|
|
1044
|
+
Identifier(node, context) {
|
|
1045
|
+
const parent = /** @type {ESTree.Node} */ (context.path.at(-1));
|
|
1046
|
+
if (is_reference(node, parent)) {
|
|
1047
|
+
const binding = context.state.scope.get(node.name);
|
|
1048
|
+
if (binding) {
|
|
1049
|
+
reads.add(binding);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
);
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
let awaited = false;
|
|
1058
|
+
|
|
1059
|
+
// TODO this should probably be attached to the scope?
|
|
1060
|
+
const promises = b.id('$$promises');
|
|
1061
|
+
|
|
1062
|
+
/**
|
|
1063
|
+
* @param {ESTree.Identifier} id
|
|
1064
|
+
* @param {NonNullable<Binding['blocker']>} blocker
|
|
1065
|
+
*/
|
|
1066
|
+
function push_declaration(id, blocker) {
|
|
1067
|
+
analysis.instance_body.declarations.push(id);
|
|
1068
|
+
|
|
1069
|
+
const binding = /** @type {Binding} */ (instance.scope.get(id.name));
|
|
1070
|
+
binding.blocker = blocker;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
/**
|
|
1074
|
+
* Analysis of blockers for functions is deferred until we know which statements are async/blockers
|
|
1075
|
+
* @type {Array<ESTree.FunctionDeclaration | ESTree.VariableDeclarator>}
|
|
1076
|
+
*/
|
|
1077
|
+
const functions = [];
|
|
1078
|
+
|
|
1079
|
+
for (let node of instance.ast.body) {
|
|
1080
|
+
if (node.type === 'ImportDeclaration') {
|
|
1081
|
+
analysis.instance_body.hoisted.push(node);
|
|
1082
|
+
continue;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
if (node.type === 'ExportDefaultDeclaration' || node.type === 'ExportAllDeclaration') {
|
|
1086
|
+
// these can't exist inside `<script>` but TypeScript doesn't know that
|
|
1087
|
+
continue;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
if (node.type === 'ExportNamedDeclaration') {
|
|
1091
|
+
if (node.declaration) {
|
|
1092
|
+
node = node.declaration;
|
|
1093
|
+
} else {
|
|
1094
|
+
continue;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
const has_await = has_await_expression(node);
|
|
1099
|
+
awaited ||= has_await;
|
|
1100
|
+
|
|
1101
|
+
if (node.type === 'FunctionDeclaration') {
|
|
1102
|
+
analysis.instance_body.sync.push(node);
|
|
1103
|
+
functions.push(node);
|
|
1104
|
+
} else if (node.type === 'VariableDeclaration') {
|
|
1105
|
+
for (const declarator of node.declarations) {
|
|
1106
|
+
if (
|
|
1107
|
+
declarator.init?.type === 'ArrowFunctionExpression' ||
|
|
1108
|
+
declarator.init?.type === 'FunctionExpression'
|
|
1109
|
+
) {
|
|
1110
|
+
// One declarator per declaration, makes things simpler. The ternary ensures more accurate source maps in the common case
|
|
1111
|
+
analysis.instance_body.sync.push(
|
|
1112
|
+
node.declarations.length === 1 ? node : b.declaration(node.kind, [declarator])
|
|
1113
|
+
);
|
|
1114
|
+
functions.push(declarator);
|
|
1115
|
+
} else if (!awaited) {
|
|
1116
|
+
// One declarator per declaration, makes things simpler. The ternary ensures more accurate source maps in the common case
|
|
1117
|
+
analysis.instance_body.sync.push(
|
|
1118
|
+
node.declarations.length === 1 ? node : b.declaration(node.kind, [declarator])
|
|
1119
|
+
);
|
|
1120
|
+
} else {
|
|
1121
|
+
/** @type {Set<Binding>} */
|
|
1122
|
+
const reads = new Set(); // TODO we're not actually using this yet
|
|
1123
|
+
|
|
1124
|
+
/** @type {Set<Binding>} */
|
|
1125
|
+
const writes = new Set();
|
|
1126
|
+
|
|
1127
|
+
trace_references(declarator, reads, writes);
|
|
1128
|
+
|
|
1129
|
+
const blocker = /** @type {NonNullable<Binding['blocker']>} */ (
|
|
1130
|
+
b.member(promises, b.literal(analysis.instance_body.async.length), true)
|
|
1131
|
+
);
|
|
1132
|
+
|
|
1133
|
+
for (const binding of writes) {
|
|
1134
|
+
binding.blocker = blocker;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
for (const id of extract_identifiers(declarator.id)) {
|
|
1138
|
+
push_declaration(id, blocker);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// one declarator per declaration, makes things simpler
|
|
1142
|
+
analysis.instance_body.async.push({
|
|
1143
|
+
node: declarator,
|
|
1144
|
+
has_await
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
} else if (awaited) {
|
|
1149
|
+
/** @type {Set<Binding>} */
|
|
1150
|
+
const reads = new Set(); // TODO we're not actually using this yet
|
|
1151
|
+
|
|
1152
|
+
/** @type {Set<Binding>} */
|
|
1153
|
+
const writes = new Set();
|
|
1154
|
+
|
|
1155
|
+
trace_references(node, reads, writes);
|
|
1156
|
+
|
|
1157
|
+
const blocker = /** @type {NonNullable<Binding['blocker']>} */ (
|
|
1158
|
+
b.member(promises, b.literal(analysis.instance_body.async.length), true)
|
|
1159
|
+
);
|
|
1160
|
+
|
|
1161
|
+
for (const binding of writes) {
|
|
1162
|
+
binding.blocker = blocker;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
if (node.type === 'ClassDeclaration') {
|
|
1166
|
+
push_declaration(node.id, blocker);
|
|
1167
|
+
analysis.instance_body.async.push({ node, has_await });
|
|
1168
|
+
} else {
|
|
1169
|
+
analysis.instance_body.async.push({ node, has_await });
|
|
1170
|
+
}
|
|
1171
|
+
} else {
|
|
1172
|
+
analysis.instance_body.sync.push(node);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
for (const fn of functions) {
|
|
1177
|
+
/** @type {Set<Binding>} */
|
|
1178
|
+
const reads_writes = new Set();
|
|
1179
|
+
const body =
|
|
1180
|
+
fn.type === 'VariableDeclarator'
|
|
1181
|
+
? /** @type {ESTree.FunctionExpression | ESTree.ArrowFunctionExpression} */ (fn.init).body
|
|
1182
|
+
: fn.body;
|
|
1183
|
+
|
|
1184
|
+
trace_references(body, reads_writes, reads_writes);
|
|
1185
|
+
|
|
1186
|
+
const max = [...reads_writes].reduce((max, binding) => {
|
|
1187
|
+
return binding.blocker ? Math.max(binding.blocker.property.value, max) : max;
|
|
1188
|
+
}, -1);
|
|
1189
|
+
|
|
1190
|
+
if (max === -1) continue;
|
|
1191
|
+
|
|
1192
|
+
const blocker = b.member(promises, b.literal(max), true);
|
|
1193
|
+
const binding = /** @type {Binding} */ (
|
|
1194
|
+
fn.type === 'FunctionDeclaration'
|
|
1195
|
+
? instance.scope.get(fn.id.name)
|
|
1196
|
+
: instance.scope.get(/** @type {ESTree.Identifier} */ (fn.id).name)
|
|
1197
|
+
);
|
|
1198
|
+
|
|
1199
|
+
binding.blocker = /** @type {typeof binding['blocker']} */ (blocker);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1121
1203
|
/**
|
|
1122
1204
|
* @param {Map<import('estree').LabeledStatement, ReactiveStatement>} unsorted_reactive_declarations
|
|
1123
1205
|
*/
|
|
@@ -26,10 +26,6 @@ export function AwaitExpression(node, context) {
|
|
|
26
26
|
if (context.state.expression) {
|
|
27
27
|
context.state.expression.has_await = true;
|
|
28
28
|
|
|
29
|
-
if (context.state.fragment && context.path.some((node) => node.type === 'ConstTag')) {
|
|
30
|
-
context.state.fragment.metadata.has_await = true;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
29
|
suspend = true;
|
|
34
30
|
}
|
|
35
31
|
|
|
@@ -56,22 +56,36 @@ export function AwaitBlock(node, context) {
|
|
|
56
56
|
catch_block = b.arrow(args, b.block([...declarations, ...block.body]));
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
node.pending
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
'await'
|
|
73
|
-
)
|
|
59
|
+
const stmt = add_svelte_meta(
|
|
60
|
+
b.call(
|
|
61
|
+
'$.await',
|
|
62
|
+
context.state.node,
|
|
63
|
+
expression,
|
|
64
|
+
node.pending
|
|
65
|
+
? b.arrow([b.id('$$anchor')], /** @type {BlockStatement} */ (context.visit(node.pending)))
|
|
66
|
+
: b.null,
|
|
67
|
+
then_block,
|
|
68
|
+
catch_block
|
|
69
|
+
),
|
|
70
|
+
node,
|
|
71
|
+
'await'
|
|
74
72
|
);
|
|
73
|
+
|
|
74
|
+
if (node.metadata.expression.has_blockers()) {
|
|
75
|
+
context.state.init.push(
|
|
76
|
+
b.stmt(
|
|
77
|
+
b.call(
|
|
78
|
+
'$.async',
|
|
79
|
+
context.state.node,
|
|
80
|
+
node.metadata.expression.blockers(),
|
|
81
|
+
b.array([]),
|
|
82
|
+
b.arrow([context.state.node], b.block([stmt]))
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
);
|
|
86
|
+
} else {
|
|
87
|
+
context.state.init.push(stmt);
|
|
88
|
+
}
|
|
75
89
|
}
|
|
76
90
|
|
|
77
91
|
/**
|
|
@@ -24,15 +24,15 @@ export function ConstTag(node, context) {
|
|
|
24
24
|
expression = b.call('$.tag', expression, b.literal(declaration.id.name));
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
context.state.consts.push(b.const(declaration.id, expression));
|
|
28
|
-
|
|
29
27
|
context.state.transform[declaration.id.name] = { read: get_value };
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
add_const_declaration(
|
|
30
|
+
context.state,
|
|
31
|
+
declaration.id,
|
|
32
|
+
expression,
|
|
33
|
+
node.metadata.expression.has_await,
|
|
34
|
+
context.state.scope.get_bindings(declaration)
|
|
35
|
+
);
|
|
36
36
|
} else {
|
|
37
37
|
const identifiers = extract_identifiers(declaration.id);
|
|
38
38
|
const tmp = b.id(context.state.scope.generate('computed_const'));
|
|
@@ -69,13 +69,13 @@ export function ConstTag(node, context) {
|
|
|
69
69
|
expression = b.call('$.tag', expression, b.literal('[@const]'));
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
context.state.
|
|
78
|
-
|
|
72
|
+
add_const_declaration(
|
|
73
|
+
context.state,
|
|
74
|
+
tmp,
|
|
75
|
+
expression,
|
|
76
|
+
node.metadata.expression.has_await,
|
|
77
|
+
context.state.scope.get_bindings(declaration)
|
|
78
|
+
);
|
|
79
79
|
|
|
80
80
|
for (const node of identifiers) {
|
|
81
81
|
context.state.transform[node.name] = {
|
|
@@ -84,3 +84,39 @@ export function ConstTag(node, context) {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @param {ComponentContext['state']} state
|
|
90
|
+
* @param {import('estree').Identifier} id
|
|
91
|
+
* @param {import('estree').Expression} expression
|
|
92
|
+
* @param {boolean} has_await
|
|
93
|
+
* @param {import('#compiler').Binding[]} bindings
|
|
94
|
+
*/
|
|
95
|
+
function add_const_declaration(state, id, expression, has_await, bindings) {
|
|
96
|
+
// we need to eagerly evaluate the expression in order to hit any
|
|
97
|
+
// 'Cannot access x before initialization' errors
|
|
98
|
+
const after = dev ? [b.stmt(b.call('$.get', id))] : [];
|
|
99
|
+
|
|
100
|
+
if (has_await || state.async_consts) {
|
|
101
|
+
const run = (state.async_consts ??= {
|
|
102
|
+
id: b.id(state.scope.generate('promises')),
|
|
103
|
+
thunks: []
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
state.consts.push(b.let(id));
|
|
107
|
+
|
|
108
|
+
const assignment = b.assignment('=', id, expression);
|
|
109
|
+
const body = after.length === 0 ? assignment : b.block([b.stmt(assignment), ...after]);
|
|
110
|
+
|
|
111
|
+
run.thunks.push(b.thunk(body, has_await));
|
|
112
|
+
|
|
113
|
+
const blocker = b.member(run.id, b.literal(run.thunks.length - 1), true);
|
|
114
|
+
|
|
115
|
+
for (const binding of bindings) {
|
|
116
|
+
binding.blocker = blocker;
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
state.consts.push(b.const(id, expression));
|
|
120
|
+
state.consts.push(...after);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -48,8 +48,6 @@ export function Fragment(node, context) {
|
|
|
48
48
|
const is_single_child_not_needing_template =
|
|
49
49
|
trimmed.length === 1 &&
|
|
50
50
|
(trimmed[0].type === 'SvelteFragment' || trimmed[0].type === 'TitleElement');
|
|
51
|
-
const has_await = context.state.init !== null && (node.metadata.has_await || false);
|
|
52
|
-
|
|
53
51
|
const template_name = context.state.scope.root.unique('root'); // TODO infer name from parent
|
|
54
52
|
|
|
55
53
|
/** @type {Statement[]} */
|
|
@@ -72,7 +70,8 @@ export function Fragment(node, context) {
|
|
|
72
70
|
metadata: {
|
|
73
71
|
namespace,
|
|
74
72
|
bound_contenteditable: context.state.metadata.bound_contenteditable
|
|
75
|
-
}
|
|
73
|
+
},
|
|
74
|
+
async_consts: undefined
|
|
76
75
|
};
|
|
77
76
|
|
|
78
77
|
for (const node of hoisted) {
|
|
@@ -153,8 +152,8 @@ export function Fragment(node, context) {
|
|
|
153
152
|
|
|
154
153
|
body.push(...state.let_directives, ...state.consts);
|
|
155
154
|
|
|
156
|
-
if (
|
|
157
|
-
body.push(b.
|
|
155
|
+
if (state.async_consts && state.async_consts.thunks.length > 0) {
|
|
156
|
+
body.push(b.var(state.async_consts.id, b.call('$.run', b.array(state.async_consts.thunks))));
|
|
158
157
|
}
|
|
159
158
|
|
|
160
159
|
if (is_text_first) {
|
|
@@ -177,13 +176,5 @@ export function Fragment(node, context) {
|
|
|
177
176
|
body.push(close);
|
|
178
177
|
}
|
|
179
178
|
|
|
180
|
-
|
|
181
|
-
return b.block([
|
|
182
|
-
b.stmt(
|
|
183
|
-
b.call('$.async_body', b.id('$$anchor'), b.arrow([b.id('$$anchor')], b.block(body), true))
|
|
184
|
-
)
|
|
185
|
-
]);
|
|
186
|
-
} else {
|
|
187
|
-
return b.block(body);
|
|
188
|
-
}
|
|
179
|
+
return b.block(body);
|
|
189
180
|
}
|
|
@@ -14,8 +14,6 @@ export function SnippetBlock(node, context) {
|
|
|
14
14
|
// TODO hoist where possible
|
|
15
15
|
/** @type {(Identifier | AssignmentPattern)[]} */
|
|
16
16
|
const args = [b.id('$$anchor')];
|
|
17
|
-
const has_await = node.body.metadata.has_await || false;
|
|
18
|
-
|
|
19
17
|
/** @type {BlockStatement} */
|
|
20
18
|
let body;
|
|
21
19
|
|
|
@@ -78,12 +76,8 @@ export function SnippetBlock(node, context) {
|
|
|
78
76
|
|
|
79
77
|
// in dev we use a FunctionExpression (not arrow function) so we can use `arguments`
|
|
80
78
|
let snippet = dev
|
|
81
|
-
? b.call(
|
|
82
|
-
|
|
83
|
-
b.id(context.state.analysis.name),
|
|
84
|
-
b.function(null, args, body, has_await)
|
|
85
|
-
)
|
|
86
|
-
: b.arrow(args, body, has_await);
|
|
79
|
+
? b.call('$.wrap_snippet', b.id(context.state.analysis.name), b.function(null, args, body))
|
|
80
|
+
: b.arrow(args, body);
|
|
87
81
|
|
|
88
82
|
const declaration = b.const(node.expression, snippet);
|
|
89
83
|
|