{"id":13726078,"url":"https://github.com/diaozheng999/ppx-react-native","last_synced_at":"2025-07-27T20:06:10.271Z","repository":{"id":57129250,"uuid":"361333496","full_name":"diaozheng999/ppx-react-native","owner":"diaozheng999","description":"PPX rewriters for ReScript React Native","archived":false,"fork":false,"pushed_at":"2021-07-09T02:01:55.000Z","size":93,"stargazers_count":2,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-14T15:15:38.805Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/diaozheng999.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-04-25T04:49:40.000Z","updated_at":"2021-07-09T02:01:57.000Z","dependencies_parsed_at":"2022-08-31T18:33:45.385Z","dependency_job_id":null,"html_url":"https://github.com/diaozheng999/ppx-react-native","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diaozheng999%2Fppx-react-native","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diaozheng999%2Fppx-react-native/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diaozheng999%2Fppx-react-native/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/diaozheng999%2Fppx-react-native/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/diaozheng999","download_url":"https://codeload.github.com/diaozheng999/ppx-react-native/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247911070,"owners_count":21016819,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T01:02:51.768Z","updated_at":"2025-04-08T19:26:46.348Z","avatar_url":"https://github.com/diaozheng999.png","language":"OCaml","funding_links":[],"categories":["OCaml"],"sub_categories":[],"readme":"# ppx-react-native\nPPX rewriters for ReScript React Native\n\n\n## Usage\n\nRecommended to use with [`ppx-install`](https://github.com/diaozheng999/ppx-install). Add to `package.json`:\n\n```json\n{\n  \"ppx\": [\"@nasi/ppx-react-native\"]\n}\n```\n\nThis package also requires [`@nasi/react-native`](https://github.com/diaozheng999/rescript-react-native) as a dependency.\n\nIf compiling with Melange, update in `bs-config.json`:\n```json\n{\n  \"ppx-flags\": [\"@nasi/ppx-react-native\"]\n}\n```\n\nalso update in `esy.json`:\n```json\n{\n  \"dependencies\": {\n    \"@nasi/ppx-react-native\": \"*\"\n  }\n}\n```\n\n## Stylesheet\n\nThis package adds the `%%stylesheet` extension, which removes the need for `Js.t` types for stylesheet calls. It allows the use of:\n\n```rescript\n%%stylesheet(let style = {\n  main: Style.create(~flex=1., ()),\n  text: Style.create(~fontSize=14., ()),\n})\n```\n\nThis will allow you to use `style.main` and `style.text` further down in the code (or in another module) if required.\n\n### Interaction with `%style`\n\nThis package also allows for `%style` extension to be used within a `%%stylesheet` extension as such:\n```rescript\n%%stylesheet(let style = {\n  main: { flex: 1. },\n  text: { fontSize: 14. },\n})\n```\nHere, you'll get `style.main` with type `Style.typed_t\u003c[\u003e #flex ]\u003e\u003e` and `style.text` with type `Style.typed_t\u003c[\u003e #fontSize ]\u003e`.\n\nThis translates to:\n```js\nlet style = ReactNative.StyleSheet.create({\n  main: { flex: 1 },\n  text: { fontSize: 14 },\n});\n```\n\n### Translation\n\nConsider the following:\n\n```rescript\n%%stylesheet(let style = {\n  style1: Style.create(~flex=1., ()),\n  style2: Style.create(~fontSize=14., ()),\n})\n```\n\nThis roughly translates to:\n```rescript\ntype stylesheet = {\n  style1: Style.t,\n  style2: Style.t,\n}\n\nlet style: stylesheet = StyleSheet.unsafeCreate({\n  style1: Style.create(~flex=1., ()),\n  style2: Style.create(~fontSize=14., ()),\n})\n```\n\nIn case a `%style` extension is present, then the tags are also added to the type definition. Consider the following:\n```rescript\n%%stylesheet(let style = {\n  style1: Style.create(~flex=1., ()),\n  style2: Style.create(~fontSize=14., ()),\n  style3: %style({ flex: 1. }),\n  style4: %style({ fontSize: 14. }),\n})\n```\n\nThis would translate to:\n```rescript\ntype stylesheet\u003c+'a, +'b\u003e = {\n  style1: Style.t,\n  style2: Style.t,\n  style3: Style.typed_t\u003c'a\u003e,\n  style4: Style.typed_t\u003c'b\u003e,\n}\n\nlet style: stylesheet\u003c[\u003e #flex ], [\u003e #fontSize ]\u003e = StyleSheet.unsafeCreate({\n  style1: Style.create(~flex=1., ()),\n  style2: Style.create(~fontSize=14., ()),\n  style3: Obj.magic(Style.create(~flex=1, ())),\n  style4: Obj.magic(Style.create(~fontSize=14., ())),\n})\n```\n\nIn both these cases, `StyleSheet.unsafeCreate` has the following definition:\n\n```rescript\n@module(\"react-native\") @scope(\"StyleSheet\")\nexternal unsafeCreate: 'a =\u003e 'a = \"create\"\n```\n\n## Style Typing\n\nSimilar to `TyXML` and `bs-css`, we can use tagged types and variance to include/exclude specific css elements. I've defined\n```rescript\n// Style.resi\ntype typed_t\u003c+'a\u003e\n```\n\nWhich allows labels, such as `#flex`, `#alignContent` etc. to be attached. So, a JavaScript style\n```javascript\nconst style = {\n  flex: 1,\n  marginTop: 20,\n  justifyContent: \"center\",\n}\n```\nwould be typed as:\n```rescript\nlet style: Style.typed_t\u003c[\u003e #flex | #marginTop | #justifyContent ]\u003e\n```\n\nAt consumption, we can write:\n```rescript\nmodule Element = {\n  @react.component\n  let make: (~style:Style.typed_t\u003c[\u003c #flex ]\u003e=?) =\u003e React.element\n}\n```\nwhere we put all the supported style attributes in the list. In the example above, this element only accept the `flex` attribute, so if we were to write:\n```rescript\n\u003cElement style /\u003e // Compile error\n```\nThis would raise a compile error, since `style` contains at least `#flex`, `#marginTop` and `#justifyContent`, and `Element` wants at most `#flex`.\n\nHowever, if we had a style:\n```rescript\nlet style1: Style.typed_t\u003c[\u003e #flex ]\u003e\n```\n\nThen\n\n```rescript\n\u003cElement style=style1 /\u003e\n```\n\nwould be allowed.\n\n### The `%style` extension\n\nNormally, it would be pretty difficult to achieve this kind of covariant tagging, since the only thing that would make sense for it would be lists, and you'll end up with lots of code that breaks the zero-cost principle. So, an extension is introduced to specifically perform this translation. It also has the benefit of making the code look closer to how it's written in JavaScript. In the example above, we fine the JavaScript as\n```javascript\nconst style = {\n  flex: 1,\n  marginTop: 20,\n  justifyContent: \"center\",\n}\n```\n\nCurrently, we would have to write:\n```rescript\nlet style = Style.style(~flex=1., ~marginTop=dp(20.), ~justifyContent=#center, ())\n```\n\nWith the `%style` extension, we'll write:\n```rescript\nlet style = %style({\n  flex: 1.,\n  marginTop: dp(20.),\n  justifyContent: #center,\n})\n```\n\n#### Translation\nThe extension roughly translates the above call to:\n```rescript\nlet style: Style.typed_t\u003c[\u003e #flex | #marginTop | #justifyContent ]\u003e = Obj.magic(\n  Style.style(\n    ~flex=1.,\n    ~marginTop=dp(20.),\n    ~justifyContent=#center,\n    ()\n  )\n)\n```\n\nThe tags are read from record fields themselves, and the field/value pair gets translated into a labelled argument to the function `style`. To give a very arbitrary example:\n```rescript\nlet arbitrary = %style({\n  field1: value1,\n  field2: value2,\n  field3: value3,\n})\n```\ntranslates to\n```rescript\nlet arbitrary: Style.typed_t\u003c[\u003e #field1 | #field2 | #field3 ]\u003e = Obj.magic(\n  Style.style(\n    ~field1=value1,\n    ~field2=value2,\n    ~field3=value3,\n    ()\n  )\n)\n```\n\n#### Practical Usage (tags)\n\nI've included `TypedStyle.res` with definitions scraped from `@types/react-native` definitions, and are clustered similarly.\n\nThey include:\n1. `flexStyle`\n2. `transformsStyle`\n3. `shadowStyleIOS`\n4. `viewStyle`\n5. `textStyleIOS`\n6. `textStyleAndroid`\n7. `textStyle`\n8. `imageStyle`\n\nI've also included utility functions to convert subsets of the above typed styles to `Style.t`. To give an example, we have:\n```rescript\nlet flexStyle: Style.typed_t\u003c[\u003c flexStyle ]\u003e =\u003e Style.t\n```\n\nWe can begin to migrate component definitions to utilise the tagged styles and use these functions for conversion in the meantime.\n\n#### Composition\n\nI've also included 2 ways of composing typed styles. These are:\n\n1. The `compose` function defined in `TypedStyle.res`\n2. Overloading the `++` operator defined in module `Infix`.\n\nBoth methods are defined using arrays, that is:\n```rescript\nopen TypedStyle.Infix // shadows the ++ operator (the ^ operator in OCaml)\n\nlet composed = style1 ++ style2 ++ style3\n```\nwould produce something like:\n```javascript\nconst composed = $caret(style1, $caret(style2, style3))\n```\nwhich is equivalent to\n```javascript\nconst composed = [style1, [style2, style3]]\n```\n\n## Implementation\n\nThe entire project is built on `esy` and `dune`, and exists in the `ppx` directory. In order to allow development work on Windows, OCaml `4.11.x` was used with `ocaml-migrate-parsetree` to allow the code to understand `4.06.1` parsetree generated by `bs-platform`.\n\nA postinstall script is created to copy the `ppx.exe` to the top-level of the package.\n\n## Limitations\n\nThe `%%stylesheet` extension only works on \"structure items\", so:\n```rescript\n%%stylesheet(let style1 = { ... }) // works\n\nmodule A = {\n  %%stylesheet(let style2 = { ... }) // should work, not tested\n}\n\nlet make = () =\u003e {\n  %%stylesheet(let style3 = { ... }) // not supported\n}\n```\n\n## Upcoming changes\n\nI'm looking to include the following changes in the PPX rewriter:\n\n1. Allow punning in the `%style` extension, allowing:\n```rescript\n@react.component\nlet make = (~fontSize) =\u003e {\n  \u003cText style={%style({ fontSize })} /\u003e\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiaozheng999%2Fppx-react-native","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdiaozheng999%2Fppx-react-native","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdiaozheng999%2Fppx-react-native/lists"}