Skip to content

Commit 0166bc4

Browse files
committed
Merge branch 'main' into opaque-rune
2 parents 8b38272 + ab1f7f4 commit 0166bc4

File tree

22 files changed

+189
-33
lines changed

22 files changed

+189
-33
lines changed

.changeset/chatty-windows-own.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: ensure snippet hoisting works in the correct scope

.changeset/cyan-ducks-train.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: restore input binding selection position

.changeset/red-worms-suffer.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: transform everything that is not a selector inside `:global`

.changeset/thick-dryers-kiss.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: don't emit assignment warnings for bindings

packages/svelte/src/compiler/phases/2-analyze/visitors/SnippetBlock.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,9 @@ function can_hoist_snippet(scope, scopes, visited = new Set()) {
9999
if (binding.initial?.type === 'SnippetBlock') {
100100
if (visited.has(binding)) continue;
101101
visited.add(binding);
102+
const snippet_scope = /** @type {Scope} */ (scopes.get(binding.initial));
102103

103-
if (can_hoist_snippet(binding.scope, scopes, visited)) {
104+
if (can_hoist_snippet(snippet_scope, scopes, visited)) {
104105
continue;
105106
}
106107
}

packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,17 @@ function build_assignment(operator, left, right, context) {
169169
}
170170
}
171171

172+
// special case — ignore `bind:prop={getter, (v) => (...)}` / `bind:value={x.y}`
173+
if (
174+
path.at(-1) === 'Component' ||
175+
path.at(-1) === 'SvelteComponent' ||
176+
(path.at(-1) === 'ArrowFunctionExpression' &&
177+
path.at(-2) === 'SequenceExpression' &&
178+
(path.at(-3) === 'Component' || path.at(-3) === 'SvelteComponent'))
179+
) {
180+
should_transform = false;
181+
}
182+
172183
if (left.type === 'MemberExpression' && should_transform) {
173184
const callee = callees[operator];
174185

packages/svelte/src/compiler/phases/3-transform/css/index.js

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ const visitors = {
7878
context.state.code.addSourcemapLocation(node.end);
7979
context.next();
8080
},
81-
Atrule(node, { state, next }) {
81+
Atrule(node, { state, next, path }) {
8282
if (is_keyframes_node(node)) {
8383
let start = node.start + node.name.length + 1;
8484
while (state.code.original[start] === ' ') start += 1;
@@ -87,7 +87,7 @@ const visitors = {
8787

8888
if (node.prelude.startsWith('-global-')) {
8989
state.code.remove(start, start + 8);
90-
} else {
90+
} else if (!is_in_global_block(path)) {
9191
state.code.prependRight(start, `${state.hash}-`);
9292
}
9393

@@ -134,7 +134,7 @@ const visitors = {
134134
}
135135
}
136136
},
137-
Rule(node, { state, next, visit }) {
137+
Rule(node, { state, next, visit, path }) {
138138
if (state.minify) {
139139
remove_preceding_whitespace(node.start, state);
140140
remove_preceding_whitespace(node.block.end - 1, state);
@@ -154,7 +154,7 @@ const visitors = {
154154
return;
155155
}
156156

157-
if (!is_used(node)) {
157+
if (!is_used(node) && !is_in_global_block(path)) {
158158
if (state.minify) {
159159
state.code.remove(node.start, node.end);
160160
} else {
@@ -182,20 +182,20 @@ const visitors = {
182182
state.code.appendLeft(node.block.end, '*/');
183183
}
184184

185-
// don't recurse into selector or body
185+
// don't recurse into selectors but visit the body
186+
visit(node.block);
186187
return;
187188
}
188-
189-
// don't recurse into body
190-
visit(node.prelude);
191-
return;
192189
}
193190

194191
next();
195192
},
196193
SelectorList(node, { state, next, path }) {
197-
// Only add comments if we're not inside a complex selector that itself is unused
198-
if (!path.find((n) => n.type === 'ComplexSelector' && !n.metadata.used)) {
194+
// Only add comments if we're not inside a complex selector that itself is unused or a global block
195+
if (
196+
!is_in_global_block(path) &&
197+
!path.find((n) => n.type === 'ComplexSelector' && !n.metadata.used)
198+
) {
199199
const children = node.children;
200200
let pruning = false;
201201
let prune_start = children[0].start;
@@ -359,6 +359,14 @@ const visitors = {
359359
}
360360
};
361361

362+
/**
363+
*
364+
* @param {Array<Css.Node>} path
365+
*/
366+
function is_in_global_block(path) {
367+
return path.some((node) => node.type === 'Rule' && node.metadata.is_global_block);
368+
}
369+
362370
/**
363371
* @param {Css.PseudoClassSelector} selector
364372
* @param {Css.Combinator | null} combinator

packages/svelte/src/compiler/phases/scope.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -628,12 +628,8 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
628628

629629
SnippetBlock(node, context) {
630630
const state = context.state;
631-
// Special-case for root-level snippets: they become part of the instance scope
632-
const is_top_level = !context.path.at(-2);
633631
let scope = state.scope;
634-
if (is_top_level) {
635-
scope = /** @type {Scope} */ (parent);
636-
}
632+
637633
scope.declare(node.expression, 'normal', 'function', node);
638634

639635
const child_scope = state.scope.child();

packages/svelte/src/internal/client/dev/assign.js

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { untrack } from '../runtime.js';
12
import * as w from '../warnings.js';
23
import { sanitize_location } from './location.js';
34

@@ -23,7 +24,12 @@ function compare(a, b, property, location) {
2324
* @param {string} location
2425
*/
2526
export function assign(object, property, value, location) {
26-
return compare((object[property] = value), object[property], property, location);
27+
return compare(
28+
(object[property] = value),
29+
untrack(() => object[property]),
30+
property,
31+
location
32+
);
2733
}
2834

2935
/**
@@ -33,7 +39,12 @@ export function assign(object, property, value, location) {
3339
* @param {string} location
3440
*/
3541
export function assign_and(object, property, value, location) {
36-
return compare((object[property] &&= value), object[property], property, location);
42+
return compare(
43+
(object[property] &&= value),
44+
untrack(() => object[property]),
45+
property,
46+
location
47+
);
3748
}
3849

3950
/**
@@ -43,7 +54,12 @@ export function assign_and(object, property, value, location) {
4354
* @param {string} location
4455
*/
4556
export function assign_or(object, property, value, location) {
46-
return compare((object[property] ||= value), object[property], property, location);
57+
return compare(
58+
(object[property] ||= value),
59+
untrack(() => object[property]),
60+
property,
61+
location
62+
);
4763
}
4864

4965
/**
@@ -53,5 +69,10 @@ export function assign_or(object, property, value, location) {
5369
* @param {string} location
5470
*/
5571
export function assign_nullish(object, property, value, location) {
56-
return compare((object[property] ??= value), object[property], property, location);
72+
return compare(
73+
(object[property] ??= value),
74+
untrack(() => object[property]),
75+
property,
76+
location
77+
);
5778
}

packages/svelte/src/internal/client/dom/elements/bindings/input.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,17 @@ export function bind_value(input, get, set = get) {
3030
// In runes mode, respect any validation in accessors (doesn't apply in legacy mode,
3131
// because we use mutable state which ensures the render effect always runs)
3232
if (runes && value !== (value = get())) {
33+
var start = input.selectionStart;
34+
var end = input.selectionEnd;
35+
3336
// the value is coerced on assignment
3437
input.value = value ?? '';
38+
39+
// Restore selection
40+
if (end !== null) {
41+
input.selectionStart = start;
42+
input.selectionEnd = Math.min(end, input.value.length);
43+
}
3544
}
3645
});
3746

0 commit comments

Comments
 (0)