bernaferrari / FigmaToCode

Generate responsive pages and apps on HTML, Tailwind, Flutter and SwiftUI.
https://www.figma.com/community/plugin/842128343887142055
GNU General Public License v3.0
3.79k stars 300 forks source link

[Proposal] AST approach for HTML, Tailwind, BEM, etc #120

Open davestewart opened 4 months ago

davestewart commented 4 months ago

Hey @bernaferrari,

Whilst working on the Tailwind renderer and the variables stuff, I noticed quite a lot of duplication, and some situations where it would be difficult or cumbersome to re-process elements once rendered as strings.

For example:

I know you have an interm "alt-node" format, but I figured an additional interim AST format would work at least in the HTML domain, so the Figma element would be parsed to this standard format, then that format could be more-easily processed then manipulated, for example:

A single renderer which walks the tree is also simpler than having each node render its HTML.

Potentially could even simplify platform implementation by:

const node = parseElement(selected)
const htmlNode = walkTree(node, classesToStyles)
const html = renderHtml(htmlNode)
const vars = walkTree(htmlNode, findVariables)
const bemNode = walkTree(htmlNode, makeBEM)
const colors = await renderVariables(vars)
renderBEM(bemNode, colors)

I had a bit of bash this morning and got an initial renderer up and running (have yet to integrate with existing platform code):

CleanShot 2024-07-19 at 15 53 38

Here's the AST:

{
  tag: 'div',
  name: 'Root',
  classes: [ 'foo', 'bar' ],
  children: [
    {
      tag: 'div',
      name: 'Container',
      classes: [ 'child' ],
      children: [
        { tag: 'div', name: 'Child 1', classes: [...] },
        { tag: 'div', name: 'Child 2', styles: {...} },
        { tag: 'input', name: 'Child 3', attrs: {...} },
        { tag: 'div', name: 'Child 4', attrs: {...} },
        { tag: 'div', name: 'Child 5', styles: {...} }
      ]
    }
  ]
}

And the HTML:

<div data-name="Root" className="foo bar">
  <div data-name="Container" className="child">
    <div data-name="Child 1" className="foo bar" />
    <div data-name="Child 2" style="baz: 1px; qux: 2px;" />
    <input data-name="Child 3" checked="true" />
    <div data-name="Child 4" title="Missing variable!" />
    <div data-name="Child 5" style="padding-left: 1px; padding-right: 1px;" />
  </div>
</div>

Anyway.

Another one for ideas to improve.

I think I can probably finish the variables stuff for now, but this would certainly be a more robust solution in the long term.

Will commit the code at some point and PR.