Issue getting generic JSX transform to work with lite-jsx

I’m working on some bindings for lite-jsx and I’m hitting an issue with how it handles children. I’m wondering if there is something I can do to resolve this.

// lit.res
type element = Jsx.element

type component<'props> = Jsx.component<'props>

type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return>
@module("@lite-jsx/core")
external jsx: (component<'props>, 'props) => element = "h"

@module("@lite-jsx/core")
external jsxKeyed: (component<'props>, 'props, ~key: string=?, @ignore unit) => element = "h"

@module("@lite-jsx/core")
external jsxs: (component<'props>, 'props) => element = "h"

@module("@lite-jsx/core")
external jsxsKeyed: (component<'props>, 'props, ~key: string=?, @ignore unit) => element = "h"

external array: array<element> => element = "%identity"
external float: float => element = "%identity"
external int: int => element = "%identity"
external string: string => element = "%identity"
@val external null: element = "null"

/* These are needed for Fragment (<> </>) support */
type fragmentProps = {children?: element}
@module("@lite-jsx/core") external jsxFragment: component<fragmentProps> = "Fragment"

module Elements = {
  type props = JsxDOM.domProps

  @module("@lite-jsx/core")
  external jsx: (string, props) => Jsx.element = "h"

  @module("@lite-jsx/core")
  external div: (string, props) => Jsx.element = "h"

  @module("@lite-jsx/core")
  external jsxKeyed: (string, props, ~key: string=?, @ignore unit) => Jsx.element = "h"

  @module("@lite-jsx/core")
  external jsxs: (string, props) => Jsx.element = "h"

  @module("@lite-jsx/core")
  external jsxsKeyed: (string, props, ~key: string=?, @ignore unit) => Jsx.element = "h"

  external someElement: element => option<element> = "%identity"
}

rescript.json has jsx configured like this:

  "jsx": {
    "version": 4,
    "module": "Lit"
  }

Everything compiles and types are all happy, but the output isn’t correct for lite-jsx.

@jsx.component
let make = (~message) =>
  <div>
    <h1> {message} </h1>
  </div>

The output is this:

// Generated by ReScript, PLEASE EDIT WITH CARE

import * as Core from "@lite-jsx/core";

function Lit_tests(props) {
  return Core.h("div", {
              children: Core.h("h1", {
                    children: props.message
                  })
            });
}

var make = Lit_tests;

export {
  make ,
}
/* @lite-jsx/core Not a pure module */

The HTML that creates is this: <div children="&lt;h1 children=&quot;Foo&quot;&gt;&lt;/h1&gt;"></div>, which isn’t right.
lite-jsx is expecting the children to not be a prop. Here’s what the expected output should be:

Core.h("div", null, h("h1", null, message))

Is there any way I can configure this to work as expected? Or maybe make some type of wrapper function to change it to what I expect?

I think that’s the difference in the new JSX transform; in which case, tweaking rescript.json should do it.

EDIT: Op, nope, that’s hardwired to React. Hm.

Well, since that wouldn’t work easily, this JS might be a starting point for wrapping h?

const wrappedH = (tag, props) => {
  const {children, ...restProps} = props ?? {};
  const childArray = Array.isArray(children) ? children : [children]
  return h(tag, restProps, ...childArray)
};
1 Like

Yup, that worked! I knew it had to be something like that.

Almost done and ready to push up to NPM.

1 Like

Done!

2 Likes