0

I am developing a custom UI component library using Svelte and each component has at least a CSS class which is the same name the component tag name. Also, I have a base component which all other components are using that. Here is the simplified codes:

Base.svelte

<script>
    export let componentName
    export let tag = 'div'
</script>
<svelte:element this={tag} class={componentName}>
    <slot />
</svelte:element>  

Foo.svelte

<script>
    import Base from './Base.svelte'
</script>
<Base tag="b" componentName="Foo">
    <slot />
</Base>

Bar.svelte

<script>
    import Base from './Base.svelte'
</script>
<Base tag="h3" componentName="Bar">
    <slot />
</Base>

App.svelte

<script>
    import Foo from './Foo.svelte'
    import Bar from './Bar.svelte'
</script>

<Foo>
    <Bar>This is Bar 1</Bar>
    <Bar>This is Bar 2</Bar>
</Foo>
<Foo>
    Foo
</Foo>

In above code, I have to set the componentName prop which actually should be a constant. Also, I should always update that in case I change the component's file name.

Is there anyway to get the current component tag name and set it in Base component?

Or, a function that returns the name similar to what we have for the component:

import { current_component } from 'svelte/internal';

Amir Pournasserian
  • 1,600
  • 5
  • 22
  • 46
  • You should not use stuff from `svelte/internal` in your code. Also, the base component strikes me as rather pointless. – H.B. Jan 04 '23 at 22:41

1 Answers1

1

You cannot get the tag name; it is not static and components can be rendered without using a tag at all via <svelte:component> or by just using the constructor in code. There are ways to get the file name, which then could be transformed.

I would approach this via pre-processing. Svelte already provides a way to do this via its config, e.g.

// svelte.config.js
import { basename } from 'path';

/** @type {import('svelte/types/compiler/preprocess').PreprocessorGroup} */
const addComponentName = {
    script: ({ content, filename }) => {
        const componentName = basename(filename, '.svelte');
        return {
            code: `const __componentName = ${JSON.stringify(componentName)};\n`
                + content,
        };
    }
}

export default { preprocess: [addComponentName] };

This adds code that sets a constant __componentName to the file name, which thus can be used in the rest of the code or the markup. You can use whatever name works for you; the underscores are there to prevent potential name collisions with the existing code.

(If you are using TypeScript you have to declare a global for this to prevent errors, as the declaration will be missing from the source files.)

H.B.
  • 166,899
  • 29
  • 327
  • 400
  • Thanks for the solution. But, if I deploy to npm, users should update ```svelte.config.js``` in order to use the components. How about using ```SvelteRegisterComponent```? here is another idea: document.addEventListener('SvelteRegisterComponent', e => { console.log(e.detail.tagName) }); – Amir Pournasserian Jan 05 '23 at 17:41
  • 1
    You should not publish the pure source to NPM but the pre-processed code, hence the user of the package should not need to do anything. – H.B. Jan 05 '23 at 19:45
  • I'm using Sveltekit to deploy to NPM. Would you give me an idea on how to pre-process the code? – Amir Pournasserian Jan 05 '23 at 20:25
  • It does that automatically – H.B. Jan 05 '23 at 20:33