Skip to content

Commit 6f3dc04

Browse files
fix: allow member access on directives (#9462)
fixes #9445
1 parent 9eb969d commit 6f3dc04

File tree

6 files changed

+196
-4
lines changed

6 files changed

+196
-4
lines changed

.changeset/itchy-lions-wash.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: allow member access on directives

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

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,22 @@ function serialize_style_directives(style_directives, element_id, context, is_at
7474
}
7575
}
7676

77+
/**
78+
* goes from nested.access to nested['access']
79+
* @param {string} expression
80+
*/
81+
function member_expression_id_to_literal(expression) {
82+
// this allow for accessing members of an object
83+
const splitted_expression = expression.split('.');
84+
85+
let new_expression = splitted_expression.shift() ?? '';
86+
87+
for (let new_piece of splitted_expression) {
88+
new_expression += `['${new_piece}']`;
89+
}
90+
return new_expression;
91+
}
92+
7793
/**
7894
* Serializes each class directive into something like `$.class_toogle(element, class_name, value)`
7995
* 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 = {
16761692
? b.literal(null)
16771693
: b.thunk(/** @type {import('estree').Expression} */ (visit(node.expression)));
16781694

1679-
state.init.push(b.stmt(b.call('$.animate', state.node, b.id(node.name), expression)));
1695+
state.init.push(
1696+
b.stmt(
1697+
b.call(
1698+
'$.animate',
1699+
state.node,
1700+
b.id(member_expression_id_to_literal(node.name)),
1701+
expression
1702+
)
1703+
)
1704+
);
16801705
},
16811706
ClassDirective(node, { state, next }) {
16821707
error(node, 'INTERNAL', 'Node should have been handled elsewhere');
@@ -1696,7 +1721,7 @@ export const template_visitors = {
16961721
b.call(
16971722
type,
16981723
state.node,
1699-
b.id(node.name),
1724+
b.id(member_expression_id_to_literal(node.name)),
17001725
expression,
17011726
node.modifiers.includes('global') ? b.true : b.false
17021727
)
@@ -2417,7 +2442,13 @@ export const template_visitors = {
24172442
/** @type {import('estree').Expression[]} */
24182443
const args = [
24192444
state.node,
2420-
b.arrow(params, b.call(serialize_get_binding(b.id(node.name), state), ...params))
2445+
b.arrow(
2446+
params,
2447+
b.call(
2448+
serialize_get_binding(b.id(member_expression_id_to_literal(node.name)), state),
2449+
...params
2450+
)
2451+
)
24212452
];
24222453

24232454
if (node.expression) {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
skip_if_ssr: true
5+
});
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// index.svelte (Svelte VERSION)
2+
// Note: compiler output will change before 5.0 is released!
3+
import "svelte/internal/disclose-version";
4+
import * as $ from "svelte/internal";
5+
6+
var frag = $.template(`<div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div> <div></div>`, true);
7+
8+
export default function Directives_with_member_access($$anchor, $$props) {
9+
$.push($$props, false);
10+
11+
const one = () => {};
12+
const nested = { one, "with-string": one };
13+
const evenmore = { nested };
14+
15+
/* Init */
16+
var fragment = $.open_frag($$anchor, true, frag);
17+
var div = $.child_frag(fragment);
18+
var div_1 = $.sibling($.sibling(div));
19+
var div_2 = $.sibling($.sibling(div_1));
20+
var div_3 = $.sibling($.sibling(div_2));
21+
22+
$.transition(div_3, one, null, false);
23+
24+
var div_4 = $.sibling($.sibling(div_3));
25+
26+
$.transition(div_4, nested['one'], null, false);
27+
28+
var div_5 = $.sibling($.sibling(div_4));
29+
30+
$.transition(div_5, evenmore['nested']['one'], null, false);
31+
32+
var div_6 = $.sibling($.sibling(div_5));
33+
34+
$.animate(div_6, one, null);
35+
36+
var div_7 = $.sibling($.sibling(div_6));
37+
38+
$.animate(div_7, nested['one'], null);
39+
40+
var div_8 = $.sibling($.sibling(div_7));
41+
42+
$.animate(div_8, evenmore['nested']['one'], null);
43+
44+
var div_9 = $.sibling($.sibling(div_8));
45+
46+
$.in(div_9, one, null, false);
47+
48+
var div_10 = $.sibling($.sibling(div_9));
49+
50+
$.in(div_10, nested['one'], null, false);
51+
52+
var div_11 = $.sibling($.sibling(div_10));
53+
54+
$.in(div_11, evenmore['nested']['one'], null, false);
55+
56+
var div_12 = $.sibling($.sibling(div_11));
57+
58+
$.out(div_12, one, null, false);
59+
60+
var div_13 = $.sibling($.sibling(div_12));
61+
62+
$.out(div_13, nested['one'], null, false);
63+
64+
var div_14 = $.sibling($.sibling(div_13));
65+
66+
$.out(div_14, evenmore['nested']['one'], null, false);
67+
68+
var div_15 = $.sibling($.sibling(div_14));
69+
var div_16 = $.sibling($.sibling(div_15));
70+
var div_17 = $.sibling($.sibling(div_16));
71+
72+
$.transition(div_17, nested['with-string'], null, false);
73+
74+
var div_18 = $.sibling($.sibling(div_17));
75+
76+
$.transition(div_18, evenmore['nested']['with-string'], null, false);
77+
78+
var div_19 = $.sibling($.sibling(div_18));
79+
80+
$.animate(div_19, nested['with-string'], null);
81+
82+
var div_20 = $.sibling($.sibling(div_19));
83+
84+
$.animate(div_20, evenmore['nested']['with-string'], null);
85+
86+
var div_21 = $.sibling($.sibling(div_20));
87+
88+
$.in(div_21, nested['with-string'], null, false);
89+
90+
var div_22 = $.sibling($.sibling(div_21));
91+
92+
$.in(div_22, evenmore['nested']['with-string'], null, false);
93+
94+
var div_23 = $.sibling($.sibling(div_22));
95+
96+
$.out(div_23, nested['with-string'], null, false);
97+
98+
var div_24 = $.sibling($.sibling(div_23));
99+
100+
$.out(div_24, evenmore['nested']['with-string'], null, false);
101+
$.action(div, $$node => one($$node));
102+
$.action(div_1, $$node => nested['one']($$node));
103+
$.action(div_2, $$node => evenmore['nested']['one']($$node));
104+
$.action(div_15, $$node => nested['with-string']($$node));
105+
$.action(div_16, $$node => evenmore['nested']['with-string']($$node));
106+
$.close_frag($$anchor, fragment);
107+
$.pop();
108+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<script>
2+
const one = ()=>{};
3+
const nested = {one, "with-string": one};
4+
const evenmore = {nested};
5+
</script>
6+
7+
<div use:one />
8+
<div use:nested.one />
9+
<div use:evenmore.nested.one />
10+
11+
<div transition:one />
12+
<div transition:nested.one />
13+
<div transition:evenmore.nested.one />
14+
15+
<div animate:one />
16+
<div animate:nested.one />
17+
<div animate:evenmore.nested.one />
18+
19+
<div in:one />
20+
<div in:nested.one />
21+
<div in:evenmore.nested.one />
22+
23+
<div out:one />
24+
<div out:nested.one />
25+
<div out:evenmore.nested.one />
26+
27+
<div use:nested.with-string />
28+
<div use:evenmore.nested.with-string />
29+
30+
<div transition:nested.with-string />
31+
<div transition:evenmore.nested.with-string />
32+
33+
<div animate:nested.with-string />
34+
<div animate:evenmore.nested.with-string />
35+
36+
<div in:nested.with-string />
37+
<div in:evenmore.nested.with-string />
38+
39+
<div out:nested.with-string />
40+
<div out:evenmore.nested.with-string />

packages/svelte/tests/snapshot/test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ import { VERSION } from 'svelte/compiler';
77

88
interface SnapshotTest extends BaseTest {
99
compileOptions?: Partial<import('#compiler').CompileOptions>;
10+
skip_if_ssr?: boolean;
1011
}
1112

1213
const { test, run } = suite<SnapshotTest>(async (config, cwd) => {
1314
compile_directory(cwd, 'client', config.compileOptions);
14-
compile_directory(cwd, 'server', config.compileOptions);
15+
if (!config.skip_if_ssr) {
16+
compile_directory(cwd, 'server', config.compileOptions);
17+
}
1518

1619
// run `UPDATE_SNAPSHOTS=true pnpm test snapshot` to update snapshot tests
1720
if (process.env.UPDATE_SNAPSHOTS) {

0 commit comments

Comments
 (0)