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

Svelte 5: Regression: Unable to differentiate between components/snippets & regular functions #9903

Closed
sxxov opened this issue Dec 13, 2023 · 3 comments

Comments

@sxxov
Copy link

sxxov commented Dec 13, 2023

Describe the problem

Explanation

Previously in Svelte 3/4, you could just do v instanceof SvelteComponent, v.constructor === SvelteComponent, or whatever prototype crawling thing at runtime you decided. However, in Svelte 5, there doesn't seem to be an exposed way to check that a function is a component/snippet, or just a regular function.

Use-case

This makes conditional rendering of polymorphic values difficult, such as if i wanted to provide either a component, or a factory/predicate that returns a component conditionally.

Personally, my use case is to create a component that can render either a parameterless-snippet, propless-component, or a value, for a library.

Describe the proposed solution

Compiler

The Svelte compiler could either brand each component it generates:

// App.js

export const App = ($$anchor, $$props) => { /* ... */ };
$.brand_component(App);  // or $.brand_snippet(snippet);

or use a WeakSet that contains every component/snippet:

// App.js

export const App = ($$anchor, $$props) => { /* ... */ };
$.register_component(App); // or $.register_snippet(snippet);

EDIT: I found snippet_symbol that Svelte already uses to validate snippets:

const snippet_symbol = Symbol.for('svelte.snippet');

Maybe this can be extended to be applied to components as well, & exposed externally to be checked?

Consumer

It could then be checked using a isComponent/isSnippet API:

<script>
  import { isComponent, isSnippet } from 'svelte';

  let { v } = $props();
</script>

{#if isComponent(v)}
  <svelte:component this={v} />
{:else if isSnippet(v)}
  {@render v()}
{:else}
  {v}
{/if}

Alternatives considered

The current hack I'm using just assumes all functions it receives are Svelte components/snippets if it has either 1 or 2 parameters, & simply decide between the render strategy based on that. It does nothing to decide if it's a component/snippet in the first place.

<script>
  let { v } = $props();
</script>

{#if typeof v === 'function'}
  {#if v.length === 2}
    <svelte:component this={v} />
  {:else if v.length === 1}
    {@render v()}
  {:else}
    {v}
  {/if}
{:else}
  {v}
{/if}

If I really would want to decide if it's a component/snippet, I guess I could use 💀runtime source-based reflection💀, or basically:

typeof v === 'function' && String(v).includes('$$anchor');

which STINKS.

Importance

would make my life easier

@dummdidumm
Copy link
Member

Duplicate of #9774

@dummdidumm dummdidumm marked this as a duplicate of #9774 Dec 13, 2023
@dummdidumm dummdidumm closed this as not planned Won't fix, can't repro, duplicate, stale Dec 13, 2023
@sxxov
Copy link
Author

sxxov commented Dec 13, 2023

@dummdidumm I don’t think this is a duplicate, since this isn’t purely asking to differentiate snippets from components, but rather both from regular functions. At their core, solving either would solve each other partially, I just went ahead & extrapolated the rest based on my use-case.

Feel free to explain to me if I’m mistaken!

@dummdidumm
Copy link
Member

It's asking basically for the same, but I'll make a note in the other issue

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

No branches or pull requests

2 participants