Merge branch 'main' into remove-was-marked

remove-was-marked
Rich Harris 7 days ago committed by GitHub
commit 1d016a16c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: account for proxified instance when updating `bind:this`

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: do not dispatch introstart event with animation of animate directive

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: ensure scheduled batch is flushed if not obsolete

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: allow `@debug` tags to reference awaited variables

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: re-run fallback props if dependencies update

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: ignore comments when reading CSS values

@ -167,6 +167,8 @@ To take a static snapshot of a deeply reactive `$state` proxy, use `$state.snaps
This is handy when you want to pass some state to an external library or API that doesn't expect a proxy, such as `structuredClone`.
If a value has a `toJSON` method, the snapshot will clone the value returned from `toJSON` instead of the original object.
## `$state.eager`
When state changes, it may not be reflected in the UI immediately if it is used by an `await` expression, because [updates are synchronized](await-expressions#Synchronized-updates).

@ -1,5 +1,13 @@
# svelte
## 5.55.5
### Patch Changes
- fix: don't mark deriveds while an effect is updating ([#18124](https://github.com/sveltejs/svelte/pull/18124))
- fix: do not dispatch introstart event with animation of animate directive ([#18122](https://github.com/sveltejs/svelte/pull/18122))
## 5.55.4
### Patch Changes

@ -2,7 +2,7 @@
"name": "svelte",
"description": "Cybernetically enhanced web apps",
"license": "MIT",
"version": "5.55.4",
"version": "5.55.5",
"type": "module",
"types": "./types/index.d.ts",
"engines": {

@ -147,6 +147,8 @@ declare namespace $state {
* </script>
* ```
*
* If `state` has a `toJSON` method, the snapshot will clone the value returned from `toJSON` instead of the original object.
*
* @see {@link https://svelte.dev/docs/svelte/$state#$state.snapshot Documentation}
*
* @param state The value to snapshot

@ -524,6 +524,21 @@ function read_value(parser) {
in_url = true;
} else if ((char === ';' || char === '{' || char === '}') && !in_url && !quote_mark) {
return value.trim();
} else if (
char === '/' &&
!in_url &&
!quote_mark &&
parser.template[parser.index + 1] === '*'
) {
parser.index += 2;
while (parser.index < parser.template.length) {
if (parser.template[parser.index] === '*' && parser.template[parser.index + 1] === '/') {
parser.index += 2;
break;
}
parser.index++;
}
continue;
}
value += char;

@ -8,6 +8,10 @@ import * as b from '#compiler/builders';
* @param {ComponentContext} context
*/
export function DebugTag(node, context) {
const blockers = node.identifiers
.map((identifier) => context.state.scope.get(identifier.name)?.blocker)
.filter((blocker) => blocker != null);
const object = b.object(
node.identifiers.map((identifier) => {
const visited = b.call('$.snapshot', /** @type {Expression} */ (context.visit(identifier)));
@ -20,9 +24,11 @@ export function DebugTag(node, context) {
})
);
const call = b.call('console.log', object);
const args = [b.thunk(b.block([b.stmt(b.call('console.log', object)), b.debugger]))];
context.state.init.push(
b.stmt(b.call('$.template_effect', b.thunk(b.block([b.stmt(call), b.debugger]))))
);
if (blockers.length > 0) {
args.push(b.array([]), b.array([]), b.array(blockers));
}
context.state.init.push(b.stmt(b.call('$.template_effect', ...args)));
}

@ -2,23 +2,34 @@
/** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */
import * as b from '#compiler/builders';
import { create_child_block } from './shared/utils.js';
/**
* @param {AST.DebugTag} node
* @param {ComponentContext} context
*/
export function DebugTag(node, context) {
const blockers = node.identifiers
.map((identifier) => context.state.scope.get(identifier.name)?.blocker)
.filter((blocker) => blocker != null);
context.state.template.push(
b.stmt(
b.call(
'console.log',
b.object(
node.identifiers.map((identifier) =>
b.prop('init', identifier, /** @type {Expression} */ (context.visit(identifier)))
...create_child_block(
[
b.stmt(
b.call(
'console.log',
b.object(
node.identifiers.map((identifier) =>
b.prop('init', identifier, /** @type {Expression} */ (context.visit(identifier)))
)
)
)
)
)
),
b.debugger
),
b.debugger
],
b.array(blockers),
false
)
);
}

@ -40,7 +40,7 @@ export function bind_this(element_or_component = {}, update, get_value, get_part
parts = get_parts?.() || [];
untrack(() => {
if (element_or_component !== get_value(...parts)) {
if (!is_bound_this(get_value(...parts), element_or_component)) {
update(element_or_component, ...parts);
// If this is an effect rerun (cause: each block context changes), then nullify the binding at
// the previous position if it isn't already taken over by a different effect.

@ -716,7 +716,7 @@ export class Batch {
if (!is_flushing_sync) {
queue_micro_task(() => {
if (current_batch !== batch) {
if (!batches.has(batch) || batch.#pending.size > 0) {
// a flushSync happened in the meantime
return;
}

@ -1,4 +1,4 @@
/** @import { Effect, Source } from './types.js' */
/** @import { Derived, Effect, Source } from './types.js' */
import { DEV } from 'esm-env';
import {
PROPS_IS_BINDABLE,
@ -283,8 +283,14 @@ export function prop(props, key, flags, fallback) {
var fallback_value = /** @type {V} */ (fallback);
var fallback_dirty = true;
var fallback_signal = /** @type {Derived<V> | undefined} */ (undefined);
var get_fallback = () => {
if (lazy && runes) {
fallback_signal ??= derived(/** @type {() => V} */ (fallback));
return get(fallback_signal);
}
if (fallback_dirty) {
fallback_dirty = false;

@ -4,5 +4,5 @@
* The current version, as set in package.json.
* @type {string}
*/
export const VERSION = '5.55.4';
export const VERSION = '5.55.5';
export const PUBLIC_VERSION = '5';

@ -0,0 +1,4 @@
p.svelte-xyz {
padding: 0 /* it's a comment */ 1em;
}

@ -0,0 +1,7 @@
<p>red</p>
<style>
p {
padding: 0 /* it's a comment */ 1em;
}
</style>

@ -0,0 +1,18 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
compileOptions: {
dev: true
},
mode: ['client', 'async-server'],
async test({ assert, logs }) {
await tick();
assert.deepEqual(logs, [{ data: 'works' }]);
},
test_ssr({ assert, logs }) {
assert.deepEqual(logs, [{ data: 'works' }]);
}
});

@ -0,0 +1,4 @@
<svelte:boundary>
{@const data = await Promise.resolve("works")}
{@debug data}
</svelte:boundary>

@ -0,0 +1,15 @@
import { tick } from 'svelte';
import { test } from '../../test';
// Ensure that microtask timing doesn't influence whether or not a scheduled batch is flushed.
// Timing can be such that the current_batch is reset before the scheduled flush runs, which
// would cause the flush to skip without the fix.
export default test({
async test({ assert, target }) {
const [btn] = target.querySelectorAll('button');
btn.click();
await tick();
assert.htmlEqual(target.innerHTML, '1 1');
}
});

@ -0,0 +1,18 @@
<script>
let a = $state(0);
let b = $state(0);
</script>
{#if a}
{@const toShow = await a}
{toShow}
{b}
{:else}
<button
onclick={async () => {
a = 1;
await 1;
await 1; // two microtasks needed to get timing right to reproduce the bug
b = 1;
}}>click</button>
{/if}

@ -0,0 +1,5 @@
<script>
const props = $props();
// svelte-ignore state_referenced_locally
export const name = props.name;
</script>

@ -0,0 +1,22 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target, logs }) {
const btn = target.querySelector('button');
flushSync(() => {
btn?.click();
});
flushSync(() => {
btn?.click();
});
assert.deepEqual(logs, [
{},
{ 0: { name: 'Row 0' } },
{ 0: { name: 'Row 0' }, 1: { name: 'Row 1' } }
]);
}
});

@ -0,0 +1,16 @@
<script>
import Row from "./Component.svelte";
const nums = $state([]);
const rows = $derived(nums.map(n => ({id: n, name: `Row ${n}` })));
const refs = $state({});
$effect(() => {
console.log({...refs});
})
</script>
<button onclick={() => nums.push(nums.length)}>Add</button>
{#each rows as row (row.id)}
<Row name={row.name} bind:this={refs[row.id]} />
{/each}

@ -1,5 +1,5 @@
<script>
let log = $state([]);
let log = [];
const fallback_value = 1;
const nested = {

@ -1,5 +1,5 @@
<script>
let log = $state([]);
let log = [];
const fallback_value = 1;
const nested = {

@ -0,0 +1,19 @@
import { flushSync, tick } from 'svelte';
import { test } from '../../test';
export default test({
accessors: false,
test({ assert, target }) {
const btn = target.querySelector('button');
btn?.click();
flushSync();
assert.htmlEqual(
target.innerHTML,
`
<p>greeting: Hola</p>
<button>Change Language</button>
`
);
}
});

@ -0,0 +1,10 @@
<script>
import Sub from './sub.svelte';
import { set_translation } from './translations.svelte.js';
</script>
<Sub />
<button onclick={() => set_translation('Hola')}>
Change Language
</button>

@ -0,0 +1,9 @@
<script>
import { get_translation } from './translations.svelte.js';
const {
p0 = get_translation()
} = $props();
</script>
<p>greeting: {p0}</p>

@ -0,0 +1,9 @@
let greeting = $state('Hello');
export function get_translation() {
return greeting;
}
export function set_translation(value) {
greeting = value;
}

@ -3345,6 +3345,8 @@ declare namespace $state {
* </script>
* ```
*
* If `state` has a `toJSON` method, the snapshot will clone the value returned from `toJSON` instead of the original object.
*
* @see {@link https://svelte.dev/docs/svelte/$state#$state.snapshot Documentation}
*
* @param state The value to snapshot

Loading…
Cancel
Save