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
package/elements.d.ts
CHANGED
|
@@ -1352,6 +1352,9 @@ export interface HTMLSelectAttributes extends HTMLAttributes<HTMLSelectElement>
|
|
|
1352
1352
|
required?: boolean | undefined | null;
|
|
1353
1353
|
size?: number | undefined | null;
|
|
1354
1354
|
value?: any;
|
|
1355
|
+
// needs both casing variants because language tools does lowercase names of non-shorthand attributes
|
|
1356
|
+
defaultValue?: any;
|
|
1357
|
+
defaultvalue?: any;
|
|
1355
1358
|
|
|
1356
1359
|
'on:change'?: ChangeEventHandler<HTMLSelectElement> | undefined | null;
|
|
1357
1360
|
onchange?: ChangeEventHandler<HTMLSelectElement> | undefined | null;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "svelte",
|
|
3
3
|
"description": "Cybernetically enhanced web apps",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "5.
|
|
5
|
+
"version": "5.57.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"types": "./types/index.d.ts",
|
|
8
8
|
"engines": {
|
|
@@ -139,13 +139,14 @@
|
|
|
139
139
|
],
|
|
140
140
|
"devDependencies": {
|
|
141
141
|
"@jridgewell/trace-mapping": "^0.3.25",
|
|
142
|
-
"@playwright/test": "^1.
|
|
142
|
+
"@playwright/test": "^1.62.0",
|
|
143
143
|
"@rollup/plugin-commonjs": "^28.0.1",
|
|
144
144
|
"@rollup/plugin-node-resolve": "^15.3.0",
|
|
145
145
|
"@rollup/plugin-terser": "^0.4.4",
|
|
146
146
|
"@rollup/plugin-virtual": "^3.0.2",
|
|
147
147
|
"@types/aria-query": "^5.0.4",
|
|
148
148
|
"@types/node": "^20.11.5",
|
|
149
|
+
"@types/trusted-types": "^2.0.7",
|
|
149
150
|
"baseline-browser-mapping": "^2.10.32",
|
|
150
151
|
"dts-buddy": "^0.5.5",
|
|
151
152
|
"esbuild": "^0.28.1",
|
|
@@ -161,7 +162,6 @@
|
|
|
161
162
|
"@jridgewell/sourcemap-codec": "^1.5.0",
|
|
162
163
|
"@sveltejs/acorn-typescript": "^1.0.10",
|
|
163
164
|
"@types/estree": "^1.0.5",
|
|
164
|
-
"@types/trusted-types": "^2.0.7",
|
|
165
165
|
"acorn": "^8.12.1",
|
|
166
166
|
"aria-query": "5.3.1",
|
|
167
167
|
"axobject-query": "^4.1.0",
|
|
@@ -1590,7 +1590,12 @@ function migrate_slot_usage(node, path, state) {
|
|
|
1590
1590
|
*/
|
|
1591
1591
|
function extract_type_and_comment(declarator, state, path) {
|
|
1592
1592
|
const str = state.str;
|
|
1593
|
-
|
|
1593
|
+
let parent = path.at(-1);
|
|
1594
|
+
|
|
1595
|
+
if (parent?.type === 'Program') {
|
|
1596
|
+
// We don't want comments from the program node
|
|
1597
|
+
parent = undefined;
|
|
1598
|
+
}
|
|
1594
1599
|
|
|
1595
1600
|
// Try to find jsdoc above the declaration
|
|
1596
1601
|
let comment_node = /** @type {Node} */ (parent)?.leadingComments?.at(-1);
|
|
@@ -59,7 +59,7 @@ export function parse(source, comments, typescript, is_script) {
|
|
|
59
59
|
|
|
60
60
|
return /** @type {Program} */ (ast);
|
|
61
61
|
} catch (err) {
|
|
62
|
-
// TODO the `return`
|
|
62
|
+
// TODO the `return` is necessary for TS<7 due to a bug; otherwise
|
|
63
63
|
// the `finally` block is regarded as unreachable
|
|
64
64
|
return handle_parse_error(err);
|
|
65
65
|
} finally {
|
|
@@ -7,8 +7,11 @@ const REGEX_CLOSING_BRACKET = /[\s\]]/;
|
|
|
7
7
|
const REGEX_ATTRIBUTE_FLAGS = /[a-zA-Z]+/y; // only `i` and `s` are valid today, but make it future-proof
|
|
8
8
|
const REGEX_COMBINATOR = /(\+|~|>|\|\|)/y;
|
|
9
9
|
const REGEX_PERCENTAGE = /\d+(\.\d+)?%/y;
|
|
10
|
+
// `of` must be preceded by whitespace, otherwise it would be part of the `<an+b>` token
|
|
11
|
+
// (`2nof` is a single dimension token). It does not need to be followed by whitespace,
|
|
12
|
+
// because a `.`, `#`, `[`, `*`, `:` or `&` already ends the `of` identifier — minifiers rely on that
|
|
10
13
|
const REGEX_NTH_OF =
|
|
11
|
-
/(even|odd|\+?(\d+|\d*n(\s*[+-]\s*\d+)?)|-\d*n(\s*\+\s*\d+))((?=\s*[,)])|\s+of\s
|
|
14
|
+
/(even|odd|\+?(\d+|\d*n(\s*[+-]\s*\d+)?)|-\d*n(\s*\+\s*\d+))((?=\s*[,)])|\s+of(\s+|(?=[.#[*:&])))/y;
|
|
12
15
|
const REGEX_WHITESPACE_OR_COLON = /[\s:]/;
|
|
13
16
|
const REGEX_LEADING_HYPHEN_OR_DIGIT = /-?\d/y;
|
|
14
17
|
const REGEX_VALID_IDENTIFIER_CHAR = /[a-zA-Z0-9_-]/;
|
|
@@ -204,15 +207,18 @@ function read_selector(parser, inside_pseudo_class = false) {
|
|
|
204
207
|
});
|
|
205
208
|
} else if (parser.eat('*')) {
|
|
206
209
|
let name = '*';
|
|
210
|
+
/** @type {string | undefined} */
|
|
211
|
+
let namespace;
|
|
207
212
|
|
|
208
213
|
if (parser.eat('|')) {
|
|
209
|
-
|
|
210
|
-
name = read_identifier(parser);
|
|
214
|
+
namespace = name;
|
|
215
|
+
name = parser.eat('*') ? '*' : read_identifier(parser);
|
|
211
216
|
}
|
|
212
217
|
|
|
213
218
|
relative_selector.selectors.push({
|
|
214
219
|
type: 'TypeSelector',
|
|
215
220
|
name,
|
|
221
|
+
...(namespace !== undefined && { namespace }),
|
|
216
222
|
start,
|
|
217
223
|
end: parser.index
|
|
218
224
|
});
|
|
@@ -314,15 +320,18 @@ function read_selector(parser, inside_pseudo_class = false) {
|
|
|
314
320
|
});
|
|
315
321
|
} else if (!parser.match_regex(REGEX_COMBINATOR)) {
|
|
316
322
|
let name = read_identifier(parser);
|
|
323
|
+
/** @type {string | undefined} */
|
|
324
|
+
let namespace;
|
|
317
325
|
|
|
318
326
|
if (parser.eat('|')) {
|
|
319
|
-
|
|
320
|
-
name = read_identifier(parser);
|
|
327
|
+
namespace = name;
|
|
328
|
+
name = parser.eat('*') ? '*' : read_identifier(parser);
|
|
321
329
|
}
|
|
322
330
|
|
|
323
331
|
relative_selector.selectors.push({
|
|
324
332
|
type: 'TypeSelector',
|
|
325
333
|
name,
|
|
334
|
+
...(namespace !== undefined && { namespace }),
|
|
326
335
|
start,
|
|
327
336
|
end: parser.index
|
|
328
337
|
});
|
|
@@ -614,7 +623,8 @@ function read_identifier(parser) {
|
|
|
614
623
|
if (char === '\\') {
|
|
615
624
|
const sequence = parser.match_regex(REGEX_UNICODE_SEQUENCE);
|
|
616
625
|
if (sequence) {
|
|
617
|
-
|
|
626
|
+
const character = String.fromCodePoint(parseInt(sequence.slice(1), 16));
|
|
627
|
+
identifier += character === '\\' ? '\\\\' : character;
|
|
618
628
|
parser.index += sequence.length;
|
|
619
629
|
} else {
|
|
620
630
|
identifier += '\\' + parser.template[parser.index + 1];
|
|
@@ -20,7 +20,7 @@ function reg_exp_entity(entity_name, is_attribute_value) {
|
|
|
20
20
|
|
|
21
21
|
/** @param {boolean} is_attribute_value */
|
|
22
22
|
function get_entity_pattern(is_attribute_value) {
|
|
23
|
-
const reg_exp_num = '#(?:
|
|
23
|
+
const reg_exp_num = '#(?:[xX][a-fA-F\\d]+|\\d+)(?:;)?';
|
|
24
24
|
const reg_exp_entities = Object.keys(entities).map(
|
|
25
25
|
/** @param {any} entity_name */ (entity_name) => reg_exp_entity(entity_name, is_attribute_value)
|
|
26
26
|
);
|
|
@@ -50,7 +50,7 @@ export function decode_character_references(html, is_attribute_value) {
|
|
|
50
50
|
// Handle named entities
|
|
51
51
|
if (entity[0] !== '#') {
|
|
52
52
|
code = entities[entity];
|
|
53
|
-
} else if (entity[1] === 'x') {
|
|
53
|
+
} else if (entity[1] === 'x' || entity[1] === 'X') {
|
|
54
54
|
code = parseInt(entity.substring(2), 16);
|
|
55
55
|
} else {
|
|
56
56
|
code = parseInt(entity.substring(1), 10);
|
|
@@ -60,7 +60,7 @@ export function decode_character_references(html, is_attribute_value) {
|
|
|
60
60
|
return match;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
return String.fromCodePoint(validate_code(code));
|
|
63
|
+
return String.fromCodePoint(validate_code(code, is_attribute_value));
|
|
64
64
|
}
|
|
65
65
|
);
|
|
66
66
|
}
|
|
@@ -75,10 +75,15 @@ const NUL = 0;
|
|
|
75
75
|
// Also see: https://en.wikipedia.org/wiki/Plane_(Unicode)
|
|
76
76
|
// Also see: https://html.spec.whatwg.org/multipage/parsing.html#preprocessing-the-input-stream
|
|
77
77
|
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
/**
|
|
79
|
+
* @param {number} code
|
|
80
|
+
* @param {boolean} is_attribute_value
|
|
81
|
+
*/
|
|
82
|
+
function validate_code(code, is_attribute_value) {
|
|
83
|
+
// line feed becomes generic whitespace, since it is collapsed along with the
|
|
84
|
+
// surrounding whitespace anyway. In an attribute value it is significant, so it
|
|
85
|
+
// is left alone there
|
|
86
|
+
if (code === 10 && !is_attribute_value) {
|
|
82
87
|
return 32;
|
|
83
88
|
}
|
|
84
89
|
|
|
@@ -122,6 +122,13 @@ const any_selector = {
|
|
|
122
122
|
*/
|
|
123
123
|
const seen = new Set();
|
|
124
124
|
|
|
125
|
+
/**
|
|
126
|
+
* @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.RenderTag | Compiler.AST.Component | Compiler.AST.SvelteComponent | Compiler.AST.SvelteSelf} node
|
|
127
|
+
*/
|
|
128
|
+
function is_inside_svelte_head(node) {
|
|
129
|
+
return node.metadata.path.some((ancestor) => ancestor.type === 'SvelteHead');
|
|
130
|
+
}
|
|
131
|
+
|
|
125
132
|
/**
|
|
126
133
|
*
|
|
127
134
|
* @param {Compiler.AST.CSS.StyleSheet} stylesheet
|
|
@@ -143,6 +150,9 @@ export function prune(stylesheet, elements) {
|
|
|
143
150
|
seen.clear();
|
|
144
151
|
|
|
145
152
|
if (
|
|
153
|
+
// Elements rendered through <svelte:head> are not style-scopable.
|
|
154
|
+
// Prevent css hash injection (class="s-...") on tags like <meta>, <link>, <script>.
|
|
155
|
+
!is_inside_svelte_head(element) &&
|
|
146
156
|
apply_selector(
|
|
147
157
|
selectors,
|
|
148
158
|
/** @type {Compiler.AST.CSS.Rule} */ (node.metadata.rule),
|
|
@@ -285,7 +285,7 @@ export function analyze_module(source, options) {
|
|
|
285
285
|
runes: true,
|
|
286
286
|
immutable: true,
|
|
287
287
|
tracing: false,
|
|
288
|
-
async_deriveds: new
|
|
288
|
+
async_deriveds: new Map(),
|
|
289
289
|
comments,
|
|
290
290
|
classes: new Map(),
|
|
291
291
|
pickled_awaits: new Set()
|
|
@@ -557,7 +557,7 @@ export function analyze_component(root, source, options) {
|
|
|
557
557
|
source,
|
|
558
558
|
snippet_renderers: new Map(),
|
|
559
559
|
snippets: new Set(),
|
|
560
|
-
async_deriveds: new
|
|
560
|
+
async_deriveds: new Map(),
|
|
561
561
|
pickled_awaits: new Set(),
|
|
562
562
|
instance_body: {
|
|
563
563
|
sync: [],
|
|
@@ -832,6 +832,11 @@ export function analyze_component(root, source, options) {
|
|
|
832
832
|
} else {
|
|
833
833
|
e.export_undefined(specifier, name);
|
|
834
834
|
}
|
|
835
|
+
} else if (binding.initial?.type === 'SnippetBlock') {
|
|
836
|
+
// If a snippet is exported, a consumer could only import this named export and not the default export (the component).
|
|
837
|
+
// In this case we need to set hasGlobal of our output to true so that e.g. vite-plugin-svelte does not tell Vite to
|
|
838
|
+
// tree-shake the CSS if the default export is not used.
|
|
839
|
+
analysis.css.has_global = true;
|
|
835
840
|
}
|
|
836
841
|
}
|
|
837
842
|
}
|
|
@@ -958,7 +963,7 @@ function calculate_blockers(instance, analysis) {
|
|
|
958
963
|
* @param {Set<Binding>} touched
|
|
959
964
|
* @param {Set<ESTree.Node>} seen
|
|
960
965
|
*/
|
|
961
|
-
const touch = (expression, scope, touched, seen
|
|
966
|
+
const touch = (expression, scope, touched, seen) => {
|
|
962
967
|
if (seen.has(expression)) return;
|
|
963
968
|
seen.add(expression);
|
|
964
969
|
|
|
@@ -1015,6 +1020,13 @@ function calculate_blockers(instance, analysis) {
|
|
|
1015
1020
|
}
|
|
1016
1021
|
}
|
|
1017
1022
|
|
|
1023
|
+
// Share seen nodes across calls so transitive assignments are only visited once.
|
|
1024
|
+
// Keep separate read/write state because the target sets can differ.
|
|
1025
|
+
/** @type {Set<ESTree.Node>} */
|
|
1026
|
+
const writes_seen = new Set();
|
|
1027
|
+
/** @type {Set<ESTree.Node>} */
|
|
1028
|
+
const reads_seen = new Set();
|
|
1029
|
+
|
|
1018
1030
|
walk(
|
|
1019
1031
|
node,
|
|
1020
1032
|
{ scope },
|
|
@@ -1044,13 +1056,7 @@ function calculate_blockers(instance, analysis) {
|
|
|
1044
1056
|
const rune = get_rune(node, context.state.scope);
|
|
1045
1057
|
if (rune === '$effect') return;
|
|
1046
1058
|
|
|
1047
|
-
|
|
1048
|
-
const touched = new Set();
|
|
1049
|
-
touch(node, context.state.scope, touched);
|
|
1050
|
-
|
|
1051
|
-
for (const b of touched) {
|
|
1052
|
-
writes.add(b);
|
|
1053
|
-
}
|
|
1059
|
+
touch(node, context.state.scope, writes, writes_seen);
|
|
1054
1060
|
},
|
|
1055
1061
|
Identifier(node, context) {
|
|
1056
1062
|
const parent = /** @type {ESTree.Node} */ (context.path.at(-1));
|
|
@@ -1066,7 +1072,7 @@ function calculate_blockers(instance, analysis) {
|
|
|
1066
1072
|
// might be called immediately, so we have to touch all references within it. Example:
|
|
1067
1073
|
// function foo() { return () => blocker; } foo(); // blocker is touched
|
|
1068
1074
|
if (node.argument) {
|
|
1069
|
-
touch(node.argument, context.state.scope, reads);
|
|
1075
|
+
touch(node.argument, context.state.scope, reads, reads_seen);
|
|
1070
1076
|
}
|
|
1071
1077
|
},
|
|
1072
1078
|
// don't look inside functions until they are called
|
|
@@ -1227,6 +1233,23 @@ function calculate_blockers(instance, analysis) {
|
|
|
1227
1233
|
|
|
1228
1234
|
flush_sync_group();
|
|
1229
1235
|
|
|
1236
|
+
// a store subscription must wait on whatever blocks the store itself; this must happen
|
|
1237
|
+
// before function tracing so that functions reading `$store` inherit the blocker
|
|
1238
|
+
for (const [name, binding] of instance.scope.declarations) {
|
|
1239
|
+
if (binding.kind !== 'store_sub') continue;
|
|
1240
|
+
|
|
1241
|
+
const store_blocker = instance.scope.get(name.slice(1))?.blocker;
|
|
1242
|
+
if (!store_blocker) continue;
|
|
1243
|
+
|
|
1244
|
+
if (
|
|
1245
|
+
!binding.blocker ||
|
|
1246
|
+
/** @type {ESTree.SimpleLiteral & { value: number }} */ (binding.blocker.property).value <
|
|
1247
|
+
/** @type {ESTree.SimpleLiteral & { value: number }} */ (store_blocker.property).value
|
|
1248
|
+
) {
|
|
1249
|
+
binding.blocker = store_blocker;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1230
1253
|
for (const fn of functions) {
|
|
1231
1254
|
/** @type {Set<Binding>} */
|
|
1232
1255
|
const reads_writes = new Set();
|
|
@@ -1235,12 +1258,15 @@ function calculate_blockers(instance, analysis) {
|
|
|
1235
1258
|
? /** @type {ESTree.FunctionExpression | ESTree.ArrowFunctionExpression} */ (fn.init)
|
|
1236
1259
|
: fn;
|
|
1237
1260
|
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
reads_writes,
|
|
1242
|
-
|
|
1243
|
-
|
|
1261
|
+
const fn_scope = /** @type {Scope} */ (instance.scopes.get(init));
|
|
1262
|
+
|
|
1263
|
+
if (init.body.type === 'BlockStatement') {
|
|
1264
|
+
trace_references(init.body, reads_writes, reads_writes, fn_scope);
|
|
1265
|
+
} else {
|
|
1266
|
+
// A concise arrow body is an implicit return, so treat it like the
|
|
1267
|
+
// `ReturnStatement` visitor in `trace_references` would.
|
|
1268
|
+
touch(init.body, fn_scope, reads_writes, new Set());
|
|
1269
|
+
}
|
|
1244
1270
|
|
|
1245
1271
|
const max = [...reads_writes].reduce((max, binding) => {
|
|
1246
1272
|
if (binding.blocker) {
|
|
@@ -1314,7 +1340,8 @@ function order_reactive_statements(unsorted_reactive_declarations) {
|
|
|
1314
1340
|
* @returns
|
|
1315
1341
|
*/
|
|
1316
1342
|
const add_declaration = (node, declaration) => {
|
|
1317
|
-
|
|
1343
|
+
// Visited set: each ReactiveStatement is stored under exactly one LabeledStatement node
|
|
1344
|
+
if (reactive_declarations.has(node)) return;
|
|
1318
1345
|
|
|
1319
1346
|
for (const binding of declaration.dependencies) {
|
|
1320
1347
|
if (declaration.assignments.has(binding)) continue;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** @import { AwaitExpression, Expression, SpreadElement, Property } from 'estree' */
|
|
2
2
|
/** @import { Context } from '../types' */
|
|
3
3
|
/** @import { AST } from '#compiler' */
|
|
4
|
+
/** @import { ExpressionMetadata } from '../../nodes.js' */
|
|
4
5
|
import * as e from '../../../errors.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -10,15 +11,20 @@ import * as e from '../../../errors.js';
|
|
|
10
11
|
export function AwaitExpression(node, context) {
|
|
11
12
|
const tla = context.state.ast_type === 'instance' && context.state.function_depth === 1;
|
|
12
13
|
|
|
13
|
-
// preserve context for awaits that precede other expressions in template or `$derived(...)`
|
|
14
14
|
if (
|
|
15
15
|
is_reactive_expression(
|
|
16
16
|
context.path,
|
|
17
17
|
context.state.derived_function_depth === context.state.function_depth
|
|
18
|
-
)
|
|
19
|
-
!is_last_evaluated_expression(context.path, node)
|
|
18
|
+
)
|
|
20
19
|
) {
|
|
21
|
-
context.state.
|
|
20
|
+
const expression = /** @type {ExpressionMetadata} */ (context.state.expression);
|
|
21
|
+
|
|
22
|
+
// preserve context for awaits that precede other expressions in template or `$derived(...)`,
|
|
23
|
+
// and for any await that follows one, so the restored context ends at the next suspension
|
|
24
|
+
if (expression.has_pickled_await || !is_last_evaluated_expression(context.path, node)) {
|
|
25
|
+
context.state.analysis.pickled_awaits.add(node);
|
|
26
|
+
expression.has_pickled_await = true;
|
|
27
|
+
}
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
let suspend = tla;
|
|
@@ -253,7 +253,7 @@ export function CallExpression(node, context) {
|
|
|
253
253
|
});
|
|
254
254
|
|
|
255
255
|
if (expression.has_await) {
|
|
256
|
-
context.state.analysis.async_deriveds.
|
|
256
|
+
context.state.analysis.async_deriveds.set(node, expression);
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
// Tell surrounding declaration tag about metadata for correct calculation of blockers etc
|
|
@@ -43,7 +43,8 @@ export function DeclarationTag(node, context) {
|
|
|
43
43
|
*/
|
|
44
44
|
export function mark_async_declaration(context, metadata, declarations) {
|
|
45
45
|
const has_await = metadata.expression.has_await;
|
|
46
|
-
|
|
46
|
+
// reads inside closures must block too, like they do in template expressions
|
|
47
|
+
const blockers = [...metadata.expression.references]
|
|
47
48
|
.map((dep) => dep.blocker)
|
|
48
49
|
.filter((b) => b !== null && b.object !== context.state.async_consts?.id);
|
|
49
50
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import { ExportNamedDeclaration, Identifier } from 'estree' */
|
|
1
|
+
/** @import { ExportNamedDeclaration, Identifier, VariableDeclaration } from 'estree' */
|
|
2
2
|
/** @import { Context } from '../types' */
|
|
3
3
|
import * as e from '../../../errors.js';
|
|
4
4
|
import { extract_identifiers } from '../../../utils/ast.js';
|
|
@@ -23,15 +23,6 @@ export function ExportNamedDeclaration(node, context) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
if (node.declaration?.type === 'VariableDeclaration') {
|
|
26
|
-
// in runes mode, forbid `export let`
|
|
27
|
-
if (
|
|
28
|
-
context.state.analysis.runes &&
|
|
29
|
-
context.state.ast_type === 'instance' &&
|
|
30
|
-
node.declaration.kind === 'let'
|
|
31
|
-
) {
|
|
32
|
-
e.legacy_export_invalid(node);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
26
|
for (const declarator of node.declaration.declarations) {
|
|
36
27
|
for (const id of extract_identifiers(declarator.id)) {
|
|
37
28
|
const binding = context.state.scope.get(id.name);
|
|
@@ -46,6 +37,15 @@ export function ExportNamedDeclaration(node, context) {
|
|
|
46
37
|
}
|
|
47
38
|
}
|
|
48
39
|
}
|
|
40
|
+
|
|
41
|
+
// in runes mode, forbid `export let`
|
|
42
|
+
if (
|
|
43
|
+
context.state.analysis.runes &&
|
|
44
|
+
context.state.ast_type === 'instance' &&
|
|
45
|
+
node.declaration.kind === 'let'
|
|
46
|
+
) {
|
|
47
|
+
e.legacy_export_invalid(node);
|
|
48
|
+
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
if (context.state.analysis.runes) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as e from '../../../errors.js';
|
|
4
4
|
import { is_event_attribute } from '../../../utils/ast.js';
|
|
5
5
|
import { disallow_children } from './shared/special-element.js';
|
|
6
|
+
import { check_global_event_reference } from './shared/utils.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @param {AST.SvelteBody} node
|
|
@@ -11,10 +12,9 @@ import { disallow_children } from './shared/special-element.js';
|
|
|
11
12
|
export function SvelteBody(node, context) {
|
|
12
13
|
disallow_children(node);
|
|
13
14
|
for (const attribute of node.attributes) {
|
|
14
|
-
if (
|
|
15
|
-
attribute
|
|
16
|
-
|
|
17
|
-
) {
|
|
15
|
+
if (attribute.type === 'Attribute' && is_event_attribute(attribute)) {
|
|
16
|
+
check_global_event_reference(attribute, context);
|
|
17
|
+
} else if (attribute.type === 'SpreadAttribute' || attribute.type === 'Attribute') {
|
|
18
18
|
e.svelte_body_illegal_attribute(attribute);
|
|
19
19
|
}
|
|
20
20
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { disallow_children } from './shared/special-element.js';
|
|
4
4
|
import * as e from '../../../errors.js';
|
|
5
5
|
import { is_event_attribute } from '../../../utils/ast.js';
|
|
6
|
+
import { check_global_event_reference } from './shared/utils.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @param {AST.SvelteDocument} node
|
|
@@ -12,10 +13,9 @@ export function SvelteDocument(node, context) {
|
|
|
12
13
|
disallow_children(node);
|
|
13
14
|
|
|
14
15
|
for (const attribute of node.attributes) {
|
|
15
|
-
if (
|
|
16
|
-
attribute
|
|
17
|
-
|
|
18
|
-
) {
|
|
16
|
+
if (attribute.type === 'Attribute' && is_event_attribute(attribute)) {
|
|
17
|
+
check_global_event_reference(attribute, context);
|
|
18
|
+
} else if (attribute.type === 'SpreadAttribute' || attribute.type === 'Attribute') {
|
|
19
19
|
e.illegal_element_attribute(attribute, 'svelte:document');
|
|
20
20
|
}
|
|
21
21
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { disallow_children } from './shared/special-element.js';
|
|
4
4
|
import * as e from '../../../errors.js';
|
|
5
5
|
import { is_event_attribute } from '../../../utils/ast.js';
|
|
6
|
+
import { check_global_event_reference } from './shared/utils.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @param {AST.SvelteWindow} node
|
|
@@ -12,10 +13,9 @@ export function SvelteWindow(node, context) {
|
|
|
12
13
|
disallow_children(node);
|
|
13
14
|
|
|
14
15
|
for (const attribute of node.attributes) {
|
|
15
|
-
if (
|
|
16
|
-
attribute
|
|
17
|
-
|
|
18
|
-
) {
|
|
16
|
+
if (attribute.type === 'Attribute' && is_event_attribute(attribute)) {
|
|
17
|
+
check_global_event_reference(attribute, context);
|
|
18
|
+
} else if (attribute.type === 'SpreadAttribute' || attribute.type === 'Attribute') {
|
|
19
19
|
e.illegal_element_attribute(attribute, 'svelte:window');
|
|
20
20
|
}
|
|
21
21
|
}
|
|
@@ -373,11 +373,23 @@ export function check_element(node, context) {
|
|
|
373
373
|
}
|
|
374
374
|
}
|
|
375
375
|
|
|
376
|
-
|
|
376
|
+
// `focus`/`blur` don't bubble, so an element that isn't focusable itself can only be notified
|
|
377
|
+
// about focus changes inside it through `focusin`/`focusout` — accept those as well
|
|
378
|
+
if (
|
|
379
|
+
!has_spread &&
|
|
380
|
+
handlers.has('mouseover') &&
|
|
381
|
+
!handlers.has('focus') &&
|
|
382
|
+
!handlers.has('focusin')
|
|
383
|
+
) {
|
|
377
384
|
w.a11y_mouse_events_have_key_events(node, 'mouseover', 'focus');
|
|
378
385
|
}
|
|
379
386
|
|
|
380
|
-
if (
|
|
387
|
+
if (
|
|
388
|
+
!has_spread &&
|
|
389
|
+
handlers.has('mouseout') &&
|
|
390
|
+
!handlers.has('blur') &&
|
|
391
|
+
!handlers.has('focusout')
|
|
392
|
+
) {
|
|
381
393
|
w.a11y_mouse_events_have_key_events(node, 'mouseout', 'blur');
|
|
382
394
|
}
|
|
383
395
|
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
validate_attribute_name,
|
|
10
10
|
validate_slot_attribute
|
|
11
11
|
} from './attribute.js';
|
|
12
|
+
import { check_global_event_reference } from './utils.js';
|
|
12
13
|
|
|
13
14
|
const EVENT_MODIFIERS = [
|
|
14
15
|
'preventDefault',
|
|
@@ -64,14 +65,7 @@ export function validate_element(node, context) {
|
|
|
64
65
|
e.attribute_invalid_event_handler(attribute);
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
value.type === 'Identifier' &&
|
|
70
|
-
value.name === attribute.name &&
|
|
71
|
-
!context.state.scope.get(value.name)
|
|
72
|
-
) {
|
|
73
|
-
w.attribute_global_event_reference(attribute, attribute.name);
|
|
74
|
-
}
|
|
68
|
+
check_global_event_reference(attribute, context);
|
|
75
69
|
}
|
|
76
70
|
|
|
77
71
|
if (attribute.name === 'slot') {
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
/** @import { Scope } from '../../../scope' */
|
|
5
5
|
/** @import { NodeLike } from '../../../../errors.js' */
|
|
6
6
|
import * as e from '../../../../errors.js';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
extract_identifiers,
|
|
9
|
+
get_attribute_expression,
|
|
10
|
+
get_parent
|
|
11
|
+
} from '../../../../utils/ast.js';
|
|
8
12
|
import * as w from '../../../../warnings.js';
|
|
9
13
|
import * as b from '#compiler/builders';
|
|
10
14
|
import { get_rune } from '../../../scope.js';
|
|
@@ -298,3 +302,21 @@ export function validate_export(node, scope, name) {
|
|
|
298
302
|
e.state_invalid_export(node);
|
|
299
303
|
}
|
|
300
304
|
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Warns when an event attribute uses the shorthand form (`{onclick}`) but the
|
|
308
|
+
* referenced name isn't declared, so it silently resolves to the global handler.
|
|
309
|
+
* @param {AST.Attribute & { value: [AST.ExpressionTag] | AST.ExpressionTag }} attribute
|
|
310
|
+
* @param {Context} context
|
|
311
|
+
*/
|
|
312
|
+
export function check_global_event_reference(attribute, context) {
|
|
313
|
+
const value = get_attribute_expression(attribute);
|
|
314
|
+
|
|
315
|
+
if (
|
|
316
|
+
value.type === 'Identifier' &&
|
|
317
|
+
value.name === attribute.name &&
|
|
318
|
+
!context.state.scope.get(value.name)
|
|
319
|
+
) {
|
|
320
|
+
w.attribute_global_event_reference(attribute, attribute.name);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
@@ -247,11 +247,11 @@ export function client_component(analysis, options) {
|
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
for (const [node] of analysis.reactive_statements) {
|
|
250
|
-
const statement =
|
|
250
|
+
const statement = state.legacy_reactive_statements.get(node);
|
|
251
251
|
if (statement === undefined) {
|
|
252
252
|
throw new Error('Could not find reactive statement');
|
|
253
253
|
}
|
|
254
|
-
instance.body.push(statement
|
|
254
|
+
instance.body.push(statement);
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
if (analysis.reactive_statements.size > 0) {
|
|
@@ -39,6 +39,12 @@ export function transform_template(state, name, flags = 0) {
|
|
|
39
39
|
const namespace = state.metadata.namespace;
|
|
40
40
|
const tree = state.options.fragments === 'tree';
|
|
41
41
|
|
|
42
|
+
const { nodes } = state.template;
|
|
43
|
+
const is_lone_anchor = nodes.length === 1 && nodes[0].type === 'comment';
|
|
44
|
+
|
|
45
|
+
// special case - `$.comment` creates the anchor more cheaply than cloning a template
|
|
46
|
+
if (is_lone_anchor) return b.id('$.comment');
|
|
47
|
+
|
|
42
48
|
const expression = tree ? state.template.as_tree() : state.template.as_html();
|
|
43
49
|
|
|
44
50
|
const key =
|