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

Type-safe HTML and SVG tags #71

Closed davesnx closed 2 years ago

davesnx commented 2 years ago

Description

This PR brings a layer of safety to writing React components that render HTML elements or SVG elements. The idea behind this isn't new, libraries like https://github.com/ocsigen/tyxml from OCaml, or https://github.com/kotlin/kotlinx.html from Kotlin Web, or https://github.com/nicojs/typed-html from TypeScript.

Since this library/ppx focuses on React, it includes the divergences from HTML standard: https://reactjs.org/docs/dom-elements.html.

Plan of action

  1. Study how tyxml validate attributes: https://github.com/ocsigen/tyxml/blob/master/lib/html_types.mli
  2. Check other implementations such as types/react
  3. Estimate how much can take to re-implement the entire spec.

Tried to use tyxml to easily integrate with our codebase, but found that their implementation to derivate a bit from our needs. The approach they took was more functional. Ensuring to provide a huge list of functions where those type-check against some nested open polyvars.

Our goal was to have a list of attributes and store this as metadata in order to generate the type-checks from the ppx. Using the types from types/react was easier to parse. I expect React's static types from TypeScript to be tested intensively and ensure we provide those React pieces that other HTML implementations will lack. That was the approach I took.

Todo

→ ~/C/g/r/jsoo-react on Type-safe-html-tags ✗ make

File "test.ml", lines 109-111, characters 2-10: 109 | ..((div ~cols:1 ~href:(("https://example.com") 110 | [@reason.raw_literal "https://example.com"]) ~children:[foo] ()) 111 | [@JSX ]) Error: prop 'cols' isn't a valid prop for a 'div'

( They should look like )

File "test.ml", lines 109-111, characters 2-10: 109 | ..((div ~cols:1 ~href:(("https://example.com") 110 | [@reason.raw_literal "https://example.com"]) ~children:[foo] ()) 111 | [@JSX ]) Error: 'div' contains an invalid prop: 'cols'. It only accepts 'Global attributes' defined in https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes



# Future

Note on (**1*): In this PR the goal wasn't to bring a nicer API to HTML/SVG elements, rather than validate its attributes. In further PRs we could bring those attributes that are straightforward to type in OCaml. Type-safe attributes might bring a lot of value to the table but they might have a cost where could be harder to learn (depending on the compiler messages), harder to document and harder to migrate into it.

# Discuss
- Do we want this?
- How errors should be displayed?
- Does it make sense to keep html.ml inside this repo?
- How can we document the types? I thought we could generate an .mli from the same metadata, but happy to discuss other approaches.
- How can we manage new additions/deletions to the spec? Flag them somehow?
- (as discussed in private, but happy to bring it again). Is there any way with ocaml.doc during the ppx generation where we could add descriptions? It will mean a lot of users could hover on the attribute and the description from MDN pops up.
davesnx commented 2 years ago

Hey @dodomorandi, saw your PR adding polyvariants to rescript-react and your comments on the jsx ppx v4 from Ricky. This PR allows us to have fine granted type-safety for all elements and the next changes will include moving away from strings and introducing polyvariants or variants for many attributes.

Would you like to participate and have a chat with me about this topic? I would love to know your opinions on the questions I ask on this PR. Let me know if you want to chat here or in discord, both work great for me :D

Hope this cc isn't to intrusive!