From 11764632b9d64621bfbf86cd1d3a65adda1dfd09 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Mon, 9 Dec 2024 17:22:42 +0100 Subject: [PATCH] fix: deconflict `get_name` for literal class properties (#14607) --- .changeset/stupid-buckets-drum.md | 5 ++++ .../3-transform/client/visitors/ClassBody.js | 24 +++++++++++++++---- .../_config.js | 3 +++ .../main.svelte | 6 +++++ 4 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 .changeset/stupid-buckets-drum.md create mode 100644 packages/svelte/tests/runtime-runes/samples/class-state-conflicting-get-name/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/class-state-conflicting-get-name/main.svelte diff --git a/.changeset/stupid-buckets-drum.md b/.changeset/stupid-buckets-drum.md new file mode 100644 index 000000000000..57d6f015f786 --- /dev/null +++ b/.changeset/stupid-buckets-drum.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: deconflict `get_name` for literal class properties diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js index 5e842a82febf..7b3a9a4d0e29 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js @@ -23,6 +23,9 @@ export function ClassBody(node, context) { /** @type {Map} */ const private_state = new Map(); + /** @type {Map<(MethodDefinition|PropertyDefinition)["key"], string>} */ + const definition_names = new Map(); + /** @type {string[]} */ const private_ids = []; @@ -34,9 +37,12 @@ export function ClassBody(node, context) { definition.key.type === 'Literal') ) { const type = definition.key.type; - const name = get_name(definition.key); + const name = get_name(definition.key, public_state); if (!name) continue; + // we store the deconflicted name in the map so that we can access it later + definition_names.set(definition.key, name); + const is_private = type === 'PrivateIdentifier'; if (is_private) private_ids.push(name); @@ -96,7 +102,7 @@ export function ClassBody(node, context) { definition.key.type === 'PrivateIdentifier' || definition.key.type === 'Literal') ) { - const name = get_name(definition.key); + const name = definition_names.get(definition.key); if (!name) continue; const is_private = definition.key.type === 'PrivateIdentifier'; @@ -210,10 +216,20 @@ export function ClassBody(node, context) { /** * @param {Identifier | PrivateIdentifier | Literal} node + * @param {Map} public_state */ -function get_name(node) { +function get_name(node, public_state) { if (node.type === 'Literal') { - return node.value?.toString().replace(regex_invalid_identifier_chars, '_'); + let name = node.value?.toString().replace(regex_invalid_identifier_chars, '_'); + + // the above could generate conflicts because it has to generate a valid identifier + // so stuff like `0` and `1` or `state%` and `state^` will result in the same string + // so we have to de-conflict. We can only check `public_state` because private state + // can't have literal keys + while (name && public_state.has(name)) { + name = '_' + name; + } + return name; } else { return node.name; } diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-conflicting-get-name/_config.js b/packages/svelte/tests/runtime-runes/samples/class-state-conflicting-get-name/_config.js new file mode 100644 index 000000000000..f47bee71df87 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-conflicting-get-name/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../test'; + +export default test({}); diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-conflicting-get-name/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-state-conflicting-get-name/main.svelte new file mode 100644 index 000000000000..aec1e67cc675 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-conflicting-get-name/main.svelte @@ -0,0 +1,6 @@ + \ No newline at end of file