Skip to content

Commit 61a11a5

Browse files
authored
chore: merge main into async branch (#16197)
* chore: merge main into async branch * adjust test * fix: make effects depend on state created inside them (#16198) * make effects depend on state created inside them * fix, add github action * disable test in async mode
1 parent 6efdc23 commit 61a11a5

File tree

77 files changed

+795
-166
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+795
-166
lines changed

.changeset/fair-laws-appear.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: match class and style directives against attribute selector

.github/workflows/ci.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,23 @@ jobs:
4343
- run: pnpm test
4444
env:
4545
CI: true
46+
TestNoAsync:
47+
permissions: {}
48+
runs-on: ubuntu-latest
49+
timeout-minutes: 10
50+
steps:
51+
- uses: actions/checkout@v4
52+
- uses: pnpm/action-setup@v4
53+
- uses: actions/setup-node@v4
54+
with:
55+
node-version: 22
56+
cache: pnpm
57+
- run: pnpm install --frozen-lockfile
58+
- run: pnpm playwright install chromium
59+
- run: pnpm test runtime-runes
60+
env:
61+
CI: true
62+
SVELTE_NO_ASYNC: true
4663
Lint:
4764
permissions: {}
4865
runs-on: ubuntu-latest

documentation/docs/07-misc/02-testing.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,12 @@ test('Effect', () => {
129129
// effects normally run after a microtask,
130130
// use flushSync to execute all pending effects synchronously
131131
flushSync();
132-
expect(log.value).toEqual([0]);
132+
expect(log).toEqual([0]);
133133

134134
count = 1;
135135
flushSync();
136136

137-
expect(log.value).toEqual([0, 1]);
137+
expect(log).toEqual([0, 1]);
138138
});
139139

140140
cleanup();
@@ -148,17 +148,13 @@ test('Effect', () => {
148148
*/
149149
export function logger(getValue) {
150150
/** @type {any[]} */
151-
let log = $state([]);
151+
let log = [];
152152

153153
$effect(() => {
154154
log.push(getValue());
155155
});
156156

157-
return {
158-
get value() {
159-
return log;
160-
}
161-
};
157+
return log;
162158
}
163159
```
164160

packages/svelte/CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
# svelte
22

3+
## 5.34.5
4+
5+
### Patch Changes
6+
7+
- fix: keep spread non-delegated event handlers up to date ([#16180](https://github.com/sveltejs/svelte/pull/16180))
8+
9+
- fix: remove undefined attributes on hydration ([#16178](https://github.com/sveltejs/svelte/pull/16178))
10+
11+
- fix: ensure sources within nested effects still register correctly ([#16193](https://github.com/sveltejs/svelte/pull/16193))
12+
13+
- fix: avoid shadowing a variable in dynamic components ([#16185](https://github.com/sveltejs/svelte/pull/16185))
14+
15+
## 5.34.4
16+
17+
### Patch Changes
18+
19+
- fix: don't set state withing `with_parent` in proxy ([#16176](https://github.com/sveltejs/svelte/pull/16176))
20+
21+
- fix: use compiler-driven reactivity in legacy mode template expressions ([#16100](https://github.com/sveltejs/svelte/pull/16100))
22+
323
## 5.34.3
424

525
### Patch Changes

packages/svelte/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "svelte",
33
"description": "Cybernetically enhanced web apps",
44
"license": "MIT",
5-
"version": "5.34.3",
5+
"version": "5.34.5",
66
"type": "module",
77
"types": "./types/index.d.ts",
88
"engines": {

packages/svelte/src/compiler/phases/1-parse/state/tag.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,10 @@ function open(parser) {
247247
error: null,
248248
pending: null,
249249
then: null,
250-
catch: null
250+
catch: null,
251+
metadata: {
252+
expression: create_expression_metadata()
253+
}
251254
});
252255

253256
if (parser.eat('then')) {
@@ -711,6 +714,9 @@ function special(parser) {
711714
declarations: [{ type: 'VariableDeclarator', id, init, start: id.start, end: init.end }],
712715
start: start + 2, // start at const, not at @const
713716
end: parser.index - 1
717+
},
718+
metadata: {
719+
expression: create_expression_metadata()
714720
}
715721
});
716722
}
@@ -737,6 +743,7 @@ function special(parser) {
737743
end: parser.index,
738744
expression: /** @type {AST.RenderTag['expression']} */ (expression),
739745
metadata: {
746+
expression: create_expression_metadata(),
740747
dynamic: false,
741748
arguments: [],
742749
path: [],

packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -532,12 +532,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element,
532532
}
533533

534534
case 'ClassSelector': {
535-
if (
536-
!attribute_matches(element, 'class', name, '~=', false) &&
537-
!element.attributes.some(
538-
(attribute) => attribute.type === 'ClassDirective' && attribute.name === name
539-
)
540-
) {
535+
if (!attribute_matches(element, 'class', name, '~=', false)) {
541536
return false;
542537
}
543538

@@ -633,6 +628,16 @@ function attribute_matches(node, name, expected_value, operator, case_insensitiv
633628
if (attribute.type === 'SpreadAttribute') return true;
634629
if (attribute.type === 'BindDirective' && attribute.name === name) return true;
635630

631+
// match attributes against the corresponding directive but bail out on exact matching
632+
if (attribute.type === 'StyleDirective' && name.toLowerCase() === 'style') return true;
633+
if (attribute.type === 'ClassDirective' && name.toLowerCase() === 'class') {
634+
if (operator == '~=') {
635+
if (attribute.name === expected_value) return true;
636+
} else {
637+
return true;
638+
}
639+
}
640+
636641
if (attribute.type !== 'Attribute') continue;
637642
if (attribute.name.toLowerCase() !== name.toLowerCase()) continue;
638643

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,9 @@ export function AssignmentExpression(node, context) {
2323
}
2424
}
2525

26+
if (context.state.expression) {
27+
context.state.expression.has_assignment = true;
28+
}
29+
2630
context.next();
2731
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,8 @@ export function AwaitBlock(node, context) {
4141

4242
mark_subtree_dynamic(context.path);
4343

44-
context.next();
44+
context.visit(node.expression, { ...context.state, expression: node.metadata.expression });
45+
if (node.pending) context.visit(node.pending);
46+
if (node.then) context.visit(node.then);
47+
if (node.catch) context.visit(node.catch);
4548
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,8 @@ export function ConstTag(node, context) {
3232
e.const_tag_invalid_placement(node);
3333
}
3434

35-
context.next();
35+
const declaration = node.declaration.declarations[0];
36+
37+
context.visit(declaration.id);
38+
context.visit(declaration.init, { ...context.state, expression: node.metadata.expression });
3639
}

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,5 @@ export function HtmlTag(node, context) {
1515
// unfortunately this is necessary in order to fix invalid HTML
1616
mark_subtree_dynamic(context.path);
1717

18-
context.next({
19-
...context.state,
20-
expression: node.metadata.expression
21-
});
18+
context.next({ ...context.state, expression: node.metadata.expression });
2219
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export function Identifier(node, context) {
9090
if (binding) {
9191
if (context.state.expression) {
9292
context.state.expression.dependencies.add(binding);
93+
context.state.expression.references.add(binding);
9394
context.state.expression.has_state ||=
9495
binding.kind !== 'static' &&
9596
!binding.is_function() &&

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@ export function KeyBlock(node, context) {
1616

1717
mark_subtree_dynamic(context.path);
1818

19-
context.visit(node.expression, {
20-
...context.state,
21-
expression: node.metadata.expression
22-
});
23-
19+
context.visit(node.expression, { ...context.state, expression: node.metadata.expression });
2420
context.visit(node.fragment);
2521
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ export function MemberExpression(node, context) {
1515
}
1616
}
1717

18-
if (context.state.expression && !is_pure(node, context)) {
19-
context.state.expression.has_state = true;
18+
if (context.state.expression) {
19+
context.state.expression.has_member_expression = true;
20+
context.state.expression.has_state ||= !is_pure(node, context);
2021
}
2122

2223
if (!is_safe_identifier(node, context.state.scope)) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export function RenderTag(node, context) {
5454

5555
mark_subtree_dynamic(context.path);
5656

57-
context.visit(callee);
57+
context.visit(callee, { ...context.state, expression: node.metadata.expression });
5858

5959
for (const arg of expression.arguments) {
6060
const metadata = create_expression_metadata();

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,9 @@ export function UpdateExpression(node, context) {
2121
}
2222
}
2323

24+
if (context.state.expression) {
25+
context.state.expression.has_assignment = true;
26+
}
27+
2428
context.next();
2529
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ export function visit_function(node, context) {
1313
scope: context.state.scope
1414
};
1515

16+
if (context.state.expression) {
17+
for (const [name] of context.state.scope.references) {
18+
const binding = context.state.scope.get(name);
19+
20+
if (binding && binding.scope.function_depth < context.state.scope.function_depth) {
21+
context.state.expression.references.add(binding);
22+
}
23+
}
24+
}
25+
1626
context.next({
1727
...context.state,
1828
function_depth: context.state.function_depth + 1,
Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,14 @@
1-
/** @import { Expression } from 'estree' */
21
/** @import { AST } from '#compiler' */
32
/** @import { ComponentContext } from '../types' */
43
import * as b from '../../../../utils/builders.js';
4+
import { build_expression } from './shared/utils.js';
55

66
/**
77
* @param {AST.AttachTag} node
88
* @param {ComponentContext} context
99
*/
1010
export function AttachTag(node, context) {
11-
context.state.init.push(
12-
b.stmt(
13-
b.call(
14-
'$.attach',
15-
context.state.node,
16-
b.thunk(/** @type {Expression} */ (context.visit(node.expression)))
17-
)
18-
)
19-
);
11+
const expression = build_expression(context, node.expression, node.metadata.expression);
12+
context.state.init.push(b.stmt(b.call('$.attach', context.state.node, b.thunk(expression))));
2013
context.next();
2114
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
/** @import { BlockStatement, Expression, Pattern, Statement } from 'estree' */
1+
/** @import { BlockStatement, Pattern, Statement } from 'estree' */
22
/** @import { AST } from '#compiler' */
33
/** @import { ComponentClientTransformState, ComponentContext } from '../types' */
44
import { extract_identifiers } from '../../../../utils/ast.js';
55
import * as b from '#compiler/builders';
66
import { create_derived } from '../utils.js';
77
import { get_value } from './shared/declarations.js';
8+
import { build_expression } from './shared/utils.js';
89

910
/**
1011
* @param {AST.AwaitBlock} node
@@ -14,7 +15,7 @@ export function AwaitBlock(node, context) {
1415
context.state.template.push_comment();
1516

1617
// Visit {#await <expression>} first to ensure that scopes are in the correct order
17-
const expression = b.thunk(/** @type {Expression} */ (context.visit(node.expression)));
18+
const expression = b.thunk(build_expression(context, node.expression, node.metadata.expression));
1819

1920
let then_block;
2021
let catch_block;

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,6 @@ import { build_component } from './shared/component.js';
88
* @param {ComponentContext} context
99
*/
1010
export function Component(node, context) {
11-
const component = build_component(
12-
node,
13-
// if it's not dynamic we will just use the node name, if it is dynamic we will use the node name
14-
// only if it's a valid identifier, otherwise we will use a default name
15-
!node.metadata.dynamic || regex_is_valid_identifier.test(node.name) ? node.name : '$$component',
16-
context
17-
);
11+
const component = build_component(node, node.name, context);
1812
context.state.init.push(component);
1913
}

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

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
/** @import { Expression, Pattern } from 'estree' */
1+
/** @import { Pattern } from 'estree' */
22
/** @import { AST } from '#compiler' */
33
/** @import { ComponentContext } from '../types' */
44
import { dev } from '../../../../state.js';
55
import { extract_identifiers } from '../../../../utils/ast.js';
66
import * as b from '#compiler/builders';
77
import { create_derived } from '../utils.js';
88
import { get_value } from './shared/declarations.js';
9+
import { build_expression } from './shared/utils.js';
910

1011
/**
1112
* @param {AST.ConstTag} node
@@ -15,15 +16,8 @@ export function ConstTag(node, context) {
1516
const declaration = node.declaration.declarations[0];
1617
// TODO we can almost certainly share some code with $derived(...)
1718
if (declaration.id.type === 'Identifier') {
18-
context.state.init.push(
19-
b.const(
20-
declaration.id,
21-
create_derived(
22-
context.state,
23-
b.thunk(/** @type {Expression} */ (context.visit(declaration.init)))
24-
)
25-
)
26-
);
19+
const init = build_expression(context, declaration.init, node.metadata.expression);
20+
context.state.init.push(b.const(declaration.id, create_derived(context.state, b.thunk(init))));
2721

2822
context.state.transform[declaration.id.name] = { read: get_value };
2923

@@ -48,13 +42,15 @@ export function ConstTag(node, context) {
4842

4943
// TODO optimise the simple `{ x } = y` case — we can just return `y`
5044
// instead of destructuring it only to return a new object
45+
const init = build_expression(
46+
{ ...context, state: child_state },
47+
declaration.init,
48+
node.metadata.expression
49+
);
5150
const fn = b.arrow(
5251
[],
5352
b.block([
54-
b.const(
55-
/** @type {Pattern} */ (context.visit(declaration.id, child_state)),
56-
/** @type {Expression} */ (context.visit(declaration.init, child_state))
57-
),
53+
b.const(/** @type {Pattern} */ (context.visit(declaration.id, child_state)), init),
5854
b.return(b.object(identifiers.map((node) => b.prop('init', node, node))))
5955
])
6056
);

0 commit comments

Comments
 (0)