Skip to content

Commit 1176463

Browse files
fix: deconflict get_name for literal class properties (#14607)
1 parent 38171f6 commit 1176463

File tree

4 files changed

+34
-4
lines changed

4 files changed

+34
-4
lines changed

.changeset/stupid-buckets-drum.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: deconflict `get_name` for literal class properties

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

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ export function ClassBody(node, context) {
2323
/** @type {Map<string, StateField>} */
2424
const private_state = new Map();
2525

26+
/** @type {Map<(MethodDefinition|PropertyDefinition)["key"], string>} */
27+
const definition_names = new Map();
28+
2629
/** @type {string[]} */
2730
const private_ids = [];
2831

@@ -34,9 +37,12 @@ export function ClassBody(node, context) {
3437
definition.key.type === 'Literal')
3538
) {
3639
const type = definition.key.type;
37-
const name = get_name(definition.key);
40+
const name = get_name(definition.key, public_state);
3841
if (!name) continue;
3942

43+
// we store the deconflicted name in the map so that we can access it later
44+
definition_names.set(definition.key, name);
45+
4046
const is_private = type === 'PrivateIdentifier';
4147
if (is_private) private_ids.push(name);
4248

@@ -96,7 +102,7 @@ export function ClassBody(node, context) {
96102
definition.key.type === 'PrivateIdentifier' ||
97103
definition.key.type === 'Literal')
98104
) {
99-
const name = get_name(definition.key);
105+
const name = definition_names.get(definition.key);
100106
if (!name) continue;
101107

102108
const is_private = definition.key.type === 'PrivateIdentifier';
@@ -210,10 +216,20 @@ export function ClassBody(node, context) {
210216

211217
/**
212218
* @param {Identifier | PrivateIdentifier | Literal} node
219+
* @param {Map<string, StateField>} public_state
213220
*/
214-
function get_name(node) {
221+
function get_name(node, public_state) {
215222
if (node.type === 'Literal') {
216-
return node.value?.toString().replace(regex_invalid_identifier_chars, '_');
223+
let name = node.value?.toString().replace(regex_invalid_identifier_chars, '_');
224+
225+
// the above could generate conflicts because it has to generate a valid identifier
226+
// so stuff like `0` and `1` or `state%` and `state^` will result in the same string
227+
// so we have to de-conflict. We can only check `public_state` because private state
228+
// can't have literal keys
229+
while (name && public_state.has(name)) {
230+
name = '_' + name;
231+
}
232+
return name;
217233
} else {
218234
return node.name;
219235
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { test } from '../../test';
2+
3+
export default test({});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<script>
2+
class Test {
3+
0 = $state();
4+
1 = $state();
5+
}
6+
</script>

0 commit comments

Comments
 (0)