ml-in-barcelona / jsoo-react

js_of_ocaml bindings for ReactJS. Based on ReasonReact.
https://ml-in-barcelona.github.io/jsoo-react
MIT License
138 stars 19 forks source link

Should jsoo-react vendor react.js? #104

Open jchavarri opened 2 years ago

jchavarri commented 2 years ago

Right now, jsoo-react calls require to bring react.js libraries:

https://github.com/ml-in-barcelona/jsoo-react/blob/6f9eba0b7e7800b9a0e483752f16f5407f50c00e/lib/imports.ml#L9-L11

This means that users of the library are forced to use some JavaScript bundler like Webpack to resolve these requires with the actual library code.

But we could instead vendor a version of React.js and connect OCaml bindings with the JavaScript library objects internally, without exposing all this connection details to end users. This is what some OCaml projects have been doing, like virtual_dom, that uses browserify to bundle the JavaScript library.

Upsides of vendoring:

Downsides:

I guess it all comes down to how "close" to JavaScript ecosystem a project is: for projects leveraging a lot of JS related tooling, using Webpack or other bundler is not a pain (actually helps). But for projects leaning more on OCaml side, with very few JavaScript dependencies, adding bundler is actually overkill.

Any other things? What do you think?

glennsl commented 2 years ago

Apologies in advance for the stream of thoughts below. I'm still very new to JSOO, and just throwing ideas out there in the hopes that something might stick. Feel free to ignore if it makes no sense :)

It also means that there is less flexibility for users to decide which version of React.js they want to use. E.g. right now jsoo-react supports v16, but some user could start using v17 for some reason just by updating their package.json.

You could always just fork jsoo-react entirely and replace the vendored library. Not sure that's necessarily a bad thing even, as it's not unlikely that other changes are needed as well in order to benefit from the upgrade. It also makes it easier to reason about backwards compatibility when it's tied to a single version.

Not an approach I'd recommend for every binding library, but for something as fundamental as react it might make sense.

Alternatively I wonder if it's possible to inject the dependency somehow. For example, ignoring the general awkwardness and the need to completely restructure jsoo-react, it might be possible to use a functor:

(* react.ml *)
include JsooReact.Make(struct 
  let react : react = Js_of_ocaml.Js.Unsafe.js_expr {|require("react")|} 
  let reactDom : reactDom = Js_of_ocaml.Js.Unsafe.js_expr {|require("react-dom")|} 
end)

Or maybe publish separate packages, using dune to conditionally compile it as vendored, so it could be opted into.

(In Rust I'd use "features" for this, but for OCaml that would require both opam and dune, at the very least, to agree on a protocol for this)

jchavarri commented 2 years ago

Thanks for sharing your thoughts @glennsl.

I remembered now why I went for current approach using require. 😅

The main downside of vendoring react js library is that then all React code gets processed by js_of_ocaml JavaScript parser. I remember having some issues with global this object while trying, but maybe it does not happen now, would be nice to give it a try. What I remember is that this would increase build time significantly, in both development and release Dune profiles.

So, I ended up going for the require approach for the following reasons:

Point 2 is pretty minor, but I think point 1 makes it very likely that consumers will need Webpack or some other bundler. I am happy to consider the proposal and build some proof of concept if there are specific cases out there of projects that use jsoo-react but don't need any other JavaScript/React dependencies :)

What do you think?

glennsl commented 2 years ago

Hmm, why would it have to process the JavaScript? And why would that be slower than webpack? I naively thought it would just be a matter of concatenation with a bit of wrapper code to glue things together.

Projects using jsoo-react will very likely use some React components library. As soon as they do so, these projects will need to use Webpack or some other bundler because js_of_ocaml will not be able to parse jsx code or any other non-standard JavaScript syntax "extensions".

I'm not sure that's true. Although I'm not a big consumer of third-party react components myself, so who knows. It certainly wouldn't be the weirdest thing I've encountered in the JavaScript universe. But why would you package the raw source code, and require consumers to use all of your builld-time tools, rather than package the processed JavaScript?

A third option might be to use <script> tags. I don't know how common it is to do this anymore, but react is at least still built for it. That would allow a bundler to be sued, but not require it, and gives the consumer even more flexibility in managing their dependencies.