Skip to content

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

@sxxov

Description

@sxxov

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

Activity

dummdidumm

dummdidumm commented on Dec 13, 2023

@dummdidumm
Member

Duplicate of #9774

sxxov

sxxov commented on Dec 13, 2023

@sxxov
Author

@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

dummdidumm commented on Dec 13, 2023

@dummdidumm
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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @dummdidumm@sxxov

        Issue actions

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