svelte 5.56.10 → 5.57.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/elements.d.ts +3 -0
- package/package.json +3 -3
- package/src/compiler/phases/1-parse/acorn.js +1 -1
- package/src/compiler/phases/1-parse/utils/html.js +12 -7
- package/src/compiler/phases/2-analyze/css/css-prune.js +10 -0
- package/src/compiler/phases/2-analyze/index.js +26 -3
- package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +10 -4
- package/src/compiler/phases/2-analyze/visitors/CallExpression.js +1 -1
- package/src/compiler/phases/2-analyze/visitors/DeclarationTag.js +2 -1
- package/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js +10 -10
- package/src/compiler/phases/2-analyze/visitors/SvelteBody.js +4 -4
- package/src/compiler/phases/2-analyze/visitors/SvelteDocument.js +4 -4
- package/src/compiler/phases/2-analyze/visitors/SvelteWindow.js +4 -4
- package/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js +14 -2
- package/src/compiler/phases/2-analyze/visitors/shared/element.js +2 -8
- package/src/compiler/phases/2-analyze/visitors/shared/utils.js +23 -1
- package/src/compiler/phases/3-transform/client/transform-client.js +2 -2
- package/src/compiler/phases/3-transform/client/transform-template/index.js +6 -0
- package/src/compiler/phases/3-transform/client/utils.js +35 -8
- package/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js +6 -6
- package/src/compiler/phases/3-transform/client/visitors/BindDirective.js +10 -13
- package/src/compiler/phases/3-transform/client/visitors/ConstTag.js +19 -8
- package/src/compiler/phases/3-transform/client/visitors/DeclarationTag.js +3 -2
- package/src/compiler/phases/3-transform/client/visitors/EachBlock.js +4 -1
- package/src/compiler/phases/3-transform/client/visitors/Fragment.js +2 -7
- package/src/compiler/phases/3-transform/client/visitors/HtmlTag.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/IfBlock.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/KeyBlock.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +106 -44
- package/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +11 -4
- package/src/compiler/phases/3-transform/client/visitors/shared/element.js +6 -1
- package/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +1 -6
- package/src/compiler/phases/3-transform/client/visitors/shared/utils.js +5 -5
- package/src/compiler/phases/3-transform/server/transform-server.js +20 -9
- package/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js +1 -1
- package/src/compiler/phases/3-transform/server/visitors/DeclarationTag.js +1 -1
- package/src/compiler/phases/3-transform/server/visitors/EachBlock.js +8 -2
- package/src/compiler/phases/3-transform/server/visitors/IfBlock.js +4 -4
- package/src/compiler/phases/3-transform/server/visitors/RegularElement.js +1 -1
- package/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js +9 -4
- package/src/compiler/phases/3-transform/server/visitors/shared/element.js +5 -4
- package/src/compiler/phases/3-transform/server/visitors/shared/utils.js +30 -0
- package/src/compiler/phases/bindings.js +3 -1
- package/src/compiler/phases/nodes.js +4 -0
- package/src/compiler/print/index.js +21 -16
- package/src/easing/index.js +62 -0
- package/src/internal/client/constants.js +9 -0
- package/src/internal/client/context.js +15 -5
- package/src/internal/client/dev/hmr.js +15 -4
- package/src/internal/client/dom/blocks/boundary.js +21 -12
- package/src/internal/client/dom/blocks/slot.js +19 -0
- package/src/internal/client/dom/blocks/svelte-head.js +8 -0
- package/src/internal/client/dom/elements/attributes.js +35 -34
- package/src/internal/client/dom/elements/bindings/select.js +99 -9
- package/src/internal/client/dom/elements/bindings/this.js +7 -2
- package/src/internal/client/dom/elements/transitions.js +81 -68
- package/src/internal/client/dom/operations.js +21 -1
- package/src/internal/client/error-handling.js +10 -2
- package/src/internal/client/index.js +9 -2
- package/src/internal/client/proxy.js +8 -3
- package/src/internal/client/reactivity/async.js +23 -3
- package/src/internal/client/reactivity/effects.js +14 -1
- package/src/internal/client/render.js +4 -8
- package/src/internal/client/runtime.js +45 -33
- package/src/internal/client/warnings.js +0 -11
- package/src/internal/server/abort-signal.js +8 -9
- package/src/internal/server/context.js +2 -2
- package/src/internal/server/index.js +1 -1
- package/src/internal/server/renderer.js +163 -52
- package/src/internal/shared/attributes.js +2 -1
- package/src/internal/shared/clone.js +2 -0
- package/src/internal/shared/context.js +3 -2
- package/src/legacy/legacy-server.js +1 -1
- package/src/reactivity/map.js +22 -0
- package/src/reactivity/url.js +2 -0
- package/src/transition/index.js +18 -7
- package/src/version.js +1 -1
- package/types/index.d.ts +135 -37
- package/types/index.d.ts.map +2 -2
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
/** @import { ComponentContext } from '../types' */
|
|
4
4
|
import { extract_identifiers, has_await_expression } from '../../../../utils/ast.js';
|
|
5
5
|
import * as b from '#compiler/builders';
|
|
6
|
+
import { async_thunk } from '../utils.js';
|
|
6
7
|
import { add_state_transformers } from './shared/declarations.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -70,7 +71,7 @@ export function add_async_declaration(context, metadata, ids, assignments, kind
|
|
|
70
71
|
context.state.consts.push(kind === 'var' ? b.var(id.name) : b.let(id.name));
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
const blockers = [...metadata.expression.
|
|
74
|
+
const blockers = [...metadata.expression.references]
|
|
74
75
|
.map((dep) => dep.blocker)
|
|
75
76
|
.filter((b) => b !== null && b.object !== context.state.async_consts?.id);
|
|
76
77
|
|
|
@@ -85,5 +86,5 @@ export function add_async_declaration(context, metadata, ids, assignments, kind
|
|
|
85
86
|
metadata.expression.has_await ||
|
|
86
87
|
assignments.some((assignment) => has_await_expression(assignment));
|
|
87
88
|
const body = assignments.length === 1 ? assignments[0].expression : b.block(assignments);
|
|
88
|
-
run.thunks.push(b.thunk(body
|
|
89
|
+
run.thunks.push(has_await ? async_thunk(body, metadata.expression) : b.thunk(body));
|
|
89
90
|
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { dev } from '../../../../state.js';
|
|
13
13
|
import { extract_paths, object } from '../../../../utils/ast.js';
|
|
14
14
|
import * as b from '#compiler/builders';
|
|
15
|
+
import { async_thunk } from '../utils.js';
|
|
15
16
|
import { get_value } from './shared/declarations.js';
|
|
16
17
|
import { build_expression, add_svelte_meta } from './shared/utils.js';
|
|
17
18
|
|
|
@@ -313,7 +314,9 @@ export function EachBlock(node, context) {
|
|
|
313
314
|
|
|
314
315
|
const has_await = node.metadata.expression.has_await;
|
|
315
316
|
|
|
316
|
-
const get_collection =
|
|
317
|
+
const get_collection = has_await
|
|
318
|
+
? async_thunk(collection, node.metadata.expression)
|
|
319
|
+
: b.thunk(collection);
|
|
317
320
|
const thunk = has_await ? b.thunk(b.call('$.get', b.id('$$collection'))) : get_collection;
|
|
318
321
|
|
|
319
322
|
const render_args = [b.id('$$anchor'), item];
|
|
@@ -141,14 +141,9 @@ export function Fragment(node, context) {
|
|
|
141
141
|
flags |= TEMPLATE_USE_IMPORT_NODE;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
// special case — we can use `$.comment` instead of creating a unique template
|
|
146
|
-
state.init.unshift(b.var(id, b.call('$.comment')));
|
|
147
|
-
} else {
|
|
148
|
-
const template_name = transform_template(state, 'root', flags);
|
|
144
|
+
const template_name = transform_template(state, 'root', flags);
|
|
149
145
|
|
|
150
|
-
|
|
151
|
-
}
|
|
146
|
+
state.init.unshift(b.var(id, b.call(template_name)));
|
|
152
147
|
|
|
153
148
|
close = b.stmt(b.call('$.append', b.id('$$anchor'), id));
|
|
154
149
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/** @import { ComponentContext } from '../types' */
|
|
3
3
|
import { is_ignored } from '../../../../state.js';
|
|
4
4
|
import * as b from '#compiler/builders';
|
|
5
|
+
import { async_thunk } from '../utils.js';
|
|
5
6
|
import { build_expression } from './shared/utils.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -46,7 +47,7 @@ export function HtmlTag(node, context) {
|
|
|
46
47
|
'$.async',
|
|
47
48
|
context.state.node,
|
|
48
49
|
node.metadata.expression.blockers(),
|
|
49
|
-
has_await ? b.array([
|
|
50
|
+
has_await ? b.array([async_thunk(expression, node.metadata.expression)]) : b.void0,
|
|
50
51
|
b.arrow(
|
|
51
52
|
has_await ? [context.state.node, b.id('$$html')] : [context.state.node],
|
|
52
53
|
b.block([statement])
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentContext } from '../types' */
|
|
4
4
|
import * as b from '#compiler/builders';
|
|
5
|
+
import { async_thunk } from '../utils.js';
|
|
5
6
|
import { build_expression, add_svelte_meta } from './shared/utils.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -117,7 +118,7 @@ export function IfBlock(node, context) {
|
|
|
117
118
|
'$.async',
|
|
118
119
|
context.state.node,
|
|
119
120
|
node.metadata.expression.blockers(),
|
|
120
|
-
has_await ? b.array([
|
|
121
|
+
has_await ? b.array([async_thunk(expression, node.metadata.expression)]) : b.void0,
|
|
121
122
|
b.arrow(
|
|
122
123
|
has_await ? [context.state.node, b.id('$$condition')] : [context.state.node],
|
|
123
124
|
b.block(statements)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentContext } from '../types' */
|
|
4
4
|
import * as b from '#compiler/builders';
|
|
5
|
+
import { async_thunk } from '../utils.js';
|
|
5
6
|
import { build_expression, add_svelte_meta } from './shared/utils.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -31,7 +32,7 @@ export function KeyBlock(node, context) {
|
|
|
31
32
|
'$.async',
|
|
32
33
|
context.state.node,
|
|
33
34
|
node.metadata.expression.blockers(),
|
|
34
|
-
has_await ? b.array([
|
|
35
|
+
has_await ? b.array([async_thunk(expression, node.metadata.expression)]) : b.void0,
|
|
35
36
|
b.arrow(
|
|
36
37
|
has_await ? [context.state.node, b.id('$$key')] : [context.state.node],
|
|
37
38
|
b.block([statement])
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
/** @import { ArrayExpression, Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression } from 'estree' */
|
|
1
|
+
/** @import { ArrayExpression, Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression, Statement } from 'estree' */
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentClientTransformState, ComponentContext } from '../types' */
|
|
4
4
|
/** @import { Scope } from '../../../scope' */
|
|
5
5
|
import {
|
|
6
6
|
cannot_be_set_statically,
|
|
7
|
-
is_boolean_attribute,
|
|
8
7
|
is_dom_property,
|
|
9
8
|
is_load_error_element
|
|
10
9
|
} from '../../../../../utils.js';
|
|
@@ -13,7 +12,6 @@ import { is_event_attribute, is_text_attribute } from '../../../../utils/ast.js'
|
|
|
13
12
|
import * as b from '#compiler/builders';
|
|
14
13
|
import {
|
|
15
14
|
create_attribute,
|
|
16
|
-
ExpressionMetadata,
|
|
17
15
|
is_custom_element_node,
|
|
18
16
|
is_customizable_select_element
|
|
19
17
|
} from '../../../nodes.js';
|
|
@@ -231,11 +229,18 @@ export function RegularElement(node, context) {
|
|
|
231
229
|
continue;
|
|
232
230
|
}
|
|
233
231
|
|
|
232
|
+
// `<select defaultValue>` needs the options to exist before it can mark one
|
|
233
|
+
// as selected, so it is handled after the children, alongside `value`
|
|
234
|
+
if (node.name === 'select' && get_attribute_name(node, attribute) === 'defaultValue') {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
234
238
|
const name = get_attribute_name(node, attribute);
|
|
235
239
|
|
|
236
240
|
if (
|
|
237
241
|
!is_custom_element &&
|
|
238
242
|
!cannot_be_set_statically(attribute.name) &&
|
|
243
|
+
(name !== 'value' || node.name !== 'textarea') &&
|
|
239
244
|
(attribute.value === true || is_text_attribute(attribute)) &&
|
|
240
245
|
(name !== 'class' || class_directives.length === 0) &&
|
|
241
246
|
(name !== 'style' || style_directives.length === 0)
|
|
@@ -434,7 +439,7 @@ export function RegularElement(node, context) {
|
|
|
434
439
|
state: child_state
|
|
435
440
|
});
|
|
436
441
|
|
|
437
|
-
if (needs_reset) {
|
|
442
|
+
if (needs_reset && !fold_reset_into_child(child_state.init, context.state.node)) {
|
|
438
443
|
child_state.init.push(b.stmt(b.call('$.reset', context.state.node)));
|
|
439
444
|
}
|
|
440
445
|
}
|
|
@@ -513,6 +518,34 @@ export function RegularElement(node, context) {
|
|
|
513
518
|
}
|
|
514
519
|
}
|
|
515
520
|
|
|
521
|
+
// deferred from the attribute loop above, so that the options it selects from
|
|
522
|
+
// have been created and had their values assigned
|
|
523
|
+
if (!has_spread && name === 'select') {
|
|
524
|
+
const default_value = /** @type {AST.Attribute[]} */ (attributes).find(
|
|
525
|
+
(attribute) => get_attribute_name(node, attribute) === 'defaultValue'
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
if (default_value) {
|
|
529
|
+
const { value, has_state } = build_attribute_value(default_value.value, context, (v, m) =>
|
|
530
|
+
context.state.memoizer.add(v, m)
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
(has_state ? context.state.update : context.state.init).push(
|
|
534
|
+
b.stmt(b.call('$.set_default_select_value', node_id, value))
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const value_attribute = lookup.get('value');
|
|
539
|
+
const dynamic_value =
|
|
540
|
+
value_attribute !== undefined &&
|
|
541
|
+
value_attribute.value !== true &&
|
|
542
|
+
!is_text_attribute(value_attribute);
|
|
543
|
+
|
|
544
|
+
if (default_value || dynamic_value || bindings.has('value')) {
|
|
545
|
+
context.state.init.push(b.stmt(b.call('$.init_select', node_id)));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
516
549
|
context.state.template.pop_element();
|
|
517
550
|
}
|
|
518
551
|
|
|
@@ -528,18 +561,12 @@ export function build_class_directives_object(
|
|
|
528
561
|
) {
|
|
529
562
|
let properties = [];
|
|
530
563
|
|
|
531
|
-
const metadata = new ExpressionMetadata();
|
|
532
|
-
|
|
533
564
|
for (const d of class_directives) {
|
|
534
|
-
metadata.merge(d.metadata.expression);
|
|
535
|
-
|
|
536
565
|
const expression = /** @type Expression */ (context.visit(d.expression));
|
|
537
|
-
properties.push(b.init(d.name, expression));
|
|
566
|
+
properties.push(b.init(d.name, memoizer.add(expression, d.metadata.expression)));
|
|
538
567
|
}
|
|
539
568
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
return memoizer.add(directives, metadata);
|
|
569
|
+
return b.object(properties);
|
|
543
570
|
}
|
|
544
571
|
|
|
545
572
|
/**
|
|
@@ -555,23 +582,17 @@ export function build_style_directives_object(
|
|
|
555
582
|
const normal = b.object([]);
|
|
556
583
|
const important = b.object([]);
|
|
557
584
|
|
|
558
|
-
const metadata = new ExpressionMetadata();
|
|
559
|
-
|
|
560
585
|
for (const d of style_directives) {
|
|
561
|
-
metadata.merge(d.metadata.expression);
|
|
562
|
-
|
|
563
586
|
const expression =
|
|
564
587
|
d.value === true
|
|
565
588
|
? build_getter(b.id(d.name), context.state)
|
|
566
589
|
: build_attribute_value(d.value, context).value;
|
|
567
590
|
|
|
568
591
|
const object = d.modifiers.includes('important') ? important : normal;
|
|
569
|
-
object.properties.push(b.init(d.name, expression));
|
|
592
|
+
object.properties.push(b.init(d.name, memoizer.add(expression, d.metadata.expression)));
|
|
570
593
|
}
|
|
571
594
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
return memoizer.add(directives, metadata);
|
|
595
|
+
return important.properties.length ? b.array([normal, important]) : normal;
|
|
575
596
|
}
|
|
576
597
|
|
|
577
598
|
/**
|
|
@@ -715,28 +736,32 @@ function build_element_special_value_attribute(
|
|
|
715
736
|
);
|
|
716
737
|
|
|
717
738
|
const evaluated = context.state.scope.evaluate(value);
|
|
718
|
-
const assignment = b.assignment('=', b.member(node_id, '__value'), value);
|
|
719
739
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
b.member(node_id, '
|
|
723
|
-
evaluated.is_defined ? assignment : b.logical('??', assignment, b.literal(''))
|
|
724
|
-
);
|
|
740
|
+
/** @param {Expression} value */
|
|
741
|
+
const build_update = (value) => {
|
|
742
|
+
const assignment = b.assignment('=', b.member(node_id, '__value'), value);
|
|
725
743
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
744
|
+
const set_value_assignment = b.assignment(
|
|
745
|
+
'=',
|
|
746
|
+
b.member(node_id, 'value'),
|
|
747
|
+
evaluated.is_defined ? assignment : b.logical('??', assignment, b.literal(''))
|
|
748
|
+
);
|
|
749
|
+
|
|
750
|
+
return b.stmt(
|
|
751
|
+
is_select_with_value
|
|
752
|
+
? b.sequence([
|
|
753
|
+
set_value_assignment,
|
|
754
|
+
// This ensures a one-way street to the DOM in case it's <select {value}>
|
|
755
|
+
// and not <select bind:value>. We need it in addition to $.init_select
|
|
756
|
+
// because the select value is not reflected as an attribute, so the
|
|
757
|
+
// mutation observer wouldn't notice.
|
|
758
|
+
b.call('$.select_option', node_id, value)
|
|
759
|
+
])
|
|
760
|
+
: synthetic
|
|
761
|
+
? assignment
|
|
762
|
+
: set_value_assignment
|
|
763
|
+
);
|
|
764
|
+
};
|
|
740
765
|
|
|
741
766
|
if (has_state) {
|
|
742
767
|
const id = b.id(state.scope.generate(`${node_id.name}_value`));
|
|
@@ -747,12 +772,49 @@ function build_element_special_value_attribute(
|
|
|
747
772
|
const init = element === 'option' ? b.object([]) : undefined;
|
|
748
773
|
|
|
749
774
|
state.init.push(b.var(id, init));
|
|
750
|
-
|
|
775
|
+
|
|
776
|
+
// the guard already evaluated `value` into `id`, so read that back rather than
|
|
777
|
+
// evaluating the same expression (and its signal reads) a second time
|
|
778
|
+
state.update.push(
|
|
779
|
+
b.if(b.binary('!==', id, b.assignment('=', id, value)), b.block([build_update(id)]))
|
|
780
|
+
);
|
|
751
781
|
} else {
|
|
752
|
-
state.init.push(
|
|
782
|
+
state.init.push(build_update(value));
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* `<p>{text}</p>` and friends produce `var x = $.child(p, true); $.reset(p);`. That pair is
|
|
788
|
+
* by far the most common shape in compiled output, and `$.only_child` does both, so fold the
|
|
789
|
+
* two together when the `$.child(...)` is the last thing we emitted for this element.
|
|
790
|
+
* @param {Statement[]} init
|
|
791
|
+
* @param {Expression} node_id
|
|
792
|
+
* @returns {boolean} whether the reset was folded in
|
|
793
|
+
*/
|
|
794
|
+
function fold_reset_into_child(init, node_id) {
|
|
795
|
+
const last = init.at(-1);
|
|
796
|
+
|
|
797
|
+
if (
|
|
798
|
+
node_id?.type !== 'Identifier' ||
|
|
799
|
+
last?.type !== 'VariableDeclaration' ||
|
|
800
|
+
last.declarations.length !== 1
|
|
801
|
+
) {
|
|
802
|
+
return false;
|
|
753
803
|
}
|
|
754
804
|
|
|
755
|
-
|
|
756
|
-
|
|
805
|
+
const call = last.declarations[0].init;
|
|
806
|
+
|
|
807
|
+
if (
|
|
808
|
+
call?.type !== 'CallExpression' ||
|
|
809
|
+
call.callee.type !== 'Identifier' ||
|
|
810
|
+
call.callee.name !== '$.child' ||
|
|
811
|
+
call.arguments[0]?.type !== 'Identifier' ||
|
|
812
|
+
call.arguments[0].name !== node_id.name
|
|
813
|
+
) {
|
|
814
|
+
return false;
|
|
757
815
|
}
|
|
816
|
+
|
|
817
|
+
call.callee = b.id('$.only_child');
|
|
818
|
+
|
|
819
|
+
return true;
|
|
758
820
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { dev, locator } from '../../../../state.js';
|
|
5
5
|
import { is_text_attribute } from '../../../../utils/ast.js';
|
|
6
6
|
import * as b from '#compiler/builders';
|
|
7
|
+
import { async_thunk } from '../utils.js';
|
|
7
8
|
import { determine_namespace_for_children } from '../../utils.js';
|
|
8
9
|
import {
|
|
9
10
|
build_attribute_value,
|
|
@@ -147,7 +148,7 @@ export function SvelteElement(node, context) {
|
|
|
147
148
|
'$.async',
|
|
148
149
|
context.state.node,
|
|
149
150
|
node.metadata.expression.blockers(),
|
|
150
|
-
has_await ? b.array([
|
|
151
|
+
has_await ? b.array([async_thunk(expression, node.metadata.expression)]) : b.void0,
|
|
151
152
|
b.arrow(
|
|
152
153
|
has_await ? [context.state.node, b.id('$$tag')] : [context.state.node],
|
|
153
154
|
b.block(statements)
|
|
@@ -6,7 +6,13 @@ import { extract_paths, save } from '../../../../utils/ast.js';
|
|
|
6
6
|
import * as b from '#compiler/builders';
|
|
7
7
|
import * as assert from '../../../../utils/assert.js';
|
|
8
8
|
import { get_rune } from '../../../scope.js';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
async_thunk,
|
|
11
|
+
get_prop_source,
|
|
12
|
+
is_prop_source,
|
|
13
|
+
is_state_source,
|
|
14
|
+
should_proxy
|
|
15
|
+
} from '../utils.js';
|
|
10
16
|
import { get_value } from './shared/declarations.js';
|
|
11
17
|
|
|
12
18
|
/**
|
|
@@ -200,9 +206,10 @@ export function VariableDeclaration(node, context) {
|
|
|
200
206
|
}
|
|
201
207
|
|
|
202
208
|
if (rune === '$derived' || rune === '$derived.by') {
|
|
203
|
-
const
|
|
209
|
+
const metadata = context.state.analysis.async_deriveds.get(
|
|
204
210
|
/** @type {CallExpression} */ (init)
|
|
205
211
|
);
|
|
212
|
+
const is_async = metadata !== undefined;
|
|
206
213
|
|
|
207
214
|
if (declarator.id.type === 'Identifier') {
|
|
208
215
|
let expression = /** @type {Expression} */ (context.visit(value));
|
|
@@ -213,7 +220,7 @@ export function VariableDeclaration(node, context) {
|
|
|
213
220
|
/** @type {Expression} */
|
|
214
221
|
let call = b.call(
|
|
215
222
|
'$.async_derived',
|
|
216
|
-
|
|
223
|
+
async_thunk(expression, metadata),
|
|
217
224
|
dev && b.literal(declarator.id.name),
|
|
218
225
|
location ? b.literal(location) : undefined
|
|
219
226
|
);
|
|
@@ -246,7 +253,7 @@ export function VariableDeclaration(node, context) {
|
|
|
246
253
|
|
|
247
254
|
call = b.call(
|
|
248
255
|
'$.async_derived',
|
|
249
|
-
|
|
256
|
+
async_thunk(expression, metadata),
|
|
250
257
|
dev &&
|
|
251
258
|
b.literal(
|
|
252
259
|
`[$derived ${declarator.id.type === 'ArrayPattern' ? 'iterable' : 'object'}]`
|
|
@@ -30,6 +30,7 @@ export function build_attribute_effect(
|
|
|
30
30
|
) {
|
|
31
31
|
/** @type {ObjectExpression['properties']} */
|
|
32
32
|
const values = [];
|
|
33
|
+
const is_select = element.type === 'RegularElement' && element.name === 'select';
|
|
33
34
|
|
|
34
35
|
const memoizer = new Memoizer();
|
|
35
36
|
|
|
@@ -48,7 +49,11 @@ export function build_attribute_effect(
|
|
|
48
49
|
context.state.init.push(b.var(id, value));
|
|
49
50
|
values.push(b.init(attribute.name, b.id(id)));
|
|
50
51
|
} else {
|
|
51
|
-
|
|
52
|
+
const name =
|
|
53
|
+
is_select && normalize_attribute(attribute.name) === 'defaultValue'
|
|
54
|
+
? 'defaultValue'
|
|
55
|
+
: attribute.name;
|
|
56
|
+
values.push(b.init(name, value));
|
|
52
57
|
}
|
|
53
58
|
} else {
|
|
54
59
|
let value = /** @type {Expression} */ (context.visit(attribute));
|
|
@@ -161,7 +161,7 @@ export function is_static_element(node) {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
if (
|
|
164
|
-
['input', 'textarea'].includes(node.name) &&
|
|
164
|
+
['input', 'textarea', 'select'].includes(node.name) &&
|
|
165
165
|
['value', 'checked'].includes(attribute.name)
|
|
166
166
|
) {
|
|
167
167
|
return false;
|
|
@@ -171,11 +171,6 @@ export function is_static_element(node) {
|
|
|
171
171
|
return false;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
// We need to apply src and loading after appending the img to the DOM for lazy loading to work
|
|
175
|
-
if (node.name === 'img' && attribute.name === 'loading') {
|
|
176
|
-
return false;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
174
|
if (attribute.value !== true && !is_text_attribute(attribute)) {
|
|
180
175
|
return false;
|
|
181
176
|
}
|
|
@@ -8,7 +8,7 @@ import { sanitize_template_string } from '../../../../../utils/sanitize_template
|
|
|
8
8
|
import { regex_is_valid_identifier } from '../../../../patterns.js';
|
|
9
9
|
import is_reference from 'is-reference';
|
|
10
10
|
import { dev, is_ignored, locator, component_name } from '../../../../../state.js';
|
|
11
|
-
import { build_getter, is_state_source } from '../../utils.js';
|
|
11
|
+
import { async_thunk, build_getter, is_state_source } from '../../utils.js';
|
|
12
12
|
import { ExpressionMetadata } from '../../../../nodes.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -16,10 +16,10 @@ import { ExpressionMetadata } from '../../../../nodes.js';
|
|
|
16
16
|
* from templates and replacing them with `$0`, `$1` etc
|
|
17
17
|
*/
|
|
18
18
|
export class Memoizer {
|
|
19
|
-
/** @type {Array<{ id: Identifier, expression: Expression }>} */
|
|
19
|
+
/** @type {Array<{ id: Identifier, expression: Expression, metadata: ExpressionMetadata }>} */
|
|
20
20
|
#sync = [];
|
|
21
21
|
|
|
22
|
-
/** @type {Array<{ id: Identifier, expression: Expression }>} */
|
|
22
|
+
/** @type {Array<{ id: Identifier, expression: Expression, metadata: ExpressionMetadata }>} */
|
|
23
23
|
#async = [];
|
|
24
24
|
|
|
25
25
|
/** @type {Set<Expression>} */
|
|
@@ -43,7 +43,7 @@ export class Memoizer {
|
|
|
43
43
|
|
|
44
44
|
const id = b.id('#'); // filled in later
|
|
45
45
|
|
|
46
|
-
(metadata.has_await ? this.#async : this.#sync).push({ id, expression });
|
|
46
|
+
(metadata.has_await ? this.#async : this.#sync).push({ id, expression, metadata });
|
|
47
47
|
|
|
48
48
|
return id;
|
|
49
49
|
}
|
|
@@ -84,7 +84,7 @@ export class Memoizer {
|
|
|
84
84
|
if (this.#async.length === 0) return;
|
|
85
85
|
// use `b.arrow` rather than `b.thunk` so that deferred async/template effects
|
|
86
86
|
// always read live bindings rather than a possibly stale snapshot.
|
|
87
|
-
return b.array(this.#async.map((memo) =>
|
|
87
|
+
return b.array(this.#async.map((memo) => async_thunk(memo.expression, memo.metadata)));
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
sync_values() {
|
|
@@ -148,7 +148,7 @@ export function server_component(analysis, options) {
|
|
|
148
148
|
const legacy_reactive_declarations = [];
|
|
149
149
|
|
|
150
150
|
for (const [node] of analysis.reactive_statements) {
|
|
151
|
-
const statement =
|
|
151
|
+
const statement = state.legacy_reactive_statements.get(node);
|
|
152
152
|
if (statement === undefined) {
|
|
153
153
|
throw new Error('Could not find reactive statement');
|
|
154
154
|
}
|
|
@@ -165,7 +165,7 @@ export function server_component(analysis, options) {
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
instance.body.push(statement
|
|
168
|
+
instance.body.push(statement);
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
if (legacy_reactive_declarations.length > 0) {
|
|
@@ -210,14 +210,25 @@ export function server_component(analysis, options) {
|
|
|
210
210
|
];
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
213
|
+
const store_subs = [...analysis.instance.scope.declarations.values()].filter(
|
|
214
|
+
(binding) => binding.kind === 'store_sub'
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
// a blocked subscription is only created once its promise resolves, so its teardown must wait until the render is done
|
|
218
|
+
const defer_store_teardown = store_subs.some((binding) => binding.blocker);
|
|
219
|
+
|
|
220
|
+
if (store_subs.length > 0) {
|
|
218
221
|
instance.body.unshift(b.var('$$store_subs'));
|
|
222
|
+
|
|
223
|
+
const unsubscribe = b.if(
|
|
224
|
+
b.id('$$store_subs'),
|
|
225
|
+
b.stmt(b.call('$.unsubscribe_stores', b.id('$$store_subs')))
|
|
226
|
+
);
|
|
227
|
+
|
|
219
228
|
template.body.push(
|
|
220
|
-
|
|
229
|
+
defer_store_teardown
|
|
230
|
+
? b.stmt(b.call('$$renderer.on_destroy', b.arrow([], b.block([unsubscribe]))))
|
|
231
|
+
: unsubscribe
|
|
221
232
|
);
|
|
222
233
|
}
|
|
223
234
|
|
|
@@ -257,7 +268,7 @@ export function server_component(analysis, options) {
|
|
|
257
268
|
);
|
|
258
269
|
}
|
|
259
270
|
|
|
260
|
-
let should_inject_context = dev || analysis.needs_context;
|
|
271
|
+
let should_inject_context = dev || analysis.needs_context || defer_store_teardown;
|
|
261
272
|
|
|
262
273
|
if (should_inject_context) {
|
|
263
274
|
component_block = b.block([
|
|
@@ -66,7 +66,7 @@ export function add_async_declaration(context, metadata, ids, assignments, kind
|
|
|
66
66
|
context.state.init.push(kind === 'var' ? b.var(id.name) : b.let(id.name));
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const blockers = [...metadata.expression.
|
|
69
|
+
const blockers = [...metadata.expression.references]
|
|
70
70
|
.map((dep) => dep.blocker)
|
|
71
71
|
.filter((b) => b !== null && b.object !== context.state.async_consts?.id);
|
|
72
72
|
|
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentContext } from '../types.js' */
|
|
4
4
|
import * as b from '#compiler/builders';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
block_close,
|
|
7
|
+
block_open,
|
|
8
|
+
block_open_else,
|
|
9
|
+
create_child_block,
|
|
10
|
+
prepend_block_marker
|
|
11
|
+
} from './shared/utils.js';
|
|
6
12
|
|
|
7
13
|
/**
|
|
8
14
|
* @param {AST.EachBlock} node
|
|
@@ -51,7 +57,7 @@ export function EachBlock(node, context) {
|
|
|
51
57
|
|
|
52
58
|
const fallback = /** @type {BlockStatement} */ (context.visit(node.fallback));
|
|
53
59
|
|
|
54
|
-
fallback
|
|
60
|
+
prepend_block_marker(fallback, /** @type {string} */ (block_open_else.value));
|
|
55
61
|
|
|
56
62
|
statements.push(
|
|
57
63
|
b.if(
|
|
@@ -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 { block_close, create_child_block } from './shared/utils.js';
|
|
5
|
+
import { block_close, create_child_block, prepend_block_marker } from './shared/utils.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* @param {AST.IfBlock} node
|
|
@@ -10,7 +10,7 @@ import { block_close, create_child_block } from './shared/utils.js';
|
|
|
10
10
|
*/
|
|
11
11
|
export function IfBlock(node, context) {
|
|
12
12
|
const consequent = /** @type {BlockStatement} */ (context.visit(node.consequent));
|
|
13
|
-
consequent
|
|
13
|
+
prepend_block_marker(consequent, `<!--[0-->`);
|
|
14
14
|
|
|
15
15
|
/** @type {IfStatement} */
|
|
16
16
|
let if_statement = b.if(/** @type {Expression} */ (context.visit(node.test)), consequent);
|
|
@@ -22,7 +22,7 @@ export function IfBlock(node, context) {
|
|
|
22
22
|
// Walk the else-if chain, flattening branches
|
|
23
23
|
for (const elseif of node.metadata.flattened ?? []) {
|
|
24
24
|
const branch = /** @type {BlockStatement} */ (context.visit(elseif.consequent));
|
|
25
|
-
branch
|
|
25
|
+
prepend_block_marker(branch, `<!--[${index++}-->`);
|
|
26
26
|
|
|
27
27
|
current_if = current_if.alternate = b.if(
|
|
28
28
|
/** @type {Expression} */ (context.visit(elseif.test)),
|
|
@@ -34,7 +34,7 @@ export function IfBlock(node, context) {
|
|
|
34
34
|
// Handle final else (or remaining async chain)
|
|
35
35
|
const final_alternate = alt ? /** @type {BlockStatement} */ (context.visit(alt)) : b.block([]);
|
|
36
36
|
|
|
37
|
-
final_alternate
|
|
37
|
+
prepend_block_marker(final_alternate, `<!--[-1-->`);
|
|
38
38
|
current_if.alternate = final_alternate;
|
|
39
39
|
|
|
40
40
|
context.state.template.push(
|
|
@@ -46,7 +46,7 @@ export function RegularElement(node, context) {
|
|
|
46
46
|
node.attributes.some(
|
|
47
47
|
(attribute) =>
|
|
48
48
|
((attribute.type === 'Attribute' || attribute.type === 'BindDirective') &&
|
|
49
|
-
attribute.name === 'value') ||
|
|
49
|
+
(attribute.name === 'value' || attribute.name.toLowerCase() === 'defaultvalue')) ||
|
|
50
50
|
attribute.type === 'SpreadAttribute'
|
|
51
51
|
);
|
|
52
52
|
const is_option_special = name === 'option';
|