pull/15045/head
Rich Harris 8 months ago
commit ed3bf0183e

@ -62,8 +62,6 @@ When [opening a new issue](https://github.com/sveltejs/svelte/issues/new/choose)
## Pull requests
> HEADS UP: Svelte 5 will likely change a lot on the compiler. For that reason, please don't open PRs that are large in scope, touch more than a couple of files etc. In other words, bug fixes are fine, but big feature PRs will likely not be merged.
### Proposing a change
If you would like to request a new feature or enhancement but are not yet thinking about opening a pull request, you can also file an issue with [feature template](https://github.com/sveltejs/svelte/issues/new?template=feature_request.yml).

@ -1,4 +1,4 @@
Copyright (c) 2016-24 [these people](https://github.com/sveltejs/svelte/graphs/contributors)
Copyright (c) 2016-2025 [these people](https://github.com/sveltejs/svelte/graphs/contributors)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

@ -24,10 +24,6 @@ You may view [our roadmap](https://svelte.dev/roadmap) if you'd like to see what
Please see the [Contributing Guide](CONTRIBUTING.md) and the [`svelte`](packages/svelte) package for information on contributing to Svelte.
### svelte.dev
The source code for https://svelte.dev lives in the [sites](https://github.com/sveltejs/svelte/tree/master/sites/svelte.dev) folder, with all the documentation right [here](https://github.com/sveltejs/svelte/tree/master/documentation). The site is built with [SvelteKit](https://svelte.dev/docs/kit).
## Is svelte.dev down?
Probably not, but it's possible. If you can't seem to access any `.dev` sites, check out [this SuperUser question and answer](https://superuser.com/q/1413402).

@ -45,7 +45,7 @@ export async function kairo_avoidable_unowned() {
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});
@ -74,7 +74,7 @@ export async function kairo_avoidable_owned() {
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});

@ -51,7 +51,7 @@ export async function kairo_broad_unowned() {
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});
@ -80,7 +80,7 @@ export async function kairo_broad_owned() {
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});

@ -51,7 +51,7 @@ export async function kairo_deep_unowned() {
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});
@ -80,7 +80,7 @@ export async function kairo_deep_owned() {
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});

@ -55,7 +55,7 @@ export async function kairo_diamond_unowned() {
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});
@ -84,7 +84,7 @@ export async function kairo_diamond_owned() {
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});

@ -48,7 +48,7 @@ export async function kairo_mux_unowned() {
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});
@ -77,7 +77,7 @@ export async function kairo_mux_owned() {
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});

@ -52,7 +52,7 @@ export async function kairo_repeated_unowned() {
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});
@ -81,7 +81,7 @@ export async function kairo_repeated_owned() {
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});

@ -65,7 +65,7 @@ export async function kairo_triangle_unowned() {
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});
@ -94,7 +94,7 @@ export async function kairo_triangle_owned() {
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});

@ -51,7 +51,7 @@ export async function kairo_unstable_unowned() {
const { run, destroy } = setup();
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});
@ -80,7 +80,7 @@ export async function kairo_unstable_owned() {
});
const { timing } = await fastest_test(10, () => {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 1000; i++) {
run();
}
});

@ -44,12 +44,7 @@ todos[0].done = !todos[0].done;
If you push a new object to the array, it will also be proxified:
```js
// @filename: ambient.d.ts
declare global {
const todos: Array<{ done: boolean, text: string }>
}
// @filename: index.js
let todos = [{ done: false, text: 'add more todos' }];
// ---cut---
todos.push({
done: false,

@ -51,3 +51,20 @@ In essence, `$derived(expression)` is equivalent to `$derived.by(() => expressio
Anything read synchronously inside the `$derived` expression (or `$derived.by` function body) is considered a _dependency_ of the derived state. When the state changes, the derived will be marked as _dirty_ and recalculated when it is next read.
To exempt a piece of state from being treated as a dependency, use [`untrack`](svelte#untrack).
## Update propagation
Svelte uses something called _push-pull reactivity_ — when state is updated, everything that depends on the state (whether directly or indirectly) is immediately notified of the change (the 'push'), but derived values are not re-evaluated until they are actually read (the 'pull').
If the new value of a derived is referentially identical to its previous value, downstream updates will be skipped. In other words, Svelte will only update the text inside the button when `large` changes, not when `count` changes, even though `large` depends on `count`:
```svelte
<script>
let count = $state(0);
let large = $derived(count > 10);
</script>
<button onclick={() => count++}>
{large}
</button>
```

@ -125,7 +125,11 @@ An effect only reruns when the object it reads changes, not when a property insi
<p>{state.value} doubled is {derived.value}</p>
```
An effect only depends on the values that it read the last time it ran. If `a` is true, changes to `b` will [not cause this effect to rerun](/playground/untitled#H4sIAAAAAAAAE3WQ0WrDMAxFf0U1hTow1vcsMfQ7lj3YjlxEXTvEymC4_vfFC6Ewtidxde8RkrJw5DGJ9j2LoO8oWnGZJvEi-GuqIn2iZ1x1istsa6dLdqaJ1RAG9sigoYdjYs0onfYJm7fdMX85q3dE59CylA30CnJtDWxjSNHjq49XeZqXEChcT9usLUAOpIbHA0yzM78oColGhDVofLS3neZSS6mqOz-XD51ZmGOAGKwne-vztk-956CL0kAJsi7decupf4l658EUZX4I8yTWt93jSI5wFC3PC5aP8g0Aje5DcQEAAA==):
An effect only depends on the values that it read the last time it ran. This has interesting implications for effects that have conditional code.
For instance, if `a` is `true` in the code snippet below, the code inside the `if` block will run and `b` will be evaluated. As such, changes to either `a` or `b` [will cause the effect to re-run](/playground/untitled#H4sIAAAAAAAAE3VQzWrDMAx-FdUU4kBp71li6EPstOxge0ox8-QQK2PD-N1nLy2F0Z2Evj9_chKkP1B04pnYscc3cRCT8xhF95IEf8-Vq0DBr8rzPB_jJ3qumNERH-E2ECNxiRF9tIubWY00lgcYNAywj6wZJS8rtk83wjwgCrXHaULLUrYwKEgVGrnkx-Dx6MNFNstK5OjSbFGbwE0gdXuT_zGYrjmAuco515Hr1p_uXak3K3MgCGS9s-9D2grU-judlQYXIencnzad-tdR79qZrMyvw9wd5Z8Yv1h09dz8mn8AkM7Pfo0BAAA=).
Conversely, if `a` is `false`, `b` will not be evaluated, and the effect will _only_ re-run when `a` changes.
```ts
let a = false;
@ -134,8 +138,8 @@ let b = false;
$effect(() => {
console.log('running');
if (a || b) {
console.log('inside if block');
if (a) {
console.log('b:', b);
}
});
```
@ -193,53 +197,7 @@ The `$effect.tracking` rune is an advanced feature that tells you whether or not
<p>in template: {$effect.tracking()}</p> <!-- true -->
```
This allows you to (for example) add things like subscriptions without causing memory leaks, by putting them in child effects. Here's a `readable` function that listens to changes from a callback function as long as it's inside a tracking context:
```ts
import { tick } from 'svelte';
export default function readable<T>(
initial_value: T,
start: (callback: (update: (v: T) => T) => T) => () => void
) {
let value = $state(initial_value);
let subscribers = 0;
let stop: null | (() => void) = null;
return {
get value() {
// If in a tracking context ...
if ($effect.tracking()) {
$effect(() => {
// ...and there's no subscribers yet...
if (subscribers === 0) {
// ...invoke the function and listen to changes to update state
stop = start((fn) => (value = fn(value)));
}
subscribers++;
// The return callback is called once a listener unlistens
return () => {
tick().then(() => {
subscribers--;
// If it was the last subscriber...
if (subscribers === 0) {
// ...stop listening to changes
stop?.();
stop = null;
}
});
};
});
}
return value;
}
};
}
```
It is used to implement abstractions like [`createSubscriber`](/docs/svelte/svelte-reactivity#createSubscriber), which will create listeners to update reactive values but _only_ if those values are being tracked (rather than, for example, read inside an event handler).
## `$effect.root`

@ -196,4 +196,6 @@ You can, of course, separate the type declaration from the annotation:
</script>
```
> [!NOTE] Interfaces for native DOM elements are provided in the `svelte/elements` module (see [Typing wrapper components](typescript#Typing-wrapper-components))
Adding types is recommended, as it ensures that people using your component can easily discover which props they should provide.

@ -50,17 +50,16 @@ The `Action` interface receives three optional type arguments — a node type (w
```svelte
<!--- file: App.svelte --->
<script>
import { on } from 'svelte/events';
/**
* @type {import('svelte/action').Action<
* HTMLDivElement,
* null,
* undefined,
* {
* onswiperight: (e: CustomEvent) => void;
* onswipeleft: (e: CustomEvent) => void;
* // ...
* }>}
* }
* >}
*/
function gestures(node) {
$effect(() => {

@ -1,23 +0,0 @@
---
title: class:
---
The `class:` directive is a convenient way to conditionally set classes on elements, as an alternative to using conditional expressions inside `class` attributes:
```svelte
<!-- These are equivalent -->
<div class={isCool ? 'cool' : ''}>...</div>
<div class:cool={isCool}>...</div>
```
As with other directives, we can use a shorthand when the name of the class coincides with the value:
```svelte
<div class:cool>...</div>
```
Multiple `class:` directives can be added to a single element:
```svelte
<div class:cool class:lame={!cool} class:potato>...</div>
```

@ -0,0 +1,90 @@
---
title: class
---
There are two ways to set classes on elements: the `class` attribute, and the `class:` directive.
## Attributes
Primitive values are treated like any other attribute:
```svelte
<div class={large ? 'large' : 'small'}>...</div>
```
> [!NOTE]
> For historical reasons, falsy values (like `false` and `NaN`) are stringified (`class="false"`), though `class={undefined}` (or `null`) cause the attribute to be omitted altogether. In a future version of Svelte, all falsy values will cause `class` to be omitted.
### Objects and arrays
Since Svelte 5.16, `class` can be an object or array, and is converted to a string using [clsx](https://github.com/lukeed/clsx).
If the value is an object, the truthy keys are added:
```svelte
<script>
let { cool } = $props();
</script>
<!-- results in `class="cool"` if `cool` is truthy,
`class="lame"` otherwise -->
<div class={{ cool, lame: !cool }}>...</div>
```
If the value is an array, the truthy values are combined:
```svelte
<!-- if `faded` and `large` are both truthy, results in
`class="saturate-0 opacity-50 scale-200"` -->
<div class={[faded && 'saturate-0 opacity-50', large && 'scale-200']}>...</div>
```
Note that whether we're using the array or object form, we can set multiple classes simultaneously with a single condition, which is particularly useful if you're using things like Tailwind.
Arrays can contain arrays and objects, and clsx will flatten them. This is useful for combining local classes with props, for example:
```svelte
<!--- file: Button.svelte --->
<script>
let props = $props();
</script>
<button {...props} class={['cool-button', props.class]}>
{@render props.children?.()}
</button>
```
The user of this component has the same flexibility to use a mixture of objects, arrays and strings:
```svelte
<!--- file: App.svelte --->
<script>
import Button from './Button.svelte';
let useTailwind = $state(false);
</script>
<Button
onclick={() => useTailwind = true}
class={{ 'bg-blue-700 sm:w-1/2': useTailwind }}
>
Accept the inevitability of Tailwind
</Button>
```
## The `class:` directive
Prior to Svelte 5.16, the `class:` directive was the most convenient way to set classes on elements conditionally.
```svelte
<!-- These are equivalent -->
<div class={{ cool, lame: !cool }}>...</div>
<div class:cool={cool} class:lame={!cool}>...</div>
```
As with other directives, we can use a shorthand when the name of the class coincides with the value:
```svelte
<div class:cool class:lame={!cool}>...</div>
```
> [!NOTE] Unless you're using an older version of Svelte, consider avoiding `class:`, since the attribute is more powerful and composable.

@ -40,7 +40,7 @@ You can now write unit tests for code inside your `.js/.ts` files:
/// file: multiplier.svelte.test.js
import { flushSync } from 'svelte';
import { expect, test } from 'vitest';
import { multiplier } from './multiplier.js';
import { multiplier } from './multiplier.svelte.js';
test('Multiplier', () => {
let double = multiplier(0, 2);
@ -53,9 +53,30 @@ test('Multiplier', () => {
});
```
```js
/// file: multiplier.svelte.js
/**
* @param {number} initial
* @param {number} k
*/
export function multiplier(initial, k) {
let count = $state(initial);
return {
get value() {
return count * k;
},
/** @param {number} c */
set: (c) => {
count = c;
}
};
}
```
### Using runes inside your test files
It is possible to use runes inside your test files. First ensure your bundler knows to route the file through the Svelte compiler before running the test by adding `.svelte` to the filename (e.g `multiplier.svelte.test.js`). After that, you can use runes inside your tests.
Since Vitest processes your test files the same way as your source files, you can use runes inside your tests as long as the filename includes `.svelte`:
```js
/// file: multiplier.svelte.test.js
@ -75,6 +96,21 @@ test('Multiplier', () => {
});
```
```js
/// file: multiplier.svelte.js
/**
* @param {() => number} getCount
* @param {number} k
*/
export function multiplier(getCount, k) {
return {
get value() {
return getCount() * k;
}
};
}
```
If the code being tested uses effects, you need to wrap the test inside `$effect.root`:
```js
@ -105,6 +141,27 @@ test('Effect', () => {
});
```
```js
/// file: logger.svelte.js
/**
* @param {() => any} getValue
*/
export function logger(getValue) {
/** @type {any[]} */
let log = $state([]);
$effect(() => {
log.push(getValue());
});
return {
get value() {
return log;
}
};
}
```
### Component testing
It is possible to test your components in isolation using Vitest.

@ -125,3 +125,4 @@ Custom elements can be a useful way to package components for consumption in a n
- The deprecated `let:` directive has no effect, because custom elements do not have a way to pass data to the parent component that fills the slot
- Polyfills are required to support older browsers
- You can use Svelte's context feature between regular Svelte components within a custom element, but you can't use them across custom elements. In other words, you can't use `setContext` on a parent custom element and read that with `getContext` in a child custom element.
- Don't declare properties or attributes starting with `on`, as their usage will be interpreted as an event listener. In other words, Svelte treats `<custom-element oneworld={true}></custom-element>` as `customElement.addEventListener('eworld', true)` (and not as `customElement.oneworld = true`)

@ -339,7 +339,31 @@ When spreading props, local event handlers must go _after_ the spread, or they r
In Svelte 4, content can be passed to components using slots. Svelte 5 replaces them with snippets which are more powerful and flexible, and as such slots are deprecated in Svelte 5.
They continue to work, however, and you can mix and match snippets and slots in your components.
They continue to work, however, and you can pass snippets to a component that uses slots:
```svelte
<!--- file: Child.svelte --->
<slot />
<hr />
<slot name="foo" message="hello" />
```
```svelte
<!--- file: Parent.svelte --->
<script>
import Child from './Child.svelte';
</script>
<Child>
default child content
{#snippet foo({ message })}
message from child: {message}
{/snippet}
</Child>
```
(The reverse is not true — you cannot pass slotted content to a component that uses [`{@render ...}`](/docs/svelte/@render) tags.)
When using custom elements, you should still use `<slot />` like before. In a future version, when Svelte removes its internal version of slots, it will leave those slots as-is, i.e. output a regular DOM tag instead of transforming it.
@ -597,29 +621,58 @@ export declare const MyComponent: Component<{
To declare that a component of a certain type is required:
```svelte
<script lang="ts">
import type { ---SvelteComponent--- +++Component+++ } from 'svelte';
import {
ComponentA,
ComponentB
} from 'component-library';
```js
import { ComponentA, ComponentB } from 'component-library';
---import type { SvelteComponent } from 'svelte';---
+++import type { Component } from 'svelte';+++
---let component: typeof SvelteComponent<{ foo: string }>---
+++let component: Component<{ foo: string }>+++ = $state(
---let C: typeof SvelteComponent<{ foo: string }> = $state(---
+++let C: Component<{ foo: string }> = $state(+++
Math.random() ? ComponentA : ComponentB
);
</script>
<svelte:component this={component} foo="bar" />
);
```
The two utility types `ComponentEvents` and `ComponentType` are also deprecated. `ComponentEvents` is obsolete because events are defined as callback props now, and `ComponentType` is obsolete because the new `Component` type is the component type already (e.g. `ComponentType<SvelteComponent<{ prop: string }>>` == `Component<{ prop: string }>`).
The two utility types `ComponentEvents` and `ComponentType` are also deprecated. `ComponentEvents` is obsolete because events are defined as callback props now, and `ComponentType` is obsolete because the new `Component` type is the component type already (i.e. `ComponentType<SvelteComponent<{ prop: string }>>` is equivalent to `Component<{ prop: string }>`).
### bind:this changes
Because components are no longer classes, using `bind:this` no longer returns a class instance with `$set`, `$on` and `$destroy` methods on it. It only returns the instance exports (`export function/const`) and, if you're using the `accessors` option, a getter/setter-pair for each property.
## `<svelte:component>` is no longer necessary
In Svelte 4, components are _static_ — if you render `<Thing>`, and the value of `Thing` changes, [nothing happens](/playground/7f1fa24f0ab44c1089dcbb03568f8dfa?version=4.2.18). To make it dynamic you had to use `<svelte:component>`.
This is no longer true in Svelte 5:
```svelte
<script>
import A from './A.svelte';
import B from './B.svelte';
let Thing = $state();
</script>
<select bind:value={Thing}>
<option value={A}>A</option>
<option value={B}>B</option>
</select>
<!-- these are equivalent -->
<Thing />
<svelte:component this={Thing} />
```
While migrating, keep in mind that your component's name should be capitalized (`Thing`) to distinguish it from elements, unless using dot notation.
### Dot notation indicates a component
In Svelte 4, `<foo.bar>` would create an element with a tag name of `"foo.bar"`. In Svelte 5, `foo.bar` is treated as a component instead. This is particularly useful inside `each` blocks:
```svelte
{#each items as item}
<item.component {...item.props} />
{/each}
```
## Whitespace handling changed
Previously, Svelte employed a very complicated algorithm to determine if whitespace should be kept or not. Svelte 5 simplifies this which makes it easier to reason about as a developer. The rules are:
@ -653,16 +706,6 @@ The `legacy` compiler option, which generated bulkier but IE-friendly code, no l
Content inside component tags becomes a snippet prop called `children`. You cannot have a separate prop by that name.
## Dot notation indicates a component
In Svelte 4, `<foo.bar>` would create an element with a tag name of `"foo.bar"`. In Svelte 5, `foo.bar` is treated as a component instead. This is particularly useful inside `each` blocks:
```svelte
{#each items as item}
<item.component {...item.props} />
{/each}
```
## Breaking changes in runes mode
Some breaking changes only apply once your component is in runes mode.
@ -700,30 +743,6 @@ In Svelte 4, doing the following triggered reactivity:
This is because the Svelte compiler treated the assignment to `foo.value` as an instruction to update anything that referenced `foo`. In Svelte 5, reactivity is determined at runtime rather than compile time, so you should define `value` as a reactive `$state` field on the `Foo` class. Wrapping `new Foo()` with `$state(...)` will have no effect — only vanilla objects and arrays are made deeply reactive.
### `<svelte:component>` is no longer necessary
In Svelte 4, components are _static_ — if you render `<Thing>`, and the value of `Thing` changes, [nothing happens](/playground/7f1fa24f0ab44c1089dcbb03568f8dfa?version=4.2.18). To make it dynamic you must use `<svelte:component>`.
This is no longer true in Svelte 5:
```svelte
<script>
import A from './A.svelte';
import B from './B.svelte';
let Thing = $state();
</script>
<select bind:value={Thing}>
<option value={A}>A</option>
<option value={B}>B</option>
</select>
<!-- these are equivalent -->
<Thing />
<svelte:component this={Thing} />
```
### Touch and wheel events are passive
When using `onwheel`, `onmousewheel`, `ontouchstart` and `ontouchmove` event attributes, the handlers are [passive](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#using_passive_listeners) to align with browser defaults. This greatly improves responsiveness by allowing the browser to scroll the document immediately, rather than waiting to see if the event handler calls `event.preventDefault()`.

@ -21,15 +21,19 @@ A component is attempting to bind to a non-bindable property `%key%` belonging t
### component_api_changed
```
%parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information
%parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5
```
See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more information.
### component_api_invalid_new
```
Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `compatibility.componentApi` compiler option to `4` to keep it working. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information
Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `compatibility.componentApi` compiler option to `4` to keep it working.
```
See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more information.
### derived_references_self
```

@ -52,7 +52,7 @@ Your `console.%method%` contained `$state` proxies. Consider using `$inspect(...
When logging a [proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy), browser devtools will log the proxy itself rather than the value it represents. In the case of Svelte, the 'target' of a `$state` proxy might not resemble its current value, which can be confusing.
The easiest way to log a value as it changes over time is to use the [`$inspect`](https://svelte.dev/docs/svelte/$inspect) rune. Alternatively, to log things on a one-off basis (for example, inside an event handler) you can use [`$state.snapshot`](https://svelte.dev/docs/svelte/$state#$state.snapshot) to take a snapshot of the current value.
The easiest way to log a value as it changes over time is to use the [`$inspect`](/docs/svelte/$inspect) rune. Alternatively, to log things on a one-off basis (for example, inside an event handler) you can use [`$state.snapshot`](/docs/svelte/$state#$state.snapshot) to take a snapshot of the current value.
### event_handler_invalid
@ -222,3 +222,15 @@ Reactive `$state(...)` proxies and the values they proxy have different identiti
```
To resolve this, ensure you're comparing values where both values were created with `$state(...)`, or neither were. Note that `$state.raw(...)` will _not_ create a state proxy.
### transition_slide_display
```
The `slide` transition does not work correctly for elements with `display: %value%`
```
The [slide](/docs/svelte/svelte-transition#slide) transition works by animating the `height` of the element, which requires a `display` style like `block`, `flex` or `grid`. It does not work for:
- `display: inline` (which is the default for elements like `<span>`), and its variants like `inline-block`, `inline-flex` and `inline-grid`
- `display: table` and `table-[name]`, which are the defaults for elements like `<table>` and `<tr>`
- `display: contents`

@ -331,7 +331,39 @@ The $ prefix is reserved, and cannot be used for variables and imports
### each_item_invalid_assignment
```
Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`)
Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`, or `bind:value={array[i]}` instead of `bind:value={entry}`)
```
In legacy mode, it was possible to reassign or bind to the each block argument itself:
```svelte
<script>
let array = [1, 2, 3];
</script>
{#each array as entry}
<!-- reassignment -->
<button on:click={() => entry = 4}>change</button>
<!-- binding -->
<input bind:value={entry}>
{/each}
```
This turned out to be buggy and unpredictable, particularly when working with derived values (such as `array.map(...)`), and as such is forbidden in runes mode. You can achieve the same outcome by using the index instead:
```svelte
<script>
let array = $state([1, 2, 3]);
</script>
{#each array as entry, i}
<!-- reassignment -->
<button onclick={() => array[i] = 4}>change</button>
<!-- binding -->
<input bind:value={array[i]}>
{/each}
```
### effect_invalid_placement
@ -1012,6 +1044,12 @@ Unexpected end of input
'%word%' is a reserved word in JavaScript and cannot be used here
```
### unterminated_string_constant
```
Unterminated string constant
```
### void_element_invalid_content
```

@ -62,7 +62,7 @@ Enforce that `autofocus` is not used on elements. Autofocusing elements can caus
### a11y_click_events_have_key_events
```
Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `<button type="button">` or `<a>` might be more appropriate. See https://svelte.dev/docs/accessibility-warnings#a11y-click-events-have-key-events for more details
Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `<button type="button">` or `<a>` might be more appropriate
```
Enforce that visible, non-interactive elements with an `onclick` event are accompanied by a keyboard event handler.

@ -2,7 +2,7 @@
title: <svelte:component>
---
In runes mode, `<MyComponent>` will re-render if the value of `MyComponent` changes. See the [Svelte 5 migration guide](/docs/svelte/v5-migration-guide#Breaking-changes-in-runes-mode-svelte:component-is-no-longer-necessary) for an example.
In runes mode, `<MyComponent>` will re-render if the value of `MyComponent` changes. See the [Svelte 5 migration guide](/docs/svelte/v5-migration-guide#svelte:component-is-no-longer-necessary) for an example.
In legacy mode, it won't — we must use `<svelte:component>`, which destroys and recreates the component instance when the value of its `this` expression changes:

@ -1,5 +1,123 @@
# svelte
## 5.17.4
### Patch Changes
- fix: never consider inert boundary effects ([#14999](https://github.com/sveltejs/svelte/pull/14999))
- fix: store access on component destroy ([#14968](https://github.com/sveltejs/svelte/pull/14968))
- fix: correctly transform `pre` with no content ([#14973](https://github.com/sveltejs/svelte/pull/14973))
- fix: wrap each block expression in derived to encapsulate effects ([#14967](https://github.com/sveltejs/svelte/pull/14967))
## 5.17.3
### Patch Changes
- fix: reset dependency read versions after reaction execution ([#14964](https://github.com/sveltejs/svelte/pull/14964))
## 5.17.2
### Patch Changes
- fix: account for parent scale when animating elements ([#14957](https://github.com/sveltejs/svelte/pull/14957))
- fix: apply `overflow: hidden` style when transitioning elements, where necessary ([#14930](https://github.com/sveltejs/svelte/pull/14930))
- fix: properly add owners to function bindings ([#14962](https://github.com/sveltejs/svelte/pull/14962))
## 5.17.1
### Patch Changes
- fix: remove bindable prop validation ([#14946](https://github.com/sveltejs/svelte/pull/14946))
- chore: tweak "invalid assignment" compiler error message ([#14955](https://github.com/sveltejs/svelte/pull/14955))
- fix: silence false-positive stale value warning ([#14958](https://github.com/sveltejs/svelte/pull/14958))
## 5.17.0
### Minor Changes
- feat: allow non-numeric values to be tweened by snapping immediately to new value ([#14941](https://github.com/sveltejs/svelte/pull/14941))
### Patch Changes
- fix: handle default values in object destructuring within "each" blocks when using characters like "}" and "]" ([#14554](https://github.com/sveltejs/svelte/pull/14554))
- fix: account for min-width/height in `slide` transition ([#14942](https://github.com/sveltejs/svelte/pull/14942))
- fix: prevent long delays causing erratic spring behaviour ([#14940](https://github.com/sveltejs/svelte/pull/14940))
- feat: warn on using `slide` transition with table elements ([#14936](https://github.com/sveltejs/svelte/pull/14936))
- chore: improve signal performance by reducing duplicate deps ([#14945](https://github.com/sveltejs/svelte/pull/14945))
## 5.16.6
### Patch Changes
- fix: Make Tween duration 0 set current to target immediately ([#14937](https://github.com/sveltejs/svelte/pull/14937))
- fix: guard against `customElements` being unavailable in browser extension contexts ([#14933](https://github.com/sveltejs/svelte/pull/14933))
- fix: treat `inert` as a boolean attribute ([#14935](https://github.com/sveltejs/svelte/pull/14935))
- fix: remove leading newline from `<pre>` contents ([#14922](https://github.com/sveltejs/svelte/pull/14922))
## 5.16.5
### Patch Changes
- fix: inherit correct namespace for `<title>` elements ([#14817](https://github.com/sveltejs/svelte/pull/14817))
- fix: don't throw `bind_invalid_export` if there's also a bindable prop with the same name ([#14813](https://github.com/sveltejs/svelte/pull/14813))
## 5.16.4
### Patch Changes
- fix: use cached indexOf array prototype method internally ([#14912](https://github.com/sveltejs/svelte/pull/14912))
- fix: make Tween work with continuous target changes ([#14895](https://github.com/sveltejs/svelte/pull/14895))
## 5.16.3
### Patch Changes
- fix: correctly parse `each` with loose parser ([#14887](https://github.com/sveltejs/svelte/pull/14887))
- fix: apply `clsx` logic to custom element `class` attributes ([#14907](https://github.com/sveltejs/svelte/pull/14907))
## 5.16.2
### Patch Changes
- fix: ensure disconnected deriveds correctly connect again ([#14899](https://github.com/sveltejs/svelte/pull/14899))
- fix: correctly highlight sources reassigned inside `trace` ([#14811](https://github.com/sveltejs/svelte/pull/14811))
## 5.16.1
### Patch Changes
- fix: ensure unowned deriveds correctly get re-linked to the graph ([#14855](https://github.com/sveltejs/svelte/pull/14855))
- fix: ensure $inspect.trace works correctly with null values ([#14853](https://github.com/sveltejs/svelte/pull/14853))
## 5.16.0
### Minor Changes
- feat: allow `class` attribute to be an object or array, using `clsx` ([#14714](https://github.com/sveltejs/svelte/pull/14714))
### Patch Changes
- fix: don't include keyframes in global scope in the keyframes to rename ([#14822](https://github.com/sveltejs/svelte/pull/14822))
## 5.15.0
### Minor Changes

@ -741,7 +741,7 @@ export interface HTMLAttributes<T extends EventTarget> extends AriaAttributes, D
accesskey?: string | undefined | null;
autocapitalize?: 'characters' | 'off' | 'on' | 'none' | 'sentences' | 'words' | undefined | null;
autofocus?: boolean | undefined | null;
class?: string | undefined | null;
class?: string | import('clsx').ClassArray | import('clsx').ClassDictionary | undefined | null;
contenteditable?: Booleanish | 'inherit' | 'plaintext-only' | undefined | null;
contextmenu?: string | undefined | null;
dir?: 'ltr' | 'rtl' | 'auto' | undefined | null;
@ -1522,7 +1522,7 @@ export interface SvelteWindowAttributes extends HTMLAttributes<Window> {
export interface SVGAttributes<T extends EventTarget> extends AriaAttributes, DOMAttributes<T> {
// Attributes which also defined in HTMLAttributes
className?: string | undefined | null;
class?: string | undefined | null;
class?: string | import('clsx').ClassArray | import('clsx').ClassDictionary | undefined | null;
color?: string | undefined | null;
height?: number | string | undefined | null;
id?: string | undefined | null;

@ -12,11 +12,15 @@
## component_api_changed
> %parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information
> %parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5
See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more information.
## component_api_invalid_new
> Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `compatibility.componentApi` compiler option to `4` to keep it working. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information
> Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `compatibility.componentApi` compiler option to `4` to keep it working.
See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more information.
## derived_references_self

@ -42,7 +42,7 @@ function add() {
When logging a [proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy), browser devtools will log the proxy itself rather than the value it represents. In the case of Svelte, the 'target' of a `$state` proxy might not resemble its current value, which can be confusing.
The easiest way to log a value as it changes over time is to use the [`$inspect`](https://svelte.dev/docs/svelte/$inspect) rune. Alternatively, to log things on a one-off basis (for example, inside an event handler) you can use [`$state.snapshot`](https://svelte.dev/docs/svelte/$state#$state.snapshot) to take a snapshot of the current value.
The easiest way to log a value as it changes over time is to use the [`$inspect`](/docs/svelte/$inspect) rune. Alternatively, to log things on a one-off basis (for example, inside an event handler) you can use [`$state.snapshot`](/docs/svelte/$state#$state.snapshot) to take a snapshot of the current value.
## event_handler_invalid
@ -186,3 +186,13 @@ To fix it, either create callback props to communicate changes, or mark `person`
```
To resolve this, ensure you're comparing values where both values were created with `$state(...)`, or neither were. Note that `$state.raw(...)` will _not_ create a state proxy.
## transition_slide_display
> The `slide` transition does not work correctly for elements with `display: %value%`
The [slide](/docs/svelte/svelte-transition#slide) transition works by animating the `height` of the element, which requires a `display` style like `block`, `flex` or `grid`. It does not work for:
- `display: inline` (which is the default for elements like `<span>`), and its variants like `inline-block`, `inline-flex` and `inline-grid`
- `display: table` and `table-[name]`, which are the defaults for elements like `<table>` and `<tr>`
- `display: contents`

@ -32,7 +32,39 @@
## each_item_invalid_assignment
> Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`)
> Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`, or `bind:value={array[i]}` instead of `bind:value={entry}`)
In legacy mode, it was possible to reassign or bind to the each block argument itself:
```svelte
<script>
let array = [1, 2, 3];
</script>
{#each array as entry}
<!-- reassignment -->
<button on:click={() => entry = 4}>change</button>
<!-- binding -->
<input bind:value={entry}>
{/each}
```
This turned out to be buggy and unpredictable, particularly when working with derived values (such as `array.map(...)`), and as such is forbidden in runes mode. You can achieve the same outcome by using the index instead:
```svelte
<script>
let array = $state([1, 2, 3]);
</script>
{#each array as entry, i}
<!-- reassignment -->
<button onclick={() => array[i] = 4}>change</button>
<!-- binding -->
<input bind:value={array[i]}>
{/each}
```
## effect_invalid_placement

@ -418,6 +418,10 @@ See https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-ele
> '%word%' is a reserved word in JavaScript and cannot be used here
## unterminated_string_constant
> Unterminated string constant
## void_element_invalid_content
> Void elements cannot have children or closing tags

@ -49,7 +49,7 @@ Enforce that `autofocus` is not used on elements. Autofocusing elements can caus
## a11y_click_events_have_key_events
> Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `<button type="button">` or `<a>` might be more appropriate. See https://svelte.dev/docs/accessibility-warnings#a11y-click-events-have-key-events for more details
> Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `<button type="button">` or `<a>` might be more appropriate
Enforce that visible, non-interactive elements with an `onclick` event are accompanied by a keyboard event handler.

@ -2,7 +2,7 @@
"name": "svelte",
"description": "Cybernetically enhanced web apps",
"license": "MIT",
"version": "5.15.0",
"version": "5.17.4",
"type": "module",
"types": "./types/index.d.ts",
"engines": {
@ -157,8 +157,9 @@
"acorn-typescript": "^1.4.13",
"aria-query": "^5.3.1",
"axobject-query": "^4.1.0",
"clsx": "^2.1.1",
"esm-env": "^1.2.1",
"esrap": "^1.3.2",
"esrap": "^1.4.2",
"is-reference": "^3.0.3",
"locate-character": "^3.0.0",
"magic-string": "^0.30.11",

@ -8,8 +8,6 @@ fs.writeFileSync(
/**
* The current version, as set in package.json.
*
* https://svelte.dev/docs/svelte-compiler#svelte-version
* @type {string}
*/
export const VERSION = '${pkg.version}';

@ -433,7 +433,7 @@ declare namespace $inspect {
* });
* </script>
*/
export function trace(name: string): void;
export function trace(name?: string): void;
// prevent intellisense from being unhelpful
/** @deprecated */

@ -11,18 +11,37 @@ import { cubicOut } from '../easing/index.js';
* @returns {AnimationConfig}
*/
export function flip(node, { from, to }, params = {}) {
var { delay = 0, duration = (d) => Math.sqrt(d) * 120, easing = cubicOut } = params;
var style = getComputedStyle(node);
var zoom = get_zoom(node); // https://drafts.csswg.org/css-viewport/#effective-zoom
// find the transform origin, expressed as a pair of values between 0 and 1
var transform = style.transform === 'none' ? '' : style.transform;
var [ox, oy] = style.transformOrigin.split(' ').map(parseFloat);
ox /= node.clientWidth;
oy /= node.clientHeight;
// calculate effect of parent transforms and zoom
var zoom = get_zoom(node); // https://drafts.csswg.org/css-viewport/#effective-zoom
var sx = node.clientWidth / to.width / zoom;
var sy = node.clientHeight / to.height / zoom;
// find the starting position of the transform origin
var fx = from.left + from.width * ox;
var fy = from.top + from.height * oy;
// find the ending position of the transform origin
var tx = to.left + to.width * ox;
var ty = to.top + to.height * oy;
// find the translation at the start of the transform
var dx = (fx - tx) * sx;
var dy = (fy - ty) * sy;
// find the relative scale at the start of the transform
var dsx = from.width / to.width;
var dsy = from.height / to.height;
var dx = (from.left + dsx * ox - (to.left + ox)) / zoom;
var dy = (from.top + dsy * oy - (to.top + oy)) / zoom;
var { delay = 0, duration = (d) => Math.sqrt(d) * 120, easing = cubicOut } = params;
return {
delay,
duration: typeof duration === 'function' ? duration(Math.sqrt(dx * dx + dy * dy)) : duration,
@ -32,7 +51,8 @@ export function flip(node, { from, to }, params = {}) {
var y = u * dy;
var sx = t + u * dsx;
var sy = t + u * dsy;
return `transform: ${transform} scale(${sx}, ${sy}) translate(${x}px, ${y}px);`;
return `transform: ${transform} translate(${x}px, ${y}px) scale(${sx}, ${sy});`;
}
};
}

File diff suppressed because it is too large Load Diff

@ -3,7 +3,6 @@
import { isIdentifierStart, isIdentifierChar } from 'acorn';
import fragment from './state/fragment.js';
import { regex_whitespace } from '../patterns.js';
import full_char_code_at from './utils/full_char_code_at.js';
import * as e from '../../errors.js';
import { create_fragment } from './utils/create.js';
import read_options from './read/options.js';
@ -230,13 +229,13 @@ export class Parser {
let i = this.index;
const code = full_char_code_at(this.template, i);
const code = /** @type {number} */ (this.template.codePointAt(i));
if (!isIdentifierStart(code, true)) return null;
i += code <= 0xffff ? 1 : 2;
while (i < this.template.length) {
const code = full_char_code_at(this.template, i);
const code = /** @type {number} */ (this.template.codePointAt(i));
if (!isIdentifierChar(code, true)) break;
i += code <= 0xffff ? 1 : 2;

@ -1,15 +1,7 @@
/** @import { Location } from 'locate-character' */
/** @import { Pattern } from 'estree' */
/** @import { Parser } from '../index.js' */
// @ts-expect-error acorn type definitions are borked in the release we use
import { isIdentifierStart } from 'acorn';
import full_char_code_at from '../utils/full_char_code_at.js';
import {
is_bracket_open,
is_bracket_close,
is_bracket_pair,
get_bracket_close
} from '../utils/bracket.js';
import { is_bracket_open, is_bracket_close, get_bracket_close } from '../utils/bracket.js';
import { parse_expression_at } from '../acorn.js';
import { regex_not_newline_characters } from '../../patterns.js';
import * as e from '../../../errors.js';
@ -23,9 +15,9 @@ export default function read_pattern(parser) {
const start = parser.index;
let i = parser.index;
const code = full_char_code_at(parser.template, i);
if (isIdentifierStart(code, true)) {
const name = /** @type {string} */ (parser.read_identifier());
const name = parser.read_identifier();
if (name !== null) {
const annotation = read_type_annotation(parser);
return {
@ -41,33 +33,15 @@ export default function read_pattern(parser) {
};
}
if (!is_bracket_open(code)) {
if (!is_bracket_open(parser.template[i])) {
e.expected_pattern(i);
}
const bracket_stack = [code];
i += code <= 0xffff ? 1 : 2;
while (i < parser.template.length) {
const code = full_char_code_at(parser.template, i);
if (is_bracket_open(code)) {
bracket_stack.push(code);
} else if (is_bracket_close(code)) {
const popped = /** @type {number} */ (bracket_stack.pop());
if (!is_bracket_pair(popped, code)) {
e.expected_token(i, String.fromCharCode(/** @type {number} */ (get_bracket_close(popped))));
}
if (bracket_stack.length === 0) {
i += code <= 0xffff ? 1 : 2;
break;
}
}
i += code <= 0xffff ? 1 : 2;
}
i = match_bracket(parser, start);
parser.index = i;
const pattern_string = parser.template.slice(start, i);
try {
// the length of the `space_with_newline` has to be start - 1
// because we added a `(` in front of the pattern_string,
@ -97,6 +71,75 @@ export default function read_pattern(parser) {
}
}
/**
* @param {Parser} parser
* @param {number} start
*/
function match_bracket(parser, start) {
const bracket_stack = [];
let i = start;
while (i < parser.template.length) {
let char = parser.template[i++];
if (char === "'" || char === '"' || char === '`') {
i = match_quote(parser, i, char);
continue;
}
if (is_bracket_open(char)) {
bracket_stack.push(char);
} else if (is_bracket_close(char)) {
const popped = /** @type {string} */ (bracket_stack.pop());
const expected = /** @type {string} */ (get_bracket_close(popped));
if (char !== expected) {
e.expected_token(i - 1, expected);
}
if (bracket_stack.length === 0) {
return i;
}
}
}
e.unexpected_eof(parser.template.length);
}
/**
* @param {Parser} parser
* @param {number} start
* @param {string} quote
*/
function match_quote(parser, start, quote) {
let is_escaped = false;
let i = start;
while (i < parser.template.length) {
const char = parser.template[i++];
if (is_escaped) {
is_escaped = false;
continue;
}
if (char === quote) {
return i;
}
if (char === '\\') {
is_escaped = true;
}
if (quote === '`' && char === '$' && parser.template[i] === '{') {
i = match_bracket(parser, i);
}
}
e.unterminated_string_constant(start);
}
/**
* @param {Parser} parser
* @returns {any}

@ -8,9 +8,31 @@ import { find_matching_bracket } from '../utils/bracket.js';
/**
* @param {Parser} parser
* @param {string} [opening_token]
* @returns {Expression | undefined}
*/
export function get_loose_identifier(parser, opening_token) {
// Find the next } and treat it as the end of the expression
const end = find_matching_bracket(parser.template, parser.index, opening_token ?? '{');
if (end) {
const start = parser.index;
parser.index = end;
// We don't know what the expression is and signal this by returning an empty identifier
return {
type: 'Identifier',
start,
end,
name: ''
};
}
}
/**
* @param {Parser} parser
* @param {string} [opening_token]
* @param {boolean} [disallow_loose]
* @returns {Expression}
*/
export default function read_expression(parser, opening_token) {
export default function read_expression(parser, opening_token, disallow_loose) {
try {
const node = parse_expression_at(parser.template, parser.ts, parser.index);
@ -41,19 +63,12 @@ export default function read_expression(parser, opening_token) {
return /** @type {Expression} */ (node);
} catch (err) {
if (parser.loose) {
// Find the next } and treat it as the end of the expression
const end = find_matching_bracket(parser.template, parser.index, opening_token ?? '{');
if (end) {
const start = parser.index;
parser.index = end;
// We don't know what the expression is and signal this by returning an empty identifier
return {
type: 'Identifier',
start,
end,
name: ''
};
// If we are in an each loop we need the error to be thrown in cases like
// `as { y = z }` so we still throw and handle the error there
if (parser.loose && !disallow_loose) {
const expression = get_loose_identifier(parser, opening_token);
if (expression) {
return expression;
}
}

@ -1,13 +1,13 @@
/** @import { ArrowFunctionExpression, Expression, Identifier, Pattern } from 'estree' */
/** @import { AST } from '#compiler' */
/** @import { Parser } from '../index.js' */
import read_pattern from '../read/context.js';
import read_expression from '../read/expression.js';
import * as e from '../../../errors.js';
import { create_fragment } from '../utils/create.js';
import { walk } from 'zimmerframe';
import { parse_expression_at } from '../acorn.js';
import * as e from '../../../errors.js';
import { create_expression_metadata } from '../../nodes.js';
import { parse_expression_at } from '../acorn.js';
import read_pattern from '../read/context.js';
import read_expression, { get_loose_identifier } from '../read/expression.js';
import { create_fragment } from '../utils/create.js';
const regex_whitespace_with_closing_curly_brace = /^\s*}/;
@ -87,7 +87,7 @@ function open(parser) {
// we get a valid expression
while (!expression) {
try {
expression = read_expression(parser);
expression = read_expression(parser, undefined, true);
} catch (err) {
end = /** @type {any} */ (err).position[0] - 2;
@ -95,7 +95,15 @@ function open(parser) {
end -= 1;
}
if (end <= start) throw err;
if (end <= start) {
if (parser.loose) {
expression = get_loose_identifier(parser);
if (expression) {
break;
}
}
throw err;
}
// @ts-expect-error parser.template is meant to be readonly, this is a special case
parser.template = template.slice(0, end);

@ -1,41 +1,30 @@
import full_char_code_at from './full_char_code_at.js';
const SQUARE_BRACKET_OPEN = '[';
const SQUARE_BRACKET_CLOSE = ']';
const CURLY_BRACKET_OPEN = '{';
const CURLY_BRACKET_CLOSE = '}';
const PARENTHESES_OPEN = '(';
const PARENTHESES_CLOSE = ')';
const SQUARE_BRACKET_OPEN = '['.charCodeAt(0);
const SQUARE_BRACKET_CLOSE = ']'.charCodeAt(0);
const CURLY_BRACKET_OPEN = '{'.charCodeAt(0);
const CURLY_BRACKET_CLOSE = '}'.charCodeAt(0);
const PARENTHESES_OPEN = '('.charCodeAt(0);
const PARENTHESES_CLOSE = ')'.charCodeAt(0);
/** @param {number} code */
export function is_bracket_open(code) {
return code === SQUARE_BRACKET_OPEN || code === CURLY_BRACKET_OPEN;
/** @param {string} char */
export function is_bracket_open(char) {
return char === SQUARE_BRACKET_OPEN || char === CURLY_BRACKET_OPEN;
}
/** @param {number} code */
export function is_bracket_close(code) {
return code === SQUARE_BRACKET_CLOSE || code === CURLY_BRACKET_CLOSE;
/** @param {string} char */
export function is_bracket_close(char) {
return char === SQUARE_BRACKET_CLOSE || char === CURLY_BRACKET_CLOSE;
}
/**
* @param {number} open
* @param {number} close
*/
export function is_bracket_pair(open, close) {
return (
(open === SQUARE_BRACKET_OPEN && close === SQUARE_BRACKET_CLOSE) ||
(open === CURLY_BRACKET_OPEN && close === CURLY_BRACKET_CLOSE)
);
}
/** @param {number} open */
/** @param {string} open */
export function get_bracket_close(open) {
if (open === SQUARE_BRACKET_OPEN) {
return SQUARE_BRACKET_CLOSE;
}
if (open === CURLY_BRACKET_OPEN) {
return CURLY_BRACKET_CLOSE;
}
if (open === PARENTHESES_OPEN) {
return PARENTHESES_CLOSE;
}
@ -132,8 +121,7 @@ function count_leading_backslashes(string, search_start_index) {
* @returns {number | undefined} The index of the closing bracket, or undefined if not found.
*/
export function find_matching_bracket(template, index, open) {
const open_code = full_char_code_at(open, 0);
const close_code = get_bracket_close(open_code);
const close = get_bracket_close(open);
let brackets = 1;
let i = index;
while (brackets > 0 && i < template.length) {
@ -159,10 +147,10 @@ export function find_matching_bracket(template, index, open) {
continue;
}
default: {
const code = full_char_code_at(template, i);
if (code === open_code) {
const char = template[i];
if (char === open) {
brackets++;
} else if (code === close_code) {
} else if (char === close) {
brackets--;
}
if (brackets === 0) {

@ -1,15 +0,0 @@
// Adapted from https://github.com/acornjs/acorn/blob/6584815dca7440e00de841d1dad152302fdd7ca5/src/tokenize.js
// Reproduced under MIT License https://github.com/acornjs/acorn/blob/master/LICENSE
/**
* @param {string} str
* @param {number} i
* @returns {number}
*/
export default function full_char_code_at(str, i) {
const code = str.charCodeAt(i);
if (code <= 0xd7ff || code >= 0xe000) return code;
const next = str.charCodeAt(i + 1);
return (code << 10) + next - 0x35fdc00;
}

@ -28,11 +28,19 @@ function is_global_block_selector(simple_selector) {
);
}
/**
*
* @param {Array<AST.CSS.Node>} path
*/
function is_in_global_block(path) {
return path.some((node) => node.type === 'Rule' && node.metadata.is_global_block);
}
/** @type {CssVisitors} */
const css_visitors = {
Atrule(node, context) {
if (is_keyframes_node(node)) {
if (!node.prelude.startsWith('-global-')) {
if (!node.prelude.startsWith('-global-') && !is_in_global_block(context.path)) {
context.state.keyframes.push(node.prelude);
}
}

@ -731,7 +731,7 @@ function attribute_matches(node, name, expected_value, operator, case_insensitiv
/** @type {string[]} */
let prev_values = [];
for (const chunk of chunks) {
const current_possible_values = get_possible_values(chunk);
const current_possible_values = get_possible_values(chunk, name === 'class');
// impossible to find out all combinations
if (!current_possible_values) return true;
@ -784,7 +784,7 @@ function attribute_matches(node, name, expected_value, operator, case_insensitiv
prev_values.push(current_possible_value);
}
});
if (prev_values.length < current_possible_values.size) {
if (prev_values.length < current_possible_values.length) {
prev_values.push(' ');
}
if (prev_values.length > 20) {

@ -4,14 +4,74 @@ const UNKNOWN = {};
/**
* @param {Node} node
* @param {boolean} is_class
* @param {Set<any>} set
* @param {boolean} is_nested
*/
function gather_possible_values(node, set) {
function gather_possible_values(node, is_class, set, is_nested = false) {
if (set.has(UNKNOWN)) {
// no point traversing any further
return;
}
if (node.type === 'Literal') {
set.add(String(node.value));
} else if (node.type === 'ConditionalExpression') {
gather_possible_values(node.consequent, set);
gather_possible_values(node.alternate, set);
gather_possible_values(node.consequent, is_class, set, is_nested);
gather_possible_values(node.alternate, is_class, set, is_nested);
} else if (node.type === 'LogicalExpression') {
if (node.operator === '&&') {
// && is a special case, because the only way the left
// hand value can be included is if it's falsy. this is
// a bit of extra work but it's worth it because
// `class={[condition && 'blah']}` is common,
// and we don't want to deopt on `condition`
const left = new Set();
gather_possible_values(node.left, is_class, left, is_nested);
if (left.has(UNKNOWN)) {
// add all non-nullish falsy values, unless this is a `class` attribute that
// will be processed by cslx, in which case falsy values are removed, unless
// they're not inside an array/object (TODO 6.0 remove that last part)
if (!is_class || !is_nested) {
set.add('');
set.add(false);
set.add(NaN);
set.add(0); // -0 and 0n are also falsy, but stringify to '0'
}
} else {
for (const value of left) {
if (!value && value != undefined && (!is_class || !is_nested)) {
set.add(value);
}
}
}
gather_possible_values(node.right, is_class, set, is_nested);
} else {
gather_possible_values(node.left, is_class, set, is_nested);
gather_possible_values(node.right, is_class, set, is_nested);
}
} else if (is_class && node.type === 'ArrayExpression') {
for (const entry of node.elements) {
if (entry) {
gather_possible_values(entry, is_class, set, true);
}
}
} else if (is_class && node.type === 'ObjectExpression') {
for (const property of node.properties) {
if (
property.type === 'Property' &&
!property.computed &&
(property.key.type === 'Identifier' || property.key.type === 'Literal')
) {
set.add(
property.key.type === 'Identifier' ? property.key.name : String(property.key.value)
);
} else {
set.add(UNKNOWN);
}
}
} else {
set.add(UNKNOWN);
}
@ -19,19 +79,20 @@ function gather_possible_values(node, set) {
/**
* @param {AST.Text | AST.ExpressionTag} chunk
* @returns {Set<string> | null}
* @param {boolean} is_class
* @returns {string[] | null}
*/
export function get_possible_values(chunk) {
export function get_possible_values(chunk, is_class) {
const values = new Set();
if (chunk.type === 'Text') {
values.add(chunk.data);
} else {
gather_possible_values(chunk.expression, values);
gather_possible_values(chunk.expression, is_class, values);
}
if (values.has(UNKNOWN)) return null;
return values;
return [...values].map((value) => String(value));
}
/**

@ -773,6 +773,8 @@ export function analyze_component(root, source, options) {
if (attribute.type !== 'Attribute') continue;
if (attribute.name.toLowerCase() !== 'class') continue;
// The dynamic class method appends the hash to the end of the class attribute on its own
if (attribute.metadata.needs_clsx) continue outer;
class_attribute = attribute;
}

@ -38,6 +38,19 @@ export function Attribute(node, context) {
mark_subtree_dynamic(context.path);
}
// class={[...]} or class={{...}} or `class={x}` need clsx to resolve the classes
if (
node.name === 'class' &&
!Array.isArray(node.value) &&
node.value !== true &&
node.value.expression.type !== 'Literal' &&
node.value.expression.type !== 'TemplateLiteral' &&
node.value.expression.type !== 'BinaryExpression'
) {
mark_subtree_dynamic(context.path);
node.metadata.needs_clsx = true;
}
if (node.value !== true) {
for (const chunk of get_attribute_chunks(node.value)) {
if (chunk.type !== 'ExpressionTag') continue;

@ -90,8 +90,11 @@ export function RegularElement(node, context) {
if (is_svg(node.name)) {
return true;
}
if (node.name === 'a') {
for (let i = context.path.length - 1; i >= 0; i--) {
if (node.name === 'a' || node.name === 'title') {
let i = context.path.length;
while (i--) {
const ancestor = context.path[i];
if (ancestor.type === 'RegularElement') {
return ancestor.metadata.svg;

@ -216,6 +216,8 @@ export function client_component(analysis, options) {
/** @type {ESTree.VariableDeclaration[]} */
const legacy_reactive_declarations = [];
let needs_store_cleanup = false;
for (const [name, binding] of analysis.instance.scope.declarations) {
if (binding.kind === 'legacy_reactive') {
legacy_reactive_declarations.push(
@ -224,7 +226,10 @@ export function client_component(analysis, options) {
}
if (binding.kind === 'store_sub') {
if (store_setup.length === 0) {
store_setup.push(b.const('$$stores', b.call('$.setup_stores')));
needs_store_cleanup = true;
store_setup.push(
b.const(b.array_pattern([b.id('$$stores'), b.id('$$cleanup')]), b.call('$.setup_stores'))
);
}
// We're creating an arrow function that gets the store value which minifies better for two or more references
@ -301,28 +306,6 @@ export function client_component(analysis, options) {
(binding.kind === 'prop' || binding.kind === 'bindable_prop') && !name.startsWith('$$')
);
if (dev && analysis.runes) {
const exports = analysis.exports.map(({ name, alias }) => b.literal(alias ?? name));
/** @type {ESTree.Literal[]} */
const bindable = [];
for (const [name, binding] of properties) {
if (binding.kind === 'bindable_prop') {
bindable.push(b.literal(binding.prop_alias ?? name));
}
}
instance.body.unshift(
b.stmt(
b.call(
'$.validate_prop_bindings',
b.id('$$props'),
b.array(bindable),
b.array(exports),
b.id(`${analysis.name}`)
)
)
);
}
if (analysis.accessors) {
for (const [name, binding] of properties) {
const key = binding.prop_alias ?? name;
@ -415,14 +398,28 @@ export function client_component(analysis, options) {
analysis.reactive_statements.size > 0 ||
component_returned_object.length > 0;
// we want the cleanup function for the stores to run as the very last thing
// so that it can effectively clean up the store subscription even after the user effects runs
if (should_inject_context) {
component_block.body.unshift(b.stmt(b.call('$.push', ...push_args)));
component_block.body.push(
component_returned_object.length > 0
? b.return(b.call('$.pop', b.object(component_returned_object)))
: b.stmt(b.call('$.pop'))
);
let to_push;
if (component_returned_object.length > 0) {
let pop_call = b.call('$.pop', b.object(component_returned_object));
to_push = needs_store_cleanup ? b.var('$$pop', pop_call) : b.return(pop_call);
} else {
to_push = b.stmt(b.call('$.pop'));
}
component_block.body.push(to_push);
}
if (needs_store_cleanup) {
component_block.body.push(b.stmt(b.call('$$cleanup')));
if (component_returned_object.length > 0) {
component_block.body.push(b.return(b.id('$$pop')));
}
}
if (analysis.uses_rest_props) {

@ -171,6 +171,7 @@ function build_assignment(operator, left, right, context) {
// special case — ignore `bind:prop={getter, (v) => (...)}` / `bind:value={x.y}`
if (
path.at(-1) === 'BindDirective' ||
path.at(-1) === 'Component' ||
path.at(-1) === 'SvelteComponent' ||
(path.at(-1) === 'ArrowFunctionExpression' &&

@ -557,6 +557,10 @@ function build_element_attribute_update_assignment(
let update;
if (name === 'class') {
if (attribute.metadata.needs_clsx) {
value = b.call('$.clsx', value);
}
if (attribute.metadata.expression.has_state && has_call) {
// ensure we're not creating a separate template effect for this so that
// potential class directives are added to the same effect and therefore always apply
@ -565,11 +569,15 @@ function build_element_attribute_update_assignment(
value = b.call('$.get', id);
has_call = false;
}
update = b.stmt(
b.call(
is_svg ? '$.set_svg_class' : is_mathml ? '$.set_mathml_class' : '$.set_class',
node_id,
value
value,
attribute.metadata.needs_clsx && context.state.analysis.css.hash
? b.literal(context.state.analysis.css.hash)
: undefined
)
);
} else if (name === 'value') {
@ -646,6 +654,14 @@ function build_custom_element_attribute_update_assignment(node_id, attribute, co
const name = attribute.name; // don't lowercase, as we set the element's property, which might be case sensitive
let { has_call, value } = build_attribute_value(attribute.value, context);
// We assume that noone's going to redefine the semantics of the class attribute on custom elements, i.e. it's still used for CSS classes
if (name === 'class' && attribute.metadata.needs_clsx) {
if (context.state.analysis.css.hash) {
value = b.array([value, b.literal(context.state.analysis.css.hash)]);
}
value = b.call('$.clsx', value);
}
const update = b.stmt(b.call('$.set_custom_element_data', node_id, b.literal(name), value));
if (attribute.metadata.expression.has_state) {

@ -174,23 +174,32 @@ export function build_component(node, component_name, context, anchor = context.
} else if (attribute.type === 'BindDirective') {
const expression = /** @type {Expression} */ (context.visit(attribute.expression));
if (dev && attribute.name !== 'this' && attribute.expression.type !== 'SequenceExpression') {
if (dev && attribute.name !== 'this') {
let should_add_owner = true;
if (attribute.expression.type !== 'SequenceExpression') {
const left = object(attribute.expression);
let binding;
if (left?.type === 'Identifier') {
binding = context.state.scope.get(left.name);
}
const binding = context.state.scope.get(left.name);
// Only run ownership addition on $state fields.
// Theoretically someone could create a `$state` while creating `$state.raw` or inside a `$derived.by`,
// but that feels so much of an edge case that it doesn't warrant a perf hit for the common case.
if (binding?.kind !== 'derived' && binding?.kind !== 'raw_state') {
if (binding?.kind === 'derived' || binding?.kind === 'raw_state') {
should_add_owner = false;
}
}
}
if (should_add_owner) {
binding_initializers.push(
b.stmt(
b.call(
b.id('$.add_owner_effect'),
b.thunk(expression),
expression.type === 'SequenceExpression'
? expression.expressions[0]
: b.thunk(expression),
b.id(component_name),
is_ignored(node, 'ownership_invalid_binding') && b.true
)

@ -86,11 +86,36 @@ export function build_element_attributes(node, context) {
} else if (attribute.name !== 'defaultValue' && attribute.name !== 'defaultChecked') {
if (attribute.name === 'class') {
class_index = attributes.length;
} else if (attribute.name === 'style') {
if (attribute.metadata.needs_clsx) {
const clsx_value = b.call(
'$.clsx',
/** @type {AST.ExpressionTag} */ (attribute.value).expression
);
attributes.push({
...attribute,
value: {
.../** @type {AST.ExpressionTag} */ (attribute.value),
expression: context.state.analysis.css.hash
? b.binary(
'+',
b.binary('+', clsx_value, b.literal(' ')),
b.literal(context.state.analysis.css.hash)
)
: clsx_value
}
});
} else {
attributes.push(attribute);
}
} else {
if (attribute.name === 'style') {
style_index = attributes.length;
}
attributes.push(attribute);
}
}
} else if (attribute.type === 'BindDirective') {
if (attribute.name === 'value' && node.name === 'select') continue;
if (

@ -5,6 +5,7 @@
import {
regex_ends_with_whitespaces,
regex_not_whitespace,
regex_starts_with_newline,
regex_starts_with_whitespaces
} from '../patterns.js';
import * as b from '../../utils/builders.js';
@ -270,6 +271,22 @@ export function clean_nodes(
var first = trimmed[0];
// initial newline inside a `<pre>` is disregarded, if not followed by another newline
if (parent.type === 'RegularElement' && parent.name === 'pre' && first?.type === 'Text') {
const text = first.data.replace(regex_starts_with_newline, '');
if (text !== first.data) {
const tmp = text.replace(regex_starts_with_newline, '');
if (text === tmp) {
first.data = text;
first.raw = first.raw.replace(regex_starts_with_newline, '');
if (first.data === '') {
trimmed.shift();
first = trimmed[0];
}
}
}
}
// Special case: Add a comment if this is a lone script tag. This ensures that our run_scripts logic in template.js
// will always be able to call node.replaceWith() on the script tag in order to make it run. If we don't add this
// and would still call node.replaceWith() on the script tag, it would be a no-op because the script tag has no parent.

@ -45,7 +45,8 @@ export function create_attribute(name, start, end, value) {
value,
metadata: {
expression: create_expression_metadata(),
delegated: null
delegated: null,
needs_clsx: false
}
};
}

@ -488,6 +488,8 @@ export namespace AST {
expression: ExpressionMetadata;
/** May be set if this is an event attribute */
delegated: null | DelegatedEvent;
/** May be `true` if this is a `class` attribute that needs `clsx` */
needs_clsx: boolean;
};
}

@ -44,84 +44,84 @@ function w(node, code, message) {
}
export const codes = [
"a11y_accesskey",
"a11y_aria_activedescendant_has_tabindex",
"a11y_aria_attributes",
"a11y_autocomplete_valid",
"a11y_autofocus",
"a11y_click_events_have_key_events",
"a11y_consider_explicit_label",
"a11y_distracting_elements",
"a11y_figcaption_index",
"a11y_figcaption_parent",
"a11y_hidden",
"a11y_img_redundant_alt",
"a11y_incorrect_aria_attribute_type",
"a11y_incorrect_aria_attribute_type_boolean",
"a11y_incorrect_aria_attribute_type_id",
"a11y_incorrect_aria_attribute_type_idlist",
"a11y_incorrect_aria_attribute_type_integer",
"a11y_incorrect_aria_attribute_type_token",
"a11y_incorrect_aria_attribute_type_tokenlist",
"a11y_incorrect_aria_attribute_type_tristate",
"a11y_interactive_supports_focus",
"a11y_invalid_attribute",
"a11y_label_has_associated_control",
"a11y_media_has_caption",
"a11y_misplaced_role",
"a11y_misplaced_scope",
"a11y_missing_attribute",
"a11y_missing_content",
"a11y_mouse_events_have_key_events",
"a11y_no_abstract_role",
"a11y_no_interactive_element_to_noninteractive_role",
"a11y_no_noninteractive_element_interactions",
"a11y_no_noninteractive_element_to_interactive_role",
"a11y_no_noninteractive_tabindex",
"a11y_no_redundant_roles",
"a11y_no_static_element_interactions",
"a11y_positive_tabindex",
"a11y_role_has_required_aria_props",
"a11y_role_supports_aria_props",
"a11y_role_supports_aria_props_implicit",
"a11y_unknown_aria_attribute",
"a11y_unknown_role",
"legacy_code",
"unknown_code",
"options_deprecated_accessors",
"options_deprecated_immutable",
"options_missing_custom_element",
"options_removed_enable_sourcemap",
"options_removed_hydratable",
"options_removed_loop_guard_timeout",
"options_renamed_ssr_dom",
"export_let_unused",
"legacy_component_creation",
"non_reactive_update",
"perf_avoid_inline_class",
"perf_avoid_nested_class",
"reactive_declaration_invalid_placement",
"reactive_declaration_module_script_dependency",
"state_referenced_locally",
"store_rune_conflict",
"css_unused_selector",
"attribute_avoid_is",
"attribute_global_event_reference",
"attribute_illegal_colon",
"attribute_invalid_property_name",
"attribute_quoted",
"bind_invalid_each_rest",
"block_empty",
"component_name_lowercase",
"element_invalid_self_closing_tag",
"event_directive_deprecated",
"node_invalid_placement_ssr",
"script_context_deprecated",
"script_unknown_attribute",
"slot_element_deprecated",
"svelte_component_deprecated",
"svelte_element_invalid_this",
"svelte_self_deprecated"
'a11y_accesskey',
'a11y_aria_activedescendant_has_tabindex',
'a11y_aria_attributes',
'a11y_autocomplete_valid',
'a11y_autofocus',
'a11y_click_events_have_key_events',
'a11y_consider_explicit_label',
'a11y_distracting_elements',
'a11y_figcaption_index',
'a11y_figcaption_parent',
'a11y_hidden',
'a11y_img_redundant_alt',
'a11y_incorrect_aria_attribute_type',
'a11y_incorrect_aria_attribute_type_boolean',
'a11y_incorrect_aria_attribute_type_id',
'a11y_incorrect_aria_attribute_type_idlist',
'a11y_incorrect_aria_attribute_type_integer',
'a11y_incorrect_aria_attribute_type_token',
'a11y_incorrect_aria_attribute_type_tokenlist',
'a11y_incorrect_aria_attribute_type_tristate',
'a11y_interactive_supports_focus',
'a11y_invalid_attribute',
'a11y_label_has_associated_control',
'a11y_media_has_caption',
'a11y_misplaced_role',
'a11y_misplaced_scope',
'a11y_missing_attribute',
'a11y_missing_content',
'a11y_mouse_events_have_key_events',
'a11y_no_abstract_role',
'a11y_no_interactive_element_to_noninteractive_role',
'a11y_no_noninteractive_element_interactions',
'a11y_no_noninteractive_element_to_interactive_role',
'a11y_no_noninteractive_tabindex',
'a11y_no_redundant_roles',
'a11y_no_static_element_interactions',
'a11y_positive_tabindex',
'a11y_role_has_required_aria_props',
'a11y_role_supports_aria_props',
'a11y_role_supports_aria_props_implicit',
'a11y_unknown_aria_attribute',
'a11y_unknown_role',
'legacy_code',
'unknown_code',
'options_deprecated_accessors',
'options_deprecated_immutable',
'options_missing_custom_element',
'options_removed_enable_sourcemap',
'options_removed_hydratable',
'options_removed_loop_guard_timeout',
'options_renamed_ssr_dom',
'export_let_unused',
'legacy_component_creation',
'non_reactive_update',
'perf_avoid_inline_class',
'perf_avoid_nested_class',
'reactive_declaration_invalid_placement',
'reactive_declaration_module_script_dependency',
'state_referenced_locally',
'store_rune_conflict',
'css_unused_selector',
'attribute_avoid_is',
'attribute_global_event_reference',
'attribute_illegal_colon',
'attribute_invalid_property_name',
'attribute_quoted',
'bind_invalid_each_rest',
'block_empty',
'component_name_lowercase',
'element_invalid_self_closing_tag',
'event_directive_deprecated',
'node_invalid_placement_ssr',
'script_context_deprecated',
'script_unknown_attribute',
'slot_element_deprecated',
'svelte_component_deprecated',
'svelte_element_invalid_this',
'svelte_self_deprecated'
];
/**
@ -129,7 +129,7 @@ export const codes = [
* @param {null | NodeLike} node
*/
export function a11y_accesskey(node) {
w(node, "a11y_accesskey", `Avoid using accesskey\nhttps://svelte.dev/e/a11y_accesskey`);
w(node, 'a11y_accesskey', `Avoid using accesskey\nhttps://svelte.dev/e/a11y_accesskey`);
}
/**
@ -137,7 +137,7 @@ export function a11y_accesskey(node) {
* @param {null | NodeLike} node
*/
export function a11y_aria_activedescendant_has_tabindex(node) {
w(node, "a11y_aria_activedescendant_has_tabindex", `An element with an aria-activedescendant attribute should have a tabindex value\nhttps://svelte.dev/e/a11y_aria_activedescendant_has_tabindex`);
w(node, 'a11y_aria_activedescendant_has_tabindex', `An element with an aria-activedescendant attribute should have a tabindex value\nhttps://svelte.dev/e/a11y_aria_activedescendant_has_tabindex`);
}
/**
@ -146,7 +146,7 @@ export function a11y_aria_activedescendant_has_tabindex(node) {
* @param {string} name
*/
export function a11y_aria_attributes(node, name) {
w(node, "a11y_aria_attributes", `\`<${name}>\` should not have aria-* attributes\nhttps://svelte.dev/e/a11y_aria_attributes`);
w(node, 'a11y_aria_attributes', `\`<${name}>\` should not have aria-* attributes\nhttps://svelte.dev/e/a11y_aria_attributes`);
}
/**
@ -156,7 +156,7 @@ export function a11y_aria_attributes(node, name) {
* @param {string} type
*/
export function a11y_autocomplete_valid(node, value, type) {
w(node, "a11y_autocomplete_valid", `'${value}' is an invalid value for 'autocomplete' on \`<input type="${type}">\`\nhttps://svelte.dev/e/a11y_autocomplete_valid`);
w(node, 'a11y_autocomplete_valid', `'${value}' is an invalid value for 'autocomplete' on \`<input type="${type}">\`\nhttps://svelte.dev/e/a11y_autocomplete_valid`);
}
/**
@ -164,15 +164,15 @@ export function a11y_autocomplete_valid(node, value, type) {
* @param {null | NodeLike} node
*/
export function a11y_autofocus(node) {
w(node, "a11y_autofocus", `Avoid using autofocus\nhttps://svelte.dev/e/a11y_autofocus`);
w(node, 'a11y_autofocus', `Avoid using autofocus\nhttps://svelte.dev/e/a11y_autofocus`);
}
/**
* Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `<button type="button">` or `<a>` might be more appropriate. See https://svelte.dev/docs/accessibility-warnings#a11y-click-events-have-key-events for more details
* Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `<button type="button">` or `<a>` might be more appropriate
* @param {null | NodeLike} node
*/
export function a11y_click_events_have_key_events(node) {
w(node, "a11y_click_events_have_key_events", `Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as \`<button type="button">\` or \`<a>\` might be more appropriate. See https://svelte.dev/docs/accessibility-warnings#a11y-click-events-have-key-events for more details\nhttps://svelte.dev/e/a11y_click_events_have_key_events`);
w(node, 'a11y_click_events_have_key_events', `Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as \`<button type="button">\` or \`<a>\` might be more appropriate\nhttps://svelte.dev/e/a11y_click_events_have_key_events`);
}
/**
@ -180,7 +180,7 @@ export function a11y_click_events_have_key_events(node) {
* @param {null | NodeLike} node
*/
export function a11y_consider_explicit_label(node) {
w(node, "a11y_consider_explicit_label", `Buttons and links should either contain text or have an \`aria-label\` or \`aria-labelledby\` attribute\nhttps://svelte.dev/e/a11y_consider_explicit_label`);
w(node, 'a11y_consider_explicit_label', `Buttons and links should either contain text or have an \`aria-label\` or \`aria-labelledby\` attribute\nhttps://svelte.dev/e/a11y_consider_explicit_label`);
}
/**
@ -189,7 +189,7 @@ export function a11y_consider_explicit_label(node) {
* @param {string} name
*/
export function a11y_distracting_elements(node, name) {
w(node, "a11y_distracting_elements", `Avoid \`<${name}>\` elements\nhttps://svelte.dev/e/a11y_distracting_elements`);
w(node, 'a11y_distracting_elements', `Avoid \`<${name}>\` elements\nhttps://svelte.dev/e/a11y_distracting_elements`);
}
/**
@ -197,7 +197,7 @@ export function a11y_distracting_elements(node, name) {
* @param {null | NodeLike} node
*/
export function a11y_figcaption_index(node) {
w(node, "a11y_figcaption_index", `\`<figcaption>\` must be first or last child of \`<figure>\`\nhttps://svelte.dev/e/a11y_figcaption_index`);
w(node, 'a11y_figcaption_index', `\`<figcaption>\` must be first or last child of \`<figure>\`\nhttps://svelte.dev/e/a11y_figcaption_index`);
}
/**
@ -205,7 +205,7 @@ export function a11y_figcaption_index(node) {
* @param {null | NodeLike} node
*/
export function a11y_figcaption_parent(node) {
w(node, "a11y_figcaption_parent", `\`<figcaption>\` must be an immediate child of \`<figure>\`\nhttps://svelte.dev/e/a11y_figcaption_parent`);
w(node, 'a11y_figcaption_parent', `\`<figcaption>\` must be an immediate child of \`<figure>\`\nhttps://svelte.dev/e/a11y_figcaption_parent`);
}
/**
@ -214,7 +214,7 @@ export function a11y_figcaption_parent(node) {
* @param {string} name
*/
export function a11y_hidden(node, name) {
w(node, "a11y_hidden", `\`<${name}>\` element should not be hidden\nhttps://svelte.dev/e/a11y_hidden`);
w(node, 'a11y_hidden', `\`<${name}>\` element should not be hidden\nhttps://svelte.dev/e/a11y_hidden`);
}
/**
@ -222,7 +222,7 @@ export function a11y_hidden(node, name) {
* @param {null | NodeLike} node
*/
export function a11y_img_redundant_alt(node) {
w(node, "a11y_img_redundant_alt", `Screenreaders already announce \`<img>\` elements as an image\nhttps://svelte.dev/e/a11y_img_redundant_alt`);
w(node, 'a11y_img_redundant_alt', `Screenreaders already announce \`<img>\` elements as an image\nhttps://svelte.dev/e/a11y_img_redundant_alt`);
}
/**
@ -232,7 +232,7 @@ export function a11y_img_redundant_alt(node) {
* @param {string} type
*/
export function a11y_incorrect_aria_attribute_type(node, attribute, type) {
w(node, "a11y_incorrect_aria_attribute_type", `The value of '${attribute}' must be a ${type}\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type`);
w(node, 'a11y_incorrect_aria_attribute_type', `The value of '${attribute}' must be a ${type}\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type`);
}
/**
@ -241,7 +241,7 @@ export function a11y_incorrect_aria_attribute_type(node, attribute, type) {
* @param {string} attribute
*/
export function a11y_incorrect_aria_attribute_type_boolean(node, attribute) {
w(node, "a11y_incorrect_aria_attribute_type_boolean", `The value of '${attribute}' must be either 'true' or 'false'. It cannot be empty\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_boolean`);
w(node, 'a11y_incorrect_aria_attribute_type_boolean', `The value of '${attribute}' must be either 'true' or 'false'. It cannot be empty\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_boolean`);
}
/**
@ -250,7 +250,7 @@ export function a11y_incorrect_aria_attribute_type_boolean(node, attribute) {
* @param {string} attribute
*/
export function a11y_incorrect_aria_attribute_type_id(node, attribute) {
w(node, "a11y_incorrect_aria_attribute_type_id", `The value of '${attribute}' must be a string that represents a DOM element ID\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_id`);
w(node, 'a11y_incorrect_aria_attribute_type_id', `The value of '${attribute}' must be a string that represents a DOM element ID\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_id`);
}
/**
@ -259,7 +259,7 @@ export function a11y_incorrect_aria_attribute_type_id(node, attribute) {
* @param {string} attribute
*/
export function a11y_incorrect_aria_attribute_type_idlist(node, attribute) {
w(node, "a11y_incorrect_aria_attribute_type_idlist", `The value of '${attribute}' must be a space-separated list of strings that represent DOM element IDs\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_idlist`);
w(node, 'a11y_incorrect_aria_attribute_type_idlist', `The value of '${attribute}' must be a space-separated list of strings that represent DOM element IDs\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_idlist`);
}
/**
@ -268,7 +268,7 @@ export function a11y_incorrect_aria_attribute_type_idlist(node, attribute) {
* @param {string} attribute
*/
export function a11y_incorrect_aria_attribute_type_integer(node, attribute) {
w(node, "a11y_incorrect_aria_attribute_type_integer", `The value of '${attribute}' must be an integer\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_integer`);
w(node, 'a11y_incorrect_aria_attribute_type_integer', `The value of '${attribute}' must be an integer\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_integer`);
}
/**
@ -278,7 +278,7 @@ export function a11y_incorrect_aria_attribute_type_integer(node, attribute) {
* @param {string} values
*/
export function a11y_incorrect_aria_attribute_type_token(node, attribute, values) {
w(node, "a11y_incorrect_aria_attribute_type_token", `The value of '${attribute}' must be exactly one of ${values}\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_token`);
w(node, 'a11y_incorrect_aria_attribute_type_token', `The value of '${attribute}' must be exactly one of ${values}\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_token`);
}
/**
@ -288,7 +288,7 @@ export function a11y_incorrect_aria_attribute_type_token(node, attribute, values
* @param {string} values
*/
export function a11y_incorrect_aria_attribute_type_tokenlist(node, attribute, values) {
w(node, "a11y_incorrect_aria_attribute_type_tokenlist", `The value of '${attribute}' must be a space-separated list of one or more of ${values}\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_tokenlist`);
w(node, 'a11y_incorrect_aria_attribute_type_tokenlist', `The value of '${attribute}' must be a space-separated list of one or more of ${values}\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_tokenlist`);
}
/**
@ -297,7 +297,7 @@ export function a11y_incorrect_aria_attribute_type_tokenlist(node, attribute, va
* @param {string} attribute
*/
export function a11y_incorrect_aria_attribute_type_tristate(node, attribute) {
w(node, "a11y_incorrect_aria_attribute_type_tristate", `The value of '${attribute}' must be exactly one of true, false, or mixed\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_tristate`);
w(node, 'a11y_incorrect_aria_attribute_type_tristate', `The value of '${attribute}' must be exactly one of true, false, or mixed\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_tristate`);
}
/**
@ -306,7 +306,7 @@ export function a11y_incorrect_aria_attribute_type_tristate(node, attribute) {
* @param {string} role
*/
export function a11y_interactive_supports_focus(node, role) {
w(node, "a11y_interactive_supports_focus", `Elements with the '${role}' interactive role must have a tabindex value\nhttps://svelte.dev/e/a11y_interactive_supports_focus`);
w(node, 'a11y_interactive_supports_focus', `Elements with the '${role}' interactive role must have a tabindex value\nhttps://svelte.dev/e/a11y_interactive_supports_focus`);
}
/**
@ -316,7 +316,7 @@ export function a11y_interactive_supports_focus(node, role) {
* @param {string} href_attribute
*/
export function a11y_invalid_attribute(node, href_value, href_attribute) {
w(node, "a11y_invalid_attribute", `'${href_value}' is not a valid ${href_attribute} attribute\nhttps://svelte.dev/e/a11y_invalid_attribute`);
w(node, 'a11y_invalid_attribute', `'${href_value}' is not a valid ${href_attribute} attribute\nhttps://svelte.dev/e/a11y_invalid_attribute`);
}
/**
@ -324,7 +324,7 @@ export function a11y_invalid_attribute(node, href_value, href_attribute) {
* @param {null | NodeLike} node
*/
export function a11y_label_has_associated_control(node) {
w(node, "a11y_label_has_associated_control", `A form label must be associated with a control\nhttps://svelte.dev/e/a11y_label_has_associated_control`);
w(node, 'a11y_label_has_associated_control', `A form label must be associated with a control\nhttps://svelte.dev/e/a11y_label_has_associated_control`);
}
/**
@ -332,7 +332,7 @@ export function a11y_label_has_associated_control(node) {
* @param {null | NodeLike} node
*/
export function a11y_media_has_caption(node) {
w(node, "a11y_media_has_caption", `\`<video>\` elements must have a \`<track kind="captions">\`\nhttps://svelte.dev/e/a11y_media_has_caption`);
w(node, 'a11y_media_has_caption', `\`<video>\` elements must have a \`<track kind="captions">\`\nhttps://svelte.dev/e/a11y_media_has_caption`);
}
/**
@ -341,7 +341,7 @@ export function a11y_media_has_caption(node) {
* @param {string} name
*/
export function a11y_misplaced_role(node, name) {
w(node, "a11y_misplaced_role", `\`<${name}>\` should not have role attribute\nhttps://svelte.dev/e/a11y_misplaced_role`);
w(node, 'a11y_misplaced_role', `\`<${name}>\` should not have role attribute\nhttps://svelte.dev/e/a11y_misplaced_role`);
}
/**
@ -349,7 +349,7 @@ export function a11y_misplaced_role(node, name) {
* @param {null | NodeLike} node
*/
export function a11y_misplaced_scope(node) {
w(node, "a11y_misplaced_scope", `The scope attribute should only be used with \`<th>\` elements\nhttps://svelte.dev/e/a11y_misplaced_scope`);
w(node, 'a11y_misplaced_scope', `The scope attribute should only be used with \`<th>\` elements\nhttps://svelte.dev/e/a11y_misplaced_scope`);
}
/**
@ -360,7 +360,7 @@ export function a11y_misplaced_scope(node) {
* @param {string} sequence
*/
export function a11y_missing_attribute(node, name, article, sequence) {
w(node, "a11y_missing_attribute", `\`<${name}>\` element should have ${article} ${sequence} attribute\nhttps://svelte.dev/e/a11y_missing_attribute`);
w(node, 'a11y_missing_attribute', `\`<${name}>\` element should have ${article} ${sequence} attribute\nhttps://svelte.dev/e/a11y_missing_attribute`);
}
/**
@ -369,7 +369,7 @@ export function a11y_missing_attribute(node, name, article, sequence) {
* @param {string} name
*/
export function a11y_missing_content(node, name) {
w(node, "a11y_missing_content", `\`<${name}>\` element should contain text\nhttps://svelte.dev/e/a11y_missing_content`);
w(node, 'a11y_missing_content', `\`<${name}>\` element should contain text\nhttps://svelte.dev/e/a11y_missing_content`);
}
/**
@ -379,7 +379,7 @@ export function a11y_missing_content(node, name) {
* @param {string} accompanied_by
*/
export function a11y_mouse_events_have_key_events(node, event, accompanied_by) {
w(node, "a11y_mouse_events_have_key_events", `'${event}' event must be accompanied by '${accompanied_by}' event\nhttps://svelte.dev/e/a11y_mouse_events_have_key_events`);
w(node, 'a11y_mouse_events_have_key_events', `'${event}' event must be accompanied by '${accompanied_by}' event\nhttps://svelte.dev/e/a11y_mouse_events_have_key_events`);
}
/**
@ -388,7 +388,7 @@ export function a11y_mouse_events_have_key_events(node, event, accompanied_by) {
* @param {string} role
*/
export function a11y_no_abstract_role(node, role) {
w(node, "a11y_no_abstract_role", `Abstract role '${role}' is forbidden\nhttps://svelte.dev/e/a11y_no_abstract_role`);
w(node, 'a11y_no_abstract_role', `Abstract role '${role}' is forbidden\nhttps://svelte.dev/e/a11y_no_abstract_role`);
}
/**
@ -398,7 +398,7 @@ export function a11y_no_abstract_role(node, role) {
* @param {string} role
*/
export function a11y_no_interactive_element_to_noninteractive_role(node, element, role) {
w(node, "a11y_no_interactive_element_to_noninteractive_role", `\`<${element}>\` cannot have role '${role}'\nhttps://svelte.dev/e/a11y_no_interactive_element_to_noninteractive_role`);
w(node, 'a11y_no_interactive_element_to_noninteractive_role', `\`<${element}>\` cannot have role '${role}'\nhttps://svelte.dev/e/a11y_no_interactive_element_to_noninteractive_role`);
}
/**
@ -407,7 +407,7 @@ export function a11y_no_interactive_element_to_noninteractive_role(node, element
* @param {string} element
*/
export function a11y_no_noninteractive_element_interactions(node, element) {
w(node, "a11y_no_noninteractive_element_interactions", `Non-interactive element \`<${element}>\` should not be assigned mouse or keyboard event listeners\nhttps://svelte.dev/e/a11y_no_noninteractive_element_interactions`);
w(node, 'a11y_no_noninteractive_element_interactions', `Non-interactive element \`<${element}>\` should not be assigned mouse or keyboard event listeners\nhttps://svelte.dev/e/a11y_no_noninteractive_element_interactions`);
}
/**
@ -417,7 +417,7 @@ export function a11y_no_noninteractive_element_interactions(node, element) {
* @param {string} role
*/
export function a11y_no_noninteractive_element_to_interactive_role(node, element, role) {
w(node, "a11y_no_noninteractive_element_to_interactive_role", `Non-interactive element \`<${element}>\` cannot have interactive role '${role}'\nhttps://svelte.dev/e/a11y_no_noninteractive_element_to_interactive_role`);
w(node, 'a11y_no_noninteractive_element_to_interactive_role', `Non-interactive element \`<${element}>\` cannot have interactive role '${role}'\nhttps://svelte.dev/e/a11y_no_noninteractive_element_to_interactive_role`);
}
/**
@ -425,7 +425,7 @@ export function a11y_no_noninteractive_element_to_interactive_role(node, element
* @param {null | NodeLike} node
*/
export function a11y_no_noninteractive_tabindex(node) {
w(node, "a11y_no_noninteractive_tabindex", `noninteractive element cannot have nonnegative tabIndex value\nhttps://svelte.dev/e/a11y_no_noninteractive_tabindex`);
w(node, 'a11y_no_noninteractive_tabindex', `noninteractive element cannot have nonnegative tabIndex value\nhttps://svelte.dev/e/a11y_no_noninteractive_tabindex`);
}
/**
@ -434,7 +434,7 @@ export function a11y_no_noninteractive_tabindex(node) {
* @param {string} role
*/
export function a11y_no_redundant_roles(node, role) {
w(node, "a11y_no_redundant_roles", `Redundant role '${role}'\nhttps://svelte.dev/e/a11y_no_redundant_roles`);
w(node, 'a11y_no_redundant_roles', `Redundant role '${role}'\nhttps://svelte.dev/e/a11y_no_redundant_roles`);
}
/**
@ -444,7 +444,7 @@ export function a11y_no_redundant_roles(node, role) {
* @param {string} handler
*/
export function a11y_no_static_element_interactions(node, element, handler) {
w(node, "a11y_no_static_element_interactions", `\`<${element}>\` with a ${handler} handler must have an ARIA role\nhttps://svelte.dev/e/a11y_no_static_element_interactions`);
w(node, 'a11y_no_static_element_interactions', `\`<${element}>\` with a ${handler} handler must have an ARIA role\nhttps://svelte.dev/e/a11y_no_static_element_interactions`);
}
/**
@ -452,7 +452,7 @@ export function a11y_no_static_element_interactions(node, element, handler) {
* @param {null | NodeLike} node
*/
export function a11y_positive_tabindex(node) {
w(node, "a11y_positive_tabindex", `Avoid tabindex values above zero\nhttps://svelte.dev/e/a11y_positive_tabindex`);
w(node, 'a11y_positive_tabindex', `Avoid tabindex values above zero\nhttps://svelte.dev/e/a11y_positive_tabindex`);
}
/**
@ -462,7 +462,7 @@ export function a11y_positive_tabindex(node) {
* @param {string} props
*/
export function a11y_role_has_required_aria_props(node, role, props) {
w(node, "a11y_role_has_required_aria_props", `Elements with the ARIA role "${role}" must have the following attributes defined: ${props}\nhttps://svelte.dev/e/a11y_role_has_required_aria_props`);
w(node, 'a11y_role_has_required_aria_props', `Elements with the ARIA role "${role}" must have the following attributes defined: ${props}\nhttps://svelte.dev/e/a11y_role_has_required_aria_props`);
}
/**
@ -472,7 +472,7 @@ export function a11y_role_has_required_aria_props(node, role, props) {
* @param {string} role
*/
export function a11y_role_supports_aria_props(node, attribute, role) {
w(node, "a11y_role_supports_aria_props", `The attribute '${attribute}' is not supported by the role '${role}'\nhttps://svelte.dev/e/a11y_role_supports_aria_props`);
w(node, 'a11y_role_supports_aria_props', `The attribute '${attribute}' is not supported by the role '${role}'\nhttps://svelte.dev/e/a11y_role_supports_aria_props`);
}
/**
@ -483,7 +483,7 @@ export function a11y_role_supports_aria_props(node, attribute, role) {
* @param {string} name
*/
export function a11y_role_supports_aria_props_implicit(node, attribute, role, name) {
w(node, "a11y_role_supports_aria_props_implicit", `The attribute '${attribute}' is not supported by the role '${role}'. This role is implicit on the element \`<${name}>\`\nhttps://svelte.dev/e/a11y_role_supports_aria_props_implicit`);
w(node, 'a11y_role_supports_aria_props_implicit', `The attribute '${attribute}' is not supported by the role '${role}'. This role is implicit on the element \`<${name}>\`\nhttps://svelte.dev/e/a11y_role_supports_aria_props_implicit`);
}
/**
@ -493,7 +493,7 @@ export function a11y_role_supports_aria_props_implicit(node, attribute, role, na
* @param {string | undefined | null} [suggestion]
*/
export function a11y_unknown_aria_attribute(node, attribute, suggestion) {
w(node, "a11y_unknown_aria_attribute", `${suggestion ? `Unknown aria attribute 'aria-${attribute}'. Did you mean '${suggestion}'?` : `Unknown aria attribute 'aria-${attribute}'`}\nhttps://svelte.dev/e/a11y_unknown_aria_attribute`);
w(node, 'a11y_unknown_aria_attribute', `${suggestion ? `Unknown aria attribute 'aria-${attribute}'. Did you mean '${suggestion}'?` : `Unknown aria attribute 'aria-${attribute}'`}\nhttps://svelte.dev/e/a11y_unknown_aria_attribute`);
}
/**
@ -503,7 +503,7 @@ export function a11y_unknown_aria_attribute(node, attribute, suggestion) {
* @param {string | undefined | null} [suggestion]
*/
export function a11y_unknown_role(node, role, suggestion) {
w(node, "a11y_unknown_role", `${suggestion ? `Unknown role '${role}'. Did you mean '${suggestion}'?` : `Unknown role '${role}'`}\nhttps://svelte.dev/e/a11y_unknown_role`);
w(node, 'a11y_unknown_role', `${suggestion ? `Unknown role '${role}'. Did you mean '${suggestion}'?` : `Unknown role '${role}'`}\nhttps://svelte.dev/e/a11y_unknown_role`);
}
/**
@ -513,7 +513,7 @@ export function a11y_unknown_role(node, role, suggestion) {
* @param {string} suggestion
*/
export function legacy_code(node, code, suggestion) {
w(node, "legacy_code", `\`${code}\` is no longer valid — please use \`${suggestion}\` instead\nhttps://svelte.dev/e/legacy_code`);
w(node, 'legacy_code', `\`${code}\` is no longer valid — please use \`${suggestion}\` instead\nhttps://svelte.dev/e/legacy_code`);
}
/**
@ -523,7 +523,7 @@ export function legacy_code(node, code, suggestion) {
* @param {string | undefined | null} [suggestion]
*/
export function unknown_code(node, code, suggestion) {
w(node, "unknown_code", `${suggestion ? `\`${code}\` is not a recognised code (did you mean \`${suggestion}\`?)` : `\`${code}\` is not a recognised code`}\nhttps://svelte.dev/e/unknown_code`);
w(node, 'unknown_code', `${suggestion ? `\`${code}\` is not a recognised code (did you mean \`${suggestion}\`?)` : `\`${code}\` is not a recognised code`}\nhttps://svelte.dev/e/unknown_code`);
}
/**
@ -531,7 +531,7 @@ export function unknown_code(node, code, suggestion) {
* @param {null | NodeLike} node
*/
export function options_deprecated_accessors(node) {
w(node, "options_deprecated_accessors", `The \`accessors\` option has been deprecated. It will have no effect in runes mode\nhttps://svelte.dev/e/options_deprecated_accessors`);
w(node, 'options_deprecated_accessors', `The \`accessors\` option has been deprecated. It will have no effect in runes mode\nhttps://svelte.dev/e/options_deprecated_accessors`);
}
/**
@ -539,7 +539,7 @@ export function options_deprecated_accessors(node) {
* @param {null | NodeLike} node
*/
export function options_deprecated_immutable(node) {
w(node, "options_deprecated_immutable", `The \`immutable\` option has been deprecated. It will have no effect in runes mode\nhttps://svelte.dev/e/options_deprecated_immutable`);
w(node, 'options_deprecated_immutable', `The \`immutable\` option has been deprecated. It will have no effect in runes mode\nhttps://svelte.dev/e/options_deprecated_immutable`);
}
/**
@ -547,7 +547,7 @@ export function options_deprecated_immutable(node) {
* @param {null | NodeLike} node
*/
export function options_missing_custom_element(node) {
w(node, "options_missing_custom_element", `The \`customElement\` option is used when generating a custom element. Did you forget the \`customElement: true\` compile option?\nhttps://svelte.dev/e/options_missing_custom_element`);
w(node, 'options_missing_custom_element', `The \`customElement\` option is used when generating a custom element. Did you forget the \`customElement: true\` compile option?\nhttps://svelte.dev/e/options_missing_custom_element`);
}
/**
@ -555,7 +555,7 @@ export function options_missing_custom_element(node) {
* @param {null | NodeLike} node
*/
export function options_removed_enable_sourcemap(node) {
w(node, "options_removed_enable_sourcemap", `The \`enableSourcemap\` option has been removed. Source maps are always generated now, and tooling can choose to ignore them\nhttps://svelte.dev/e/options_removed_enable_sourcemap`);
w(node, 'options_removed_enable_sourcemap', `The \`enableSourcemap\` option has been removed. Source maps are always generated now, and tooling can choose to ignore them\nhttps://svelte.dev/e/options_removed_enable_sourcemap`);
}
/**
@ -563,7 +563,7 @@ export function options_removed_enable_sourcemap(node) {
* @param {null | NodeLike} node
*/
export function options_removed_hydratable(node) {
w(node, "options_removed_hydratable", `The \`hydratable\` option has been removed. Svelte components are always hydratable now\nhttps://svelte.dev/e/options_removed_hydratable`);
w(node, 'options_removed_hydratable', `The \`hydratable\` option has been removed. Svelte components are always hydratable now\nhttps://svelte.dev/e/options_removed_hydratable`);
}
/**
@ -571,7 +571,7 @@ export function options_removed_hydratable(node) {
* @param {null | NodeLike} node
*/
export function options_removed_loop_guard_timeout(node) {
w(node, "options_removed_loop_guard_timeout", `The \`loopGuardTimeout\` option has been removed\nhttps://svelte.dev/e/options_removed_loop_guard_timeout`);
w(node, 'options_removed_loop_guard_timeout', `The \`loopGuardTimeout\` option has been removed\nhttps://svelte.dev/e/options_removed_loop_guard_timeout`);
}
/**
@ -579,7 +579,7 @@ export function options_removed_loop_guard_timeout(node) {
* @param {null | NodeLike} node
*/
export function options_renamed_ssr_dom(node) {
w(node, "options_renamed_ssr_dom", `\`generate: "dom"\` and \`generate: "ssr"\` options have been renamed to "client" and "server" respectively\nhttps://svelte.dev/e/options_renamed_ssr_dom`);
w(node, 'options_renamed_ssr_dom', `\`generate: "dom"\` and \`generate: "ssr"\` options have been renamed to "client" and "server" respectively\nhttps://svelte.dev/e/options_renamed_ssr_dom`);
}
/**
@ -588,7 +588,7 @@ export function options_renamed_ssr_dom(node) {
* @param {string} name
*/
export function export_let_unused(node, name) {
w(node, "export_let_unused", `Component has unused export property '${name}'. If it is for external reference only, please consider using \`export const ${name}\`\nhttps://svelte.dev/e/export_let_unused`);
w(node, 'export_let_unused', `Component has unused export property '${name}'. If it is for external reference only, please consider using \`export const ${name}\`\nhttps://svelte.dev/e/export_let_unused`);
}
/**
@ -596,7 +596,7 @@ export function export_let_unused(node, name) {
* @param {null | NodeLike} node
*/
export function legacy_component_creation(node) {
w(node, "legacy_component_creation", `Svelte 5 components are no longer classes. Instantiate them using \`mount\` or \`hydrate\` (imported from 'svelte') instead.\nhttps://svelte.dev/e/legacy_component_creation`);
w(node, 'legacy_component_creation', `Svelte 5 components are no longer classes. Instantiate them using \`mount\` or \`hydrate\` (imported from 'svelte') instead.\nhttps://svelte.dev/e/legacy_component_creation`);
}
/**
@ -605,7 +605,7 @@ export function legacy_component_creation(node) {
* @param {string} name
*/
export function non_reactive_update(node, name) {
w(node, "non_reactive_update", `\`${name}\` is updated, but is not declared with \`$state(...)\`. Changing its value will not correctly trigger updates\nhttps://svelte.dev/e/non_reactive_update`);
w(node, 'non_reactive_update', `\`${name}\` is updated, but is not declared with \`$state(...)\`. Changing its value will not correctly trigger updates\nhttps://svelte.dev/e/non_reactive_update`);
}
/**
@ -613,7 +613,7 @@ export function non_reactive_update(node, name) {
* @param {null | NodeLike} node
*/
export function perf_avoid_inline_class(node) {
w(node, "perf_avoid_inline_class", `Avoid 'new class' — instead, declare the class at the top level scope\nhttps://svelte.dev/e/perf_avoid_inline_class`);
w(node, 'perf_avoid_inline_class', `Avoid 'new class' — instead, declare the class at the top level scope\nhttps://svelte.dev/e/perf_avoid_inline_class`);
}
/**
@ -621,7 +621,7 @@ export function perf_avoid_inline_class(node) {
* @param {null | NodeLike} node
*/
export function perf_avoid_nested_class(node) {
w(node, "perf_avoid_nested_class", `Avoid declaring classes below the top level scope\nhttps://svelte.dev/e/perf_avoid_nested_class`);
w(node, 'perf_avoid_nested_class', `Avoid declaring classes below the top level scope\nhttps://svelte.dev/e/perf_avoid_nested_class`);
}
/**
@ -629,7 +629,7 @@ export function perf_avoid_nested_class(node) {
* @param {null | NodeLike} node
*/
export function reactive_declaration_invalid_placement(node) {
w(node, "reactive_declaration_invalid_placement", `Reactive declarations only exist at the top level of the instance script\nhttps://svelte.dev/e/reactive_declaration_invalid_placement`);
w(node, 'reactive_declaration_invalid_placement', `Reactive declarations only exist at the top level of the instance script\nhttps://svelte.dev/e/reactive_declaration_invalid_placement`);
}
/**
@ -637,7 +637,7 @@ export function reactive_declaration_invalid_placement(node) {
* @param {null | NodeLike} node
*/
export function reactive_declaration_module_script_dependency(node) {
w(node, "reactive_declaration_module_script_dependency", `Reassignments of module-level declarations will not cause reactive statements to update\nhttps://svelte.dev/e/reactive_declaration_module_script_dependency`);
w(node, 'reactive_declaration_module_script_dependency', `Reassignments of module-level declarations will not cause reactive statements to update\nhttps://svelte.dev/e/reactive_declaration_module_script_dependency`);
}
/**
@ -645,7 +645,7 @@ export function reactive_declaration_module_script_dependency(node) {
* @param {null | NodeLike} node
*/
export function state_referenced_locally(node) {
w(node, "state_referenced_locally", `State referenced in its own scope will never update. Did you mean to reference it inside a closure?\nhttps://svelte.dev/e/state_referenced_locally`);
w(node, 'state_referenced_locally', `State referenced in its own scope will never update. Did you mean to reference it inside a closure?\nhttps://svelte.dev/e/state_referenced_locally`);
}
/**
@ -654,7 +654,7 @@ export function state_referenced_locally(node) {
* @param {string} name
*/
export function store_rune_conflict(node, name) {
w(node, "store_rune_conflict", `It looks like you're using the \`$${name}\` rune, but there is a local binding called \`${name}\`. Referencing a local variable with a \`$\` prefix will create a store subscription. Please rename \`${name}\` to avoid the ambiguity\nhttps://svelte.dev/e/store_rune_conflict`);
w(node, 'store_rune_conflict', `It looks like you're using the \`$${name}\` rune, but there is a local binding called \`${name}\`. Referencing a local variable with a \`$\` prefix will create a store subscription. Please rename \`${name}\` to avoid the ambiguity\nhttps://svelte.dev/e/store_rune_conflict`);
}
/**
@ -663,7 +663,7 @@ export function store_rune_conflict(node, name) {
* @param {string} name
*/
export function css_unused_selector(node, name) {
w(node, "css_unused_selector", `Unused CSS selector "${name}"\nhttps://svelte.dev/e/css_unused_selector`);
w(node, 'css_unused_selector', `Unused CSS selector "${name}"\nhttps://svelte.dev/e/css_unused_selector`);
}
/**
@ -671,7 +671,7 @@ export function css_unused_selector(node, name) {
* @param {null | NodeLike} node
*/
export function attribute_avoid_is(node) {
w(node, "attribute_avoid_is", `The "is" attribute is not supported cross-browser and should be avoided\nhttps://svelte.dev/e/attribute_avoid_is`);
w(node, 'attribute_avoid_is', `The "is" attribute is not supported cross-browser and should be avoided\nhttps://svelte.dev/e/attribute_avoid_is`);
}
/**
@ -680,7 +680,7 @@ export function attribute_avoid_is(node) {
* @param {string} name
*/
export function attribute_global_event_reference(node, name) {
w(node, "attribute_global_event_reference", `You are referencing \`globalThis.${name}\`. Did you forget to declare a variable with that name?\nhttps://svelte.dev/e/attribute_global_event_reference`);
w(node, 'attribute_global_event_reference', `You are referencing \`globalThis.${name}\`. Did you forget to declare a variable with that name?\nhttps://svelte.dev/e/attribute_global_event_reference`);
}
/**
@ -688,7 +688,7 @@ export function attribute_global_event_reference(node, name) {
* @param {null | NodeLike} node
*/
export function attribute_illegal_colon(node) {
w(node, "attribute_illegal_colon", `Attributes should not contain ':' characters to prevent ambiguity with Svelte directives\nhttps://svelte.dev/e/attribute_illegal_colon`);
w(node, 'attribute_illegal_colon', `Attributes should not contain ':' characters to prevent ambiguity with Svelte directives\nhttps://svelte.dev/e/attribute_illegal_colon`);
}
/**
@ -698,7 +698,7 @@ export function attribute_illegal_colon(node) {
* @param {string} right
*/
export function attribute_invalid_property_name(node, wrong, right) {
w(node, "attribute_invalid_property_name", `'${wrong}' is not a valid HTML attribute. Did you mean '${right}'?\nhttps://svelte.dev/e/attribute_invalid_property_name`);
w(node, 'attribute_invalid_property_name', `'${wrong}' is not a valid HTML attribute. Did you mean '${right}'?\nhttps://svelte.dev/e/attribute_invalid_property_name`);
}
/**
@ -706,7 +706,7 @@ export function attribute_invalid_property_name(node, wrong, right) {
* @param {null | NodeLike} node
*/
export function attribute_quoted(node) {
w(node, "attribute_quoted", `Quoted attributes on components and custom elements will be stringified in a future version of Svelte. If this isn't what you want, remove the quotes\nhttps://svelte.dev/e/attribute_quoted`);
w(node, 'attribute_quoted', `Quoted attributes on components and custom elements will be stringified in a future version of Svelte. If this isn't what you want, remove the quotes\nhttps://svelte.dev/e/attribute_quoted`);
}
/**
@ -715,7 +715,7 @@ export function attribute_quoted(node) {
* @param {string} name
*/
export function bind_invalid_each_rest(node, name) {
w(node, "bind_invalid_each_rest", `The rest operator (...) will create a new object and binding '${name}' with the original object will not work\nhttps://svelte.dev/e/bind_invalid_each_rest`);
w(node, 'bind_invalid_each_rest', `The rest operator (...) will create a new object and binding '${name}' with the original object will not work\nhttps://svelte.dev/e/bind_invalid_each_rest`);
}
/**
@ -723,7 +723,7 @@ export function bind_invalid_each_rest(node, name) {
* @param {null | NodeLike} node
*/
export function block_empty(node) {
w(node, "block_empty", `Empty block\nhttps://svelte.dev/e/block_empty`);
w(node, 'block_empty', `Empty block\nhttps://svelte.dev/e/block_empty`);
}
/**
@ -732,7 +732,7 @@ export function block_empty(node) {
* @param {string} name
*/
export function component_name_lowercase(node, name) {
w(node, "component_name_lowercase", `\`<${name}>\` will be treated as an HTML element unless it begins with a capital letter\nhttps://svelte.dev/e/component_name_lowercase`);
w(node, 'component_name_lowercase', `\`<${name}>\` will be treated as an HTML element unless it begins with a capital letter\nhttps://svelte.dev/e/component_name_lowercase`);
}
/**
@ -741,7 +741,7 @@ export function component_name_lowercase(node, name) {
* @param {string} name
*/
export function element_invalid_self_closing_tag(node, name) {
w(node, "element_invalid_self_closing_tag", `Self-closing HTML tags for non-void elements are ambiguous — use \`<${name} ...></${name}>\` rather than \`<${name} ... />\`\nhttps://svelte.dev/e/element_invalid_self_closing_tag`);
w(node, 'element_invalid_self_closing_tag', `Self-closing HTML tags for non-void elements are ambiguous — use \`<${name} ...></${name}>\` rather than \`<${name} ... />\`\nhttps://svelte.dev/e/element_invalid_self_closing_tag`);
}
/**
@ -750,7 +750,7 @@ export function element_invalid_self_closing_tag(node, name) {
* @param {string} name
*/
export function event_directive_deprecated(node, name) {
w(node, "event_directive_deprecated", `Using \`on:${name}\` to listen to the ${name} event is deprecated. Use the event attribute \`on${name}\` instead\nhttps://svelte.dev/e/event_directive_deprecated`);
w(node, 'event_directive_deprecated', `Using \`on:${name}\` to listen to the ${name} event is deprecated. Use the event attribute \`on${name}\` instead\nhttps://svelte.dev/e/event_directive_deprecated`);
}
/**
@ -759,7 +759,7 @@ export function event_directive_deprecated(node, name) {
* @param {string} message
*/
export function node_invalid_placement_ssr(node, message) {
w(node, "node_invalid_placement_ssr", `${message}. When rendering this component on the server, the resulting HTML will be modified by the browser (by moving, removing, or inserting elements), likely resulting in a \`hydration_mismatch\` warning\nhttps://svelte.dev/e/node_invalid_placement_ssr`);
w(node, 'node_invalid_placement_ssr', `${message}. When rendering this component on the server, the resulting HTML will be modified by the browser (by moving, removing, or inserting elements), likely resulting in a \`hydration_mismatch\` warning\nhttps://svelte.dev/e/node_invalid_placement_ssr`);
}
/**
@ -767,7 +767,7 @@ export function node_invalid_placement_ssr(node, message) {
* @param {null | NodeLike} node
*/
export function script_context_deprecated(node) {
w(node, "script_context_deprecated", `\`context="module"\` is deprecated, use the \`module\` attribute instead\nhttps://svelte.dev/e/script_context_deprecated`);
w(node, 'script_context_deprecated', `\`context="module"\` is deprecated, use the \`module\` attribute instead\nhttps://svelte.dev/e/script_context_deprecated`);
}
/**
@ -775,7 +775,7 @@ export function script_context_deprecated(node) {
* @param {null | NodeLike} node
*/
export function script_unknown_attribute(node) {
w(node, "script_unknown_attribute", `Unrecognized attribute — should be one of \`generics\`, \`lang\` or \`module\`. If this exists for a preprocessor, ensure that the preprocessor removes it\nhttps://svelte.dev/e/script_unknown_attribute`);
w(node, 'script_unknown_attribute', `Unrecognized attribute — should be one of \`generics\`, \`lang\` or \`module\`. If this exists for a preprocessor, ensure that the preprocessor removes it\nhttps://svelte.dev/e/script_unknown_attribute`);
}
/**
@ -783,7 +783,7 @@ export function script_unknown_attribute(node) {
* @param {null | NodeLike} node
*/
export function slot_element_deprecated(node) {
w(node, "slot_element_deprecated", `Using \`<slot>\` to render parent content is deprecated. Use \`{@render ...}\` tags instead\nhttps://svelte.dev/e/slot_element_deprecated`);
w(node, 'slot_element_deprecated', `Using \`<slot>\` to render parent content is deprecated. Use \`{@render ...}\` tags instead\nhttps://svelte.dev/e/slot_element_deprecated`);
}
/**
@ -791,7 +791,7 @@ export function slot_element_deprecated(node) {
* @param {null | NodeLike} node
*/
export function svelte_component_deprecated(node) {
w(node, "svelte_component_deprecated", `\`<svelte:component>\` is deprecated in runes mode — components are dynamic by default\nhttps://svelte.dev/e/svelte_component_deprecated`);
w(node, 'svelte_component_deprecated', `\`<svelte:component>\` is deprecated in runes mode — components are dynamic by default\nhttps://svelte.dev/e/svelte_component_deprecated`);
}
/**
@ -799,7 +799,7 @@ export function svelte_component_deprecated(node) {
* @param {null | NodeLike} node
*/
export function svelte_element_invalid_this(node) {
w(node, "svelte_element_invalid_this", `\`this\` should be an \`{expression}\`. Using a string attribute value will cause an error in future versions of Svelte\nhttps://svelte.dev/e/svelte_element_invalid_this`);
w(node, 'svelte_element_invalid_this', `\`this\` should be an \`{expression}\`. Using a string attribute value will cause an error in future versions of Svelte\nhttps://svelte.dev/e/svelte_element_invalid_this`);
}
/**
@ -809,5 +809,5 @@ export function svelte_element_invalid_this(node) {
* @param {string} basename
*/
export function svelte_self_deprecated(node, name, basename) {
w(node, "svelte_self_deprecated", `\`<svelte:self>\` is deprecated — use self-imports (e.g. \`import ${name} from './${basename}'\`) instead\nhttps://svelte.dev/e/svelte_self_deprecated`);
w(node, 'svelte_self_deprecated', `\`<svelte:self>\` is deprecated — use self-imports (e.g. \`import ${name} from './${basename}'\`) instead\nhttps://svelte.dev/e/svelte_self_deprecated`);
}

@ -278,7 +278,7 @@ declare const SnippetReturn: unique symbol;
* ```
* You can only call a snippet through the `{@render ...}` tag.
*
* https://svelte.dev/docs/svelte/snippet
* See the [snippet documentation](https://svelte.dev/docs/svelte/snippet) for more info.
*
* @template Parameters the parameters that the snippet expects (if any) as a tuple.
*/

@ -15,7 +15,7 @@ export let tracing_expressions = null;
*/
function log_entry(signal, entry) {
const debug = signal.debug;
const value = signal.v;
const value = signal.trace_need_increase ? signal.trace_v : signal.v;
if (value === UNINITIALIZED) {
return;
@ -42,16 +42,15 @@ function log_entry(signal, entry) {
const type = (signal.f & DERIVED) !== 0 ? '$derived' : '$state';
const current_reaction = /** @type {Reaction} */ (active_reaction);
const status =
signal.version > current_reaction.version || current_reaction.version === 0 ? 'dirty' : 'clean';
const dirty = signal.wv > current_reaction.wv || current_reaction.wv === 0;
// eslint-disable-next-line no-console
console.groupCollapsed(
`%c${type}`,
status !== 'clean'
? 'color: CornflowerBlue; font-weight: bold'
: 'color: grey; font-weight: bold',
typeof value === 'object' && STATE_SYMBOL in value ? snapshot(value, true) : value
dirty ? 'color: CornflowerBlue; font-weight: bold' : 'color: grey; font-weight: bold',
typeof value === 'object' && value !== null && STATE_SYMBOL in value
? snapshot(value, true)
: value
);
if (type === '$derived') {
@ -119,7 +118,7 @@ export function trace(label, fn) {
console.groupEnd();
}
if (previously_tracing_expressions !== null) {
if (previously_tracing_expressions !== null && tracing_expressions !== null) {
for (const [signal, entry] of tracing_expressions.entries) {
var prev_entry = previously_tracing_expressions.get(signal);

@ -35,8 +35,9 @@ import { source, mutable_source, internal_set } from '../../reactivity/sources.j
import { array_from, is_array } from '../../../shared/utils.js';
import { INERT } from '../../constants.js';
import { queue_micro_task } from '../task.js';
import { active_effect, active_reaction } from '../../runtime.js';
import { active_effect, active_reaction, get } from '../../runtime.js';
import { DEV } from 'esm-env';
import { derived_safe_equal } from '../../reactivity/deriveds.js';
/**
* The row of a keyed each block that is currently updating. We track this
@ -135,15 +136,17 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
var was_empty = false;
block(() => {
// TODO: ideally we could use derived for runes mode but because of the ability
// to use a store which can be mutated, we can't do that here as mutating a store
// will still result in the collection array being the same from the store
var each_array = derived_safe_equal(() => {
var collection = get_collection();
var array = is_array(collection)
? collection
: collection == null
? []
: array_from(collection);
return is_array(collection) ? collection : collection == null ? [] : array_from(collection);
});
block(() => {
var array = get(each_array);
var length = array.length;
if (was_empty && length === 0) {
@ -254,7 +257,7 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
// that a mutation occurred and it's made the collection MAYBE_DIRTY, so reading the
// collection again can provide consistency to the reactive graph again as the deriveds
// will now be `CLEAN`.
get_collection();
get(each_array);
});
if (hydrating) {

@ -14,6 +14,7 @@ import {
set_active_reaction
} from '../../runtime.js';
import { attach, is_attachment_key } from './attachments.js';
import { clsx } from '../../../shared/attributes.js';
/**
* The value/checked attribute in the template actually corresponds to the defaultValue property, so we need
@ -221,7 +222,10 @@ export function set_custom_element_data(node, prop, value) {
// Don't compute setters for custom elements while they aren't registered yet,
// because during their upgrade/instantiation they might add more setters.
// Instead, fall back to a simple "an object, then set as property" heuristic.
setters_cache.has(node.nodeName) || customElements.get(node.tagName.toLowerCase())
setters_cache.has(node.nodeName) ||
// customElements may not be available in browser extension contexts
!customElements ||
customElements.get(node.tagName.toLowerCase())
? get_setters(node).includes(prop)
: value && typeof value === 'object'
) {
@ -268,6 +272,10 @@ export function set_attributes(
}
}
if (next.class) {
next.class = clsx(next.class);
}
if (css_hash !== undefined) {
next.class = next.class ? next.class + ' ' + css_hash : css_hash;
}

@ -3,12 +3,13 @@ import { hydrating } from '../hydration.js';
/**
* @param {SVGElement} dom
* @param {string} value
* @param {string} [hash]
* @returns {void}
*/
export function set_svg_class(dom, value) {
export function set_svg_class(dom, value, hash) {
// @ts-expect-error need to add __className to patched prototype
var prev_class_name = dom.__className;
var next_class_name = to_class(value);
var next_class_name = to_class(value, hash);
if (hydrating && dom.getAttribute('class') === next_class_name) {
// In case of hydration don't reset the class as it's already correct.
@ -32,12 +33,13 @@ export function set_svg_class(dom, value) {
/**
* @param {MathMLElement} dom
* @param {string} value
* @param {string} [hash]
* @returns {void}
*/
export function set_mathml_class(dom, value) {
export function set_mathml_class(dom, value, hash) {
// @ts-expect-error need to add __className to patched prototype
var prev_class_name = dom.__className;
var next_class_name = to_class(value);
var next_class_name = to_class(value, hash);
if (hydrating && dom.getAttribute('class') === next_class_name) {
// In case of hydration don't reset the class as it's already correct.
@ -61,12 +63,13 @@ export function set_mathml_class(dom, value) {
/**
* @param {HTMLElement} dom
* @param {string} value
* @param {string} [hash]
* @returns {void}
*/
export function set_class(dom, value) {
export function set_class(dom, value, hash) {
// @ts-expect-error need to add __className to patched prototype
var prev_class_name = dom.__className;
var next_class_name = to_class(value);
var next_class_name = to_class(value, hash);
if (hydrating && dom.className === next_class_name) {
// In case of hydration don't reset the class as it's already correct.
@ -79,7 +82,7 @@ export function set_class(dom, value) {
// Removing the attribute when the value is only an empty string causes
// peformance issues vs simply making the className an empty string. So
// we should only remove the class if the the value is nullish.
if (value == null) {
if (value == null && !hash) {
dom.removeAttribute('class');
} else {
dom.className = next_class_name;
@ -93,10 +96,11 @@ export function set_class(dom, value) {
/**
* @template V
* @param {V} value
* @param {string} [hash]
* @returns {string | V}
*/
function to_class(value) {
return value == null ? '' : value;
function to_class(value, hash) {
return (value == null ? '' : value) + (hash ? ' ' + hash : '');
}
/**

@ -192,6 +192,13 @@ export function transition(flags, element, get_fn, get_params) {
var inert = element.inert;
/**
* The default overflow style, stashed so we can revert changes during the transition
* that are necessary to work around a Safari <18 bug
* TODO 6.0 remove this, if older versions of Safari have died out enough
*/
var overflow = element.style.overflow;
/** @type {Animation | undefined} */
var intro;
@ -242,6 +249,8 @@ export function transition(flags, element, get_fn, get_params) {
// Ensure we cancel the animation to prevent leaking
intro?.abort();
intro = current_options = undefined;
element.style.overflow = overflow;
});
},
out(fn) {
@ -382,16 +391,29 @@ function animate(element, options, counterpart, t2, on_finish) {
var keyframes = [];
if (duration > 0) {
/**
* Whether or not the CSS includes `overflow: hidden`, in which case we need to
* add it as an inline style to work around a Safari <18 bug
* TODO 6.0 remove this, if possible
*/
var needs_overflow_hidden = false;
if (css) {
var n = Math.ceil(duration / (1000 / 60)); // `n` must be an integer, or we risk missing the `t2` value
for (var i = 0; i <= n; i += 1) {
var t = t1 + delta * easing(i / n);
var styles = css(t, 1 - t);
keyframes.push(css_to_keyframe(styles));
var styles = css_to_keyframe(css(t, 1 - t));
keyframes.push(styles);
needs_overflow_hidden ||= styles.overflow === 'hidden';
}
}
if (needs_overflow_hidden) {
/** @type {HTMLElement} */ (element).style.overflow = 'hidden';
}
get_t = () => {
var time = /** @type {number} */ (
/** @type {globalThis.Animation} */ (animation).currentTime

@ -54,7 +54,7 @@ export function bind_not_bindable(key, component, name) {
}
/**
* %parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information
* %parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5
* @param {string} parent
* @param {string} method
* @param {string} component
@ -62,7 +62,7 @@ export function bind_not_bindable(key, component, name) {
*/
export function component_api_changed(parent, method, component) {
if (DEV) {
const error = new Error(`component_api_changed\n${parent} called \`${method}\` on an instance of ${component}, which is no longer valid in Svelte 5. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information\nhttps://svelte.dev/e/component_api_changed`);
const error = new Error(`component_api_changed\n${parent} called \`${method}\` on an instance of ${component}, which is no longer valid in Svelte 5\nhttps://svelte.dev/e/component_api_changed`);
error.name = 'Svelte error';
throw error;
@ -72,14 +72,14 @@ export function component_api_changed(parent, method, component) {
}
/**
* Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `compatibility.componentApi` compiler option to `4` to keep it working. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information
* Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `compatibility.componentApi` compiler option to `4` to keep it working.
* @param {string} component
* @param {string} name
* @returns {never}
*/
export function component_api_invalid_new(component, name) {
if (DEV) {
const error = new Error(`component_api_invalid_new\nAttempted to instantiate ${component} with \`new ${name}\`, which is no longer valid in Svelte 5. If this component is not under your control, set the \`compatibility.componentApi\` compiler option to \`4\` to keep it working. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information\nhttps://svelte.dev/e/component_api_invalid_new`);
const error = new Error(`component_api_invalid_new\nAttempted to instantiate ${component} with \`new ${name}\`, which is no longer valid in Svelte 5. If this component is not under your control, set the \`compatibility.componentApi\` compiler option to \`4\` to keep it working.\nhttps://svelte.dev/e/component_api_invalid_new`);
error.name = 'Svelte error';
throw error;

@ -151,7 +151,7 @@ export {
setContext,
hasContext
} from './runtime.js';
export { validate_binding, validate_each_keys, validate_prop_bindings } from './validate.js';
export { validate_binding, validate_each_keys } from './validate.js';
export { raf } from './timing.js';
export { proxy } from './proxy.js';
export { create_custom_element } from './dom/elements/custom-element.js';
@ -162,7 +162,7 @@ export {
$window as window,
$document as document
} from './dom/operations.js';
export { attr } from '../shared/attributes.js';
export { attr, clsx } from '../shared/attributes.js';
export { snapshot } from '../shared/clone.js';
export { noop, fallback } from '../shared/utils.js';
export {

@ -16,7 +16,7 @@ import {
set_signal_status,
skip_reaction,
update_reaction,
increment_version,
increment_write_version,
set_active_effect,
component_context
} from '../runtime.js';
@ -58,8 +58,9 @@ export function derived(fn) {
f: flags,
fn,
reactions: null,
rv: 0,
v: /** @type {V} */ (null),
version: 0,
wv: 0,
parent: parent_derived ?? active_effect
};
@ -182,7 +183,7 @@ export function update_derived(derived) {
if (!derived.equals(value)) {
derived.v = value;
derived.version = increment_version();
derived.wv = increment_write_version();
}
}

@ -35,7 +35,8 @@ import {
INSPECT_EFFECT,
HEAD_EFFECT,
MAYBE_DIRTY,
EFFECT_HAS_DERIVED
EFFECT_HAS_DERIVED,
BOUNDARY_EFFECT
} from '../constants.js';
import { set } from './sources.js';
import * as e from '../errors.js';
@ -110,7 +111,7 @@ function create_effect(type, fn, sync, push = true) {
prev: null,
teardown: null,
transitions: null,
version: 0
wv: 0
};
if (DEV) {
@ -142,7 +143,7 @@ function create_effect(type, fn, sync, push = true) {
effect.first === null &&
effect.nodes_start === null &&
effect.teardown === null &&
(effect.f & EFFECT_HAS_DERIVED) === 0;
(effect.f & (EFFECT_HAS_DERIVED | BOUNDARY_EFFECT)) === 0;
if (!inert && !is_root && push) {
if (parent_effect !== null) {

@ -297,8 +297,10 @@ export function prop(props, key, flags, fallback) {
var is_entry_props = STATE_SYMBOL in props || LEGACY_PROPS in props;
var setter =
get_descriptor(props, key)?.set ??
(is_entry_props && bindable && key in props ? (v) => (props[key] = v) : undefined);
(bindable &&
(get_descriptor(props, key)?.set ??
(is_entry_props && key in props && ((v) => (props[key] = v))))) ||
undefined;
var fallback_value = /** @type {V} */ (fallback);
var fallback_dirty = true;

@ -12,7 +12,7 @@ import {
set_untracked_writes,
set_signal_status,
untrack,
increment_version,
increment_write_version,
update_effect,
derived_sources,
set_derived_sources,
@ -57,7 +57,8 @@ export function source(v, stack) {
v,
reactions: null,
equals,
version: 0
rv: 0,
wv: 0
};
if (DEV && tracing_mode_flag) {
@ -167,11 +168,16 @@ export function set(source, value) {
*/
export function internal_set(source, value) {
if (!source.equals(value)) {
var old_value = source.v;
source.v = value;
source.version = increment_version();
source.wv = increment_write_version();
if (DEV && tracing_mode_flag) {
source.updated = get_stack('UpdatedAt');
if (active_effect != null) {
source.trace_need_increase = true;
source.trace_v ??= old_value;
}
}
mark_reactions(source, DIRTY);

@ -1,7 +1,8 @@
/** @import { StoreReferencesContainer } from '#client' */
/** @import { Store } from '#shared' */
import { subscribe_to_store } from '../../../store/utils.js';
import { noop } from '../../shared/utils.js';
import { get as get_store } from '../../../store/shared/index.js';
import { define_property, noop } from '../../shared/utils.js';
import { get } from '../runtime.js';
import { teardown } from './effects.js';
import { mutable_source, set } from './sources.js';
@ -13,6 +14,8 @@ import { mutable_source, set } from './sources.js';
*/
let is_store_binding = false;
let IS_UNMOUNTED = Symbol();
/**
* Gets the current value of a store. If the store isn't subscribed to yet, it will create a proxy
* signal that will be updated when the store is. The store references container is needed to
@ -30,7 +33,8 @@ export function store_get(store, store_name, stores) {
unsubscribe: noop
});
if (entry.store !== store) {
// if the component that setup this is already unmounted we don't want to register a subscription
if (entry.store !== store && !(IS_UNMOUNTED in stores)) {
entry.unsubscribe();
entry.store = store ?? null;
@ -54,6 +58,13 @@ export function store_get(store, store_name, stores) {
}
}
// if the component that setup this stores is already unmounted the source will be out of sync
// so we just use the `get` for the stores, less performant but it avoids to create a memory leak
// and it will keep the value consistent
if (store && IS_UNMOUNTED in stores) {
return get_store(store);
}
return get(entry.source);
}
@ -103,20 +114,26 @@ export function invalidate_store(stores, store_name) {
/**
* Unsubscribes from all auto-subscribed stores on destroy
* @returns {StoreReferencesContainer}
* @returns {[StoreReferencesContainer, ()=>void]}
*/
export function setup_stores() {
/** @type {StoreReferencesContainer} */
const stores = {};
function cleanup() {
teardown(() => {
for (var store_name in stores) {
const ref = stores[store_name];
ref.unsubscribe();
}
define_property(stores, IS_UNMOUNTED, {
enumerable: false,
value: true
});
});
}
return stores;
return [stores, cleanup];
}
/**

@ -4,19 +4,23 @@ export interface Signal {
/** Flags bitmask */
f: number;
/** Write version */
version: number;
wv: number;
}
export interface Value<V = unknown> extends Signal {
/** Signals that read from this signal */
reactions: null | Reaction[];
/** Equality function */
equals: Equals;
/** Signals that read from this signal */
reactions: null | Reaction[];
/** Read version */
rv: number;
/** The latest value for this signal */
v: V;
/** Dev only */
created?: Error | null;
updated?: Error | null;
trace_need_increase?: boolean;
trace_v?: V;
debug?: null | (() => void);
}

@ -1,6 +1,6 @@
/** @import { ComponentContext, Derived, Effect, Reaction, Signal, Source, Value } from '#client' */
import { DEV } from 'esm-env';
import { define_property, get_descriptors, get_prototype_of } from '../shared/utils.js';
import { define_property, get_descriptors, get_prototype_of, index_of } from '../shared/utils.js';
import {
destroy_block_effect_children,
destroy_effect_children,
@ -127,8 +127,14 @@ export function set_untracked_writes(value) {
untracked_writes = value;
}
/** @type {number} Used by sources and deriveds for handling updates to unowned deriveds it starts from 1 to differentiate between a created effect and a run one for tracing */
let current_version = 1;
/**
* @type {number} Used by sources and deriveds for handling updates.
* Version starts from 1 so that unowned deriveds differentiate between a created effect and a run one for tracing
**/
let write_version = 1;
/** @type {number} Used to version each read of a source of derived to avoid duplicating depedencies inside a reaction */
let read_version = 0;
// If we are working with a get() chain that has no active container,
// to prevent memory leaks, we skip adding the reaction.
@ -168,8 +174,8 @@ export function set_dev_current_component_function(fn) {
dev_current_component_function = fn;
}
export function increment_version() {
return ++current_version;
export function increment_write_version() {
return ++write_version;
}
/** @returns {boolean} */
@ -196,35 +202,37 @@ export function check_dirtiness(reaction) {
if (dependencies !== null) {
var i;
if ((flags & DISCONNECTED) !== 0) {
for (i = 0; i < dependencies.length; i++) {
(dependencies[i].reactions ??= []).push(reaction);
var dependency;
var is_disconnected = (flags & DISCONNECTED) !== 0;
var is_unowned_connected = is_unowned && active_effect !== null && !skip_reaction;
var length = dependencies.length;
// If we are working with a disconnected or an unowned signal that is now connected (due to an active effect)
// then we need to re-connect the reaction to the dependency
if (is_disconnected || is_unowned_connected) {
for (i = 0; i < length; i++) {
dependency = dependencies[i];
// We always re-add all reactions (even duplicates) if the derived was
// previously disconnected
if (is_disconnected || !dependency?.reactions?.includes(reaction)) {
(dependency.reactions ??= []).push(reaction);
}
}
if (is_disconnected) {
reaction.f ^= DISCONNECTED;
}
}
for (i = 0; i < dependencies.length; i++) {
var dependency = dependencies[i];
for (i = 0; i < length; i++) {
dependency = dependencies[i];
if (check_dirtiness(/** @type {Derived} */ (dependency))) {
update_derived(/** @type {Derived} */ (dependency));
}
// If we are working with an unowned signal as part of an effect (due to !skip_reaction)
// and the version hasn't changed, we still need to check that this reaction
// is linked to the dependency source otherwise future updates will not be caught.
if (
is_unowned &&
active_effect !== null &&
!skip_reaction &&
!dependency?.reactions?.includes(reaction)
) {
(dependency.reactions ??= []).push(reaction);
}
if (dependency.version > reaction.version) {
if (dependency.wv > reaction.wv) {
return true;
}
}
@ -396,6 +404,7 @@ export function update_reaction(reaction) {
skip_reaction = !is_flushing_effect && (flags & UNOWNED) !== 0;
derived_sources = null;
component_context = reaction.ctx;
read_version++;
try {
var result = /** @type {Function} */ (0, reaction.fn)();
@ -425,6 +434,14 @@ export function update_reaction(reaction) {
deps.length = skipped_deps;
}
// If we are returning to an previous reaction then
// we need to increment the read version to ensure that
// any dependencies in this reaction aren't marked with
// the same version
if (previous_reaction !== null) {
read_version++;
}
return result;
} finally {
new_deps = previous_deps;
@ -446,7 +463,7 @@ export function update_reaction(reaction) {
function remove_reaction(signal, dependency) {
let reactions = dependency.reactions;
if (reactions !== null) {
var index = reactions.indexOf(signal);
var index = index_of.call(reactions, signal);
if (index !== -1) {
var new_length = reactions.length - 1;
if (new_length === 0) {
@ -526,7 +543,24 @@ export function update_effect(effect) {
execute_effect_teardown(effect);
var teardown = update_reaction(effect);
effect.teardown = typeof teardown === 'function' ? teardown : null;
effect.version = current_version;
effect.wv = write_version;
var deps = effect.deps;
// In DEV, we need to handle a case where $inspect.trace() might
// incorrectly state a source dependency has not changed when it has.
// That's beacuse that source was changed by the same effect, causing
// the versions to match. We can avoid this by incrementing the version
if (DEV && tracing_mode_flag && (effect.f & DIRTY) !== 0 && deps !== null) {
for (let i = 0; i < deps.length; i++) {
var dep = deps[i];
if (dep.trace_need_increase) {
dep.wv = increment_write_version();
dep.trace_need_increase = undefined;
dep.trace_v = undefined;
}
}
}
if (DEV) {
dev_effect_stack.push(effect);
@ -861,7 +895,8 @@ export function get(signal) {
e.state_unsafe_local_read();
}
var deps = active_reaction.deps;
if (signal.rv < read_version) {
signal.rv = read_version;
// If the signal is accessing the same dependencies in the same
// order as it did last time, increment `skipped_deps`
// rather than updating `new_deps`, which creates GC cost
@ -883,6 +918,7 @@ export function get(signal) {
set_signal_status(active_effect, DIRTY);
schedule_effect(active_effect);
}
}
} else if (is_derived && /** @type {Derived} */ (signal).deps === null) {
var derived = /** @type {Derived} */ (signal);
var parent = derived.parent;

@ -1,5 +1,5 @@
import { dev_current_component_function } from './runtime.js';
import { get_descriptor, is_array } from '../shared/utils.js';
import { is_array } from '../shared/utils.js';
import * as e from './errors.js';
import { FILENAME } from '../../constants.js';
import { render_effect } from './reactivity/effects.js';
@ -38,29 +38,6 @@ export function validate_each_keys(collection, key_fn) {
});
}
/**
* @param {Record<string, any>} $$props
* @param {string[]} bindable
* @param {string[]} exports
* @param {Function & { [FILENAME]: string }} component
*/
export function validate_prop_bindings($$props, bindable, exports, component) {
for (const key in $$props) {
var setter = get_descriptor($$props, key)?.set;
var name = component.name;
if (setter) {
if (exports.includes(key)) {
e.bind_invalid_export(component[FILENAME], key, name);
}
if (!bindable.includes(key)) {
e.bind_not_bindable(key, component[FILENAME], name);
}
}
}
}
/**
* @param {string} binding
* @param {() => Record<string, any>} get_object

@ -76,7 +76,7 @@ export function hydration_attribute_changed(attribute, html, value) {
*/
export function hydration_html_changed(location) {
if (DEV) {
console.warn(`%c[svelte] hydration_html_changed\n%c${location ? `The value of an \`{@html ...}\` block ${location} changed between server and client renders. The client value will be ignored in favour of the server value` : "The value of an `{@html ...}` block changed between server and client renders. The client value will be ignored in favour of the server value"}\nhttps://svelte.dev/e/hydration_html_changed`, bold, normal);
console.warn(`%c[svelte] hydration_html_changed\n%c${location ? `The value of an \`{@html ...}\` block ${location} changed between server and client renders. The client value will be ignored in favour of the server value` : 'The value of an `{@html ...}` block changed between server and client renders. The client value will be ignored in favour of the server value'}\nhttps://svelte.dev/e/hydration_html_changed`, bold, normal);
} else {
console.warn(`https://svelte.dev/e/hydration_html_changed`);
}
@ -88,7 +88,7 @@ export function hydration_html_changed(location) {
*/
export function hydration_mismatch(location) {
if (DEV) {
console.warn(`%c[svelte] hydration_mismatch\n%c${location ? `Hydration failed because the initial UI does not match what was rendered on the server. The error occurred near ${location}` : "Hydration failed because the initial UI does not match what was rendered on the server"}\nhttps://svelte.dev/e/hydration_mismatch`, bold, normal);
console.warn(`%c[svelte] hydration_mismatch\n%c${location ? `Hydration failed because the initial UI does not match what was rendered on the server. The error occurred near ${location}` : 'Hydration failed because the initial UI does not match what was rendered on the server'}\nhttps://svelte.dev/e/hydration_mismatch`, bold, normal);
} else {
console.warn(`https://svelte.dev/e/hydration_mismatch`);
}
@ -149,7 +149,7 @@ export function ownership_invalid_binding(parent, child, owner) {
*/
export function ownership_invalid_mutation(component, owner) {
if (DEV) {
console.warn(`%c[svelte] ownership_invalid_mutation\n%c${component ? `${component} mutated a value owned by ${owner}. This is strongly discouraged. Consider passing values to child components with \`bind:\`, or use a callback instead` : "Mutating a value outside the component that created it is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead"}\nhttps://svelte.dev/e/ownership_invalid_mutation`, bold, normal);
console.warn(`%c[svelte] ownership_invalid_mutation\n%c${component ? `${component} mutated a value owned by ${owner}. This is strongly discouraged. Consider passing values to child components with \`bind:\`, or use a callback instead` : 'Mutating a value outside the component that created it is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead'}\nhttps://svelte.dev/e/ownership_invalid_mutation`, bold, normal);
} else {
console.warn(`https://svelte.dev/e/ownership_invalid_mutation`);
}
@ -166,3 +166,15 @@ export function state_proxy_equality_mismatch(operator) {
console.warn(`https://svelte.dev/e/state_proxy_equality_mismatch`);
}
}
/**
* The `slide` transition does not work correctly for elements with `display: %value%`
* @param {string} value
*/
export function transition_slide_display(value) {
if (DEV) {
console.warn(`%c[svelte] transition_slide_display\n%cThe \`slide\` transition does not work correctly for elements with \`display: ${value}\`\nhttps://svelte.dev/e/transition_slide_display`, bold, normal);
} else {
console.warn(`https://svelte.dev/e/transition_slide_display`);
}
}

@ -2,7 +2,7 @@
/** @import { Component, Payload, RenderOutput } from '#server' */
/** @import { Store } from '#shared' */
export { FILENAME, HMR } from '../../constants.js';
import { attr } from '../shared/attributes.js';
import { attr, clsx } from '../shared/attributes.js';
import { is_promise, noop } from '../shared/utils.js';
import { subscribe_to_store } from '../../store/utils.js';
import {
@ -195,6 +195,10 @@ export function spread_attributes(attrs, classes, styles, flags = 0) {
: style_object_to_string(styles);
}
if (attrs.class) {
attrs.class = clsx(attrs.class);
}
if (classes) {
const classlist = attrs.class ? [attrs.class] : [];
@ -522,7 +526,7 @@ export function once(get_value) {
};
}
export { attr };
export { attr, clsx };
export { html } from './blocks/html.js';

@ -1,4 +1,5 @@
import { escape_html } from '../../escaping.js';
import { clsx as _clsx } from 'clsx';
/**
* `<div translate={false}>` should be rendered as `<div translate="no">` and _not_
@ -26,3 +27,16 @@ export function attr(name, value, is_boolean = false) {
const assignment = is_boolean ? '' : `="${escape_html(normalized, true)}"`;
return ` ${name}${assignment}`;
}
/**
* Small wrapper around clsx to preserve Svelte's (weird) handling of falsy values.
* TODO Svelte 6 revisit this, and likely turn all falsy values into the empty string (what clsx also does)
* @param {any} value
*/
export function clsx(value) {
if (typeof value === 'object') {
return _clsx(value);
} else {
return value ?? '';
}
}

@ -1,6 +1,7 @@
// Store the references to globals in case someone tries to monkey patch these, causing the below
// to de-opt (this occurs often when using popular extensions).
export var is_array = Array.isArray;
export var index_of = Array.prototype.indexOf;
export var array_from = Array.from;
export var object_keys = Object.keys;
export var define_property = Object.defineProperty;

@ -29,7 +29,7 @@ export function state_snapshot_uncloneable(properties) {
? `The following properties cannot be cloned with \`$state.snapshot\` — the return value contains the originals:
${properties}`
: "Value cannot be cloned with `$state.snapshot` — the original value was returned"}\nhttps://svelte.dev/e/state_snapshot_uncloneable`, bold, normal);
: 'Value cannot be cloned with `$state.snapshot` — the original value was returned'}\nhttps://svelte.dev/e/state_snapshot_uncloneable`, bold, normal);
} else {
console.warn(`https://svelte.dev/e/state_snapshot_uncloneable`);
}

@ -108,12 +108,17 @@ export function spring(value, opts = {}) {
return false;
}
inv_mass = Math.min(inv_mass + inv_mass_recovery_rate, 1);
// clamp elapsed time to 1/30th of a second, so that longer pauses
// (blocked thread or inactive tab) don't cause the spring to go haywire
const elapsed = Math.min(now - last_time, 1000 / 30);
/** @type {TickContext} */
const ctx = {
inv_mass,
opts: spring,
settled: true,
dt: ((now - last_time) * 60) / 1000
dt: (elapsed * 60) / 1000
};
// @ts-ignore
const next_value = tick_spring(ctx, last_value, value, target_value);
@ -236,6 +241,10 @@ export class Spring {
this.#task ??= loop((now) => {
this.#inverse_mass = Math.min(this.#inverse_mass + inv_mass_recovery_rate, 1);
// clamp elapsed time to 1/30th of a second, so that longer pauses
// (blocked thread or inactive tab) don't cause the spring to go haywire
const elapsed = Math.min(now - this.#last_time, 1000 / 30);
/** @type {import('./private').TickContext} */
const ctx = {
inv_mass: this.#inverse_mass,
@ -245,7 +254,7 @@ export class Spring {
precision: this.#precision.v
},
settled: true,
dt: ((now - this.#last_time) * 60) / 1000
dt: (elapsed * 60) / 1000
};
var next = tick_spring(ctx, this.#last_value, this.#current.v, this.#target.v);

@ -72,7 +72,8 @@ function get_interpolator(a, b) {
return (t) => a + t * delta;
}
throw new Error(`Cannot interpolate ${type} values`);
// for non-numeric values, snap to the final value immediately
return () => b;
}
/**
@ -230,10 +231,6 @@ export class Tween {
set(value, options) {
set(this.#target, value);
let previous_value = this.#current.v;
let previous_task = this.#task;
let started = false;
let {
delay = 0,
duration = 400,
@ -241,10 +238,18 @@ export class Tween {
interpolate = get_interpolator
} = { ...this.#defaults, ...options };
if (duration === 0) {
this.#task?.abort();
set(this.#current, value);
return Promise.resolve();
}
const start = raf.now() + delay;
/** @type {(t: number) => T} */
let fn;
let started = false;
let previous_task = this.#task;
this.#task = loop((now) => {
if (now < start) {
@ -254,10 +259,12 @@ export class Tween {
if (!started) {
started = true;
fn = interpolate(/** @type {any} */ (previous_value), value);
const prev = this.#current.v;
fn = interpolate(prev, value);
if (typeof duration === 'function') {
duration = duration(/** @type {any} */ (previous_value), value);
duration = duration(prev, value);
}
previous_task?.abort();

@ -1,4 +1,8 @@
/** @import { BlurParams, CrossfadeParams, DrawParams, FadeParams, FlyParams, ScaleParams, SlideParams, TransitionConfig } from './public' */
import { DEV } from 'esm-env';
import * as w from '../internal/client/warnings.js';
/** @param {number} x */
const linear = (x) => x;
@ -92,6 +96,8 @@ export function fly(
};
}
var slide_warning = false;
/**
* Slides an element in and out.
*
@ -101,6 +107,13 @@ export function fly(
*/
export function slide(node, { delay = 0, duration = 400, easing = cubic_out, axis = 'y' } = {}) {
const style = getComputedStyle(node);
if (DEV && !slide_warning && /(contents|inline|table)/.test(style.display)) {
slide_warning = true;
Promise.resolve().then(() => (slide_warning = false));
w.transition_slide_display(style.display);
}
const opacity = +style.opacity;
const primary_property = axis === 'y' ? 'height' : 'width';
const primary_property_value = parseFloat(style[primary_property]);
@ -131,7 +144,8 @@ export function slide(node, { delay = 0, duration = 400, easing = cubic_out, axi
`margin-${secondary_properties[0]}: ${t * margin_start_value}px;` +
`margin-${secondary_properties[1]}: ${t * margin_end_value}px;` +
`border-${secondary_properties[0]}-width: ${t * border_width_start_value}px;` +
`border-${secondary_properties[1]}-width: ${t * border_width_end_value}px;`
`border-${secondary_properties[1]}-width: ${t * border_width_end_value}px;` +
`min-${primary_property}: 0`
};
}

@ -156,6 +156,7 @@ const DOM_BOOLEAN_ATTRIBUTES = [
'formnovalidate',
'hidden',
'indeterminate',
'inert',
'ismap',
'loop',
'multiple',
@ -214,7 +215,6 @@ const DOM_PROPERTIES = [
'playsInline',
'readOnly',
'value',
'inert',
'volume',
'defaultValue',
'defaultChecked',

@ -2,9 +2,7 @@
/**
* The current version, as set in package.json.
*
* https://svelte.dev/docs/svelte-compiler#svelte-version
* @type {string}
*/
export const VERSION = '5.15.0';
export const VERSION = '5.17.4';
export const PUBLIC_VERSION = '5';

@ -4,6 +4,6 @@ export default test({
error: {
code: 'each_item_invalid_assignment',
message:
'Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`)'
'Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`, or `bind:value={array[i]}` instead of `bind:value={entry}`)'
}
});

@ -4,6 +4,6 @@ export default test({
error: {
code: 'each_item_invalid_assignment',
message:
'Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`)'
'Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`, or `bind:value={array[i]}` instead of `bind:value={entry}`)'
}
});

@ -4,6 +4,6 @@ export default test({
error: {
code: 'each_item_invalid_assignment',
message:
'Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`)'
'Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`, or `bind:value={array[i]}` instead of `bind:value={entry}`)'
}
});

@ -0,0 +1,20 @@
import { test } from '../../test';
export default test({
warnings: [
{
code: 'css_unused_selector',
message: 'Unused CSS selector ".unused"\nhttps://svelte.dev/e/css_unused_selector',
start: {
line: 24,
column: 1,
character: 548
},
end: {
line: 24,
column: 8,
character: 555
}
}
]
});

@ -0,0 +1,12 @@
.used1.svelte-xyz { color: green; }
.used2.svelte-xyz { color: green; }
.used3.svelte-xyz { color: green; }
.used4.svelte-xyz { color: green; }
.used5.svelte-xyz { color: green; }
.used6.svelte-xyz { color: green; }
.used7.svelte-xyz { color: green; }
.used8.svelte-xyz { color: green; }
.used9.svelte-xyz { color: green; }
/* (unused) .unused { color: red; }*/

@ -0,0 +1,25 @@
<script>
let condition = Math.random() < 0.5;
</script>
<p class={['used1']}></p>
<p class={[{ used2: true }]}></p>
<p class={{ used3: true }}></p>
<p class={{ 'used4 used5': true }}></p>
<p class={{ used6 }}></p>
<p class={[condition ? 'used7' : 'used8']}></p>
<p class={[condition && 'used9']}></p>
<style>
.used1 { color: green; }
.used2 { color: green; }
.used3 { color: green; }
.used4 { color: green; }
.used5 { color: green; }
.used6 { color: green; }
.used7 { color: green; }
.used8 { color: green; }
.used9 { color: green; }
.unused { color: red; }
</style>

@ -0,0 +1,5 @@
<h1 class={[foo]}>hello world</h1>
<style>
.x { color: green; }
</style>

@ -0,0 +1,5 @@
<h1 class={{ foo: true, ...rest }}>hello world</h1>
<style>
.x { color: green; }
</style>

@ -0,0 +1,5 @@
<h1 class={{ [foo]: true }}>hello world</h1>
<style>
.x { color: green; }
</style>

@ -74,6 +74,10 @@
animation: svelte-xyz-test 1s;
}
.y{
animation: test-in 1s;
}
@keyframes test-in{
to{
opacity: 1;

@ -76,6 +76,10 @@
animation: test 1s;
}
.y{
animation: test-in 1s;
}
@keyframes test-in{
to{
opacity: 1;

@ -0,0 +1,5 @@
import { test } from '../../test';
// A note about _expected.html: It is different from body.html because we're
// testing against target.innerHTML which already removed the redundant first newline
export default test({});

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save