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

Component from the DOM #102

Open
lucafabbian opened this issue Mar 15, 2024 · 7 comments
Open

Component from the DOM #102

lucafabbian opened this issue Mar 15, 2024 · 7 comments

Comments

@lucafabbian
Copy link

Is there a way to mount the current DOM as a component? Would it ever be?

I would like to create a component from the webpage itself instead of declaring it as a string.
This may be really beneficial for SEOs, and also provide a nice way to get syntax highlighting. It would be game changing from the developer experience.

A quick way to achieve this would be to get the component's innerHTML, parse it, find special tags and then eval the result, I was wondering if you'll be interested in something like this. If you wish, I may work on this and propose a pull request.

@justin-schroeder
Copy link
Owner

There is not currently a way to do that, but this is squarely on the path for adding ssr/hydration support. That said, arrow is still quite experimental and doesn’t have any funding so time is quite limited — in otherwords dont hold your breath waiting for it 😂

@lucafabbian
Copy link
Author

What if we add a stupid svelte-like syntax that just acts as a shortcut and could then by parsed just using string replacement + eval? In this way it will add nearly any code to arrowjs.

for example:

import {evalTemplate} from "arrow-js"
const appElement = document.getElementById('app');

window.data = reactive({
  name: 'hello'
})

const template = evalTemplate(appElement.outerHTML)
template(appElement)
<div id="app">
  {#if name == 'hello'}
    <div>hello</div>
  {:else}
    <div>not hello </div>
  {/if}

  { app.name } 
</div>

@justin-schroeder
Copy link
Owner

justin-schroeder commented Mar 15, 2024

I sorta doubt we’ll ever do control flow tags/templates in arrow. instead they would be more like hydration hints. The actual control flow would always be in javascript. Simple pseudo code example:

html`<div>Hello <span>${() => state.location}<span></div>`

this would server render something like:

<div data-fragment="812ye79gh" data-ssr-pos="[[0,1,0]]">Hello <span>world</span></div>

This could then be re-hydrated directly from the dom:

const el = document.querySelector('[data-fragment="812ye79gh"]')
rehydrate(el, [() => state.location])

Obviously it gets much more complicated than that, but there is the basic idea. The control flow would stay in pure js that ships along with the html.

@lucafabbian
Copy link
Author

lucafabbian commented Apr 15, 2024

That looks great, but it does not address the issue I'm facing. I don't want to use arrow-js inside a server side rendered pipeline, I would argue the best selling point of arrow-js is the lack of a build step.

You throw your interface on a static file server and you are done. Simple and beautiful.
Yet, terrible for SEOs and not very developer friendly - no syntax highlighting and so on. If we were able to build components directly from the innerHTML of an element, it would be way nicer. Maybe not as a core feature of arrow-js, but as a plugin (even though, the template parser would be just like 100 bytes).

I've made a proof of concept with this and other extensions bundled here: https://github.com/lucafabbian/archeryjs/blob/main/src/index.ts
With the relevant function being:

const extractTemplate = (template : string) => {
    let a = 'html`' + template + '`'

  const replacements = [
    ['&gt;', '>'],
    ['&lt;', '<'],
    ['&amp;', '&'],

    [/{#if(.*?)}/g, (_, a) => '${ () => '+ a + ' && html`'],
    ['{/if}', '`}'],

    [/{#for(.*?)of(.*?)}/g, (_, b, a) => '${ () =>'+ a + '.map( (' + b + ') => html`'],
    ['{/for}', '`)}'],


    ['{{', '${($event)=>'],
    ['}}', '}'],
    ['\\{', '{'],
    ['\\}', '}'],
  ]

  for( const [old,replace] of replacements){ // @ts-ignore
    a = a.replaceAll(old, replace)
  }
  
  return window.eval(a);
}

I understand this is not in the original project scope, yet I've included this in several small projects at work, and it works like a charm.

The way you would use this would be something like

const elem = // get element from DOM
const template= extractTemplate(elem.innerHTML)
template(elem)

@justin-schroeder
Copy link
Owner

Gotchya, I hear what you’re saying. There may be some way to do this well. I’m curious what would draw you to using Arrow for this use case vs something like Apline?

@lucafabbian
Copy link
Author

In the end, we actually switched to Alpine, and we are kinda happy with that.
ArrowJS would be better because there is less "magic" behind. You know exactly what's happening and where. Alpine auto-mounts things, and its syntax it's html-centric, whereas we would prefer a JavaScript first approach.

For example, we were using RunCSS to write html with tailwind without preprocessing. ArrowJS let us introduce a hook and process tailwind classes there, while with Alpine we resolved to just watch the whole html tree with a mutation observer.

@justin-schroeder
Copy link
Owner

ouch, yeah thats a big observer haha. Well, arrow is a passion project for me mostly, sadly i dont get nearly enough time to work on it compared to the other OSS work im doing. That said, its my favorite and if I’m ever able to get some time I plan to really flesh it out, so stay tuned for future updates.

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