Change JSX output from React to SolidJs?

It seems that handling JSX like the preserve option from TypeScript, is the best solution. It would enable to use Rescript without React.
Here is the docs from solids about typescript configuration.

2 Likes

I thought “direct JSX” means JSX preserve mode. If that means the new React 17 JSX transform thing, it doesn’t change anything related to SolidJS support.

2 Likes

Thanks it’s clear now.

1 Like

I’ve created an issue with a rough outline on how to explore implementing preserve mode.

This is going to be pretty much orthogonal to the ppx work on JSX, as likely this won’t run the ppx at all (unless some preparation work is required and that’s the right spot for it).

1 Like

That issue is open to contribution if someone is interested.

In terms of planning, an open question remains on what else should one consider supporting, in addition to SolidJs (and whatever else can take advantage of preserve mode).

1 Like

+1 on preserve mode. It’s the safest and most future proof bet imo. We can then specialize further as we see where the community goes with the support - is any of the other fws outside of React gaining traction with ReScript?

First class support, like for React, is very valuable because it means any ReScript project could theoretically skip using Babel. Less transforming, faster, etc. That doesn’t matter with something like SolidJs of course, where the whole SolidJs optimizer/compiler is a Babel plugin. But there might be other fws around that gain traction, that have the same characteristics as React, where first classing could be a huge performance gain. Time will tell!

7 Likes

Actually, first-class support for SolidJS is also a viable option, especially looking at other players like Bun implementing SolidJS JSX transforms natively. While I also agree that relying on the preserve option makes much more sense for now, IMO building a PPX for it will be pretty considerable one if the user group grows large enough.

3 Likes

If it’s going to be possible to use something like Bun (or maybe swc) with ReScript, maybe the pain caused by the slowness of Babel will go away anyway :slight_smile:

2 Likes

Yes, I think this is a good point too.

1 Like

After playing around with rescript-solidjs for a while I found a lot of problems with the HyperScript approach. The most notable one is that HyperScript creates all components “inside out”. The innermost components are created first. This makes it incompatible with Context (at least I didn’t get it to work).

Since Context is a central piece of solid (for example it is used in solid-app-router). It became really frustrating to use.

So I tried the approach mentioned by @mosheduminer. There was already a babel plugin to transform react backt to jsx: (Plugin. I used this as a starting point to create a transform specific for ReScript:
babel-plugin-rescript-react-to-jsx

The result works surprisingly well! So I updated rescript-solidjs to use this approach instead.

As a bonus it is no longer necessary to adapt the control flow components.They bind directly to solid now. The updated bindings are basically zero cost.

1 Like

After multiple experiments with Solid and ReScript I’m no longer sure that a JSX preserve mode alone is sufficient to make it work. The JavaScript code generated by ReScript breaks reactivity.

For example this component:

@react.component
let make = (~text) => {
    <div> {text->React.string} </div>
}

is compiled to:

function Component(Props) {
  var text = Props.text;
  return ReactDOMRe.createDOMElementVariadic("div", undefined, [text]);
}

The variable assignment var text = Props.text breaks Solid’s reactivity.

Assuming the code generated in preserve mode looks similar to this:

function Component(Props) {
  var text = Props.text;
  return <div>{text}</div>;
}

the problem would still persist.

Is there any way to influence the way the compiler handles Props?

What is the desired output?

I imagined something like:

function Component(Props) {
  var text = function() { return Props.text }
  return <div>{text()}</div>
}

This preserves reactivity, right?

This is quite interesting. First of all, I have no experience with Solid.js at all. Is it workable with Solid.js?

@react.component
let make = (~text) => <div>{text->React.string}</div>
function Comp(props) => {
  return <div>{props.text}</div>;
}
1 Like

To track reactivity in Solid, reactivity getters (which can be a function or a property of a store) should be accessed at the point where the reactivity should be tracked. Therefore the expression should be inlined or converted as a function and invoked as needed.

So your code will work, although I’m not sure how the type stuffs like React.string would work in a framework-agnostic manner. Should we introduce JSX module like TS providing the JSX namespace?

React.string will be disappeared in emitting to js anyway.

Yes, IMHO, I guess we might need the more abstracted modules such as JSX, JSX ppx for the processes of compiler after parsing.

1 Like

The React.string ist just a binding to the %identity. Like this:

external string: string => element = "%identity"

Its purpose ist to make the types match. ReScript expects all nodes in JSX to be of type React.element. So it is only for the type system and will be removed completely at compile time. Therefore it is probably not relevant here.

1 Like

This keeping rescript-react and compiler in sync is already a bit problematic for upgrades.
In general, if there’s a specialised ppx in the compiler, then everything that depends on its details should be in the compiler too. In particular the types that the ppx emits. Rather than referring to rescript-react.
But at first one needs a migration processs that requires changing nothing in existing projects.
Eventually, some compiler-provided jsx type seems the way to go.