diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index 56c5c512e00a..942bfab8c6a1 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -34,6 +34,7 @@ import { EACH_ITEM_REACTIVE, EACH_KEYED } from '../../../../../constants.js'; +import { regex_is_valid_identifier } from '../../../patterns.js'; /** * Serializes each style directive into something like `$.style(element, style_property, value)` @@ -75,19 +76,24 @@ function serialize_style_directives(style_directives, element_id, context, is_at } /** - * goes from nested.access to nested['access'] - * @param {string} expression + * For unfortunate legacy reasons, directive names can look like this `use:a.b-c` + * This turns that string into a member expression + * @param {string} name */ -function member_expression_id_to_literal(expression) { +function parse_directive_name(name) { // this allow for accessing members of an object - const splitted_expression = expression.split('.'); + const parts = name.split('.'); + let part = /** @type {string} */ (parts.shift()); - let new_expression = splitted_expression.shift() ?? ''; + /** @type {import('estree').Identifier | import('estree').MemberExpression} */ + let expression = b.id(part); - for (let new_piece of splitted_expression) { - new_expression += `['${new_piece}']`; + while ((part = /** @type {string} */ (parts.shift()))) { + const computed = !regex_is_valid_identifier.test(part); + expression = b.member(expression, computed ? b.literal(part) : b.id(part), computed); } - return new_expression; + + return expression; } /** @@ -1697,7 +1703,7 @@ export const template_visitors = { b.call( '$.animate', state.node, - b.id(member_expression_id_to_literal(node.name)), + /** @type {import('estree').Expression} */ (visit(parse_directive_name(node.name))), expression ) ) @@ -1721,7 +1727,7 @@ export const template_visitors = { b.call( type, state.node, - b.id(member_expression_id_to_literal(node.name)), + /** @type {import('estree').Expression} */ (visit(parse_directive_name(node.name))), expression, node.modifiers.includes('global') ? b.true : b.false ) @@ -2445,7 +2451,7 @@ export const template_visitors = { b.arrow( params, b.call( - serialize_get_binding(b.id(member_expression_id_to_literal(node.name)), state), + /** @type {import('estree').Expression} */ (visit(parse_directive_name(node.name))), ...params ) ) diff --git a/packages/svelte/tests/runtime-runes/samples/directives-with-member-access/_config.js b/packages/svelte/tests/runtime-runes/samples/directives-with-member-access/_config.js new file mode 100644 index 000000000000..29a23b32246e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/directives-with-member-access/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +// no need to compare the rendered HTML — we only care +// that the generated code is valid +export default test({}); diff --git a/packages/svelte/tests/runtime-runes/samples/directives-with-member-access/main.svelte b/packages/svelte/tests/runtime-runes/samples/directives-with-member-access/main.svelte new file mode 100644 index 000000000000..66eb1eed36a7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/directives-with-member-access/main.svelte @@ -0,0 +1,19 @@ + + + +
+
+
+
+
diff --git a/packages/svelte/tests/snapshot/samples/directives-with-member-access/_config.js b/packages/svelte/tests/snapshot/samples/directives-with-member-access/_config.js deleted file mode 100644 index 43e2501b6ef0..000000000000 --- a/packages/svelte/tests/snapshot/samples/directives-with-member-access/_config.js +++ /dev/null @@ -1,5 +0,0 @@ -import { test } from '../../test'; - -export default test({ - skip_if_ssr: true -}); diff --git a/packages/svelte/tests/snapshot/samples/directives-with-member-access/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/directives-with-member-access/_expected/client/index.svelte.js deleted file mode 100644 index 3c60a11a7688..000000000000 --- a/packages/svelte/tests/snapshot/samples/directives-with-member-access/_expected/client/index.svelte.js +++ /dev/null @@ -1,108 +0,0 @@ -// index.svelte (Svelte VERSION) -// Note: compiler output will change before 5.0 is released! -import "svelte/internal/disclose-version"; -import * as $ from "svelte/internal"; - -var frag = $.template(`
`, true); - -export default function Directives_with_member_access($$anchor, $$props) { - $.push($$props, false); - - const one = () => {}; - const nested = { one, "with-string": one }; - const evenmore = { nested }; - - /* Init */ - var fragment = $.open_frag($$anchor, true, frag); - var div = $.child_frag(fragment); - var div_1 = $.sibling($.sibling(div)); - var div_2 = $.sibling($.sibling(div_1)); - var div_3 = $.sibling($.sibling(div_2)); - - $.transition(div_3, one, null, false); - - var div_4 = $.sibling($.sibling(div_3)); - - $.transition(div_4, nested['one'], null, false); - - var div_5 = $.sibling($.sibling(div_4)); - - $.transition(div_5, evenmore['nested']['one'], null, false); - - var div_6 = $.sibling($.sibling(div_5)); - - $.animate(div_6, one, null); - - var div_7 = $.sibling($.sibling(div_6)); - - $.animate(div_7, nested['one'], null); - - var div_8 = $.sibling($.sibling(div_7)); - - $.animate(div_8, evenmore['nested']['one'], null); - - var div_9 = $.sibling($.sibling(div_8)); - - $.in(div_9, one, null, false); - - var div_10 = $.sibling($.sibling(div_9)); - - $.in(div_10, nested['one'], null, false); - - var div_11 = $.sibling($.sibling(div_10)); - - $.in(div_11, evenmore['nested']['one'], null, false); - - var div_12 = $.sibling($.sibling(div_11)); - - $.out(div_12, one, null, false); - - var div_13 = $.sibling($.sibling(div_12)); - - $.out(div_13, nested['one'], null, false); - - var div_14 = $.sibling($.sibling(div_13)); - - $.out(div_14, evenmore['nested']['one'], null, false); - - var div_15 = $.sibling($.sibling(div_14)); - var div_16 = $.sibling($.sibling(div_15)); - var div_17 = $.sibling($.sibling(div_16)); - - $.transition(div_17, nested['with-string'], null, false); - - var div_18 = $.sibling($.sibling(div_17)); - - $.transition(div_18, evenmore['nested']['with-string'], null, false); - - var div_19 = $.sibling($.sibling(div_18)); - - $.animate(div_19, nested['with-string'], null); - - var div_20 = $.sibling($.sibling(div_19)); - - $.animate(div_20, evenmore['nested']['with-string'], null); - - var div_21 = $.sibling($.sibling(div_20)); - - $.in(div_21, nested['with-string'], null, false); - - var div_22 = $.sibling($.sibling(div_21)); - - $.in(div_22, evenmore['nested']['with-string'], null, false); - - var div_23 = $.sibling($.sibling(div_22)); - - $.out(div_23, nested['with-string'], null, false); - - var div_24 = $.sibling($.sibling(div_23)); - - $.out(div_24, evenmore['nested']['with-string'], null, false); - $.action(div, $$node => one($$node)); - $.action(div_1, $$node => nested['one']($$node)); - $.action(div_2, $$node => evenmore['nested']['one']($$node)); - $.action(div_15, $$node => nested['with-string']($$node)); - $.action(div_16, $$node => evenmore['nested']['with-string']($$node)); - $.close_frag($$anchor, fragment); - $.pop(); -} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/directives-with-member-access/index.svelte b/packages/svelte/tests/snapshot/samples/directives-with-member-access/index.svelte deleted file mode 100644 index b5487aae33cb..000000000000 --- a/packages/svelte/tests/snapshot/samples/directives-with-member-access/index.svelte +++ /dev/null @@ -1,40 +0,0 @@ - - -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
- -
-
- -
-
- -
-
- -
-
\ No newline at end of file diff --git a/packages/svelte/tests/snapshot/test.ts b/packages/svelte/tests/snapshot/test.ts index 495af0df443f..26f3a1a1b7ee 100644 --- a/packages/svelte/tests/snapshot/test.ts +++ b/packages/svelte/tests/snapshot/test.ts @@ -7,14 +7,11 @@ import { VERSION } from 'svelte/compiler'; interface SnapshotTest extends BaseTest { compileOptions?: Partial; - skip_if_ssr?: boolean; } const { test, run } = suite(async (config, cwd) => { compile_directory(cwd, 'client', config.compileOptions); - if (!config.skip_if_ssr) { - compile_directory(cwd, 'server', config.compileOptions); - } + compile_directory(cwd, 'server', config.compileOptions); // run `UPDATE_SNAPSHOTS=true pnpm test snapshot` to update snapshot tests if (process.env.UPDATE_SNAPSHOTS) {