Rich Harris 8 months ago
commit c5bab8d6f2

@ -1,5 +0,0 @@
---
'svelte': minor
---
feat: allow `<template>` elements to contain any child

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: ensure resume effects are scheduled in topological order

@ -1,5 +0,0 @@
---
'svelte': patch
---
fix: remove listener on `bind_current_time` teardown

@ -71,6 +71,18 @@ The user of this component has the same flexibility to use a mixture of objects,
</Button>
```
Svelte also exposes the `ClassValue` type, which is the type of value that the `class` attribute on elements accept. This is useful if you want to use a type-safe class name in component props:
```svelte
<script lang="ts">
import type { ClassValue } from 'svelte/elements';
const props: { class: ClassValue } = $props();
</script>
<div class={['original', props.class]}>...</div>
```
## The `class:` directive
Prior to Svelte 5.16, the `class:` directive was the most convenient way to set classes on elements conditionally.

@ -1,5 +1,31 @@
# svelte
## 5.19.0
### Minor Changes
- feat: Expose `ClassValue` from `svelte/elements` ([#15035](https://github.com/sveltejs/svelte/pull/15035))
### Patch Changes
- fix: create fewer deriveds for concatenated strings ([#15041](https://github.com/sveltejs/svelte/pull/15041))
- fix: correctly parse leading comments in function binding ([#15020](https://github.com/sveltejs/svelte/pull/15020))
## 5.18.0
### Minor Changes
- feat: allow `<template>` elements to contain any child ([#15007](https://github.com/sveltejs/svelte/pull/15007))
### Patch Changes
- fix: ensure resume effects are scheduled in topological order ([#15012](https://github.com/sveltejs/svelte/pull/15012))
- fix: bump esrap ([#15015](https://github.com/sveltejs/svelte/pull/15015))
- fix: remove listener on `bind_current_time` teardown ([#15013](https://github.com/sveltejs/svelte/pull/15013))
## 5.17.5
### Patch 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 | import('clsx').ClassArray | import('clsx').ClassDictionary | undefined | null;
class?: ClassValue | 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 | import('clsx').ClassArray | import('clsx').ClassDictionary | undefined | null;
class?: ClassValue | undefined | null;
color?: string | undefined | null;
height?: number | string | undefined | null;
id?: string | undefined | null;
@ -2059,3 +2059,5 @@ export interface SvelteHTMLElements {
[name: string]: { [name: string]: any };
}
export type ClassValue = string | import('clsx').ClassArray | import('clsx').ClassDictionary;

@ -2,7 +2,7 @@
"name": "svelte",
"description": "Cybernetically enhanced web apps",
"license": "MIT",
"version": "5.17.5",
"version": "5.19.0",
"type": "module",
"types": "./types/index.d.ts",
"engines": {
@ -155,7 +155,7 @@
"axobject-query": "^4.1.0",
"clsx": "^2.1.1",
"esm-env": "^1.2.1",
"esrap": "^1.4.2",
"esrap": "^1.4.3",
"is-reference": "^3.0.3",
"locate-character": "^3.0.0",
"magic-string": "^0.30.11",

@ -38,6 +38,10 @@ export default function read_expression(parser, opening_token, disallow_loose) {
let num_parens = 0;
if (node.leadingComments !== undefined && node.leadingComments.length > 0) {
parser.index = node.leadingComments.at(-1).end;
}
for (let i = parser.index; i < /** @type {number} */ (node.start); i += 1) {
if (parser.template[i] === '(') num_parens += 1;
}

@ -132,8 +132,19 @@ export function BindDirective(node, context) {
}
let i = /** @type {number} */ (node.expression.start);
let leading_comments_start = /**@type {any}*/ (node.expression.leadingComments?.at(0))?.start;
let leading_comments_end = /**@type {any}*/ (node.expression.leadingComments?.at(-1))?.end;
while (context.state.analysis.source[--i] !== '{') {
if (context.state.analysis.source[i] === '(') {
if (
context.state.analysis.source[i] === '(' &&
// if the parenthesis is in a leading comment we don't need to throw the error
!(
leading_comments_start &&
leading_comments_end &&
i <= leading_comments_end &&
i >= leading_comments_start
)
) {
e.bind_invalid_parens(node, node.name);
}
}

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

@ -0,0 +1,10 @@
<script>
let value = '';
</script>
<input bind:value={
/** ( */
() => value,
(v) => value = v.toLowerCase()
}
/>

@ -0,0 +1,326 @@
{
"css": null,
"js": [],
"start": 37,
"end": 117,
"type": "Root",
"fragment": {
"type": "Fragment",
"nodes": [
{
"type": "Text",
"start": 35,
"end": 37,
"raw": "\n\n",
"data": "\n\n"
},
{
"type": "RegularElement",
"start": 37,
"end": 117,
"name": "input",
"attributes": [
{
"start": 44,
"end": 114,
"type": "BindDirective",
"name": "value",
"expression": {
"type": "SequenceExpression",
"start": 68,
"end": 112,
"loc": {
"start": {
"line": 7,
"column": 1
},
"end": {
"line": 8,
"column": 31
}
},
"expressions": [
{
"type": "ArrowFunctionExpression",
"start": 68,
"end": 79,
"loc": {
"start": {
"line": 7,
"column": 1
},
"end": {
"line": 7,
"column": 12
}
},
"id": null,
"expression": true,
"generator": false,
"async": false,
"params": [],
"body": {
"type": "Identifier",
"start": 74,
"end": 79,
"loc": {
"start": {
"line": 7,
"column": 7
},
"end": {
"line": 7,
"column": 12
}
},
"name": "value"
}
},
{
"type": "ArrowFunctionExpression",
"start": 82,
"end": 112,
"loc": {
"start": {
"line": 8,
"column": 1
},
"end": {
"line": 8,
"column": 31
}
},
"id": null,
"expression": true,
"generator": false,
"async": false,
"params": [
{
"type": "Identifier",
"start": 83,
"end": 84,
"loc": {
"start": {
"line": 8,
"column": 2
},
"end": {
"line": 8,
"column": 3
}
},
"name": "v"
}
],
"body": {
"type": "AssignmentExpression",
"start": 89,
"end": 112,
"loc": {
"start": {
"line": 8,
"column": 8
},
"end": {
"line": 8,
"column": 31
}
},
"operator": "=",
"left": {
"type": "Identifier",
"start": 89,
"end": 94,
"loc": {
"start": {
"line": 8,
"column": 8
},
"end": {
"line": 8,
"column": 13
}
},
"name": "value"
},
"right": {
"type": "CallExpression",
"start": 97,
"end": 112,
"loc": {
"start": {
"line": 8,
"column": 16
},
"end": {
"line": 8,
"column": 31
}
},
"callee": {
"type": "MemberExpression",
"start": 97,
"end": 110,
"loc": {
"start": {
"line": 8,
"column": 16
},
"end": {
"line": 8,
"column": 29
}
},
"object": {
"type": "Identifier",
"start": 97,
"end": 98,
"loc": {
"start": {
"line": 8,
"column": 16
},
"end": {
"line": 8,
"column": 17
}
},
"name": "v"
},
"property": {
"type": "Identifier",
"start": 99,
"end": 110,
"loc": {
"start": {
"line": 8,
"column": 18
},
"end": {
"line": 8,
"column": 29
}
},
"name": "toLowerCase"
},
"computed": false,
"optional": false
},
"arguments": [],
"optional": false
}
}
}
],
"leadingComments": [
{
"type": "Block",
"value": "* ( ",
"start": 58,
"end": 66
}
]
},
"modifiers": []
}
],
"fragment": {
"type": "Fragment",
"nodes": []
}
}
]
},
"options": null,
"instance": {
"type": "Script",
"start": 0,
"end": 35,
"context": "default",
"content": {
"type": "Program",
"start": 8,
"end": 26,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 3,
"column": 0
}
},
"body": [
{
"type": "VariableDeclaration",
"start": 10,
"end": 25,
"loc": {
"start": {
"line": 2,
"column": 1
},
"end": {
"line": 2,
"column": 16
}
},
"declarations": [
{
"type": "VariableDeclarator",
"start": 14,
"end": 24,
"loc": {
"start": {
"line": 2,
"column": 5
},
"end": {
"line": 2,
"column": 15
}
},
"id": {
"type": "Identifier",
"start": 14,
"end": 19,
"loc": {
"start": {
"line": 2,
"column": 5
},
"end": {
"line": 2,
"column": 10
}
},
"name": "value"
},
"init": {
"type": "Literal",
"start": 22,
"end": 24,
"loc": {
"start": {
"line": 2,
"column": 13
},
"end": {
"line": 2,
"column": 15
}
},
"value": "",
"raw": "''"
}
}
],
"kind": "let"
}
],
"sourceType": "module"
},
"attributes": []
}
}

@ -16,11 +16,11 @@ export default function Text_nodes_deriveds($$anchor) {
}
var p = root();
const stringified_text = $.derived(() => text1() ?? '');
const stringified_text_1 = $.derived(() => text2() ?? '');
const expression = $.derived(() => text1() ?? '');
const expression_1 = $.derived(() => text2() ?? '');
var text = $.child(p);
$.template_effect(() => $.set_text(text, `${$.get(stringified_text)}${$.get(stringified_text_1)}`));
$.template_effect(() => $.set_text(text, `${$.get(expression)}${$.get(expression_1)}`));
$.reset(p);
$.append($$anchor, p);
}

@ -0,0 +1,10 @@
<script>
let value = '';
</script>
<input bind:value={
/** ( */
() => value,
(v) => value = v.toLowerCase()
}
/>

@ -87,8 +87,8 @@ importers:
specifier: ^1.2.1
version: 1.2.1
esrap:
specifier: ^1.4.2
version: 1.4.2
specifier: ^1.4.3
version: 1.4.3
is-reference:
specifier: ^3.0.3
version: 3.0.3
@ -1118,8 +1118,8 @@ packages:
resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
engines: {node: '>=0.10'}
esrap@1.4.2:
resolution: {integrity: sha512-FhVlJzvTw7ZLxYZ7RyHwQCFE64dkkpzGNNnphaGCLwjqGk1SQcqzbgdx9FowPCktx6NOSHkzvcZ3vsvdH54YXA==}
esrap@1.4.3:
resolution: {integrity: sha512-Xddc1RsoFJ4z9nR7W7BFaEPIp4UXoeQ0+077UdWLxbafMQFyU79sQJMk7kxNgRwQ9/aVgaKacCHC2pUACGwmYw==}
esrecurse@4.3.0:
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
@ -3324,7 +3324,7 @@ snapshots:
dependencies:
estraverse: 5.3.0
esrap@1.4.2:
esrap@1.4.3:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0

Loading…
Cancel
Save