woutdp / live_svelte

Svelte inside Phoenix LiveView with seamless end-to-end reactivity
https://hexdocs.pm/live_svelte
MIT License
1.29k stars 53 forks source link

Placing components into sub-directories generates render error #100

Closed ahacking closed 11 months ago

ahacking commented 11 months ago

I need to organize components into folders, however I have been unable get a simple single line "Hello World" component to work if I place it within a sub-directory of assets/svelte.

For example, if I have a component in assets/svelte/test/HelloWorld.svelte:

Hello World

And I use it in my markup:

<.svelte name="HelloWorld" />

I get the following error:

NodeJS.Error at GET / Cannot read properties of undefined (reading 'render') TypeError: Cannot read properties of undefined (reading 'render') at render2 (/home/andrew/work/ls_test/priv/svelte/server.js:530:22) at callModuleFunction (/home/andrew/work/ls_test/deps/nodejs/priv/server.js:32:23) at getResponse (/home/andrew/work/ls_test/deps/nodejs/priv/server.js:44:26) at Interface.onLine (/home/andrew/work/ls_test/deps/nodejs/priv/server.js:53:39) at Interface.emit (node:events:514:28) at [_onLine] [as _onLine] (node:internal/readline/interface:416:12) at [_normalWrite] [as _normalWrite] (node:internal/readline/interface:610:22) at Socket.ondata (node:internal/readline/interface:243:23) at Socket.emit (node:events:514:28) at addChunk (node:internal/streams/readable:376:12)

I thought, this might be a simple fix....

I tried changing live_svelte/assets/js/liv_svelte/utils.js to handle sub-directories:

import { basename } from 'path'

export function normalizeComponents(components) {
  if (!Array.isArray(components.default) || !Array.isArray(components.filenames)) return components

  const normalized = {}
  for (const [index, module] of components.default.entries()) {
    const Component = module.default
    //const name = components.filenames[index].replace("../svelte/", "").replace(".svelte", "")
    const name = basename(components.filenames[index])
    normalized[name] = Component
  }
  return normalized
}

And I also noticed the component convenience support deps/live_svelte/lib/mix/components.ex didn't account for sub-directories either.

So I changed that to:

def get_svelte_components do
    "./assets/svelte/**/*.svelte"
    |> Path.wildcard()
    |> Enum.map(fn path -> Path.basename(path, ".svelte") end)
  end

After force rebuilding deps/live_svelte and priv/static/svelte I still get the same error.

It seems there isintent in the code to support sub-directories as svelte/**/*.svelte is used elsewhere.

What am I missing to get this to work?

My ultimate goal is to release a starter project using shadcn-svelte and live_svelte. Shadcn-svelte organises code into sub-directories which also uses an index.ts in each folder.

I have got everything else working, Typescript support and tested shadcn-svelte with a simple switch component (without its corresponding index.ts) and it works just fine, so it seems I am very close to having a very compelling headless UI for liveview that does accessibility right, all enabled by live_svelte and themed with tailwind via shadcn_svelte.

Screenshot from 2023-12-12 13-05-14

woutdp commented 11 months ago

Did you try:

<.svelte name="your_sub_directory/HelloWorld" />
ahacking commented 11 months ago

Did you try:

<.svelte name="your_sub_directory/HelloWorld" />

Thank-you so much! I can't believe I missed that in the docs.

I can confirm that it renders without error. (I also backed out all my changes above).

I also verified that the index.ts from chadcn-svelte which contains variants also works just fine:

    <div class="flex mt-8 gap-4">
      <.svelte name="HelloWorld" />
      <.svelte name="badge/badge">Default</.svelte>
      <.svelte name="badge/badge" props={%{variant: "secondary"}}>Secondary</.svelte>
      <.svelte name="badge/badge" props={%{variant: "destructive"}}>Destructive</.svelte>
      <.svelte name="badge/badge" props={%{variant: "outline"}}>Outline</.svelte>
    </div>

Screenshot from 2023-12-12 14-09-30

My remaining question is, is there any issue with the LiveSvelte.Components helper working with components in sub-folders?

woutdp commented 11 months ago

Happy to hear it works! If there's a way you think that can improve the docs, feel free to suggest or create a PR.

My remaining question is, is there any issue with the LiveSvelte.Components helper working with components in sub-folders?

That might not be working correctly with subfolders as there's limitations to the name of a component, like slashes won't work for example.

ahacking commented 11 months ago

Happy to hear it works! If there's a way you think that can improve the docs, feel free to suggest or create a PR.

Certainly. I will see what I come up with for naming and resolving components in sub directories with the helper.

And thanks again for all your work on LiveSvelte and putting out those videos as I would not have discovered it otherwise. I have mentioned LiveSvelte quite a few times in ElxirForums as an interesting solution to the challenge of responsive UI and the lack of a good UI component system and I'm quite surprised it hasn't garnered more attention.

Once I properly bed things down I plan to share my starter project with full documentation on how to set things up and my learnings.