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

Nested custom components in MDX/Mdsvex #297

Open
tobiasBora opened this issue Jan 10, 2025 · 7 comments
Open

Nested custom components in MDX/Mdsvex #297

tobiasBora opened this issue Jan 10, 2025 · 7 comments

Comments

@tobiasBora
Copy link

tobiasBora commented Jan 10, 2025

I know that custom components are yet to be implemented (can't wait for this), but I was thinking that it might be worse mentioning that it would be great to have support for nested custom components. For instance, I'd love to use in my website nested the collapsible container from https://playground.lexical.dev/, and save them in markdown (via custom components in MDX/Mdsvex files) like:

<CollabsibleContainer title="Click to see details of the proof">
  To prove that $1 + 1 + 1 = 3$, we will start by proving that $1 + 1 = 2$:
  <CollapsibleContainer title="Click to see/hide the proof that $1+1$ = 2" collapsed={false}>
    Raise one finger. Raise another one. See you have 2 raised fingers.
  <CollabsibleContainer>
</CollapsibleContainer>

Note that regexp "cannot count", so cannot know how to properly match closing tags, hence a proper MDX/Mdsvex parser must be implemented for this feature.

See also #32

@kyoshino kyoshino added the enhancement New feature or request label Jan 10, 2025
@kyoshino
Copy link
Member

I’ll figure out how to do this, but there is no plan to support Svelte components because it‘s too costly to run the Svelte compiler on the fly.

@tobiasBora
Copy link
Author

Ok, thanks a lot.

there is no plan to support Svelte components because it‘s too costly to run the Svelte compiler on the fly.

What do you mean exactly? I mainly want the markdown to contain e.g. <Foo prop="bar">, and then the static site builder will take care of turning it into a svelte/react/lit/… component. But having a way to preview the component would be cool, but can't I just add it just like another component used by sveltia? (which already uses svelte under the hood, so should already involve at some point a svelte compilation)

@kyoshino
Copy link
Member

kyoshino commented Jan 10, 2025

Svelte is very different from other frameworks. Components must be complied in advance at the build time. Sveltia CMS being built with Svelte doesn’t make it easy to support Svelte components at all.

To support Svelte components in the CMS, it has to download the latest version of the compiler, initialize it, compile a component, and inject the produced vanilla JavaScript code to the CMS UI, all on the fly, like what Svelte Playground does. It takes time to finish it. And it doesn’t work or is very slow on mobile/tablet apparently because the compiler is too big. So I don’t think it’s feasible to support Svelte components.

React support is a must for Netlify CMS compatibility. I also have a plan to support Preact+HTM because it’s lightweight and easy to write components with the combo. Vue support may also be possible, but not sure.

@tobiasBora
Copy link
Author

Ok, I think we have a different model in mind: I think you imagine the setting where you just have a bare html page + load sveltia via a CDN in the html, and read in the github the source code of the svelte element itself. I agree, this would not be efficient (but this is not what I had in mind). I have in mind a setting where I pre-compile svelte components (for instance via a npm project that compiles both sveltia & the users custom components). This way the browser already loads pre-compiled svelte components, and does not need to do any compilation.

A similar solution (but maybe more generic) could also be to only support custom elements (framework agnostic as defined in https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements), and then for instance use svelte custom elements support, or lit, or react (that can also be rendered into custom elements)… This solution could also work with a simple CDN for instance that would load sveltia + a browser that just loads the JS containing the compiled custom element.

For instance, this page explains how to create a file bundle.js that you can load like:

<html>
<head><script defer src='/build/bundle.js'></script></head>
<body>
  <what-ever>foo</what-ever>
</body>
</html>

and this would work on any platforms supporting bare JS, including mobile…

@tobiasBora
Copy link
Author

For the fun I tried a minimal solution that I described in https://stackoverflow.com/a/79346499/4987648 a minimalist method (no bundler…) to compile this:

<svelte:options customElement="my-component" />
<script>
  export let name = 'World';
</script>

<style>
  h1 {
      color: royalblue;
  }
</style>

<h1>Hello {name}!</h1>

into a resulting file MyComponent.js:

import 'svelte/internal/disclose-version';
import 'svelte/internal/flags/legacy';
import * as $ from 'svelte/internal/client';

var root = $.template(`<h1 class="svelte-11kgty1"> </h1>`);

const $$css = {
    hash: 'svelte-11kgty1',
    code: 'h1.svelte-11kgty1 {color:royalblue;}'
};

export default function MyComponent($$anchor, $$props) {
    $.push($$props, false);
    $.append_styles($$anchor, $$css);

    let name = $.prop($$props, 'name', 12, 'World');
    var h1 = root();
    var text = $.child(h1);

    $.reset(h1);
    $.template_effect(() => $.set_text(text, `Hello ${name() ?? ''}!`));
    $.append($$anchor, h1);

    return $.pop({
        get name() {
            return name();
        },
        set name($$value) {
            name($$value);
            $.flush_sync();
        }
    });
}

customElements.define('my-component', $.create_custom_element(MyComponent, { name: {} }, [], [], true));

Just load it via:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Svelte Custom Element</title>
  <script type="importmap">
    {
      "imports": {
        "svelte/": "https://esm.run/svelte/"
      }
    }
  </script>
  <script src="MyComponent.js" type="module"></script>

</head>
<body>
  <p>The source code should be between this: </p>

  <my-component name="Svelte"></my-component>
  
  <p>and this</p>
</body>
</html>

Enjoy svelte components without heavy compilation on the browser!

@kyoshino
Copy link
Member

kyoshino commented Jan 10, 2025

Yeah, web components would work, no matter how it’s created. Svelte support is not involved there. If you see the document, toBlock and toPreview should return a HTML string snippet, where you could use any available tags. (Not sure if it works with Netlify/Decap CMS)

@tobiasBora
Copy link
Author

One important issue of what you link is that pattern being a regexp, it cannot be used when the same component is nested into itself, as the pattern would match the first open tag with the first close tag. Said differently, this is impossible to parse with regexp:

<foo><foo></foo></foo>'

@kyoshino kyoshino removed the enhancement New feature or request label Mar 7, 2025
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