From 6f3dc04c824e7ae6e5a2b0524a743d25a8baa496 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Wed, 15 Nov 2023 12:53:23 +0100 Subject: [PATCH] fix: allow member access on directives (#9462) fixes #9445 --- .changeset/itchy-lions-wash.md | 5 + .../3-transform/client/visitors/template.js | 37 +++++- .../directives-with-member-access/_config.js | 5 + .../_expected/client/index.svelte.js | 108 ++++++++++++++++++ .../index.svelte | 40 +++++++ packages/svelte/tests/snapshot/test.ts | 5 +- 6 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 .changeset/itchy-lions-wash.md create mode 100644 packages/svelte/tests/snapshot/samples/directives-with-member-access/_config.js create mode 100644 packages/svelte/tests/snapshot/samples/directives-with-member-access/_expected/client/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/directives-with-member-access/index.svelte diff --git a/.changeset/itchy-lions-wash.md b/.changeset/itchy-lions-wash.md new file mode 100644 index 000000000000..fdd9085d5ec4 --- /dev/null +++ b/.changeset/itchy-lions-wash.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: allow member access on directives 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 a8ac31593498..56c5c512e00a 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 @@ -74,6 +74,22 @@ function serialize_style_directives(style_directives, element_id, context, is_at } } +/** + * goes from nested.access to nested['access'] + * @param {string} expression + */ +function member_expression_id_to_literal(expression) { + // this allow for accessing members of an object + const splitted_expression = expression.split('.'); + + let new_expression = splitted_expression.shift() ?? ''; + + for (let new_piece of splitted_expression) { + new_expression += `['${new_piece}']`; + } + return new_expression; +} + /** * Serializes each class directive into something like `$.class_toogle(element, class_name, value)` * and adds it either to init or update, depending on whether or not the value or the attributes are dynamic. @@ -1676,7 +1692,16 @@ export const template_visitors = { ? b.literal(null) : b.thunk(/** @type {import('estree').Expression} */ (visit(node.expression))); - state.init.push(b.stmt(b.call('$.animate', state.node, b.id(node.name), expression))); + state.init.push( + b.stmt( + b.call( + '$.animate', + state.node, + b.id(member_expression_id_to_literal(node.name)), + expression + ) + ) + ); }, ClassDirective(node, { state, next }) { error(node, 'INTERNAL', 'Node should have been handled elsewhere'); @@ -1696,7 +1721,7 @@ export const template_visitors = { b.call( type, state.node, - b.id(node.name), + b.id(member_expression_id_to_literal(node.name)), expression, node.modifiers.includes('global') ? b.true : b.false ) @@ -2417,7 +2442,13 @@ export const template_visitors = { /** @type {import('estree').Expression[]} */ const args = [ state.node, - b.arrow(params, b.call(serialize_get_binding(b.id(node.name), state), ...params)) + b.arrow( + params, + b.call( + serialize_get_binding(b.id(member_expression_id_to_literal(node.name)), state), + ...params + ) + ) ]; if (node.expression) { 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 new file mode 100644 index 000000000000..43e2501b6ef0 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/directives-with-member-access/_config.js @@ -0,0 +1,5 @@ +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 new file mode 100644 index 000000000000..3c60a11a7688 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/directives-with-member-access/_expected/client/index.svelte.js @@ -0,0 +1,108 @@ +// 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 new file mode 100644 index 000000000000..b5487aae33cb --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/directives-with-member-access/index.svelte @@ -0,0 +1,40 @@ + + +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/packages/svelte/tests/snapshot/test.ts b/packages/svelte/tests/snapshot/test.ts index 26f3a1a1b7ee..495af0df443f 100644 --- a/packages/svelte/tests/snapshot/test.ts +++ b/packages/svelte/tests/snapshot/test.ts @@ -7,11 +7,14 @@ 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); - compile_directory(cwd, 'server', config.compileOptions); + if (!config.skip_if_ssr) { + compile_directory(cwd, 'server', config.compileOptions); + } // run `UPDATE_SNAPSHOTS=true pnpm test snapshot` to update snapshot tests if (process.env.UPDATE_SNAPSHOTS) {