Skip to content

Commit 2d2508a

Browse files
feat: allow for literal property definition with state on classes (#11326)
closes #11316 --------- Co-authored-by: Simon Holthausen <[email protected]>
1 parent c7bdef5 commit 2d2508a

File tree

5 files changed

+57
-5
lines changed

5 files changed

+57
-5
lines changed

.changeset/honest-nails-share.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
feat: allow for literal property definition with state on classes

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

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as b from '../../../../utils/builders.js';
44
import * as assert from '../../../../utils/assert.js';
55
import { get_prop_source, is_state_source, should_proxy_or_freeze } from '../utils.js';
66
import { extract_paths } from '../../../../utils/ast.js';
7+
import { regex_invalid_identifier_chars } from '../../../patterns.js';
78

89
/** @type {import('../types.js').ComponentVisitors} */
910
export const javascript_visitors_runes = {
@@ -20,9 +21,13 @@ export const javascript_visitors_runes = {
2021
for (const definition of node.body) {
2122
if (
2223
definition.type === 'PropertyDefinition' &&
23-
(definition.key.type === 'Identifier' || definition.key.type === 'PrivateIdentifier')
24+
(definition.key.type === 'Identifier' ||
25+
definition.key.type === 'PrivateIdentifier' ||
26+
definition.key.type === 'Literal')
2427
) {
25-
const { type, name } = definition.key;
28+
const type = definition.key.type;
29+
const name = get_name(definition.key);
30+
if (!name) continue;
2631

2732
const is_private = type === 'PrivateIdentifier';
2833
if (is_private) private_ids.push(name);
@@ -79,9 +84,12 @@ export const javascript_visitors_runes = {
7984
for (const definition of node.body) {
8085
if (
8186
definition.type === 'PropertyDefinition' &&
82-
(definition.key.type === 'Identifier' || definition.key.type === 'PrivateIdentifier')
87+
(definition.key.type === 'Identifier' ||
88+
definition.key.type === 'PrivateIdentifier' ||
89+
definition.key.type === 'Literal')
8390
) {
84-
const name = definition.key.name;
91+
const name = get_name(definition.key);
92+
if (!name) continue;
8593

8694
const is_private = definition.key.type === 'PrivateIdentifier';
8795
const field = (is_private ? private_state : public_state).get(name);
@@ -160,7 +168,6 @@ export const javascript_visitors_runes = {
160168
);
161169
}
162170
}
163-
164171
continue;
165172
}
166173
}
@@ -437,3 +444,14 @@ export const javascript_visitors_runes = {
437444
context.next();
438445
}
439446
};
447+
448+
/**
449+
* @param {import('estree').Identifier | import('estree').PrivateIdentifier | import('estree').Literal} node
450+
*/
451+
function get_name(node) {
452+
if (node.type === 'Literal') {
453+
return node.value?.toString().replace(regex_invalid_identifier_chars, '_');
454+
} else {
455+
return node.name;
456+
}
457+
}

packages/svelte/src/compiler/phases/patterns.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export const regex_only_whitespaces = /^[ \t\n\r\f]+$/;
1515
export const regex_not_newline_characters = /[^\n]/g;
1616

1717
export const regex_is_valid_identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/;
18+
// used in replace all to remove all invalid chars from a literal identifier
19+
export const regex_invalid_identifier_chars = /(^[^a-zA-Z_$]|[^a-zA-Z0-9_$])/g;
1820

1921
export const regex_starts_with_vowel = /^[aeiou]/;
2022
export const regex_heading_tags = /^h[1-6]$/;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
html: `<button>false</button>`,
5+
6+
async test({ assert, target }) {
7+
const btn = target.querySelector('button');
8+
9+
await btn?.click();
10+
assert.htmlEqual(target.innerHTML, `<button>true</button>`);
11+
12+
await btn?.click();
13+
assert.htmlEqual(target.innerHTML, `<button>false</button>`);
14+
}
15+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
class Toggle {
3+
"aria-pressed" = $state(false);
4+
5+
toggle(){
6+
this["aria-pressed"] = !this["aria-pressed"]
7+
}
8+
}
9+
const toggle = new Toggle();
10+
</script>
11+
12+
<button on:click={() => toggle.toggle()}>{toggle["aria-pressed"]}</button>

0 commit comments

Comments
 (0)