svelte 5.56.9 → 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/migrate/index.js +6 -1
- package/src/compiler/phases/1-parse/acorn.js +1 -1
- package/src/compiler/phases/1-parse/read/style.js +16 -6
- 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 +45 -18
- 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/AssignmentExpression.js +16 -3
- 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 +119 -46
- package/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/UpdateExpression.js +0 -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/css/index.js +5 -1
- 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/LabeledStatement.js +1 -0
- 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/VariableDeclaration.js +4 -2
- package/src/compiler/phases/3-transform/server/visitors/shared/element.js +53 -47
- package/src/compiler/phases/3-transform/server/visitors/shared/utils.js +31 -1
- package/src/compiler/phases/bindings.js +3 -1
- package/src/compiler/phases/nodes.js +4 -0
- package/src/compiler/print/index.js +95 -24
- package/src/easing/index.js +62 -0
- package/src/internal/client/constants.js +9 -0
- package/src/internal/client/context.js +33 -56
- 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/css-props.js +3 -3
- 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/css.js +6 -1
- package/src/internal/client/dom/elements/attributes.js +37 -35
- 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/events.js +19 -1
- 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/batch.js +1 -0
- package/src/internal/client/reactivity/effects.js +14 -1
- package/src/internal/client/reactivity/sources.js +7 -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 +9 -48
- package/src/internal/server/index.js +1 -1
- package/src/internal/server/renderer.js +164 -52
- package/src/internal/shared/attributes.js +4 -2
- package/src/internal/shared/clone.js +2 -0
- package/src/internal/shared/context.js +53 -0
- package/src/internal/shared/errors.js +2 -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 +148 -46
- package/types/index.d.ts.map +2 -2
|
@@ -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() {
|
|
@@ -355,7 +355,11 @@ const visitors = {
|
|
|
355
355
|
continue;
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
-
if (
|
|
358
|
+
if (
|
|
359
|
+
selector.type === 'TypeSelector' &&
|
|
360
|
+
selector.name === '*' &&
|
|
361
|
+
selector.namespace === undefined
|
|
362
|
+
) {
|
|
359
363
|
context.state.code.update(selector.start, selector.end, modifier);
|
|
360
364
|
} else {
|
|
361
365
|
context.state.code.appendLeft(selector.end, modifier);
|
|
@@ -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';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import { BlockStatement } from 'estree' */
|
|
1
|
+
/** @import { BlockStatement, Statement } from 'estree' */
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentContext } from '../types' */
|
|
4
4
|
import * as b from '#compiler/builders';
|
|
@@ -84,6 +84,9 @@ export function SvelteBoundary(node, context) {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
const props = b.object([]);
|
|
87
|
+
/** @type {Statement[]} */
|
|
88
|
+
const init = [];
|
|
89
|
+
|
|
87
90
|
if (failed_attribute && !failed_snippet) {
|
|
88
91
|
const failed_callee = build_attribute_value(
|
|
89
92
|
failed_attribute.value,
|
|
@@ -95,13 +98,15 @@ export function SvelteBoundary(node, context) {
|
|
|
95
98
|
|
|
96
99
|
props.properties.push(b.init('failed', failed_callee));
|
|
97
100
|
} else if (failed_snippet) {
|
|
98
|
-
context.visit(failed_snippet, context.state);
|
|
101
|
+
context.visit(failed_snippet, { ...context.state, init });
|
|
99
102
|
props.properties.push(b.init('failed', failed_snippet.expression));
|
|
100
103
|
}
|
|
101
104
|
|
|
102
|
-
|
|
103
|
-
b.
|
|
105
|
+
const boundary = b.stmt(
|
|
106
|
+
b.call('$$renderer.boundary', props, b.arrow([b.id('$$renderer')], children_body))
|
|
104
107
|
);
|
|
108
|
+
|
|
109
|
+
context.state.template.push(init.length > 0 ? b.block([...init, boundary]) : boundary);
|
|
105
110
|
}
|
|
106
111
|
|
|
107
112
|
/**
|
|
@@ -102,9 +102,11 @@ export function VariableDeclaration(node, context) {
|
|
|
102
102
|
} else {
|
|
103
103
|
const call = /** @type {CallExpression} */ (declarator.init);
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
// - cannot be a SpreadElement because refused during analysis
|
|
106
|
+
// - use args[0] rather than value to avoid visiting twice (above in const value = ... and below in for-ofs)
|
|
107
|
+
let rhs = /** @type {Expression} */ (call.arguments[0]);
|
|
106
108
|
|
|
107
|
-
if (rune
|
|
109
|
+
if (rune === '$derived.by' || call.arguments[0].type !== 'Identifier') {
|
|
108
110
|
const id = b.id(context.state.scope.generate('$$d'));
|
|
109
111
|
|
|
110
112
|
rhs = b.call(id);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/** @import { ComponentContext, ComponentServerTransformState } from '../../types.js' */
|
|
4
4
|
import { is_event_attribute, is_text_attribute } from '../../../../../utils/ast.js';
|
|
5
5
|
import { binding_properties } from '../../../../bindings.js';
|
|
6
|
-
import {
|
|
6
|
+
import { ExpressionMetadata, is_custom_element_node } from '../../../../nodes.js';
|
|
7
7
|
import { regex_starts_with_newline } from '../../../../patterns.js';
|
|
8
8
|
import * as b from '#compiler/builders';
|
|
9
9
|
import {
|
|
@@ -21,6 +21,11 @@ import { escape_html } from '../../../../../../escaping.js';
|
|
|
21
21
|
|
|
22
22
|
const WHITESPACE_INSENSITIVE_ATTRIBUTES = ['class', 'style'];
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {{ type: 'transformed', name: string, expression: Expression }} TransformedAttribute
|
|
26
|
+
* An attribute whose expression has already been transformed and must not be visited again.
|
|
27
|
+
*/
|
|
28
|
+
|
|
24
29
|
/**
|
|
25
30
|
* Writes the output to the template output. Some elements may have attributes on them that require the
|
|
26
31
|
* their output to be the child content instead. In this case, an object is returned.
|
|
@@ -29,7 +34,7 @@ const WHITESPACE_INSENSITIVE_ATTRIBUTES = ['class', 'style'];
|
|
|
29
34
|
* @param {(expression: Expression, metadata: ExpressionMetadata) => Expression} transform
|
|
30
35
|
*/
|
|
31
36
|
export function build_element_attributes(node, context, transform) {
|
|
32
|
-
/** @type {Array<AST.Attribute | AST.SpreadAttribute>} */
|
|
37
|
+
/** @type {Array<AST.Attribute | AST.SpreadAttribute | TransformedAttribute>} */
|
|
33
38
|
const attributes = [];
|
|
34
39
|
|
|
35
40
|
/** @type {AST.ClassDirective[]} */
|
|
@@ -145,42 +150,26 @@ export function build_element_attributes(node, context, transform) {
|
|
|
145
150
|
attr.value[0].data === 'checkbox'
|
|
146
151
|
);
|
|
147
152
|
|
|
148
|
-
attributes.push(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
build_attribute_value(value_attribute.value, context, transform)
|
|
163
|
-
),
|
|
164
|
-
metadata: {
|
|
165
|
-
expression: new ExpressionMetadata()
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
])
|
|
169
|
-
);
|
|
153
|
+
attributes.push({
|
|
154
|
+
type: 'transformed',
|
|
155
|
+
name: 'checked',
|
|
156
|
+
expression: is_checkbox
|
|
157
|
+
? b.call(
|
|
158
|
+
b.member(expression, 'includes'),
|
|
159
|
+
build_attribute_value(value_attribute.value, context, transform)
|
|
160
|
+
)
|
|
161
|
+
: b.binary(
|
|
162
|
+
'===',
|
|
163
|
+
expression,
|
|
164
|
+
build_attribute_value(value_attribute.value, context, transform)
|
|
165
|
+
)
|
|
166
|
+
});
|
|
170
167
|
} else {
|
|
171
|
-
attributes.push(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
end: -1,
|
|
177
|
-
expression,
|
|
178
|
-
metadata: {
|
|
179
|
-
expression: new ExpressionMetadata()
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
])
|
|
183
|
-
);
|
|
168
|
+
attributes.push({
|
|
169
|
+
type: 'transformed',
|
|
170
|
+
name: get_attribute_name(node, attribute),
|
|
171
|
+
expression
|
|
172
|
+
});
|
|
184
173
|
}
|
|
185
174
|
} else if (attribute.type === 'SpreadAttribute') {
|
|
186
175
|
attributes.push(attribute);
|
|
@@ -217,7 +206,21 @@ export function build_element_attributes(node, context, transform) {
|
|
|
217
206
|
} else {
|
|
218
207
|
const css_hash = node.metadata.scoped ? context.state.analysis.css.hash : null;
|
|
219
208
|
|
|
220
|
-
for (const attribute of /** @type {AST.Attribute
|
|
209
|
+
for (const attribute of /** @type {Array<AST.Attribute | TransformedAttribute>} */ (
|
|
210
|
+
attributes
|
|
211
|
+
)) {
|
|
212
|
+
if (attribute.type === 'transformed') {
|
|
213
|
+
context.state.template.push(
|
|
214
|
+
b.call(
|
|
215
|
+
'$.attr',
|
|
216
|
+
b.literal(attribute.name),
|
|
217
|
+
attribute.expression,
|
|
218
|
+
is_boolean_attribute(attribute.name) && b.true
|
|
219
|
+
)
|
|
220
|
+
);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
|
|
221
224
|
const name = get_attribute_name(node, attribute);
|
|
222
225
|
const can_use_literal =
|
|
223
226
|
(name !== 'class' || class_directives.length === 0) &&
|
|
@@ -298,15 +301,19 @@ function get_attribute_name(element, attribute) {
|
|
|
298
301
|
|
|
299
302
|
/**
|
|
300
303
|
* @param {AST.RegularElement | AST.SvelteElement} element
|
|
301
|
-
* @param {Array<AST.Attribute | AST.SpreadAttribute | AST.BindDirective>} attributes
|
|
304
|
+
* @param {Array<AST.Attribute | AST.SpreadAttribute | AST.BindDirective | TransformedAttribute>} attributes
|
|
302
305
|
* @param {ComponentContext} context
|
|
303
306
|
* @param {(expression: Expression, metadata: ExpressionMetadata) => Expression} transform
|
|
304
307
|
*/
|
|
305
308
|
export function build_spread_object(element, attributes, context, transform) {
|
|
309
|
+
const is_select = element.type === 'RegularElement' && element.name === 'select';
|
|
306
310
|
const object = b.object(
|
|
307
311
|
attributes.map((attribute) => {
|
|
308
|
-
if (attribute.type === '
|
|
309
|
-
|
|
312
|
+
if (attribute.type === 'transformed') {
|
|
313
|
+
return b.prop('init', b.key(attribute.name), attribute.expression);
|
|
314
|
+
} else if (attribute.type === 'Attribute') {
|
|
315
|
+
let name = get_attribute_name(element, attribute);
|
|
316
|
+
if (is_select && name === 'defaultvalue') name = 'defaultValue';
|
|
310
317
|
const value = build_attribute_value(
|
|
311
318
|
attribute.value,
|
|
312
319
|
context,
|
|
@@ -317,10 +324,9 @@ export function build_spread_object(element, attributes, context, transform) {
|
|
|
317
324
|
return b.prop('init', b.key(name), value);
|
|
318
325
|
} else if (attribute.type === 'BindDirective') {
|
|
319
326
|
const name = get_attribute_name(element, attribute);
|
|
327
|
+
const expression = /** @type {Expression} */ (context.visit(attribute.expression));
|
|
320
328
|
const value =
|
|
321
|
-
|
|
322
|
-
? b.call(attribute.expression.expressions[0])
|
|
323
|
-
: /** @type {Expression} */ (context.visit(attribute.expression));
|
|
329
|
+
expression.type === 'SequenceExpression' ? b.call(expression.expressions[0]) : expression;
|
|
324
330
|
|
|
325
331
|
return b.prop('init', b.key(name), value);
|
|
326
332
|
}
|
|
@@ -340,7 +346,7 @@ export function build_spread_object(element, attributes, context, transform) {
|
|
|
340
346
|
/**
|
|
341
347
|
*
|
|
342
348
|
* @param {AST.RegularElement | AST.SvelteElement} element
|
|
343
|
-
* @param {Array<AST.Attribute | AST.SpreadAttribute>} attributes
|
|
349
|
+
* @param {Array<AST.Attribute | AST.SpreadAttribute | TransformedAttribute>} attributes
|
|
344
350
|
* @param {AST.StyleDirective[]} style_directives
|
|
345
351
|
* @param {AST.ClassDirective[]} class_directives
|
|
346
352
|
* @param {ComponentContext} context
|
|
@@ -356,7 +362,7 @@ function build_element_spread_attributes(
|
|
|
356
362
|
) {
|
|
357
363
|
const args = prepare_element_spread(
|
|
358
364
|
element,
|
|
359
|
-
|
|
365
|
+
attributes,
|
|
360
366
|
style_directives,
|
|
361
367
|
class_directives,
|
|
362
368
|
context,
|
|
@@ -410,7 +416,7 @@ export function prepare_element_spread_object(element, context, transform) {
|
|
|
410
416
|
/**
|
|
411
417
|
* Prepare args for $.attributes(...): compute object, css_hash, classes, styles and flags.
|
|
412
418
|
* @param {AST.RegularElement | AST.SvelteElement} element
|
|
413
|
-
* @param {Array<AST.Attribute | AST.SpreadAttribute | AST.BindDirective>} attributes
|
|
419
|
+
* @param {Array<AST.Attribute | AST.SpreadAttribute | AST.BindDirective | TransformedAttribute>} attributes
|
|
414
420
|
* @param {AST.StyleDirective[]} style_directives
|
|
415
421
|
* @param {AST.ClassDirective[]} class_directives
|
|
416
422
|
* @param {ComponentContext} context
|
|
@@ -180,6 +180,36 @@ export function build_template(template) {
|
|
|
180
180
|
return statements;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Prepends a hydration marker (e.g. `<!--[0-->`) to a branch. The branch has already been
|
|
185
|
+
* turned into statements by `build_template`, so if it happens to start with a static
|
|
186
|
+
* `$$renderer.push(...)` we fold the marker into that call rather than emitting a second one.
|
|
187
|
+
* @param {BlockStatement} block
|
|
188
|
+
* @param {string} marker
|
|
189
|
+
*/
|
|
190
|
+
export function prepend_block_marker(block, marker) {
|
|
191
|
+
const first = block.body[0];
|
|
192
|
+
|
|
193
|
+
if (
|
|
194
|
+
first?.type === 'ExpressionStatement' &&
|
|
195
|
+
first.expression.type === 'CallExpression' &&
|
|
196
|
+
first.expression.callee.type === 'Identifier' &&
|
|
197
|
+
first.expression.callee.name === '$$renderer.push' &&
|
|
198
|
+
first.expression.arguments.length === 1 &&
|
|
199
|
+
first.expression.arguments[0].type === 'TemplateLiteral'
|
|
200
|
+
) {
|
|
201
|
+
const quasi = first.expression.arguments[0].quasis[0];
|
|
202
|
+
|
|
203
|
+
// markers never contain characters that need escaping in a template literal
|
|
204
|
+
quasi.value.cooked = marker + quasi.value.cooked;
|
|
205
|
+
quasi.value.raw = marker + quasi.value.raw;
|
|
206
|
+
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
block.body.unshift(b.stmt(b.call(b.id('$$renderer.push'), b.literal(marker))));
|
|
211
|
+
}
|
|
212
|
+
|
|
183
213
|
/**
|
|
184
214
|
*
|
|
185
215
|
* @param {AST.Attribute['value']} value
|
|
@@ -286,7 +316,7 @@ export function build_getter(node, state) {
|
|
|
286
316
|
}
|
|
287
317
|
|
|
288
318
|
if (binding.kind === 'derived') {
|
|
289
|
-
return (binding.declaration_kind === 'var' ? b.maybe_call : b.call)(
|
|
319
|
+
return (binding.declaration_kind === 'var' ? b.maybe_call : b.call)(node);
|
|
290
320
|
}
|
|
291
321
|
|
|
292
322
|
return node;
|
|
@@ -23,7 +23,9 @@ export const binding_properties = {
|
|
|
23
23
|
event: 'durationchange',
|
|
24
24
|
omit_in_ssr: true
|
|
25
25
|
},
|
|
26
|
-
focused: {
|
|
26
|
+
focused: {
|
|
27
|
+
omit_in_ssr: true // no corresponding HTML attribute
|
|
28
|
+
},
|
|
27
29
|
paused: {
|
|
28
30
|
valid_elements: ['audio', 'video'],
|
|
29
31
|
omit_in_ssr: true,
|
|
@@ -77,6 +77,9 @@ export class ExpressionMetadata {
|
|
|
77
77
|
/** True if the expression contains `await` */
|
|
78
78
|
has_await = false;
|
|
79
79
|
|
|
80
|
+
/** True if an `await` restores the reaction context afterwards, so the thunk must end it */
|
|
81
|
+
has_pickled_await = false;
|
|
82
|
+
|
|
80
83
|
/** True if the expression includes a member expression */
|
|
81
84
|
has_member_expression = false;
|
|
82
85
|
|
|
@@ -142,6 +145,7 @@ export class ExpressionMetadata {
|
|
|
142
145
|
this.has_state ||= source.has_state;
|
|
143
146
|
this.has_call ||= source.has_call;
|
|
144
147
|
this.has_await ||= source.has_await;
|
|
148
|
+
this.has_pickled_await ||= source.has_pickled_await;
|
|
145
149
|
this.has_member_expression ||= source.has_member_expression;
|
|
146
150
|
this.has_assignment ||= source.has_assignment;
|
|
147
151
|
this.#blockers = null; // so that blockers are recalculated
|