Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot use exported functions from a named slot in another slot #7485

Closed
legnaleurc opened this issue Apr 25, 2022 · 3 comments
Closed

Cannot use exported functions from a named slot in another slot #7485

legnaleurc opened this issue Apr 25, 2022 · 3 comments
Labels
bug compiler Changes relating to the compiler

Comments

@legnaleurc
Copy link

legnaleurc commented Apr 25, 2022

Describe the bug

<!-- Inner.svelte -->
<script>
  function innerCall() {
    console.info('I AM INNER');
  }
</script>
<slot name="inner_slot" {innerCall} />

<!-- Outer.svelte -->
<Inner>
  <svelte:fragment slot="inner_slot" let:innerCall>
    <slot outerCall={() => innerCall()} />
  </svelte:fragment>
</Inner>

This will throw an exception like:

Cannot read properties of undefined (reading 'push')

while compiling.

I haven't test all cases, but I think this only happens for named slots.
If Inner only provides default slot then this will work.

And if I don't use arrow function, just pass innerCall as-is, then it can pass.
e.g. <slot outerCall={innerCall} />

Also use innerCall in normal HTML tags works.
e.g. <button on:click={() => innerCall()}

Reproduction

https://svelte.dev/repl/f3777f8560d74cb9a6bdc1f6e1eb334e?version=3.47.0

Logs

file: .../Outer.svelte
> Cannot read properties of undefined (reading 'push')
    at Object.leave (.../svelte/compiler.mjs:19376:75)
    at SyncWalker.visit (.../svelte/compiler.mjs:6297:16)
    at walk (.../svelte/compiler.mjs:6338:18)
    at Expression.manipulate (.../svelte/compiler.mjs:19248:22)
    at .../svelte/compiler.mjs:25445:91
    at Array.map (<anonymous>)
    at get_value (.../svelte/compiler.mjs:25445:10)
    at .../svelte/compiler.mjs:25434:27
    at Array.map (<anonymous>)
    at get_slot_data (.../svelte/compiler.mjs:25426:14)


### System Info

```shell
System:
    OS: Linux 5.10 Debian GNU/Linux 11 (bullseye) 11 (bullseye)
    CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
    Memory: 11.34 GB / 12.38 GB
    Container: Yes
    Shell: 5.1.4 - /bin/bash
  Binaries:
    Node: 16.14.0 - ~/.nvm/versions/node/v16.14.0/bin/node
    Yarn: 1.22.17 - ~/.nvm/versions/node/v16.14.0/bin/yarn
    npm: 8.3.1 - ~/.nvm/versions/node/v16.14.0/bin/npm
  npmPackages:
    svelte: ^3.44.0 => 3.47.0

Severity

annoyance

@dummdidumm dummdidumm added bug compiler Changes relating to the compiler labels Apr 25, 2022
@legnaleurc
Copy link
Author

legnaleurc commented Apr 28, 2022

I did some search and test with the compiler, I think there is a problem (or an undocumented behavior) for named-slot props and default slot props.

If I change the Outer.svelte to this:

<!-- Outer.svelte -->
<Inner let:innerCall>
  <svelte:fragment slot="inner_slot">
    <slot outerCall={() => innerCall()} />
  </svelte:fragment>
</Inner>

then there is no error.

My expectation is:

  • Props in default slot
    • Use let: at the component root to get the value, and thus can be used anywhere inside the component.
  • Props in named slot
    • Use let: at the component which assigned to that slot to get the value.
    • Use let: at the component root will get undefined (or compile error).

The current behavior:

  • Props in default slot
    • Meet my expectation
  • Props in named slot
    • Use let: at the component which assigned to that slot to get the value.
      • Sometimes work sometimes not.
    • Use let: at the component root will get undefined (or compile error).
      • You can use let: at the component root. Will get undefined for other slots.

This is something already reported in #7245.

In fact, all props in named-slots are exposed to the component root.
You can use let: at the root, and only the correct named-slot will receive the value, others will simply get undefined.

My example:
https://svelte.dev/repl/e9567f359ae44cd28af552e463d6357a?version=3.47.0

So you can (and maybe should) ignore what official doc said:

> Named slots can also have props; use the `let` directive on an element with a `slot="..."` attribute, instead of on the component itself.

Just always use let: at the component root.

The fact that this test case passes kind of proved this:
https://github.com/sveltejs/svelte/tree/master/test/runtime/samples/component-slot-nested-in-slot

@magentaqin
Copy link
Contributor

magentaqin commented Jul 8, 2022

The bug is here is not about named slot or default slot, as both will cause error:

<svelte:fragment let:innerCall>
    <slot outerCall={() => innerCall()} />
 </svelte:fragment>

or

<svelte:fragment let:innerCall slot="inner_slot">
  <slot outerCall={() => innerCall()} />
</svelte:fragment>

As @legnaleurc said, the bug here could be solved by use let: at the root, but it contradicts with the fact that <button on:click={() => innerCall()} works when use let: at the svelte:fragment, like:

<svelte:fragment let:innerCall slot="inner_slot">
    <button on:click={() => innerCall()}
</svelte:fragment>

After debugging, I think the culprit is this line, which has not put node type "SlotTemplate" into consideration.
Screen Shot 2022-07-08 at 10 11 05 PM

Maybe you'd wonder why pass innerCall as-is works but arrow function does not work.

<svelte:fragment let:innerCall slot="inner_slot">
  <slot outerCall={innerCall} />
</svelte:fragment>

The reason is that it is evaluated as "literal" type and not entering "function_expression" if block.
Screen Shot 2022-07-08 at 10 44 23 PM

@Conduitry
Copy link
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug compiler Changes relating to the compiler
Projects
None yet
Development

No branches or pull requests

4 participants