RFC new react ppx

I’m working on the new syntax for jsx spread props in the PR. https://github.com/rescript-lang/syntax/pull/517 I’ve reached a new design of react ppx through the discussion with @cristianoc and I’ve designed the new react ppx output as per his suggestion. It is based on the more general type checking of the structural typings feature RFC: More general type checking for structural typings - #50 by tsnobip I think it is much cleaner implementation and closed to js-react in terms of interface. What do you guys think?

JSX

module Foo = {
  @react.component
  let make = (~id, ~name) => {
    <div> {id->React.string} {name->React.string} </div>
  }
}

@react.component
let make = () => {
  <>
    <div id="1" onClick={_ => ()}> {`div`->React.string} </div>
    <Foo id="2" name=`Foo` />
  </>
}

As-is

module Foo = {
  @obj
  external makeProps: (~id: 'id, ~name: 'name, ~key: string=?, unit) => {"id": 'id, "name": 'name} =
    ""
  let make =
    (@warning("-16") ~id, @warning("-16") ~name) => {
      ReactDOMRe.createDOMElementVariadic("div", [{id->React.string}, {name->React.string}])
    }
  let make = {
    let \"V3$Foo" = (\"Props": {"id": 'id, "name": 'name}) =>
      make(~name=\"Props"["name"], ~id=\"Props"["id"])
    \"V3$Foo"
  }
}

@obj external makeProps: (~key: string=?, unit) => {.} = ""

let make = () => {
  ReactDOMRe.createElement(
    ReasonReact.fragment,
    [
      ReactDOMRe.createDOMElementVariadic(
        "div",
        ~props=ReactDOMRe.domProps(~id="1", ~onClick={_ => ()}, ()),
        [{`div`->React.string}],
      ),
      React.createElement(Foo.make, Foo.makeProps(~id="2", ~name=`Foo`, ())),
    ],
  )
}
let make = {
  let \"V3" = (\"Props": {.}) => make()
  \"V3"
}

To-be

module Foo = {
  @obj
  type make<'id, 'name> = {
    id: 'id,
    name: 'name,
    key: option<string>,
  }
  let make = ({id, name, key}: make<'id, 'name>) => {
    ReactDOMRe.createDOMElementVariadic("div", [{id->React.string}, {name->React.string}])
  }
}

@obj type make = {key: option<string>}
let make = ({key}: make) => {
  ReactDOMRe.createElement(
    ReasonReact.fragment,
    [
      ReactDOMRe.createDOMElementVariadic(
        "div",
        ~props={id: Some("1"), onClick: Some({_ => ()})},
        [{`div`->React.string}],
      ),
      Foo.make({id: "2", name: `Foo`}),
    ],
  )
}

EDIT:
Keep the lowercase in v3 style to avoid the changes in the @rescript/react bindings.

@obj type make = {key: option<string>}
let make = ({key}: make) => {
  ReactDOMRe.createElement(
    ReasonReact.fragment,
    [
      ReactDOMRe.createDOMElementVariadic(
        "div",
        ~props=ReactDOMRe.domProps(~id="1", ~onClick={_ => ()}, ()),
        [{`div`->React.string}],
      ),
      Foo.make({id: "2", name: `Foo`}),
    ],
  )
}
8 Likes

Is non-React JSX support (at least preserve) relevant to discuss at here? If the PR is about doing a rework on JSX transformation, it’d be a nice opportunity to revisit that one.

4 Likes

I don’t know the direction of the core team. As far as I’ve looked into the source code, it seems quite tightly coupled to react only. Therefore, it has to be considered in the larger frame to support other js rendering libraries or frameworks, such as solid.js. As you mentioned ‘at least preserve’ seems more tricky, I think. Because the jsx needs to be parsed to normal function and arguments in order for the type checker to do its job. So some other stage, such as js emitter, needs to convert it to jsx after type checking.

2 Likes

How about transforming the whole JSX as %raw() and replacing the expressions inside JSX with temp variables/functions for typechecking? Is that too hacky to have in a standard language toolchain?

1 Like

Are you thinking about SolidJS output? :slight_smile:

Seems like somebody recently made it work, actually, but their setup relies heavily on Babel, which obviously kills the speed advantage of the ReScript compiler.

Then again, I’ve no idea how much work babel-plugin-jsx-dom-expressions actually does, probably not just mechanical transformation of JSX. :man_shrugging:

Are you thinking about SolidJS output? :slight_smile:

Primarily, but also Vue JSX and more.

Then again, I’ve no idea how much work babel-plugin-jsx-dom-expressions actually does, probably not just mechanical transformation of JSX. :man_shrugging:

Previously I’ve dug down inside of it while trying to port dom-expressions to be used with SWC. It’s somewhat complicated - but not that complicated to make it a huge work to rewrite it in OCaml.

However, I’d prefer to make preserve mode rather than building PPXes for each framework coming up. Then it’ll be possible to have it replugged into anything, such as Babel and SWC.

I made some examples of the desired output and I believe emitting this along with the React one and DCEing the React output(used only for typechecking) will basically make it work. Since it’s quite hacky, it’d be much nicer to have it as a third-party PPX if possible.

1 Like

Looks like this covers my question :smiley:

If it’s a PPX, I wonder how much work it’s going to take to keep it up to date with the Babel (and potentially, SWC), plugin?

Damn, mixing edgy tech is such a combinatory explosion :see_no_evil:

No idea :cry: I haven’t done any in-depth development with PPX.