Skip to content

Commit 62c39ee

Browse files
committed
Merge branch 'main' into props-bindable-2
2 parents ef0bc44 + a339c28 commit 62c39ee

File tree

70 files changed

+1932
-2548
lines changed

Some content is hidden

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

70 files changed

+1932
-2548
lines changed

.changeset/pre.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"afraid-moose-matter",
1414
"angry-books-jam",
1515
"angry-plums-punch",
16+
"beige-cobras-smoke",
1617
"beige-flies-wash",
1718
"beige-mirrors-listen",
1819
"beige-rabbits-shave",
@@ -63,6 +64,7 @@
6364
"dry-eggs-retire",
6465
"dull-coins-vanish",
6566
"dull-mangos-wave",
67+
"dull-pots-add",
6668
"dull-roses-relate",
6769
"early-ads-tie",
6870
"eight-steaks-shout",
@@ -166,6 +168,7 @@
166168
"lucky-schools-hang",
167169
"lucky-toes-begin",
168170
"many-trees-fix",
171+
"mighty-cooks-scream",
169172
"mighty-files-hammer",
170173
"moody-carrots-lay",
171174
"moody-frogs-exist",
@@ -192,6 +195,7 @@
192195
"old-mails-sneeze",
193196
"old-oranges-compete",
194197
"olive-kangaroos-brake",
198+
"olive-mice-fix",
195199
"olive-seals-sell",
196200
"olive-shirts-complain",
197201
"olive-socks-kick",
@@ -233,6 +237,7 @@
233237
"rotten-rules-invite",
234238
"rude-ghosts-tickle",
235239
"selfish-dragons-knock",
240+
"selfish-spies-help",
236241
"selfish-tools-hide",
237242
"serious-kids-deliver",
238243
"serious-needles-joke",
@@ -265,6 +270,7 @@
265270
"small-papayas-laugh",
266271
"small-sheep-type",
267272
"smart-parents-swim",
273+
"smart-turkeys-tell",
268274
"smart-zebras-pay",
269275
"smooth-rings-rush",
270276
"soft-clocks-remember",
@@ -306,6 +312,7 @@
306312
"tasty-cheetahs-appear",
307313
"tasty-numbers-perform",
308314
"tasty-steaks-smile",
315+
"ten-eels-move",
309316
"ten-foxes-repeat",
310317
"ten-jokes-divide",
311318
"ten-peaches-sleep",

.changeset/selfish-spies-help.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: improve element class attribute behaviour

.changeset/small-spiders-fail.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+
breaking: apply fallback value every time in runes mode

packages/svelte/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@
1111
/motion.d.ts
1212
/store.d.ts
1313
/transition.d.ts
14+
15+
/scripts/_bundle.js

packages/svelte/CHANGELOG.md

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

3+
## 5.0.0-next.81
4+
5+
### Patch Changes
6+
7+
- feat: add support for webkitdirectory DOM boolean attribute ([#10847](https://github.com/sveltejs/svelte/pull/10847))
8+
9+
- fix: don't override instance methods during legacy class creation ([#10834](https://github.com/sveltejs/svelte/pull/10834))
10+
11+
- fix: adjust scope parent for named slots ([#10843](https://github.com/sveltejs/svelte/pull/10843))
12+
13+
- fix: improve handling of unowned derived signals ([#10842](https://github.com/sveltejs/svelte/pull/10842))
14+
15+
- fix: improve element class attribute behaviour ([#10856](https://github.com/sveltejs/svelte/pull/10856))
16+
17+
- fix: ensure select value is updated upon select option removal ([#10846](https://github.com/sveltejs/svelte/pull/10846))
18+
19+
- fix: ensure capture events don't call delegated events ([#10831](https://github.com/sveltejs/svelte/pull/10831))
20+
321
## 5.0.0-next.80
422

523
### 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.0.0-next.80",
5+
"version": "5.0.0-next.81",
66
"type": "module",
77
"types": "./types/index.d.ts",
88
"engines": {

packages/svelte/scripts/check-treeshakeability.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ async function bundle_code(entry) {
1212
virtual({
1313
__entry__: entry
1414
}),
15+
{
16+
name: 'resolve-svelte',
17+
resolveId(importee) {
18+
if (importee.startsWith('svelte')) {
19+
const entry = pkg.exports[importee.replace('svelte', '.')];
20+
return path.resolve(entry.browser ?? entry.default);
21+
}
22+
}
23+
},
1524
nodeResolve({
1625
exportConditions: ['production', 'import', 'browser', 'default']
1726
})
@@ -65,7 +74,7 @@ for (const key in pkg.exports) {
6574
}
6675

6776
const client_main = path.resolve(pkg.exports['.'].browser);
68-
const without_hydration = await bundle_code(
77+
const bundle = await bundle_code(
6978
// Use all features which contain hydration code to ensure it's treeshakeable
7079
compile(
7180
`
@@ -99,15 +108,18 @@ const without_hydration = await bundle_code(
99108
{ filename: 'App.svelte' }
100109
).js.code
101110
);
102-
if (!without_hydration.includes('current_hydration_fragment')) {
111+
112+
if (!bundle.includes('current_hydration_fragment')) {
103113
// eslint-disable-next-line no-console
104114
console.error(`✅ Hydration code treeshakeable`);
105115
} else {
106116
// eslint-disable-next-line no-console
107-
console.error(without_hydration);
117+
console.error(bundle);
108118
// eslint-disable-next-line no-console
109119
console.error(`❌ Hydration code not treeshakeable`);
110120
failed = true;
121+
122+
fs.writeFileSync('scripts/_bundle.js', bundle);
111123
}
112124

113125
// eslint-disable-next-line no-console

packages/svelte/src/compiler/phases/2-analyze/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,7 @@ const common_visitors = {
10881088
}
10891089

10901090
if (
1091+
context.state.analysis.runes &&
10911092
node !== binding.node &&
10921093
// If we have $state that can be proxied or frozen and isn't re-assigned, then that means
10931094
// it's likely not using a primitive value and thus this warning isn't that helpful.

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -271,10 +271,23 @@ export function client_component(source, analysis, options) {
271271
for (const [name, binding] of properties) {
272272
const key = binding.prop_alias ?? name;
273273

274-
component_returned_object.push(
275-
b.get(key, [b.return(b.call(b.id(name)))]),
276-
b.set(key, [b.stmt(b.call(b.id(name), b.id('$$value'))), b.stmt(b.call('$.flushSync'))])
277-
);
274+
const getter = b.get(key, [b.return(b.call(b.id(name)))]);
275+
276+
const setter = b.set(key, [
277+
b.stmt(b.call(b.id(name), b.id('$$value'))),
278+
b.stmt(b.call('$.flushSync'))
279+
]);
280+
281+
if (analysis.runes && binding.initial) {
282+
// turn `set foo($$value)` into `set foo($$value = expression)`
283+
setter.value.params[0] = {
284+
type: 'AssignmentPattern',
285+
left: b.id('$$value'),
286+
right: /** @type {import('estree').Expression} */ (binding.initial)
287+
};
288+
}
289+
290+
component_returned_object.push(getter, setter);
278291
}
279292
}
280293

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

Lines changed: 81 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,14 @@ import {
2828
AttributeAliases,
2929
DOMBooleanAttributes,
3030
EACH_INDEX_REACTIVE,
31+
EACH_IS_ANIMATED,
3132
EACH_IS_CONTROLLED,
3233
EACH_IS_STRICT_EQUALS,
3334
EACH_ITEM_REACTIVE,
34-
EACH_KEYED
35+
EACH_KEYED,
36+
TRANSITION_GLOBAL,
37+
TRANSITION_IN,
38+
TRANSITION_OUT
3539
} from '../../../../../constants.js';
3640
import { regex_is_valid_identifier } from '../../../patterns.js';
3741
import { javascript_visitors_runes } from './javascript-runes.js';
@@ -473,6 +477,7 @@ function serialize_dynamic_element_attributes(attributes, context, element_id) {
473477
function serialize_element_attribute_update_assignment(element, node_id, attribute, context) {
474478
const state = context.state;
475479
const name = get_attribute_name(element, attribute, context);
480+
const is_svg = context.state.metadata.namespace === 'svg';
476481
let [contains_call_expression, value] = serialize_attribute_value(attribute.value, context);
477482

478483
// The foreign namespace doesn't have any special handling, everything goes through the attr function
@@ -507,13 +512,19 @@ function serialize_element_attribute_update_assignment(element, node_id, attribu
507512
if (name === 'class') {
508513
if (singular) {
509514
return {
510-
singular: b.stmt(b.call('$.class_name_effect', node_id, b.thunk(singular))),
511-
grouped: b.stmt(b.call('$.class_name', node_id, singular)),
515+
singular: b.stmt(
516+
b.call(
517+
is_svg ? '$.svg_class_name_effect' : '$.class_name_effect',
518+
node_id,
519+
b.thunk(singular)
520+
)
521+
),
522+
grouped: b.stmt(b.call(is_svg ? '$.svg_class_name' : '$.class_name', node_id, singular)),
512523
skip_condition: true
513524
};
514525
}
515526
return {
516-
grouped: b.stmt(b.call('$.class_name', node_id, value)),
527+
grouped: b.stmt(b.call(is_svg ? '$.svg_class_name' : '$.class_name', node_id, value)),
517528
skip_condition: true
518529
};
519530
} else if (!DOMProperties.includes(name)) {
@@ -1916,7 +1927,7 @@ export const template_visitors = {
19161927
state.init.push(
19171928
b.stmt(
19181929
b.call(
1919-
'$.animate',
1930+
'$.animation',
19201931
state.node,
19211932
b.thunk(
19221933
/** @type {import('estree').Expression} */ (visit(parse_directive_name(node.name)))
@@ -1933,25 +1944,21 @@ export const template_visitors = {
19331944
error(node, 'INTERNAL', 'Node should have been handled elsewhere');
19341945
},
19351946
TransitionDirective(node, { state, visit }) {
1936-
const type = node.intro && node.outro ? '$.transition' : node.intro ? '$.in' : '$.out';
1937-
const expression =
1938-
node.expression === null
1939-
? b.literal(null)
1940-
: b.thunk(/** @type {import('estree').Expression} */ (visit(node.expression)));
1947+
let flags = node.modifiers.includes('global') ? TRANSITION_GLOBAL : 0;
1948+
if (node.intro) flags |= TRANSITION_IN;
1949+
if (node.outro) flags |= TRANSITION_OUT;
19411950

1942-
state.init.push(
1943-
b.stmt(
1944-
b.call(
1945-
type,
1946-
state.node,
1947-
b.thunk(
1948-
/** @type {import('estree').Expression} */ (visit(parse_directive_name(node.name)))
1949-
),
1950-
expression,
1951-
node.modifiers.includes('global') ? b.true : b.false
1952-
)
1953-
)
1954-
);
1951+
const args = [
1952+
b.literal(flags),
1953+
state.node,
1954+
b.thunk(/** @type {import('estree').Expression} */ (visit(parse_directive_name(node.name))))
1955+
];
1956+
1957+
if (node.expression) {
1958+
args.push(b.thunk(/** @type {import('estree').Expression} */ (visit(node.expression))));
1959+
}
1960+
1961+
state.init.push(b.stmt(b.call('$.transition', ...args)));
19551962
},
19561963
RegularElement(node, context) {
19571964
if (node.name === 'noscript') {
@@ -2339,6 +2346,19 @@ export const template_visitors = {
23392346
each_type |= EACH_ITEM_REACTIVE;
23402347
}
23412348

2349+
// Since `animate:` can only appear on elements that are the sole child of a keyed each block,
2350+
// we can determine at compile time whether the each block is animated or not (in which
2351+
// case it should measure animated elements before and after reconciliation).
2352+
if (
2353+
node.key &&
2354+
node.body.nodes.some((child) => {
2355+
if (child.type !== 'RegularElement' && child.type !== 'SvelteElement') return false;
2356+
return child.attributes.some((attr) => attr.type === 'AnimateDirective');
2357+
})
2358+
) {
2359+
each_type |= EACH_IS_ANIMATED;
2360+
}
2361+
23422362
if (each_node_meta.is_controlled) {
23432363
each_type |= EACH_IS_CONTROLLED;
23442364
}
@@ -2551,22 +2571,44 @@ export const template_visitors = {
25512571
context.visit(node.consequent)
25522572
);
25532573

2554-
context.state.after_update.push(
2555-
b.stmt(
2556-
b.call(
2557-
'$.if',
2558-
context.state.node,
2559-
b.thunk(/** @type {import('estree').Expression} */ (context.visit(node.test))),
2560-
b.arrow([b.id('$$anchor')], consequent),
2561-
node.alternate
2562-
? b.arrow(
2563-
[b.id('$$anchor')],
2564-
/** @type {import('estree').BlockStatement} */ (context.visit(node.alternate))
2565-
)
2566-
: b.literal(null)
2567-
)
2568-
)
2569-
);
2574+
const args = [
2575+
context.state.node,
2576+
b.thunk(/** @type {import('estree').Expression} */ (context.visit(node.test))),
2577+
b.arrow([b.id('$$anchor')], consequent),
2578+
node.alternate
2579+
? b.arrow(
2580+
[b.id('$$anchor')],
2581+
/** @type {import('estree').BlockStatement} */ (context.visit(node.alternate))
2582+
)
2583+
: b.literal(null)
2584+
];
2585+
2586+
if (node.elseif) {
2587+
// We treat this...
2588+
//
2589+
// {#if x}
2590+
// ...
2591+
// {:else}
2592+
// {#if y}
2593+
// <div transition:foo>...</div>
2594+
// {/if}
2595+
// {/if}
2596+
//
2597+
// ...slightly differently to this...
2598+
//
2599+
// {#if x}
2600+
// ...
2601+
// {:else if y}
2602+
// <div transition:foo>...</div>
2603+
// {/if}
2604+
//
2605+
// ...even though they're logically equivalent. In the first case, the
2606+
// transition will only play when `y` changes, but in the second it
2607+
// should play when `x` or `y` change — both are considered 'local'
2608+
args.push(b.literal(true));
2609+
}
2610+
2611+
context.state.after_update.push(b.stmt(b.call('$.if', ...args)));
25702612
},
25712613
AwaitBlock(node, context) {
25722614
context.state.template.push('<!>');

packages/svelte/src/compiler/utils/builders.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ export function function_declaration(id, params, body) {
219219
/**
220220
* @param {string} name
221221
* @param {import('estree').Statement[]} body
222-
* @returns {import('estree').Property}
222+
* @returns {import('estree').Property & { value: import('estree').FunctionExpression}}}
223223
*/
224224
export function get(name, body) {
225225
return prop('get', key(name), function_builder(null, [], block(body)));
@@ -314,11 +314,12 @@ export function object_pattern(properties) {
314314
}
315315

316316
/**
317+
* @template {import('estree').Expression} Value
317318
* @param {'init' | 'get' | 'set'} kind
318319
* @param {import('estree').Expression} key
319-
* @param {import('estree').Expression} value
320+
* @param {Value} value
320321
* @param {boolean} computed
321-
* @returns {import('estree').Property}
322+
* @returns {import('estree').Property & { value: Value }}
322323
*/
323324
export function prop(kind, key, value, computed = false) {
324325
return { type: 'Property', kind, key, value, method: false, shorthand: false, computed };
@@ -364,7 +365,7 @@ export function sequence(expressions) {
364365
/**
365366
* @param {string} name
366367
* @param {import('estree').Statement[]} body
367-
* @returns {import('estree').Property}
368+
* @returns {import('estree').Property & { value: import('estree').FunctionExpression}}
368369
*/
369370
export function set(name, body) {
370371
return prop('set', key(name), function_builder(null, [id('$$value')], block(body)));

0 commit comments

Comments
 (0)