├── .gitattributes ├── .github └── workflows │ └── nodejs.yml ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── rescript.json └── src ├── React.bs.js ├── React.res ├── ReactDOM.bs.js ├── ReactDOM.res ├── ReactDOMServer.bs.js ├── ReactDOMServer.res ├── ReactDOMStatic.bs.js ├── ReactDOMStatic.res ├── ReactDOMStyle.bs.js ├── ReactDOMStyle.res ├── ReactEvent.bs.js ├── ReactEvent.res ├── ReactEvent.resi ├── ReactTestUtils.bs.js ├── ReactTestUtils.res ├── ReactTestUtils.resi ├── RescriptReactErrorBoundary.bs.js ├── RescriptReactErrorBoundary.res ├── RescriptReactErrorBoundary.resi ├── RescriptReactRouter.bs.js ├── RescriptReactRouter.res └── RescriptReactRouter.resi /.gitattributes: -------------------------------------------------------------------------------- 1 | # Tell github that .res and .resi files are ReScript 2 | *.res linguist-language=ReScript 3 | *.resi linguist-language=ReScript 4 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [16.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v3 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: npm install 28 | - run: npm run build 29 | - run: npm run test 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules* 3 | finalOutput/*.js 4 | .merlin 5 | .install 6 | /lib/bs/ 7 | /lib/js/ 8 | /lib/ocaml/ 9 | /docs/ 10 | *.log 11 | .bsb.lock 12 | _esy 13 | _build 14 | *.install 15 | src/legacy/*.bs.js 16 | 17 | # Editor 18 | /.idea/ 19 | 20 | # React 21 | !/src/react/*.js 22 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | lib/bs/ 2 | .merlin 3 | docs 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | > **Tags:** 4 | > 5 | > - :boom: [Breaking Change] 6 | > - :eyeglasses: [Spec Compliance] 7 | > - :rocket: [New Feature] 8 | > - :bug: [Bug Fix] 9 | > - :memo: [Documentation] 10 | > - :house: [Internal] 11 | > - :nail_care: [Polish] 12 | 13 | ## 0.14.0-rc.1 14 | 15 | #### :rocket: New Feature 16 | 17 | - Bindings for new React 19 APIs. https://github.com/rescript-lang/rescript-react/pull/133 18 | 19 | #### :boom: Breaking Change 20 | 21 | - Bumped React peer dependency to 19.0. https://github.com/rescript-lang/rescript-react/pull/139 22 | - Removed legacy JSX v3 modules and deprecated functions. https://github.com/rescript-lang/rescript-react/pull/129 23 | - Removed support for curried mode. https://github.com/rescript-lang/rescript-react/pull/131 24 | 25 | ## 0.13.1 26 | 27 | #### :boom: Breaking Change 28 | 29 | - Deprecate JSX 3. https://github.com/rescript-lang/rescript-react/pull/120 30 | - Deprecate ReactDOMStyle.make. https://github.com/rescript-lang/rescript-react/pull/127 31 | 32 | #### :nail_care: Polish 33 | 34 | - ReScriptReactRouter: get rid of pipe last. https://github.com/rescript-lang/rescript-react/pull/126 35 | 36 | ## 0.13.0 37 | 38 | #### :boom: Breaking Change 39 | 40 | - Replace usages of `%external` by binding to `globalThis`. This is to support upcoming ReScript 12 versions. For older browsers, it may be necessary to polyfill `globalThis`. 41 | 42 | #### :bug: Bug Fix 43 | 44 | - Remove hardcoded `require` so it works in both common js and ES module modes. (https://github.com/rescript-lang/rescript-react/pull/117) 45 | 46 | ## 0.12.2 47 | 48 | - Fix incorrect usage of `@uncurry`. 49 | - bsconfig.json -> rescript.json. 50 | 51 | ## 0.12.1 52 | 53 | - Undeprecate numbered hooks for a smoother upgrading experience. 54 | 55 | ## 0.12.0 56 | 57 | - Requires ReScript 11.0.0 or newer. 58 | 59 | ## 0.12.0-alpha.3 60 | 61 | #### :boom: Breaking Change 62 | 63 | - Deprecated use\*N functions in favor of changing the signature of the main hook function. 64 | - For example, useEffect instead of useEffectN e.g. `useEffect3(f, (a, b, c))` -> `useEffect(f, (a, b, c))` 65 | - The affected hooks include `useEffect`, `useLayoutEffect`, `useCallback`, `useMemo`, `useImperativeHandle`, `useInsertionEffect` 66 | - With this change, it is now possible to pass any value as the second argument `'deps`. In case you pass an invalid value, you will get a warning from React at runtime. You should be using one of the following values for the dependency array: 67 | - 0 dependencies: `[]` 68 | - 1 dependency: `[a]` 69 | - more than 1 dependency: `(a, b, ...)` 70 | - For calling `useEffect`, `useLayoutEffect` etc. _without_ a dependency array (meaning that the effect is executed on every render), there are now separate bindings `useEffectOnEveryRender`, `useLayoutEffectOnEveryRender` etc. 71 | 72 | ## 0.12.0-alpha.2 73 | 74 | #### :rocket: New Feature 75 | 76 | - Added `React.lazy_`. 77 | 78 | #### :boom: Breaking Change 79 | 80 | - Requires ReScript 11.0.0-alpha.6 or newer. 81 | 82 | #### :bug: Bug Fix 83 | 84 | - Fixed children type for `Fragment`, `StrictMode` and `Suspense`. 85 | 86 | #### :nail_care: Polish 87 | 88 | - Removed experimental `SuspenseList` component. 89 | 90 | ## 0.12.0-alpha.1 91 | 92 | #### :rocket: New Feature 93 | 94 | - Compatibility with ReScript 11 uncurried mode. 95 | - Added `gap` prop to `ReactDOMStyle.make`. 96 | 97 | #### :boom: Breaking Change 98 | 99 | - Removed `React.callback` type. 100 | 101 | #### :bug: Bug Fix 102 | 103 | - Updated `React_V3` compatibility module to define record field `current` for `ref`. 104 | 105 | ## 0.11.0 106 | 107 | No changes compared to rc.3. 108 | 109 | ## 0.11.0-rc.3 110 | 111 | - Changed `React.jsxKeyed(s)`, `ReactDOM.jsxKeyed(s)` having key as optional argument. 112 | - Changed `React.fragmentProps` children field to optional. 113 | - Removed the helper functions for JSX v4 from `React.res`. 114 | - Made `React.component` conversion function zero cost. 115 | 116 | ## 0.11.0-rc.2 117 | 118 | - Fixed JSX PPX V3 backward compatibility. 119 | 120 | ## 0.11.0-rc.1 121 | 122 | - Added React 18 bindings. 123 | - Added binding for `React.isValidElement`. 124 | - Added backgroundFilter prop in `ReactDOMStyle`. 125 | - Relaxed React version requirement to v18.0.0+. 126 | - `ReactDOM.domProps` is now an alias for `JsxDOM.domProps` (defined in the compiler repo) 127 | - Deprecated types `ReactDOM.props`, `ReactDOM.Props.props`, `ReactDOM.Props.domProps`. 128 | - Deprecated `ReactDOM.stringToComponent`. 129 | 130 | ## 0.11.0-alpha.1 131 | 132 | - `RescriptReactErrorBoundary` component now implemented using `@react.component`, so it is compatible with JSX V4. 133 | - `ReactV3` module added for backward compatibility to JSX V3. See [the V3 compatibility mode](https://github.com/rescript-lang/syntax/blob/master/cli/JSXV4.md) 134 | - Fixed `react-dom/server` and `react-dom/test-utils` imports. 135 | - Fixed `createRoot` bindings. 136 | - Added `Keyboard.code` binding. 137 | - Renamed `Changes.md` to `CHANGELOG.md` to align with the rescript-compiler repo. 138 | 139 | **Breaking:** 140 | 141 | - **IMPORTANT** The `React` module has been modified in a breaking way to support the new JSX PPX V4. 142 | 143 | - Removed the deprecation attribute from apis of the new jsx transform (introduced in React v17). 144 | 145 | - New version requirements: 146 | 147 | - ReScript compiler V10.1+ 148 | - ReactJS v18.2.0+ 149 | 150 | ## v0.10.3 151 | 152 | - Temporarily remove peerDependency for bs-platform, so we can gracefully do the transition to our new `rescript` package 153 | 154 | ## v0.10.2 155 | 156 | - Added `.js` extension to externals to comply to ES6 module conventions 157 | - Bump peerDependencies versions 158 | 159 | ## v0.10.1 160 | 161 | This is the initial version of `rescript-react`, a port of `reason-react@0.9` that will be almost equivalent, except for a few long needed minor breaking changes. 162 | 163 | It is intended to be used with our newest ReScript React-JSX transformations (> JSX v3) and React v16.8. 164 | 165 | For history on previous evolution of the code, check out the original [reason-react history](https://github.com/reasonml/reason-react/blob/master/HISTORY.md) 166 | 167 | **Breaking:** 168 | 169 | - **IMPORTANT:** Currently, old third-party packages that are still dependent on `reason-react` will not mix with other `@rescript/react` based code due to a build system problem. Which means that every third-party dependency needs to be upgraded to `@rescript/react` first to make it compatible. See [this forum discussion](https://forum.rescript-lang.org/t/discussion-reason-react-rescript-react-migration-path/1086) for more details. 170 | 171 | - Removed legacy modules ("record api"): 172 | 173 | - `ReasonReactCompat` 174 | - `ReactDOMServerRe` 175 | - `ReactEventRe` 176 | - `ReasonReactOptimizedCreateClass` 177 | 178 | - Renamed existing modules: 179 | 180 | - `ReasonReactErrorBoundary` -> `RescriptReactErrorBoundary` 181 | - `ReasonReactRouter` -> `RescriptReactRouter` 182 | - (Note: Usually the two only valid styles would be `ReScript` or `rescript`, the latter being the version for technical writing. We are using `Rescript` here, since it is essentially the capitalized version of `rescript`) 183 | 184 | - Removed all functionality from `ReasonReact` and `ReactDOMRe` that is not needed for react-jsx v3 compatibility 185 | - Moved `React.SuspenseList` to `React.Experimental.SuspenseList` to be more consistent with our Experimental features 186 | 187 | **Misc:** 188 | 189 | - Renamed `HISTORY.md` to `Changes.md` (aligning with other core projects, like genType) 190 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sander, 2020 The ReScript Project 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## @rescript/react 2 | 3 | > The Official [ReScript](https://rescript-lang.org) Bindings for [ReactJS](https://react.dev/) 4 | 5 | - [Introduction](https://rescript-lang.org/docs/react/latest/introduction) 6 | - [Installation](https://rescript-lang.org/docs/react/latest/installation) 7 | 8 | ### Versions 9 | 10 | | @rescript/react | ReScript | ReactJS | Documentation | 11 | | --------------- | ---------------------------------- | ------- | ----------------------------------------------------------------- | 12 | | 0.14.x | 11.0+ (JSX4 + uncurried mode only) | 19 | | 13 | | 0.12.x, 0.13.x | 11.0+ | 18 | [Link](https://rescript-lang.org/docs/react/latest/introduction) | 14 | | 0.11.0 | 10.1 | 18 | [Link](https://rescript-lang.org/docs/react/v0.11.0/introduction) | 15 | | 0.10.3 | 8.3 | 16.8.1+ | [Link](https://rescript-lang.org/docs/react/v0.10.0/introduction) | 16 | 17 | ### Development 18 | 19 | ``` 20 | npm install 21 | 22 | # Starts the ReScript compiler in watch mode 23 | npm start 24 | ``` 25 | 26 | ### Acknowledgements 27 | 28 | This project is the next evolution of the original [reason-react](https://github.com/reasonml/reason-react) bindings. 29 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rescript/react", 3 | "version": "0.14.0-rc.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@rescript/react", 9 | "version": "0.14.0-rc.1", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "react": "^19.1.0", 13 | "react-dom": "^19.1.0", 14 | "rescript": "^11.0.0" 15 | }, 16 | "peerDependencies": { 17 | "react": ">=19.0.0", 18 | "react-dom": ">=19.0.0" 19 | } 20 | }, 21 | "node_modules/react": { 22 | "version": "19.1.0", 23 | "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", 24 | "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", 25 | "dev": true, 26 | "license": "MIT", 27 | "engines": { 28 | "node": ">=0.10.0" 29 | } 30 | }, 31 | "node_modules/react-dom": { 32 | "version": "19.1.0", 33 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", 34 | "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", 35 | "dev": true, 36 | "license": "MIT", 37 | "dependencies": { 38 | "scheduler": "^0.26.0" 39 | }, 40 | "peerDependencies": { 41 | "react": "^19.1.0" 42 | } 43 | }, 44 | "node_modules/rescript": { 45 | "version": "11.0.0", 46 | "resolved": "https://registry.npmjs.org/rescript/-/rescript-11.0.0.tgz", 47 | "integrity": "sha512-uIUwDZZmDUb7ymGkBiiGioxMg8hXh1mze/2k/qhYQcZGgi7PrLHQIW9AksM7gb9WnpjCAvFsA8U2VgC0nA468w==", 48 | "dev": true, 49 | "hasInstallScript": true, 50 | "bin": { 51 | "bsc": "bsc", 52 | "bstracing": "lib/bstracing", 53 | "rescript": "rescript" 54 | }, 55 | "engines": { 56 | "node": ">=10" 57 | } 58 | }, 59 | "node_modules/scheduler": { 60 | "version": "0.26.0", 61 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", 62 | "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", 63 | "dev": true, 64 | "license": "MIT" 65 | } 66 | }, 67 | "dependencies": { 68 | "react": { 69 | "version": "19.1.0", 70 | "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", 71 | "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", 72 | "dev": true 73 | }, 74 | "react-dom": { 75 | "version": "19.1.0", 76 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", 77 | "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", 78 | "dev": true, 79 | "requires": { 80 | "scheduler": "^0.26.0" 81 | } 82 | }, 83 | "rescript": { 84 | "version": "11.0.0", 85 | "resolved": "https://registry.npmjs.org/rescript/-/rescript-11.0.0.tgz", 86 | "integrity": "sha512-uIUwDZZmDUb7ymGkBiiGioxMg8hXh1mze/2k/qhYQcZGgi7PrLHQIW9AksM7gb9WnpjCAvFsA8U2VgC0nA468w==", 87 | "dev": true 88 | }, 89 | "scheduler": { 90 | "version": "0.26.0", 91 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", 92 | "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", 93 | "dev": true 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rescript/react", 3 | "version": "0.14.0-rc.1", 4 | "description": "React bindings for ReScript", 5 | "files": [ 6 | "README.md", 7 | "CHANGELOG.md", 8 | "LICENSE", 9 | "rescript.json", 10 | "src/**/*.res", 11 | "src/**/*.resi" 12 | ], 13 | "scripts": { 14 | "build": "rescript build", 15 | "start": "rescript build -w", 16 | "clean": "rescript clean -with-deps", 17 | "test": "echo 'tests disabled for now'" 18 | }, 19 | "keywords": [ 20 | "rescript", 21 | "react" 22 | ], 23 | "author": "Ricky Vetter", 24 | "license": "MIT", 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/rescript-lang/rescript-react.git" 28 | }, 29 | "homepage": "https://rescript-lang.org/docs/react/latest/introduction", 30 | "devDependencies": { 31 | "react": "^19.1.0", 32 | "react-dom": "^19.1.0", 33 | "rescript": "^11.0.0" 34 | }, 35 | "peerDependencies": { 36 | "react": ">=19.0.0", 37 | "react-dom": ">=19.0.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /rescript.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rescript/react", 3 | "jsx": { 4 | "version": 4, 5 | "mode": "classic" 6 | }, 7 | "sources": [{ "dir": "src", "subdirs": true }], 8 | "package-specs": [{ "module": "commonjs", "in-source": true }], 9 | "suffix": ".bs.js", 10 | "bs-dev-dependencies": [], 11 | "bsc-flags": [] 12 | } 13 | -------------------------------------------------------------------------------- /src/React.bs.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | var React = require("react"); 5 | 6 | var Children = {}; 7 | 8 | var Context = {}; 9 | 10 | var Fragment = {}; 11 | 12 | var StrictMode = {}; 13 | 14 | var Suspense = {}; 15 | 16 | function lazy_(load) { 17 | return React.lazy(async function () { 18 | return { 19 | default: await load() 20 | }; 21 | }); 22 | } 23 | 24 | var Uncurried = {}; 25 | 26 | exports.Children = Children; 27 | exports.Context = Context; 28 | exports.Fragment = Fragment; 29 | exports.StrictMode = StrictMode; 30 | exports.Suspense = Suspense; 31 | exports.lazy_ = lazy_; 32 | exports.Uncurried = Uncurried; 33 | /* react Not a pure module */ 34 | -------------------------------------------------------------------------------- /src/React.res: -------------------------------------------------------------------------------- 1 | type element = Jsx.element 2 | 3 | @val external null: element = "null" 4 | 5 | external float: float => element = "%identity" 6 | external int: int => element = "%identity" 7 | external string: string => element = "%identity" 8 | external promise: promise => element = "%identity" 9 | 10 | external array: array => element = "%identity" 11 | 12 | type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return> 13 | 14 | type component<'props> = Jsx.component<'props> 15 | 16 | external component: componentLike<'props, element> => component<'props> = "%identity" 17 | 18 | @module("react") 19 | external createElement: (component<'props>, 'props) => element = "createElement" 20 | 21 | @module("react") 22 | external cloneElement: (element, 'props) => element = "cloneElement" 23 | 24 | @module("react") 25 | external isValidElement: 'a => bool = "isValidElement" 26 | 27 | @variadic @module("react") 28 | external createElementVariadic: (component<'props>, 'props, array) => element = 29 | "createElement" 30 | 31 | @module("react/jsx-runtime") 32 | external jsx: (component<'props>, 'props) => element = "jsx" 33 | 34 | @module("react/jsx-runtime") 35 | external jsxKeyed: (component<'props>, 'props, ~key: string=?, @ignore unit) => element = "jsx" 36 | 37 | @module("react/jsx-runtime") 38 | external jsxs: (component<'props>, 'props) => element = "jsxs" 39 | 40 | @module("react/jsx-runtime") 41 | external jsxsKeyed: (component<'props>, 'props, ~key: string=?, @ignore unit) => element = "jsxs" 42 | 43 | type fragmentProps = {children?: element} 44 | 45 | @module("react/jsx-runtime") external jsxFragment: component = "Fragment" 46 | 47 | type ref<'value> = {mutable current: 'value} 48 | 49 | @module("react") 50 | external createRef: unit => ref> = "createRef" 51 | 52 | module Children = { 53 | @module("react") @scope("Children") 54 | external map: (element, element => element) => element = "map" 55 | @module("react") @scope("Children") 56 | external mapWithIndex: (element, (element, int) => element) => element = "map" 57 | @module("react") @scope("Children") 58 | external forEach: (element, element => unit) => unit = "forEach" 59 | @module("react") @scope("Children") 60 | external forEachWithIndex: (element, (element, int) => unit) => unit = "forEach" 61 | @module("react") @scope("Children") 62 | external count: element => int = "count" 63 | @module("react") @scope("Children") 64 | external only: element => element = "only" 65 | @module("react") @scope("Children") 66 | external toArray: element => array = "toArray" 67 | } 68 | 69 | module Context = { 70 | type t<'context> 71 | 72 | type props<'context> = { 73 | value: 'context, 74 | children: element, 75 | } 76 | 77 | @get 78 | external provider: t<'context> => component> = "Provider" 79 | } 80 | 81 | @module("react") 82 | external createContext: 'a => Context.t<'a> = "createContext" 83 | 84 | @module("react") 85 | external forwardRef: (('props, Js.Nullable.t>) => element) => component<'props> = 86 | "forwardRef" 87 | 88 | @module("react") 89 | external memo: component<'props> => component<'props> = "memo" 90 | 91 | @module("react") 92 | external memoCustomCompareProps: ( 93 | component<'props>, 94 | ('props, 'props) => bool, 95 | ) => component<'props> = "memo" 96 | 97 | @module("react") external fragment: component = "Fragment" 98 | 99 | module Fragment = { 100 | type props = {key?: string, children: element} 101 | 102 | @module("react") 103 | external make: component = "Fragment" 104 | } 105 | 106 | module StrictMode = { 107 | type props = {key?: string, children: element} 108 | 109 | @module("react") 110 | external make: component = "StrictMode" 111 | } 112 | 113 | module Suspense = { 114 | type props = {key?: string, children?: element, fallback?: element} 115 | 116 | @module("react") 117 | external make: component = "Suspense" 118 | } 119 | 120 | type dynamicallyImportedModule<'a> = {default: component<'a>} 121 | 122 | @module("react") 123 | external lazy_: (unit => promise>) => component<'a> = "lazy" 124 | 125 | let lazy_ = load => lazy_(async () => {default: await load()}) 126 | 127 | /* HOOKS */ 128 | 129 | /* 130 | * Yeah, we know this api isn't great. tl;dr: useReducer instead. 131 | * It's because useState can take functions or non-function values and treats 132 | * them differently. Lazy initializer + callback which returns state is the 133 | * only way to safely have any type of state and be able to update it correctly. 134 | */ 135 | @module("react") 136 | external useState: (unit => 'state) => ('state, ('state => 'state) => unit) = "useState" 137 | 138 | @module("react") 139 | external useReducer: (('state, 'action) => 'state, 'state) => ('state, 'action => unit) = 140 | "useReducer" 141 | 142 | @module("react") 143 | external useReducerWithMapState: ( 144 | ('state, 'action) => 'state, 145 | 'initialState, 146 | 'initialState => 'state, 147 | ) => ('state, 'action => unit) = "useReducer" 148 | 149 | @module("react") 150 | external useEffectOnEveryRender: (unit => option unit>) => unit = "useEffect" 151 | @module("react") 152 | external useEffect: (unit => option unit>, 'deps) => unit = "useEffect" 153 | @module("react") 154 | external useEffect0: (unit => option unit>, @as(json`[]`) _) => unit = "useEffect" 155 | @module("react") 156 | external useEffect1: (unit => option unit>, array<'a>) => unit = "useEffect" 157 | @module("react") 158 | external useEffect2: (unit => option unit>, ('a, 'b)) => unit = "useEffect" 159 | @module("react") 160 | external useEffect3: (unit => option unit>, ('a, 'b, 'c)) => unit = "useEffect" 161 | @module("react") 162 | external useEffect4: (unit => option unit>, ('a, 'b, 'c, 'd)) => unit = "useEffect" 163 | @module("react") 164 | external useEffect5: (unit => option unit>, ('a, 'b, 'c, 'd, 'e)) => unit = "useEffect" 165 | @module("react") 166 | external useEffect6: (unit => option unit>, ('a, 'b, 'c, 'd, 'e, 'f)) => unit = "useEffect" 167 | @module("react") 168 | external useEffect7: (unit => option unit>, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => unit = 169 | "useEffect" 170 | 171 | @module("react") 172 | external useLayoutEffectOnEveryRender: (unit => option unit>) => unit = "useLayoutEffect" 173 | @module("react") 174 | external useLayoutEffect: (unit => option unit>, 'deps) => unit = "useLayoutEffect" 175 | @module("react") 176 | external useLayoutEffect0: (unit => option unit>, @as(json`[]`) _) => unit = 177 | "useLayoutEffect" 178 | @module("react") 179 | external useLayoutEffect1: (unit => option unit>, array<'a>) => unit = "useLayoutEffect" 180 | @module("react") 181 | external useLayoutEffect2: (unit => option unit>, ('a, 'b)) => unit = "useLayoutEffect" 182 | @module("react") 183 | external useLayoutEffect3: (unit => option unit>, ('a, 'b, 'c)) => unit = "useLayoutEffect" 184 | @module("react") 185 | external useLayoutEffect4: (unit => option unit>, ('a, 'b, 'c, 'd)) => unit = 186 | "useLayoutEffect" 187 | @module("react") 188 | external useLayoutEffect5: (unit => option unit>, ('a, 'b, 'c, 'd, 'e)) => unit = 189 | "useLayoutEffect" 190 | @module("react") 191 | external useLayoutEffect6: (unit => option unit>, ('a, 'b, 'c, 'd, 'e, 'f)) => unit = 192 | "useLayoutEffect" 193 | @module("react") 194 | external useLayoutEffect7: (unit => option unit>, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => unit = 195 | "useLayoutEffect" 196 | 197 | @module("react") 198 | external useMemo: (unit => 'any, 'deps) => 'any = "useMemo" 199 | 200 | @module("react") 201 | external useMemo0: (unit => 'any, @as(json`[]`) _) => 'any = "useMemo" 202 | 203 | @module("react") 204 | external useMemo1: (unit => 'any, array<'a>) => 'any = "useMemo" 205 | 206 | @module("react") 207 | external useMemo2: (unit => 'any, ('a, 'b)) => 'any = "useMemo" 208 | 209 | @module("react") 210 | external useMemo3: (unit => 'any, ('a, 'b, 'c)) => 'any = "useMemo" 211 | 212 | @module("react") 213 | external useMemo4: (unit => 'any, ('a, 'b, 'c, 'd)) => 'any = "useMemo" 214 | 215 | @module("react") 216 | external useMemo5: (unit => 'any, ('a, 'b, 'c, 'd, 'e)) => 'any = "useMemo" 217 | 218 | @module("react") 219 | external useMemo6: (unit => 'any, ('a, 'b, 'c, 'd, 'e, 'f)) => 'any = "useMemo" 220 | 221 | @module("react") 222 | external useMemo7: (unit => 'any, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => 'any = "useMemo" 223 | 224 | @module("react") 225 | external useCallback: ('f, 'deps) => 'f = "useCallback" 226 | 227 | @module("react") 228 | external useCallback0: ('f, @as(json`[]`) _) => 'f = "useCallback" 229 | 230 | @module("react") 231 | external useCallback1: ('f, array<'a>) => 'f = "useCallback" 232 | 233 | @module("react") 234 | external useCallback2: ('f, ('a, 'b)) => 'f = "useCallback" 235 | 236 | @module("react") 237 | external useCallback3: ('f, ('a, 'b, 'c)) => 'f = "useCallback" 238 | 239 | @module("react") 240 | external useCallback4: ('f, ('a, 'b, 'c, 'd)) => 'f = "useCallback" 241 | 242 | @module("react") 243 | external useCallback5: ('f, ('a, 'b, 'c, 'd, 'e)) => 'f = "useCallback" 244 | 245 | @module("react") 246 | external useCallback6: ('callback, ('a, 'b, 'c, 'd, 'e, 'f)) => 'callback = "useCallback" 247 | 248 | @module("react") 249 | external useCallback7: ('callback, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => 'callback = "useCallback" 250 | 251 | @module("react") 252 | external useContext: Context.t<'any> => 'any = "useContext" 253 | 254 | @module("react") 255 | external usePromise: promise<'a> => 'a = "use" 256 | 257 | @module("react") external useRef: 'value => ref<'value> = "useRef" 258 | 259 | @module("react") 260 | external useImperativeHandleOnEveryRender: (Js.Nullable.t>, unit => 'value) => unit = 261 | "useImperativeHandle" 262 | 263 | @module("react") 264 | external useImperativeHandle: (Js.Nullable.t>, unit => 'value, 'deps) => unit = 265 | "useImperativeHandle" 266 | 267 | @module("react") 268 | external useImperativeHandle0: ( 269 | Js.Nullable.t>, 270 | unit => 'value, 271 | @as(json`[]`) _, 272 | ) => unit = "useImperativeHandle" 273 | 274 | @module("react") 275 | external useImperativeHandle1: (Js.Nullable.t>, unit => 'value, array<'a>) => unit = 276 | "useImperativeHandle" 277 | 278 | @module("react") 279 | external useImperativeHandle2: (Js.Nullable.t>, unit => 'value, ('a, 'b)) => unit = 280 | "useImperativeHandle" 281 | 282 | @module("react") 283 | external useImperativeHandle3: (Js.Nullable.t>, unit => 'value, ('a, 'b, 'c)) => unit = 284 | "useImperativeHandle" 285 | 286 | @module("react") 287 | external useImperativeHandle4: ( 288 | Js.Nullable.t>, 289 | unit => 'value, 290 | ('a, 'b, 'c, 'd), 291 | ) => unit = "useImperativeHandle" 292 | 293 | @module("react") 294 | external useImperativeHandle5: ( 295 | Js.Nullable.t>, 296 | unit => 'value, 297 | ('a, 'b, 'c, 'd, 'e), 298 | ) => unit = "useImperativeHandle" 299 | 300 | @module("react") 301 | external useImperativeHandle6: ( 302 | Js.Nullable.t>, 303 | unit => 'value, 304 | ('a, 'b, 'c, 'd, 'e, 'f), 305 | ) => unit = "useImperativeHandle" 306 | 307 | @module("react") 308 | external useImperativeHandle7: ( 309 | Js.Nullable.t>, 310 | unit => 'value, 311 | ('a, 'b, 'c, 'd, 'e, 'f, 'g), 312 | ) => unit = "useImperativeHandle" 313 | 314 | @module("react") external useId: unit => string = "useId" 315 | 316 | /** `useDeferredValue` is a React Hook that lets you defer updating a part of the UI. */ 317 | @module("react") 318 | external useDeferredValue: ('value, ~initialValue: 'value=?) => 'value = "useDeferredValue" 319 | 320 | @module("react") 321 | external useInsertionEffectOnEveryRender: (unit => option unit>) => unit = 322 | "useInsertionEffect" 323 | @module("react") 324 | external useInsertionEffect: (unit => option unit>, 'deps) => unit = "useInsertionEffect" 325 | @module("react") 326 | external useInsertionEffect0: (unit => option unit>, @as(json`[]`) _) => unit = 327 | "useInsertionEffect" 328 | @module("react") 329 | external useInsertionEffect1: (unit => option unit>, array<'a>) => unit = 330 | "useInsertionEffect" 331 | @module("react") 332 | external useInsertionEffect2: (unit => option unit>, ('a, 'b)) => unit = 333 | "useInsertionEffect" 334 | @module("react") 335 | external useInsertionEffect3: (unit => option unit>, ('a, 'b, 'c)) => unit = 336 | "useInsertionEffect" 337 | @module("react") 338 | external useInsertionEffect4: (unit => option unit>, ('a, 'b, 'c, 'd)) => unit = 339 | "useInsertionEffect" 340 | @module("react") 341 | external useInsertionEffect5: (unit => option unit>, ('a, 'b, 'c, 'd, 'e)) => unit = 342 | "useInsertionEffect" 343 | @module("react") 344 | external useInsertionEffect6: (unit => option unit>, ('a, 'b, 'c, 'd, 'e, 'f)) => unit = 345 | "useInsertionEffect" 346 | @module("react") 347 | external useInsertionEffect7: (unit => option unit>, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => unit = 348 | "useInsertionEffect" 349 | 350 | @module("react") 351 | external useSyncExternalStore: ( 352 | ~subscribe: (unit => unit) => unit => unit, 353 | ~getSnapshot: unit => 'state, 354 | ) => 'state = "useSyncExternalStore" 355 | 356 | @module("react") 357 | external useSyncExternalStoreWithServerSnapshot: ( 358 | ~subscribe: (unit => unit) => unit => unit, 359 | ~getSnapshot: unit => 'state, 360 | ~getServerSnapshot: unit => 'state, 361 | ) => 'state = "useSyncExternalStore" 362 | 363 | module Uncurried = { 364 | @module("react") 365 | external useState: (unit => 'state) => ('state, ('state => 'state) => unit) = "useState" 366 | 367 | @module("react") 368 | external useReducer: (('state, 'action) => 'state, 'state) => ('state, 'action => unit) = 369 | "useReducer" 370 | 371 | @module("react") 372 | external useReducerWithMapState: ( 373 | ('state, 'action) => 'state, 374 | 'initialState, 375 | 'initialState => 'state, 376 | ) => ('state, 'action => unit) = "useReducer" 377 | 378 | @module("react") 379 | external useCallback: ('f, 'deps) => 'f = "useCallback" 380 | 381 | @module("react") 382 | external useCallback0: ('f, @as(json`[]`) _) => 'f = "useCallback" 383 | 384 | @module("react") 385 | external useCallback1: ('f, array<'a>) => 'f = "useCallback" 386 | 387 | @module("react") 388 | external useCallback2: ('f, ('a, 'b)) => 'f = "useCallback" 389 | 390 | @module("react") 391 | external useCallback3: ('f, ('a, 'b, 'c)) => 'f = "useCallback" 392 | 393 | @module("react") 394 | external useCallback4: ('f, ('a, 'b, 'c, 'd)) => 'f = "useCallback" 395 | 396 | @module("react") 397 | external useCallback5: ('f, ('a, 'b, 'c, 'd, 'e)) => 'f = "useCallback" 398 | 399 | @module("react") 400 | external useCallback6: ('callback, ('a, 'b, 'c, 'd, 'e, 'f)) => 'callback = "useCallback" 401 | 402 | @module("react") 403 | external useCallback7: ('callback, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => 'callback = "useCallback" 404 | } 405 | 406 | @set 407 | external setDisplayName: (component<'props>, string) => unit = "displayName" 408 | 409 | @get @return(nullable) 410 | external displayName: component<'props> => option = "displayName" 411 | 412 | // Actions 413 | 414 | type transitionFunction = unit => promise 415 | 416 | type transitionStartFunction = transitionFunction => unit 417 | 418 | /** `useTransition` is a React Hook that lets you render a part of the UI in the background. */ 419 | @module("react") 420 | external useTransition: unit => (bool, transitionStartFunction) = "useTransition" 421 | 422 | type action<'state, 'payload> = ('state, 'payload) => promise<'state> 423 | 424 | type formAction<'formData> = 'formData => promise 425 | 426 | /** `useActionState` is a Hook that allows you to update state based on the result of a form action. */ 427 | @module("react") 428 | external useActionState: ( 429 | action<'state, 'payload>, 430 | 'state, 431 | ~permalink: string=?, 432 | ) => ('state, formAction<'payload>, bool) = "useActionState" 433 | 434 | /** `useOptimistic` is a React Hook that lets you optimistically update the UI. */ 435 | @module("react") 436 | external useOptimistic: ( 437 | 'state, 438 | ~updateFn: ('state, 'action) => 'state=?, 439 | ) => ('state, 'action => unit) = "useOptimistic" 440 | 441 | /** `act` is a test helper to apply pending React updates before making assertions. */ 442 | @module("react") 443 | external act: (unit => promise) => promise = "act" 444 | -------------------------------------------------------------------------------- /src/ReactDOM.bs.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | var Caml_option = require("rescript/lib/js/caml_option.js"); 5 | 6 | var Root = {}; 7 | 8 | var Client = { 9 | Root: Root 10 | }; 11 | 12 | function getString(formData, name) { 13 | var value = formData.get(name); 14 | if (!(value == null) && typeof value === "string") { 15 | return Caml_option.some(value); 16 | } 17 | 18 | } 19 | 20 | function getFile(formData, name) { 21 | var value = formData.get(name); 22 | if (!(value == null) && typeof value !== "string") { 23 | return Caml_option.some(value); 24 | } 25 | 26 | } 27 | 28 | function getAll(t, string) { 29 | return t.getAll(string).map(function (value) { 30 | if (typeof value === "string") { 31 | return { 32 | TAG: "String", 33 | _0: value 34 | }; 35 | } else { 36 | return { 37 | TAG: "File", 38 | _0: value 39 | }; 40 | } 41 | }); 42 | } 43 | 44 | var $$FormData = { 45 | getString: getString, 46 | getFile: getFile, 47 | getAll: getAll 48 | }; 49 | 50 | var Ref = {}; 51 | 52 | var Style; 53 | 54 | exports.Client = Client; 55 | exports.$$FormData = $$FormData; 56 | exports.Ref = Ref; 57 | exports.Style = Style; 58 | /* No side effect */ 59 | -------------------------------------------------------------------------------- /src/ReactDOM.res: -------------------------------------------------------------------------------- 1 | /* First time reading a ReScript file? */ 2 | /* `external` is the foreign function call in OCaml. */ 3 | /* here we're saying `I guarantee that on the JS side, we have a `render` function in the module "react-dom" 4 | that takes in a reactElement, a dom element, and returns unit (nothing) */ 5 | /* It's like `let`, except you're pointing the implementation to the JS side. The compiler will inline these 6 | calls and add the appropriate `require("react-dom")` in the file calling this `render` */ 7 | 8 | // Helper so that ReactDOM itself doesn't bring any runtime 9 | @val @return(nullable) 10 | external querySelector: string => option = "document.querySelector" 11 | 12 | module Client = { 13 | module Root = { 14 | type t 15 | 16 | @send external render: (t, React.element) => unit = "render" 17 | 18 | @send external unmount: (t, unit) => unit = "unmount" 19 | } 20 | 21 | @module("react-dom/client") 22 | external createRoot: Dom.element => Root.t = "createRoot" 23 | 24 | @module("react-dom/client") 25 | external hydrateRoot: (Dom.element, React.element) => Root.t = "hydrateRoot" 26 | } 27 | 28 | // Very rudimentary form data bindings 29 | module FormData = { 30 | type t 31 | type file 32 | 33 | type formValue = 34 | | String(string) 35 | | File(file) 36 | 37 | @new external make: unit => t = "FormData" 38 | 39 | @send external append: (t, string, ~filename: string=?) => unit = "append" 40 | @send external delete: (t, string) => unit = "delete" 41 | @return(nullable) @send external getUnsafe: (t, string) => option<'a> = "get" 42 | @send external getAllUnsafe: (t, string) => array<'a> = "getAll" 43 | 44 | let getString = (formData, name) => { 45 | switch formData->getUnsafe(name) { 46 | | Some(value) => Js.typeof(value) === "string" ? Some(value) : None 47 | | _ => None 48 | } 49 | } 50 | 51 | external _asFile: 'a => file = "%identity" 52 | 53 | let getFile = (formData, name) => { 54 | switch formData->getUnsafe(name) { 55 | | Some(value) => Js.typeof(value) === "string" ? None : Some(value->_asFile) 56 | | _ => None 57 | } 58 | } 59 | 60 | let getAll = (t, string) => { 61 | t 62 | ->getAllUnsafe(string) 63 | ->Js.Array2.map(value => { 64 | Js.typeof(value) === "string" ? String(value) : File(value->_asFile) 65 | }) 66 | } 67 | 68 | @send external set: (string, string) => unit = "set" 69 | @send external has: string => bool = "has" 70 | // @send external keys: t => Iterator.t = "keys"; 71 | // @send external values: t => Iterator.t = "values"; 72 | } 73 | 74 | @module("react-dom") 75 | external createPortal: (React.element, Dom.element) => React.element = "createPortal" 76 | 77 | external domElementToObj: Dom.element => {..} = "%identity" 78 | 79 | type style = ReactDOMStyle.t 80 | 81 | type domRef = JsxDOM.domRef 82 | 83 | module Ref = { 84 | type t = domRef 85 | type currentDomRef = React.ref> 86 | type callbackDomRef = Js.nullable => option unit> 87 | 88 | external domRef: currentDomRef => domRef = "%identity" 89 | external callbackDomRef: callbackDomRef => domRef = "%identity" 90 | } 91 | 92 | // Hooks 93 | 94 | type formStatus<'state> = { 95 | /** If true, this means the parent
is pending submission. Otherwise, false. */ 96 | pending: bool, 97 | /** An object implementing the FormData interface that contains the data the parent is submitting. If there is no active submission or no parent , it will be null. */ 98 | data: FormData.t, 99 | /** This represents whether the parent is submitting with either a GET or POST HTTP method. By default, a will use the GET method and can be specified by the method property. */ 100 | method: [#get | #post], 101 | /** A reference to the function passed to the action prop on the parent . If there is no parent , the property is null. If there is a URI value provided to the action prop, or no action prop specified, status.action will be null. */ 102 | action: React.action<'state, FormData.t>, 103 | } 104 | 105 | external formAction: React.formAction => string = "%identity" 106 | 107 | /** `useFormStatus` is a Hook that gives you status information of the last form submission. */ 108 | @module("react-dom") 109 | external useFormStatus: unit => formStatus<'state> = "useFormStatus" 110 | 111 | // Resource Preloading APIs 112 | 113 | /** The CORS policy to use. */ 114 | type crossOrigin = [ 115 | | #anonymous 116 | | #"use-credentials" 117 | ] 118 | 119 | /** The Referrer header to send when fetching. */ 120 | type referrerPolicy = [ 121 | | #"referrer-when-downgrade" 122 | | #"no-referrer" 123 | | #origin 124 | | #"origin-when-cross-origin" 125 | | #"unsafe-url" 126 | ] 127 | 128 | /** Suggests a relative priority for fetching the resource. */ 129 | type fetchPriority = [#auto | #high | #low] 130 | 131 | /** `prefetchDNS` lets you eagerly look up the IP of a server that you expect to load resources from. */ 132 | @module("react-dom") 133 | external prefetchDNS: string => unit = "prefetchDNS" 134 | 135 | /** `preconnect` lets you eagerly connect to a server that you expect to load resources from. */ 136 | @module("react-dom") 137 | external preconnect: string => unit = "preconnect" 138 | 139 | type preloadOptions = { 140 | /** The type of resource. */ 141 | @as("as") 142 | as_: [ 143 | | #audio 144 | | #document 145 | | #embed 146 | | #fetch 147 | | #font 148 | | #image 149 | | #object 150 | | #script 151 | | #style 152 | | #track 153 | | #video 154 | | #worker 155 | ], 156 | /** The CORS policy to use. It is required when as is set to "fetch". */ 157 | crossOrigin?: crossOrigin, 158 | /** The Referrer header to send when fetching. */ 159 | referrerPolicy?: referrerPolicy, 160 | /** A cryptographic hash of the resource, to verify its authenticity. */ 161 | integrity?: string, 162 | /** The MIME type of the resource. */ 163 | @as("type") 164 | type_?: string, 165 | /** A cryptographic nonce to allow the resource when using a strict Content Security Policy. */ 166 | nonce?: string, 167 | /** Suggests a relative priority for fetching the resource. */ 168 | fetchPriority?: fetchPriority, 169 | /** For use only with as: "image". Specifies the source set of the image. */ 170 | imageSrcSet?: string, 171 | /** For use only with as: "image". Specifies the sizes of the image. */ 172 | imageSizes?: string, 173 | } 174 | 175 | /** `preload` lets you eagerly fetch a resource such as a stylesheet, font, or external script that you expect to use. */ 176 | @module("react-dom") 177 | external preload: (string, preloadOptions) => unit = "preload" 178 | 179 | type preloadModuleOptions = { 180 | /** The type of resource. */ 181 | @as("as") 182 | as_: [#script], 183 | /** The CORS policy to use. It is required when as is set to "fetch". */ 184 | crossOrigin?: crossOrigin, 185 | /** A cryptographic hash of the resource, to verify its authenticity. */ 186 | integrity?: string, 187 | /** A cryptographic nonce to allow the resource when using a strict Content Security Policy. */ 188 | nonce?: string, 189 | } 190 | 191 | /** `preloadModule` lets you eagerly fetch an ESM module that you expect to use. */ 192 | @module("react-dom") 193 | external preloadModule: (string, preloadModuleOptions) => unit = "preloadModule" 194 | 195 | type preinitOptions = { 196 | /** The type of resource. */ 197 | @as("as") 198 | as_: [#script | #style], 199 | /** Required with stylesheets. Says where to insert the stylesheet relative to others. Stylesheets with higher precedence can override those with lower precedence. */ 200 | precedence?: [#reset | #low | #medium | #high], 201 | /** The CORS policy to use. It is required when as is set to "fetch". */ 202 | crossOrigin?: crossOrigin, 203 | /** The Referrer header to send when fetching. */ 204 | referrerPolicy?: referrerPolicy, 205 | /** A cryptographic hash of the resource, to verify its authenticity. */ 206 | integrity?: string, 207 | nonce?: string, 208 | /** Suggests a relative priority for fetching the resource. */ 209 | fetchPriority?: fetchPriority, 210 | } 211 | 212 | /** `preinit` lets you eagerly fetch and evaluate a stylesheet or external script. */ 213 | @module("react-dom") 214 | external preinit: (string, preinitOptions) => unit = "preinit" 215 | 216 | /** To preinit an ESM module, call the `preinitModule` function from react-dom. */ 217 | @module("react-dom") 218 | external preinitModule: (string, preloadModuleOptions) => unit = "preinitModule" 219 | 220 | // Runtime 221 | 222 | type domProps = JsxDOM.domProps 223 | 224 | @variadic @module("react") 225 | external createElement: (string, ~props: domProps=?, array) => React.element = 226 | "createElement" 227 | 228 | @variadic @module("react") 229 | external createDOMElementVariadic: ( 230 | string, 231 | ~props: domProps=?, 232 | array, 233 | ) => React.element = "createElement" 234 | 235 | external someElement: React.element => option = "%identity" 236 | 237 | @module("react/jsx-runtime") 238 | external jsx: (string, JsxDOM.domProps) => Jsx.element = "jsx" 239 | 240 | @module("react/jsx-runtime") 241 | external jsxKeyed: (string, JsxDOM.domProps, ~key: string=?, @ignore unit) => Jsx.element = "jsx" 242 | 243 | @module("react/jsx-runtime") 244 | external jsxs: (string, JsxDOM.domProps) => Jsx.element = "jsxs" 245 | 246 | @module("react/jsx-runtime") 247 | external jsxsKeyed: (string, JsxDOM.domProps, ~key: string=?, @ignore unit) => Jsx.element = "jsxs" 248 | 249 | module Style = ReactDOMStyle 250 | -------------------------------------------------------------------------------- /src/ReactDOMServer.bs.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | /* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */ 3 | -------------------------------------------------------------------------------- /src/ReactDOMServer.res: -------------------------------------------------------------------------------- 1 | @module("react-dom/server") 2 | external renderToString: React.element => string = "renderToString" 3 | 4 | @module("react-dom/server") 5 | external renderToStaticMarkup: React.element => string = "renderToStaticMarkup" 6 | -------------------------------------------------------------------------------- /src/ReactDOMStatic.bs.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | /* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */ 3 | -------------------------------------------------------------------------------- /src/ReactDOMStatic.res: -------------------------------------------------------------------------------- 1 | type abortSignal // WebAPI.EventAPI.abortSignal 2 | 3 | type nodeStream // NodeJs.Stream.stream 4 | 5 | type readableStream // WebAPI.FileAPI.readableStream 6 | 7 | type prerenderOptions<'error> = { 8 | bootstrapScriptContent?: string, 9 | bootstrapScripts?: array, 10 | bootstrapModules?: array, 11 | identifierPrefix?: string, 12 | namespaceURI?: string, 13 | onError?: 'error => unit, 14 | progressiveChunkSize?: int, 15 | signal?: abortSignal, 16 | } 17 | 18 | type staticResult = {prelude: readableStream} 19 | 20 | @module("react-dom/static") 21 | external prerender: (React.element, ~options: prerenderOptions<'error>=?) => promise = 22 | "prerender" 23 | 24 | type staticResultNode = {prelude: nodeStream} 25 | 26 | @module("react-dom/static") 27 | external prerenderToNodeStream: ( 28 | React.element, 29 | ~options: prerenderOptions<'error>=?, 30 | ) => promise = "prerenderToNodeStream" 31 | -------------------------------------------------------------------------------- /src/ReactDOMStyle.bs.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | 5 | function unsafeAddProp(style, key, value) { 6 | var dict = {}; 7 | dict[key] = value; 8 | return Object.assign({}, style, dict); 9 | } 10 | 11 | exports.unsafeAddProp = unsafeAddProp; 12 | /* No side effect */ 13 | -------------------------------------------------------------------------------- /src/ReactDOMStyle.res: -------------------------------------------------------------------------------- 1 | type t = JsxDOMStyle.t 2 | 3 | /* CSS2Properties: https://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSS2Properties */ 4 | @val 5 | external combine: (@as(json`{}`) _, t, t) => t = "Object.assign" 6 | 7 | external _dictToStyle: Js.Dict.t => t = "%identity" 8 | 9 | let unsafeAddProp = (style, key, value) => { 10 | let dict = Js.Dict.empty() 11 | Js.Dict.set(dict, key, value) 12 | combine(style, _dictToStyle(dict)) 13 | } 14 | 15 | @val 16 | external unsafeAddStyle: (@as(json`{}`) _, t, {..}) => t = "Object.assign" 17 | -------------------------------------------------------------------------------- /src/ReactEvent.bs.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | 5 | var Synthetic = {}; 6 | 7 | var Clipboard = {}; 8 | 9 | var Composition = {}; 10 | 11 | var Keyboard = {}; 12 | 13 | var Focus = {}; 14 | 15 | var Form = {}; 16 | 17 | var Mouse = {}; 18 | 19 | var Pointer = {}; 20 | 21 | var $$Selection = {}; 22 | 23 | var $$Touch = {}; 24 | 25 | var UI = {}; 26 | 27 | var Wheel = {}; 28 | 29 | var Media = {}; 30 | 31 | var $$Image = {}; 32 | 33 | var $$Animation = {}; 34 | 35 | var Transition = {}; 36 | 37 | exports.Synthetic = Synthetic; 38 | exports.Clipboard = Clipboard; 39 | exports.Composition = Composition; 40 | exports.Keyboard = Keyboard; 41 | exports.Focus = Focus; 42 | exports.Form = Form; 43 | exports.Mouse = Mouse; 44 | exports.Pointer = Pointer; 45 | exports.$$Selection = $$Selection; 46 | exports.$$Touch = $$Touch; 47 | exports.UI = UI; 48 | exports.Wheel = Wheel; 49 | exports.Media = Media; 50 | exports.$$Image = $$Image; 51 | exports.$$Animation = $$Animation; 52 | exports.Transition = Transition; 53 | /* No side effect */ 54 | -------------------------------------------------------------------------------- /src/ReactEvent.res: -------------------------------------------------------------------------------- 1 | type synthetic<'a> = JsxEvent.synthetic<'a> 2 | 3 | module MakeEventWithType = ( 4 | Type: { 5 | type t 6 | }, 7 | ) => { 8 | @get external bubbles: Type.t => bool = "bubbles" 9 | @get external cancelable: Type.t => bool = "cancelable" 10 | @get 11 | external currentTarget: Type.t => {..} = "currentTarget" /* Should return Dom.eventTarget */ 12 | @get external defaultPrevented: Type.t => bool = "defaultPrevented" 13 | @get external eventPhase: Type.t => int = "eventPhase" 14 | @get external isTrusted: Type.t => bool = "isTrusted" 15 | @get external nativeEvent: Type.t => {..} = "nativeEvent" /* Should return Dom.event */ 16 | @send external preventDefault: Type.t => unit = "preventDefault" 17 | @send 18 | external isDefaultPrevented: Type.t => bool = "isDefaultPrevented" 19 | @send external stopPropagation: Type.t => unit = "stopPropagation" 20 | @send 21 | external isPropagationStopped: Type.t => bool = "isPropagationStopped" 22 | @get external target: Type.t => {..} = "target" /* Should return Dom.eventTarget */ 23 | @get external timeStamp: Type.t => float = "timeStamp" 24 | @get external type_: Type.t => string = "type" 25 | @send external persist: Type.t => unit = "persist" 26 | } 27 | 28 | module Synthetic = { 29 | type tag = JsxEvent.Synthetic.tag 30 | type t = synthetic 31 | @get external bubbles: synthetic<'a> => bool = "bubbles" 32 | @get external cancelable: synthetic<'a> => bool = "cancelable" 33 | @get 34 | external currentTarget: synthetic<'a> => {..} = 35 | "currentTarget" /* Should return Dom.eventTarget */ 36 | @get 37 | external defaultPrevented: synthetic<'a> => bool = "defaultPrevented" 38 | @get external eventPhase: synthetic<'a> => int = "eventPhase" 39 | @get external isTrusted: synthetic<'a> => bool = "isTrusted" 40 | @get 41 | external nativeEvent: synthetic<'a> => {..} = "nativeEvent" /* Should return Dom.event */ 42 | @send 43 | external preventDefault: synthetic<'a> => unit = "preventDefault" 44 | @send 45 | external isDefaultPrevented: synthetic<'a> => bool = "isDefaultPrevented" 46 | @send 47 | external stopPropagation: synthetic<'a> => unit = "stopPropagation" 48 | @send 49 | external isPropagationStopped: synthetic<'a> => bool = "isPropagationStopped" 50 | @get external target: synthetic<'a> => {..} = "target" /* Should return Dom.eventTarget */ 51 | @get external timeStamp: synthetic<'a> => float = "timeStamp" 52 | @get external type_: synthetic<'a> => string = "type" 53 | @send external persist: synthetic<'a> => unit = "persist" 54 | } 55 | 56 | /* Cast any event type to the general synthetic type. This is safe, since synthetic is more general */ 57 | external toSyntheticEvent: synthetic<'a> => Synthetic.t = "%identity" 58 | 59 | module Clipboard = { 60 | type tag = JsxEvent.Clipboard.tag 61 | type t = synthetic 62 | include MakeEventWithType({ 63 | type t = t 64 | }) 65 | @get external clipboardData: t => {..} = "clipboardData" /* Should return Dom.dataTransfer */ 66 | } 67 | 68 | module Composition = { 69 | type tag = JsxEvent.Composition.tag 70 | type t = synthetic 71 | include MakeEventWithType({ 72 | type t = t 73 | }) 74 | @get external data: t => string = "data" 75 | } 76 | 77 | module Keyboard = { 78 | type tag = JsxEvent.Keyboard.tag 79 | type t = synthetic 80 | include MakeEventWithType({ 81 | type t = t 82 | }) 83 | @get external altKey: t => bool = "altKey" 84 | @get external charCode: t => int = "charCode" 85 | @get external code: t => string = "code" 86 | @get external ctrlKey: t => bool = "ctrlKey" 87 | @send 88 | external getModifierState: (t, string) => bool = "getModifierState" 89 | @get external key: t => string = "key" 90 | @get external keyCode: t => int = "keyCode" 91 | @get external locale: t => string = "locale" 92 | @get external location: t => int = "location" 93 | @get external metaKey: t => bool = "metaKey" 94 | @get external repeat: t => bool = "repeat" 95 | @get external shiftKey: t => bool = "shiftKey" 96 | @get external which: t => int = "which" 97 | } 98 | 99 | module Focus = { 100 | type tag = JsxEvent.Focus.tag 101 | type t = synthetic 102 | include MakeEventWithType({ 103 | type t = t 104 | }) 105 | @get @return(nullable) 106 | external relatedTarget: t => option<{..}> = "relatedTarget" /* Should return Dom.eventTarget */ 107 | } 108 | 109 | module Form = { 110 | type tag = JsxEvent.Form.tag 111 | type t = synthetic 112 | include MakeEventWithType({ 113 | type t = t 114 | }) 115 | } 116 | 117 | module Mouse = { 118 | type tag = JsxEvent.Mouse.tag 119 | type t = synthetic 120 | include MakeEventWithType({ 121 | type t = t 122 | }) 123 | @get external altKey: t => bool = "altKey" 124 | @get external button: t => int = "button" 125 | @get external buttons: t => int = "buttons" 126 | @get external clientX: t => int = "clientX" 127 | @get external clientY: t => int = "clientY" 128 | @get external ctrlKey: t => bool = "ctrlKey" 129 | @send 130 | external getModifierState: (t, string) => bool = "getModifierState" 131 | @get external metaKey: t => bool = "metaKey" 132 | @get external movementX: t => int = "movementX" 133 | @get external movementY: t => int = "movementY" 134 | @get external pageX: t => int = "pageX" 135 | @get external pageY: t => int = "pageY" 136 | @get @return(nullable) 137 | external relatedTarget: t => option<{..}> = "relatedTarget" /* Should return Dom.eventTarget */ 138 | @get external screenX: t => int = "screenX" 139 | @get external screenY: t => int = "screenY" 140 | @get external shiftKey: t => bool = "shiftKey" 141 | } 142 | 143 | module Pointer = { 144 | type tag = JsxEvent.Pointer.tag 145 | type t = synthetic 146 | include MakeEventWithType({ 147 | type t = t 148 | }) 149 | 150 | // UIEvent 151 | @get external detail: t => int = "detail" 152 | @get external view: t => Dom.window = "view" /* Should return DOMAbstractView/WindowProxy */ 153 | 154 | // MouseEvent 155 | @get external screenX: t => int = "screenX" 156 | @get external screenY: t => int = "screenY" 157 | @get external clientX: t => int = "clientX" 158 | @get external clientY: t => int = "clientY" 159 | @get external pageX: t => int = "pageX" 160 | @get external pageY: t => int = "pageY" 161 | @get external movementX: t => int = "movementX" 162 | @get external movementY: t => int = "movementY" 163 | 164 | @get external ctrlKey: t => bool = "ctrlKey" 165 | @get external shiftKey: t => bool = "shiftKey" 166 | @get external altKey: t => bool = "altKey" 167 | @get external metaKey: t => bool = "metaKey" 168 | @send 169 | external getModifierState: (t, string) => bool = "getModifierState" 170 | 171 | @get external button: t => int = "button" 172 | @get external buttons: t => int = "buttons" 173 | 174 | @get @return(nullable) 175 | external relatedTarget: t => option<{..}> = "relatedTarget" /* Should return Dom.eventTarget */ 176 | 177 | // PointerEvent 178 | @get external pointerId: t => Dom.eventPointerId = "pointerId" 179 | @get external width: t => float = "width" 180 | @get external height: t => float = "height" 181 | @get external pressure: t => float = "pressure" 182 | @get external tangentialPressure: t => float = "tangentialPressure" 183 | @get external tiltX: t => int = "tiltX" 184 | @get external tiltY: t => int = "tiltY" 185 | @get external twist: t => int = "twist" 186 | @get external pointerType: t => string = "pointerType" 187 | @get external isPrimary: t => bool = "isPrimary" 188 | } 189 | 190 | module Selection = { 191 | type tag = JsxEvent.Selection.tag 192 | type t = synthetic 193 | include MakeEventWithType({ 194 | type t = t 195 | }) 196 | } 197 | 198 | module Touch = { 199 | type tag = JsxEvent.Touch.tag 200 | type t = synthetic 201 | include MakeEventWithType({ 202 | type t = t 203 | }) 204 | @get external altKey: t => bool = "altKey" 205 | @get external changedTouches: t => {..} = "changedTouches" /* Should return Dom.touchList */ 206 | @get external ctrlKey: t => bool = "ctrlKey" 207 | @send 208 | external getModifierState: (t, string) => bool = "getModifierState" 209 | @get external metaKey: t => bool = "metaKey" 210 | @get external shiftKey: t => bool = "shiftKey" 211 | @get external targetTouches: t => {..} = "targetTouches" /* Should return Dom.touchList */ 212 | @get external touches: t => {..} = "touches" /* Should return Dom.touchList */ 213 | } 214 | 215 | module UI = { 216 | type tag = JsxEvent.UI.tag 217 | type t = synthetic 218 | include MakeEventWithType({ 219 | type t = t 220 | }) 221 | @get external detail: t => int = "detail" 222 | @get external view: t => Dom.window = "view" /* Should return DOMAbstractView/WindowProxy */ 223 | } 224 | 225 | module Wheel = { 226 | type tag = JsxEvent.Wheel.tag 227 | type t = synthetic 228 | include MakeEventWithType({ 229 | type t = t 230 | }) 231 | @get external deltaMode: t => int = "deltaMode" 232 | @get external deltaX: t => float = "deltaX" 233 | @get external deltaY: t => float = "deltaY" 234 | @get external deltaZ: t => float = "deltaZ" 235 | } 236 | 237 | module Media = { 238 | type tag = JsxEvent.Media.tag 239 | type t = synthetic 240 | include MakeEventWithType({ 241 | type t = t 242 | }) 243 | } 244 | 245 | module Image = { 246 | type tag = JsxEvent.Image.tag 247 | type t = synthetic 248 | include MakeEventWithType({ 249 | type t = t 250 | }) 251 | } 252 | 253 | module Animation = { 254 | type tag = JsxEvent.Animation.tag 255 | type t = synthetic 256 | include MakeEventWithType({ 257 | type t = t 258 | }) 259 | @get external animationName: t => string = "animationName" 260 | @get external pseudoElement: t => string = "pseudoElement" 261 | @get external elapsedTime: t => float = "elapsedTime" 262 | } 263 | 264 | module Transition = { 265 | type tag = JsxEvent.Transition.tag 266 | type t = synthetic 267 | include MakeEventWithType({ 268 | type t = t 269 | }) 270 | @get external propertyName: t => string = "propertyName" 271 | @get external pseudoElement: t => string = "pseudoElement" 272 | @get external elapsedTime: t => float = "elapsedTime" 273 | } 274 | -------------------------------------------------------------------------------- /src/ReactEvent.resi: -------------------------------------------------------------------------------- 1 | /* This is the whole synthetic event system of ReactJS/ReasonReact. The first module `Synthetic` represents 2 | the generic synthetic event. The rest are the specific ones. 3 | 4 | In each module, the type `t` commonly means "the type of that module" (OCaml convention). In our case, e.g. 5 | `ReactEvent.Mouse.t` represents a ReactJS synthetic mouse event. You'd use it to type your props: 6 | 7 | ``` 8 | type props = { 9 | onClick: ReactEvent.Mouse.t => unit 10 | }; 11 | ``` 12 | 13 | All the methods and properties of a type of event are in the module, as seen below. 14 | 15 | Each module also has a `tag` type. You can ignore it; they're only needed by their `t` type. This way, we 16 | get to allow a base `Synthetic` event module with generic methods. So e.g. even a mouse event (`Mouse.t`) 17 | get to be passed to a generic handler: 18 | 19 | ``` 20 | let handleClick = ({state, props}, event) => { 21 | ReactEvent.Mouse.preventDefault(event); 22 | ... 23 | }; 24 | let handleSubmit = ({state, props}, event) => { 25 | // this handler can be triggered by either a Keyboard or a Mouse event; 26 | // conveniently use the generic preventDefault 27 | ReactEvent.Synthetic.preventDefault(event); 28 | ... 29 | }; 30 | 31 | let render = (_) => ; 32 | ``` 33 | 34 | How to translate idioms from ReactJS: 35 | 36 | 1. myMouseEvent.preventDefault() -> ReactEvent.Mouse.preventDefault(myMouseEvent) 37 | 2. myKeyboardEvent.which -> ReactEvent.Keyboard.which(myKeyboardEvent) 38 | */ 39 | type synthetic<'a> = JsxEvent.synthetic<'a> 40 | 41 | module Synthetic: { 42 | type tag = JsxEvent.Synthetic.tag 43 | type t = synthetic 44 | @get external bubbles: synthetic<'a> => bool = "bubbles" 45 | @get external cancelable: synthetic<'a> => bool = "cancelable" 46 | @get 47 | external currentTarget: synthetic<'a> => {..} = "currentTarget" 48 | @get 49 | external defaultPrevented: synthetic<'a> => bool = "defaultPrevented" 50 | @get external eventPhase: synthetic<'a> => int = "eventPhase" 51 | @get external isTrusted: synthetic<'a> => bool = "isTrusted" 52 | @get 53 | external nativeEvent: synthetic<'a> => {..} = "nativeEvent" 54 | @send 55 | external preventDefault: synthetic<'a> => unit = "preventDefault" 56 | @send 57 | external isDefaultPrevented: synthetic<'a> => bool = "isDefaultPrevented" 58 | @send 59 | external stopPropagation: synthetic<'a> => unit = "stopPropagation" 60 | @send 61 | external isPropagationStopped: synthetic<'a> => bool = "isPropagationStopped" 62 | @get external target: synthetic<'a> => {..} = "target" 63 | @get external timeStamp: synthetic<'a> => float = "timeStamp" 64 | @get external type_: synthetic<'a> => string = "type" 65 | @send external persist: synthetic<'a> => unit = "persist" 66 | } 67 | 68 | /* Cast any event type to the general synthetic type. This is safe, since synthetic is more general */ 69 | external toSyntheticEvent: synthetic<'a> => Synthetic.t = "%identity" 70 | 71 | module Clipboard: { 72 | type tag = JsxEvent.Clipboard.tag 73 | type t = synthetic 74 | @get external bubbles: t => bool = "bubbles" 75 | @get external cancelable: t => bool = "cancelable" 76 | @get external currentTarget: t => {..} = "currentTarget" 77 | @get external defaultPrevented: t => bool = "defaultPrevented" 78 | @get external eventPhase: t => int = "eventPhase" 79 | @get external isTrusted: t => bool = "isTrusted" 80 | @get external nativeEvent: t => {..} = "nativeEvent" 81 | @send external preventDefault: t => unit = "preventDefault" 82 | @send external isDefaultPrevented: t => bool = "isDefaultPrevented" 83 | @send external stopPropagation: t => unit = "stopPropagation" 84 | @send external isPropagationStopped: t => bool = "isPropagationStopped" 85 | @get external target: t => {..} = "target" 86 | @get external timeStamp: t => float = "timeStamp" 87 | @get external type_: t => string = "type" 88 | @send external persist: t => unit = "persist" 89 | @get external clipboardData: t => {..} = "clipboardData" /* Should return Dom.dataTransfer */ 90 | } 91 | 92 | module Composition: { 93 | type tag = JsxEvent.Composition.tag 94 | type t = synthetic 95 | @get external bubbles: t => bool = "bubbles" 96 | @get external cancelable: t => bool = "cancelable" 97 | @get external currentTarget: t => {..} = "currentTarget" 98 | @get external defaultPrevented: t => bool = "defaultPrevented" 99 | @get external eventPhase: t => int = "eventPhase" 100 | @get external isTrusted: t => bool = "isTrusted" 101 | @get external nativeEvent: t => {..} = "nativeEvent" 102 | @send external preventDefault: t => unit = "preventDefault" 103 | @send external isDefaultPrevented: t => bool = "isDefaultPrevented" 104 | @send external stopPropagation: t => unit = "stopPropagation" 105 | @send external isPropagationStopped: t => bool = "isPropagationStopped" 106 | @get external target: t => {..} = "target" 107 | @get external timeStamp: t => float = "timeStamp" 108 | @get external type_: t => string = "type" 109 | @send external persist: t => unit = "persist" 110 | @get external data: t => string = "data" 111 | } 112 | 113 | module Keyboard: { 114 | type tag = JsxEvent.Keyboard.tag 115 | type t = synthetic 116 | @get external bubbles: t => bool = "bubbles" 117 | @get external cancelable: t => bool = "cancelable" 118 | @get external currentTarget: t => {..} = "currentTarget" 119 | @get external defaultPrevented: t => bool = "defaultPrevented" 120 | @get external eventPhase: t => int = "eventPhase" 121 | @get external isTrusted: t => bool = "isTrusted" 122 | @get external nativeEvent: t => {..} = "nativeEvent" 123 | @send external preventDefault: t => unit = "preventDefault" 124 | @send external isDefaultPrevented: t => bool = "isDefaultPrevented" 125 | @send external stopPropagation: t => unit = "stopPropagation" 126 | @send external isPropagationStopped: t => bool = "isPropagationStopped" 127 | @get external target: t => {..} = "target" 128 | @get external timeStamp: t => float = "timeStamp" 129 | @get external type_: t => string = "type" 130 | @send external persist: t => unit = "persist" 131 | @get external altKey: t => bool = "altKey" 132 | @get external charCode: t => int = "charCode" 133 | @get external code: t => string = "code" 134 | @get external ctrlKey: t => bool = "ctrlKey" 135 | @send 136 | external getModifierState: (t, string) => bool = "getModifierState" 137 | @get external key: t => string = "key" 138 | @get external keyCode: t => int = "keyCode" 139 | @get external locale: t => string = "locale" 140 | @get external location: t => int = "location" 141 | @get external metaKey: t => bool = "metaKey" 142 | @get external repeat: t => bool = "repeat" 143 | @get external shiftKey: t => bool = "shiftKey" 144 | @get external which: t => int = "which" 145 | } 146 | 147 | module Focus: { 148 | type tag = JsxEvent.Focus.tag 149 | type t = synthetic 150 | @get external bubbles: t => bool = "bubbles" 151 | @get external cancelable: t => bool = "cancelable" 152 | @get external currentTarget: t => {..} = "currentTarget" 153 | @get external defaultPrevented: t => bool = "defaultPrevented" 154 | @get external eventPhase: t => int = "eventPhase" 155 | @get external isTrusted: t => bool = "isTrusted" 156 | @get external nativeEvent: t => {..} = "nativeEvent" 157 | @send external preventDefault: t => unit = "preventDefault" 158 | @send external isDefaultPrevented: t => bool = "isDefaultPrevented" 159 | @send external stopPropagation: t => unit = "stopPropagation" 160 | @send external isPropagationStopped: t => bool = "isPropagationStopped" 161 | @get external target: t => {..} = "target" 162 | @get external timeStamp: t => float = "timeStamp" 163 | @get external type_: t => string = "type" 164 | @send external persist: t => unit = "persist" 165 | @get @return(nullable) 166 | external relatedTarget: t => option<{..}> = "relatedTarget" /* Should return Dom.eventTarget */ 167 | } 168 | 169 | module Form: { 170 | type tag = JsxEvent.Form.tag 171 | type t = synthetic 172 | @get external bubbles: t => bool = "bubbles" 173 | @get external cancelable: t => bool = "cancelable" 174 | @get external currentTarget: t => {..} = "currentTarget" 175 | @get external defaultPrevented: t => bool = "defaultPrevented" 176 | @get external eventPhase: t => int = "eventPhase" 177 | @get external isTrusted: t => bool = "isTrusted" 178 | @get external nativeEvent: t => {..} = "nativeEvent" 179 | @send external preventDefault: t => unit = "preventDefault" 180 | @send external isDefaultPrevented: t => bool = "isDefaultPrevented" 181 | @send external stopPropagation: t => unit = "stopPropagation" 182 | @send external isPropagationStopped: t => bool = "isPropagationStopped" 183 | @get external target: t => {..} = "target" 184 | @get external timeStamp: t => float = "timeStamp" 185 | @get external type_: t => string = "type" 186 | @send external persist: t => unit = "persist" 187 | } 188 | 189 | module Mouse: { 190 | type tag = JsxEvent.Mouse.tag 191 | type t = synthetic 192 | @get external bubbles: t => bool = "bubbles" 193 | @get external cancelable: t => bool = "cancelable" 194 | @get external currentTarget: t => {..} = "currentTarget" 195 | @get external defaultPrevented: t => bool = "defaultPrevented" 196 | @get external eventPhase: t => int = "eventPhase" 197 | @get external isTrusted: t => bool = "isTrusted" 198 | @get external nativeEvent: t => {..} = "nativeEvent" 199 | @send external preventDefault: t => unit = "preventDefault" 200 | @send external isDefaultPrevented: t => bool = "isDefaultPrevented" 201 | @send external stopPropagation: t => unit = "stopPropagation" 202 | @send external isPropagationStopped: t => bool = "isPropagationStopped" 203 | @get external target: t => {..} = "target" 204 | @get external timeStamp: t => float = "timeStamp" 205 | @get external type_: t => string = "type" 206 | @send external persist: t => unit = "persist" 207 | @get external altKey: t => bool = "altKey" 208 | @get external button: t => int = "button" 209 | @get external buttons: t => int = "buttons" 210 | @get external clientX: t => int = "clientX" 211 | @get external clientY: t => int = "clientY" 212 | @get external ctrlKey: t => bool = "ctrlKey" 213 | @send 214 | external getModifierState: (t, string) => bool = "getModifierState" 215 | @get external metaKey: t => bool = "metaKey" 216 | @get external movementX: t => int = "movementX" 217 | @get external movementY: t => int = "movementY" 218 | @get external pageX: t => int = "pageX" 219 | @get external pageY: t => int = "pageY" 220 | @get @return(nullable) 221 | external relatedTarget: t => option<{..}> = "relatedTarget" /* Should return Dom.eventTarget */ 222 | @get external screenX: t => int = "screenX" 223 | @get external screenY: t => int = "screenY" 224 | @get external shiftKey: t => bool = "shiftKey" 225 | } 226 | 227 | module Pointer: { 228 | type tag = JsxEvent.Pointer.tag 229 | type t = synthetic 230 | 231 | // Event 232 | @get external type_: t => string = "type" 233 | @get external target: t => {..} = "target" 234 | @get external currentTarget: t => {..} = "currentTarget" 235 | 236 | @get external eventPhase: t => int = "eventPhase" 237 | 238 | @send external stopPropagation: t => unit = "stopPropagation" // aka cancelBubble 239 | @get external bubbles: t => bool = "bubbles" 240 | @get external cancelable: t => bool = "cancelable" 241 | @send external preventDefault: t => unit = "preventDefault" 242 | @get external defaultPrevented: t => bool = "defaultPrevented" 243 | 244 | @get external isTrusted: t => bool = "isTrusted" 245 | @get external timeStamp: t => float = "timeStamp" 246 | 247 | // SyntheticEvent 248 | @get external nativeEvent: t => {..} = "nativeEvent" 249 | @send external isDefaultPrevented: t => bool = "isDefaultPrevented" 250 | @send external isPropagationStopped: t => bool = "isPropagationStopped" 251 | @send external persist: t => unit = "persist" 252 | 253 | // UIEvent 254 | @get external detail: t => int = "detail" 255 | @get external view: t => Dom.window = "view" /* Should return DOMAbstractView/WindowProxy */ 256 | 257 | // MouseEvent 258 | @get external screenX: t => int = "screenX" 259 | @get external screenY: t => int = "screenY" 260 | @get external clientX: t => int = "clientX" 261 | @get external clientY: t => int = "clientY" 262 | @get external pageX: t => int = "pageX" 263 | @get external pageY: t => int = "pageY" 264 | @get external movementX: t => int = "movementX" 265 | @get external movementY: t => int = "movementY" 266 | 267 | @get external ctrlKey: t => bool = "ctrlKey" 268 | @get external shiftKey: t => bool = "shiftKey" 269 | @get external altKey: t => bool = "altKey" 270 | @get external metaKey: t => bool = "metaKey" 271 | @send 272 | external getModifierState: (t, string) => bool = "getModifierState" 273 | 274 | @get external button: t => int = "button" 275 | @get external buttons: t => int = "buttons" 276 | 277 | @get @return(nullable) 278 | external relatedTarget: t => option<{..}> = "relatedTarget" /* Should return Dom.eventTarget */ 279 | 280 | // PointerEvent 281 | @get external pointerId: t => Dom.eventPointerId = "pointerId" 282 | @get external width: t => float = "width" 283 | @get external height: t => float = "height" 284 | @get external pressure: t => float = "pressure" 285 | @get external tangentialPressure: t => float = "tangentialPressure" 286 | @get external tiltX: t => int = "tiltX" 287 | @get external tiltY: t => int = "tiltY" 288 | @get external twist: t => int = "twist" 289 | @get external pointerType: t => string = "pointerType" 290 | @get external isPrimary: t => bool = "isPrimary" 291 | } 292 | 293 | module Selection: { 294 | type tag = JsxEvent.Selection.tag 295 | type t = synthetic 296 | @get external bubbles: t => bool = "bubbles" 297 | @get external cancelable: t => bool = "cancelable" 298 | @get external currentTarget: t => {..} = "currentTarget" 299 | @get external defaultPrevented: t => bool = "defaultPrevented" 300 | @get external eventPhase: t => int = "eventPhase" 301 | @get external isTrusted: t => bool = "isTrusted" 302 | @get external nativeEvent: t => {..} = "nativeEvent" 303 | @send external preventDefault: t => unit = "preventDefault" 304 | @send external isDefaultPrevented: t => bool = "isDefaultPrevented" 305 | @send external stopPropagation: t => unit = "stopPropagation" 306 | @send external isPropagationStopped: t => bool = "isPropagationStopped" 307 | @get external target: t => {..} = "target" 308 | @get external timeStamp: t => float = "timeStamp" 309 | @get external type_: t => string = "type" 310 | @send external persist: t => unit = "persist" 311 | } 312 | 313 | module Touch: { 314 | type tag = JsxEvent.Touch.tag 315 | type t = synthetic 316 | @get external bubbles: t => bool = "bubbles" 317 | @get external cancelable: t => bool = "cancelable" 318 | @get external currentTarget: t => {..} = "currentTarget" 319 | @get external defaultPrevented: t => bool = "defaultPrevented" 320 | @get external eventPhase: t => int = "eventPhase" 321 | @get external isTrusted: t => bool = "isTrusted" 322 | @get external nativeEvent: t => {..} = "nativeEvent" 323 | @send external preventDefault: t => unit = "preventDefault" 324 | @send external isDefaultPrevented: t => bool = "isDefaultPrevented" 325 | @send external stopPropagation: t => unit = "stopPropagation" 326 | @send external isPropagationStopped: t => bool = "isPropagationStopped" 327 | @get external target: t => {..} = "target" 328 | @get external timeStamp: t => float = "timeStamp" 329 | @get external type_: t => string = "type" 330 | @send external persist: t => unit = "persist" 331 | @get external altKey: t => bool = "altKey" 332 | @get external changedTouches: t => {..} = "changedTouches" /* Should return Dom.touchList */ 333 | @get external ctrlKey: t => bool = "ctrlKey" 334 | @send 335 | external getModifierState: (t, string) => bool = "getModifierState" 336 | @get external metaKey: t => bool = "metaKey" 337 | @get external shiftKey: t => bool = "shiftKey" 338 | @get external targetTouches: t => {..} = "targetTouches" /* Should return Dom.touchList */ 339 | @get external touches: t => {..} = "touches" /* Should return Dom.touchList */ 340 | } 341 | 342 | module UI: { 343 | type tag = JsxEvent.UI.tag 344 | type t = synthetic 345 | @get external bubbles: t => bool = "bubbles" 346 | @get external cancelable: t => bool = "cancelable" 347 | @get external currentTarget: t => {..} = "currentTarget" 348 | @get external defaultPrevented: t => bool = "defaultPrevented" 349 | @get external eventPhase: t => int = "eventPhase" 350 | @get external isTrusted: t => bool = "isTrusted" 351 | @get external nativeEvent: t => {..} = "nativeEvent" 352 | @send external preventDefault: t => unit = "preventDefault" 353 | @send external isDefaultPrevented: t => bool = "isDefaultPrevented" 354 | @send external stopPropagation: t => unit = "stopPropagation" 355 | @send external isPropagationStopped: t => bool = "isPropagationStopped" 356 | @get external target: t => {..} = "target" 357 | @get external timeStamp: t => float = "timeStamp" 358 | @get external type_: t => string = "type" 359 | @send external persist: t => unit = "persist" 360 | @get external detail: t => int = "detail" 361 | @get external view: t => Dom.window = "view" /* Should return DOMAbstractView/WindowProxy */ 362 | } 363 | 364 | module Wheel: { 365 | type tag = JsxEvent.Wheel.tag 366 | type t = synthetic 367 | @get external bubbles: t => bool = "bubbles" 368 | @get external cancelable: t => bool = "cancelable" 369 | @get external currentTarget: t => {..} = "currentTarget" 370 | @get external defaultPrevented: t => bool = "defaultPrevented" 371 | @get external eventPhase: t => int = "eventPhase" 372 | @get external isTrusted: t => bool = "isTrusted" 373 | @get external nativeEvent: t => {..} = "nativeEvent" 374 | @send external preventDefault: t => unit = "preventDefault" 375 | @send external isDefaultPrevented: t => bool = "isDefaultPrevented" 376 | @send external stopPropagation: t => unit = "stopPropagation" 377 | @send external isPropagationStopped: t => bool = "isPropagationStopped" 378 | @get external target: t => {..} = "target" 379 | @get external timeStamp: t => float = "timeStamp" 380 | @get external type_: t => string = "type" 381 | @send external persist: t => unit = "persist" 382 | @get external deltaMode: t => int = "deltaMode" 383 | @get external deltaX: t => float = "deltaX" 384 | @get external deltaY: t => float = "deltaY" 385 | @get external deltaZ: t => float = "deltaZ" 386 | } 387 | 388 | module Media: { 389 | type tag = JsxEvent.Media.tag 390 | type t = synthetic 391 | @get external bubbles: t => bool = "bubbles" 392 | @get external cancelable: t => bool = "cancelable" 393 | @get external currentTarget: t => {..} = "currentTarget" 394 | @get external defaultPrevented: t => bool = "defaultPrevented" 395 | @get external eventPhase: t => int = "eventPhase" 396 | @get external isTrusted: t => bool = "isTrusted" 397 | @get external nativeEvent: t => {..} = "nativeEvent" 398 | @send external preventDefault: t => unit = "preventDefault" 399 | @send external isDefaultPrevented: t => bool = "isDefaultPrevented" 400 | @send external stopPropagation: t => unit = "stopPropagation" 401 | @send external isPropagationStopped: t => bool = "isPropagationStopped" 402 | @get external target: t => {..} = "target" 403 | @get external timeStamp: t => float = "timeStamp" 404 | @get external type_: t => string = "type" 405 | @send external persist: t => unit = "persist" 406 | } 407 | 408 | module Image: { 409 | type tag = JsxEvent.Image.tag 410 | type t = synthetic 411 | @get external bubbles: t => bool = "bubbles" 412 | @get external cancelable: t => bool = "cancelable" 413 | @get external currentTarget: t => {..} = "currentTarget" 414 | @get external defaultPrevented: t => bool = "defaultPrevented" 415 | @get external eventPhase: t => int = "eventPhase" 416 | @get external isTrusted: t => bool = "isTrusted" 417 | @get external nativeEvent: t => {..} = "nativeEvent" 418 | @send external preventDefault: t => unit = "preventDefault" 419 | @send external isDefaultPrevented: t => bool = "isDefaultPrevented" 420 | @send external stopPropagation: t => unit = "stopPropagation" 421 | @send external isPropagationStopped: t => bool = "isPropagationStopped" 422 | @get external target: t => {..} = "target" 423 | @get external timeStamp: t => float = "timeStamp" 424 | @get external type_: t => string = "type" 425 | @send external persist: t => unit = "persist" 426 | } 427 | 428 | module Animation: { 429 | type tag = JsxEvent.Animation.tag 430 | type t = synthetic 431 | @get external bubbles: t => bool = "bubbles" 432 | @get external cancelable: t => bool = "cancelable" 433 | @get external currentTarget: t => {..} = "currentTarget" 434 | @get external defaultPrevented: t => bool = "defaultPrevented" 435 | @get external eventPhase: t => int = "eventPhase" 436 | @get external isTrusted: t => bool = "isTrusted" 437 | @get external nativeEvent: t => {..} = "nativeEvent" 438 | @send external preventDefault: t => unit = "preventDefault" 439 | @send external isDefaultPrevented: t => bool = "isDefaultPrevented" 440 | @send external stopPropagation: t => unit = "stopPropagation" 441 | @send external isPropagationStopped: t => bool = "isPropagationStopped" 442 | @get external target: t => {..} = "target" 443 | @get external timeStamp: t => float = "timeStamp" 444 | @get external type_: t => string = "type" 445 | @send external persist: t => unit = "persist" 446 | @get external animationName: t => string = "animationName" 447 | @get external pseudoElement: t => string = "pseudoElement" 448 | @get external elapsedTime: t => float = "elapsedTime" 449 | } 450 | 451 | module Transition: { 452 | type tag = JsxEvent.Transition.tag 453 | type t = synthetic 454 | @get external bubbles: t => bool = "bubbles" 455 | @get external cancelable: t => bool = "cancelable" 456 | @get external currentTarget: t => {..} = "currentTarget" 457 | @get external defaultPrevented: t => bool = "defaultPrevented" 458 | @get external eventPhase: t => int = "eventPhase" 459 | @get external isTrusted: t => bool = "isTrusted" 460 | @get external nativeEvent: t => {..} = "nativeEvent" 461 | @send external preventDefault: t => unit = "preventDefault" 462 | @send external isDefaultPrevented: t => bool = "isDefaultPrevented" 463 | @send external stopPropagation: t => unit = "stopPropagation" 464 | @send external isPropagationStopped: t => bool = "isPropagationStopped" 465 | @get external target: t => {..} = "target" 466 | @get external timeStamp: t => float = "timeStamp" 467 | @get external type_: t => string = "type" 468 | @send external persist: t => unit = "persist" 469 | @get external propertyName: t => string = "propertyName" 470 | @get external pseudoElement: t => string = "pseudoElement" 471 | @get external elapsedTime: t => float = "elapsedTime" 472 | } 473 | -------------------------------------------------------------------------------- /src/ReactTestUtils.bs.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | var Caml_option = require("rescript/lib/js/caml_option.js"); 5 | var TestUtils = require("react-dom/test-utils"); 6 | 7 | function act(func) { 8 | var reactFunc = function () { 9 | func(); 10 | }; 11 | TestUtils.act(reactFunc); 12 | } 13 | 14 | function actAsync(func) { 15 | return TestUtils.act(function () { 16 | return func(); 17 | }); 18 | } 19 | 20 | function changeWithValue(element, value) { 21 | var $$event = { 22 | target: { 23 | value: value 24 | } 25 | }; 26 | TestUtils.Simulate.change(element, $$event); 27 | } 28 | 29 | function changeWithChecked(element, value) { 30 | var $$event = { 31 | target: { 32 | checked: value 33 | } 34 | }; 35 | TestUtils.Simulate.change(element, $$event); 36 | } 37 | 38 | var Simulate = { 39 | changeWithValue: changeWithValue, 40 | changeWithChecked: changeWithChecked 41 | }; 42 | 43 | function findBySelector(element, selector) { 44 | return element.querySelector(selector); 45 | } 46 | 47 | function findByAllSelector(element, selector) { 48 | return Array.from(element.querySelectorAll(selector)); 49 | } 50 | 51 | function findBySelectorAndTextContent(element, selector, content) { 52 | return Caml_option.undefined_to_opt(Array.from(element.querySelectorAll(selector)).find(function (node) { 53 | return node.textContent === content; 54 | })); 55 | } 56 | 57 | function findBySelectorAndPartialTextContent(element, selector, content) { 58 | return Caml_option.undefined_to_opt(Array.from(element.querySelectorAll(selector)).find(function (node) { 59 | return node.textContent.includes(content); 60 | })); 61 | } 62 | 63 | var DOM = { 64 | findBySelector: findBySelector, 65 | findByAllSelector: findByAllSelector, 66 | findBySelectorAndTextContent: findBySelectorAndTextContent, 67 | findBySelectorAndPartialTextContent: findBySelectorAndPartialTextContent 68 | }; 69 | 70 | function prepareContainer(container, param) { 71 | var containerElement = document.createElement("div"); 72 | var body = document.body; 73 | if (body !== undefined) { 74 | Caml_option.valFromOption(body).appendChild(containerElement); 75 | } 76 | container.contents = Caml_option.some(containerElement); 77 | } 78 | 79 | function cleanupContainer(container, param) { 80 | var contents = container.contents; 81 | if (contents !== undefined) { 82 | Caml_option.valFromOption(contents).remove(); 83 | } 84 | container.contents = undefined; 85 | } 86 | 87 | function getContainer(container) { 88 | var contents = container.contents; 89 | if (contents !== undefined) { 90 | return Caml_option.valFromOption(contents); 91 | } 92 | throw { 93 | RE_EXN_ID: "Not_found", 94 | Error: new Error() 95 | }; 96 | } 97 | 98 | exports.act = act; 99 | exports.actAsync = actAsync; 100 | exports.Simulate = Simulate; 101 | exports.DOM = DOM; 102 | exports.prepareContainer = prepareContainer; 103 | exports.cleanupContainer = cleanupContainer; 104 | exports.getContainer = getContainer; 105 | /* react-dom/test-utils Not a pure module */ 106 | -------------------------------------------------------------------------------- /src/ReactTestUtils.res: -------------------------------------------------------------------------------- 1 | type undefined = Js.undefined 2 | 3 | let undefined: undefined = Js.Undefined.empty 4 | 5 | @module("react-dom/test-utils") 6 | external reactAct: (unit => undefined) => unit = "act" 7 | 8 | let act: (unit => unit) => unit = func => { 9 | let reactFunc = () => { 10 | func() 11 | undefined 12 | } 13 | reactAct(reactFunc) 14 | } 15 | 16 | @module("react-dom/test-utils") 17 | external reactActAsync: (unit => Js.Promise.t<'a>) => Js.Promise.t = "act" 18 | 19 | let actAsync = func => { 20 | let reactFunc = () => func() 21 | reactActAsync(reactFunc) 22 | } 23 | 24 | @module("react-dom/test-utils") 25 | external isElement: 'element => bool = "isElement" 26 | 27 | @module("react-dom/test-utils") 28 | external isElementOfType: ('element, React.component<'props>) => bool = "isElementOfType" 29 | 30 | @module("react-dom/test-utils") 31 | external isDOMComponent: 'element => bool = "isDOMComponent" 32 | 33 | @module("react-dom/test-utils") 34 | external isCompositeComponent: 'element => bool = "isCompositeComponent" 35 | 36 | @module("react-dom/test-utils") 37 | external isCompositeComponentWithType: ('element, React.component<'props>) => bool = 38 | "isCompositeComponentWithType" 39 | 40 | module Simulate = { 41 | @module("react-dom/test-utils") @scope("Simulate") 42 | external click: Dom.element => unit = "click" 43 | @module("react-dom/test-utils") @scope("Simulate") 44 | external clickWithEvent: (Dom.element, 'event) => unit = "click" 45 | @module("react-dom/test-utils") @scope("Simulate") 46 | external change: Dom.element => unit = "change" 47 | @module("react-dom/test-utils") @scope("Simulate") 48 | external blur: Dom.element => unit = "blur" 49 | @module("react-dom/test-utils") @scope("Simulate") 50 | external changeWithEvent: (Dom.element, 'event) => unit = "change" 51 | let changeWithValue = (element, value) => { 52 | let event = { 53 | "target": { 54 | "value": value, 55 | }, 56 | } 57 | changeWithEvent(element, event) 58 | } 59 | let changeWithChecked = (element, value) => { 60 | let event = { 61 | "target": { 62 | "checked": value, 63 | }, 64 | } 65 | changeWithEvent(element, event) 66 | } 67 | @module("react-dom/test-utils") @scope("Simulate") 68 | external canPlay: Dom.element => unit = "canPlay" 69 | @module("react-dom/test-utils") @scope("Simulate") 70 | external timeUpdate: Dom.element => unit = "timeUpdate" 71 | @module("react-dom/test-utils") @scope("Simulate") 72 | external ended: Dom.element => unit = "ended" 73 | @module("react-dom/test-utils") @scope("Simulate") 74 | external focus: Dom.element => unit = "focus" 75 | } 76 | 77 | @val external document: Dom.document = "document" 78 | 79 | @send 80 | external querySelector: (Dom.element, string) => option = "querySelector" 81 | 82 | @send 83 | external querySelectorAll: (Dom.element, string) => Js.Array.array_like = 84 | "querySelectorAll" 85 | 86 | @get external textContent: Dom.element => string = "textContent" 87 | @get external body: Dom.document => option = "body" 88 | @send 89 | external createElement: (Dom.document, string) => Dom.element = "createElement" 90 | @send external remove: Dom.element => unit = "remove" 91 | @send 92 | external appendChild: (Dom.element, Dom.element) => Dom.element = "appendChild" 93 | 94 | let querySelectorAll = (element, string) => Js.Array.from(querySelectorAll(element, string)) 95 | 96 | module DOM = { 97 | @return(nullable) @get 98 | external value: Dom.element => option = "value" 99 | 100 | let findBySelector = (element, selector) => querySelector(element, selector) 101 | 102 | let findByAllSelector = (element, selector) => querySelectorAll(element, selector) 103 | 104 | let findBySelectorAndTextContent = (element, selector, content) => 105 | querySelectorAll(element, selector)->Js.Array2.find(node => node->textContent === content) 106 | 107 | let findBySelectorAndPartialTextContent = (element, selector, content) => 108 | querySelectorAll(element, selector)->Js.Array2.find(node => 109 | node->textContent->Js.String2.includes(content) 110 | ) 111 | } 112 | 113 | let prepareContainer = (container: ref>, ()) => { 114 | let containerElement = document->createElement("div") 115 | switch document->body { 116 | | Some(body) => body->appendChild(containerElement)->ignore 117 | | None => () 118 | } 119 | container := Some(containerElement) 120 | } 121 | 122 | let cleanupContainer = (container: ref>, ()) => { 123 | switch container.contents { 124 | | Some(contents) => remove(contents) 125 | | None => () 126 | } 127 | container := None 128 | } 129 | 130 | let getContainer = container => 131 | switch container.contents { 132 | | Some(contents) => contents 133 | | None => raise(Not_found) 134 | } 135 | -------------------------------------------------------------------------------- /src/ReactTestUtils.resi: -------------------------------------------------------------------------------- 1 | let act: (unit => unit) => unit 2 | 3 | let actAsync: (unit => Js.Promise.t<'a>) => Js.Promise.t 4 | 5 | @module("react-dom/test-utils") 6 | external isElement: 'element => bool = "isElement" 7 | 8 | @module("react-dom/test-utils") 9 | external isElementOfType: ('element, React.component<'props>) => bool = "isElementOfType" 10 | 11 | @module("react-dom/test-utils") 12 | external isDOMComponent: 'element => bool = "isDOMComponent" 13 | 14 | @module("react-dom/test-utils") 15 | external isCompositeComponent: 'element => bool = "isCompositeComponent" 16 | 17 | @module("react-dom/test-utils") 18 | external isCompositeComponentWithType: ('element, React.component<'props>) => bool = 19 | "isCompositeComponentWithType" 20 | 21 | module Simulate: { 22 | @module("react-dom/test-utils") @scope("Simulate") 23 | external click: Dom.element => unit = "click" 24 | @module("react-dom/test-utils") @scope("Simulate") 25 | external clickWithEvent: (Dom.element, 'event) => unit = "click" 26 | @module("react-dom/test-utils") @scope("Simulate") 27 | external change: Dom.element => unit = "change" 28 | @module("react-dom/test-utils") @scope("Simulate") 29 | external blur: Dom.element => unit = "blur" 30 | @module("react-dom/test-utils") @scope("Simulate") 31 | external changeWithEvent: (Dom.element, 'event) => unit = "change" 32 | let changeWithValue: (Dom.element, string) => unit 33 | let changeWithChecked: (Dom.element, bool) => unit 34 | @module("react-dom/test-utils") @scope("Simulate") 35 | external canPlay: Dom.element => unit = "canPlay" 36 | @module("react-dom/test-utils") @scope("Simulate") 37 | external timeUpdate: Dom.element => unit = "timeUpdate" 38 | @module("react-dom/test-utils") @scope("Simulate") 39 | external ended: Dom.element => unit = "ended" 40 | @module("react-dom/test-utils") @scope("Simulate") 41 | external focus: Dom.element => unit = "focus" 42 | } 43 | 44 | module DOM: { 45 | @return(nullable) @get 46 | external value: Dom.element => option = "value" 47 | 48 | let findBySelector: (Dom.element, string) => option 49 | let findByAllSelector: (Dom.element, string) => array 50 | let findBySelectorAndTextContent: (Dom.element, string, string) => option 51 | let findBySelectorAndPartialTextContent: (Dom.element, string, string) => option 52 | } 53 | 54 | let prepareContainer: (ref>, unit) => unit 55 | let cleanupContainer: (ref>, unit) => unit 56 | let getContainer: ref> => Dom.element 57 | -------------------------------------------------------------------------------- /src/RescriptReactErrorBoundary.bs.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | var React = require("react"); 5 | 6 | var noOp = (function (_x) {}); 7 | 8 | var reactComponentClass = React.Component; 9 | 10 | noOp(reactComponentClass); 11 | 12 | var ErrorBoundary = (function (Component) { 13 | function ErrorBoundary(props) { 14 | Component.call(this); 15 | this.state = { error: undefined }; 16 | } 17 | ErrorBoundary.prototype = Object.create(Component.prototype); 18 | ErrorBoundary.prototype.componentDidCatch = function (error, info) { 19 | this.setState({ error: { error: error, info: info } }); 20 | }; 21 | ErrorBoundary.prototype.render = function () { 22 | return this.state.error != undefined 23 | ? this.props.fallback(this.state.error) 24 | : this.props.children; 25 | }; 26 | return ErrorBoundary; 27 | })(reactComponentClass); 28 | ; 29 | 30 | var make = ErrorBoundary; 31 | 32 | exports.make = make; 33 | /* reactComponentClass Not a pure module */ 34 | -------------------------------------------------------------------------------- /src/RescriptReactErrorBoundary.res: -------------------------------------------------------------------------------- 1 | /*** 2 | * Important note on this module: 3 | * As soon as React provides a mechanism for error-catching using functional component, 4 | * this is likely to be deprecated and/or move to user space. 5 | */ 6 | type info = {componentStack: string} 7 | 8 | type params<'error> = { 9 | error: 'error, 10 | info: info, 11 | } 12 | 13 | type reactComponentClass 14 | @module("react") external component: reactComponentClass = "Component" 15 | let noOp: reactComponentClass => unit = %raw(`function (_x) {}`) 16 | let reactComponentClass = component 17 | // this is so that the compiler doesn't optimize away the previous line 18 | noOp(reactComponentClass) 19 | 20 | %%raw(` 21 | 22 | var ErrorBoundary = (function (Component) { 23 | function ErrorBoundary(props) { 24 | Component.call(this); 25 | this.state = { error: undefined }; 26 | } 27 | ErrorBoundary.prototype = Object.create(Component.prototype); 28 | ErrorBoundary.prototype.componentDidCatch = function (error, info) { 29 | this.setState({ error: { error: error, info: info } }); 30 | }; 31 | ErrorBoundary.prototype.render = function () { 32 | return this.state.error != undefined 33 | ? this.props.fallback(this.state.error) 34 | : this.props.children; 35 | }; 36 | return ErrorBoundary; 37 | })(reactComponentClass); 38 | `) 39 | 40 | @react.component @val 41 | external make: ( 42 | ~children: React.element, 43 | ~fallback: params<'error> => React.element, 44 | ) => React.element = "ErrorBoundary" 45 | -------------------------------------------------------------------------------- /src/RescriptReactErrorBoundary.resi: -------------------------------------------------------------------------------- 1 | /*** 2 | * Important note on this module: 3 | * As soon as React provides a mechanism for error-catching using functional component, 4 | * this is likely to be deprecated and/or move to user space. 5 | */ 6 | type info = {componentStack: string} 7 | 8 | type params<'error> = { 9 | error: 'error, 10 | info: info, 11 | } 12 | 13 | @react.component 14 | let make: (~children: React.element, ~fallback: params<'error> => React.element) => React.element 15 | -------------------------------------------------------------------------------- /src/RescriptReactRouter.bs.js: -------------------------------------------------------------------------------- 1 | // Generated by ReScript, PLEASE EDIT WITH CARE 2 | 'use strict'; 3 | 4 | var React = require("react"); 5 | var Caml_option = require("rescript/lib/js/caml_option.js"); 6 | 7 | function safeMakeEvent(eventName) { 8 | if (typeof Event === "function") { 9 | return new Event(eventName); 10 | } 11 | var $$event = document.createEvent("Event"); 12 | $$event.initEvent(eventName, true, true); 13 | return $$event; 14 | } 15 | 16 | function pathParse(str) { 17 | switch (str) { 18 | case "" : 19 | case "/" : 20 | return /* [] */0; 21 | default: 22 | var raw = str.slice(1); 23 | var match = raw[raw.length - 1 | 0]; 24 | var raw$1 = match === "/" ? raw.slice(0, -1) : raw; 25 | var match$1 = raw$1.split("?", 2); 26 | var raw$2 = match$1.length !== 2 ? raw$1 : match$1[0]; 27 | var a = raw$2.split("/").filter(function (item) { 28 | return item.length !== 0; 29 | }); 30 | var _i = a.length - 1 | 0; 31 | var _res = /* [] */0; 32 | while(true) { 33 | var res = _res; 34 | var i = _i; 35 | if (i < 0) { 36 | return res; 37 | } 38 | _res = { 39 | hd: a[i], 40 | tl: res 41 | }; 42 | _i = i - 1 | 0; 43 | continue ; 44 | }; 45 | } 46 | } 47 | 48 | function path(serverUrlString, param) { 49 | var match = globalThis.window; 50 | if (serverUrlString !== undefined) { 51 | return pathParse(serverUrlString); 52 | } else if (match !== undefined) { 53 | return pathParse(Caml_option.valFromOption(match).location.pathname); 54 | } else { 55 | return /* [] */0; 56 | } 57 | } 58 | 59 | function hash() { 60 | var $$window = globalThis.window; 61 | if ($$window === undefined) { 62 | return ""; 63 | } 64 | var raw = Caml_option.valFromOption($$window).location.hash; 65 | switch (raw) { 66 | case "" : 67 | case "#" : 68 | return ""; 69 | default: 70 | return raw.slice(1); 71 | } 72 | } 73 | 74 | function searchParse(str) { 75 | switch (str) { 76 | case "" : 77 | case "?" : 78 | return ""; 79 | default: 80 | var match = str.split("?", 2); 81 | if (match.length !== 2) { 82 | return ""; 83 | } else { 84 | return match[1]; 85 | } 86 | } 87 | } 88 | 89 | function search(serverUrlString, param) { 90 | var match = globalThis.window; 91 | if (serverUrlString !== undefined) { 92 | return searchParse(serverUrlString); 93 | } else if (match !== undefined) { 94 | return searchParse(Caml_option.valFromOption(match).location.search); 95 | } else { 96 | return ""; 97 | } 98 | } 99 | 100 | function push(path) { 101 | var match = globalThis.history; 102 | var match$1 = globalThis.window; 103 | if (match !== undefined && match$1 !== undefined) { 104 | Caml_option.valFromOption(match).pushState(null, "", path); 105 | Caml_option.valFromOption(match$1).dispatchEvent(safeMakeEvent("popstate")); 106 | return ; 107 | } 108 | 109 | } 110 | 111 | function replace(path) { 112 | var match = globalThis.history; 113 | var match$1 = globalThis.window; 114 | if (match !== undefined && match$1 !== undefined) { 115 | Caml_option.valFromOption(match).replaceState(null, "", path); 116 | Caml_option.valFromOption(match$1).dispatchEvent(safeMakeEvent("popstate")); 117 | return ; 118 | } 119 | 120 | } 121 | 122 | function urlNotEqual(a, b) { 123 | if (a.hash !== b.hash || a.search !== b.search) { 124 | return true; 125 | } else { 126 | var _aList = a.path; 127 | var _bList = b.path; 128 | while(true) { 129 | var bList = _bList; 130 | var aList = _aList; 131 | if (!aList) { 132 | if (bList) { 133 | return true; 134 | } else { 135 | return false; 136 | } 137 | } 138 | if (!bList) { 139 | return true; 140 | } 141 | if (aList.hd !== bList.hd) { 142 | return true; 143 | } 144 | _bList = bList.tl; 145 | _aList = aList.tl; 146 | continue ; 147 | }; 148 | } 149 | } 150 | 151 | function url(serverUrlString, param) { 152 | return { 153 | path: path(serverUrlString, undefined), 154 | hash: hash(), 155 | search: search(serverUrlString, undefined) 156 | }; 157 | } 158 | 159 | function watchUrl(callback) { 160 | var $$window = globalThis.window; 161 | if ($$window === undefined) { 162 | return function () { 163 | 164 | }; 165 | } 166 | var watcherID = function () { 167 | callback(url(undefined, undefined)); 168 | }; 169 | Caml_option.valFromOption($$window).addEventListener("popstate", watcherID); 170 | return watcherID; 171 | } 172 | 173 | function unwatchUrl(watcherID) { 174 | var $$window = globalThis.window; 175 | if ($$window !== undefined) { 176 | Caml_option.valFromOption($$window).removeEventListener("popstate", watcherID); 177 | return ; 178 | } 179 | 180 | } 181 | 182 | function useUrl(serverUrl, param) { 183 | var match = React.useState(function () { 184 | if (serverUrl !== undefined) { 185 | return serverUrl; 186 | } else { 187 | return url(undefined, undefined); 188 | } 189 | }); 190 | var setUrl = match[1]; 191 | var url$1 = match[0]; 192 | React.useEffect((function () { 193 | var watcherId = watchUrl(function (url) { 194 | setUrl(function (param) { 195 | return url; 196 | }); 197 | }); 198 | var newUrl = url(undefined, undefined); 199 | if (urlNotEqual(newUrl, url$1)) { 200 | setUrl(function (param) { 201 | return newUrl; 202 | }); 203 | } 204 | return (function () { 205 | unwatchUrl(watcherId); 206 | }); 207 | }), []); 208 | return url$1; 209 | } 210 | 211 | var dangerouslyGetInitialUrl = url; 212 | 213 | exports.push = push; 214 | exports.replace = replace; 215 | exports.watchUrl = watchUrl; 216 | exports.unwatchUrl = unwatchUrl; 217 | exports.dangerouslyGetInitialUrl = dangerouslyGetInitialUrl; 218 | exports.useUrl = useUrl; 219 | /* react Not a pure module */ 220 | -------------------------------------------------------------------------------- /src/RescriptReactRouter.res: -------------------------------------------------------------------------------- 1 | @scope("globalThis") 2 | external window: option = "window" 3 | 4 | @scope("globalThis") 5 | external history: option = "history" 6 | 7 | @get external location: Dom.window => Dom.location = "location" 8 | 9 | /* actually the cb is Dom.event => unit, but let's restrict the access for now */ 10 | @send 11 | external addEventListener: (Dom.window, string, unit => unit) => unit = "addEventListener" 12 | 13 | @send 14 | external removeEventListener: (Dom.window, string, unit => unit) => unit = "removeEventListener" 15 | 16 | @send 17 | external dispatchEvent: (Dom.window, Dom.event) => unit = "dispatchEvent" 18 | 19 | @get external pathname: Dom.location => string = "pathname" 20 | 21 | @get external hash: Dom.location => string = "hash" 22 | 23 | @get external search: Dom.location => string = "search" 24 | 25 | @send 26 | external pushState: (Dom.history, @as(json`null`) _, @as("") _, ~href: string) => unit = "pushState" 27 | 28 | @send 29 | external replaceState: (Dom.history, @as(json`null`) _, @as("") _, ~href: string) => unit = 30 | "replaceState" 31 | 32 | @val external event: 'a = "Event" 33 | 34 | @new external makeEventIE11Compatible: string => Dom.event = "Event" 35 | 36 | @val @scope("document") 37 | external createEventNonIEBrowsers: string => Dom.event = "createEvent" 38 | 39 | @send 40 | external initEventNonIEBrowsers: (Dom.event, string, bool, bool) => unit = "initEvent" 41 | 42 | let safeMakeEvent = eventName => 43 | if Js.typeof(event) == "function" { 44 | makeEventIE11Compatible(eventName) 45 | } else { 46 | let event = createEventNonIEBrowsers("Event") 47 | initEventNonIEBrowsers(event, eventName, true, true) 48 | event 49 | } 50 | 51 | /* This is copied from array.ml. We want to cut dependencies for rescript-react so 52 | that it's friendlier to use in size-constrained codebases */ 53 | let arrayToList = a => { 54 | let rec tolist = (i, res) => 55 | if i < 0 { 56 | res 57 | } else { 58 | tolist(i - 1, list{a->Js.Array2.unsafe_get(i), ...res}) 59 | } 60 | tolist(a->Js.Array2.length - 1, list{}) 61 | } 62 | /* if we ever roll our own parser in the future, make sure you test all url combinations 63 | e.g. foo.com/?#bar 64 | */ 65 | /* sigh URLSearchParams doesn't work on IE11, edge16, etc. */ 66 | /* actually you know what, not gonna provide search for now. It's a mess. 67 | We'll let users roll their own solution/data structure for now */ 68 | let pathParse = str => 69 | switch str { 70 | | "" 71 | | "/" => 72 | list{} 73 | | raw => 74 | /* remove the preceeding /, which every pathname seems to have */ 75 | let raw = raw->Js.String2.sliceToEnd(~from=1) 76 | /* remove the trailing /, which some pathnames might have. Ugh */ 77 | let raw = switch raw->Js.String2.get(raw->Js.String2.length - 1) { 78 | | "/" => raw->Js.String2.slice(~from=0, ~to_=-1) 79 | | _ => raw 80 | } 81 | /* remove search portion if present in string */ 82 | let raw = switch raw->Js.String2.splitAtMost("?", ~limit=2) { 83 | | [path, _] => path 84 | | _ => raw 85 | } 86 | 87 | raw->Js.String2.split("/")->Js.Array2.filter(item => item->Js.String2.length != 0)->arrayToList 88 | } 89 | let path = (~serverUrlString=?, ()) => 90 | switch (serverUrlString, window) { 91 | | (None, None) => list{} 92 | | (Some(serverUrlString), _) => pathParse(serverUrlString) 93 | | (_, Some(window: Dom.window)) => pathParse(window->location->pathname) 94 | } 95 | let hash = () => 96 | switch window { 97 | | None => "" 98 | | Some(window: Dom.window) => 99 | switch window->location->hash { 100 | | "" 101 | | "#" => "" 102 | | raw => 103 | /* remove the preceeding #, which every hash seems to have. 104 | Why is this even included in location.hash?? */ 105 | raw->Js.String2.sliceToEnd(~from=1) 106 | } 107 | } 108 | let searchParse = str => 109 | switch str { 110 | | "" 111 | | "?" => "" 112 | | raw => 113 | switch raw->Js.String2.splitAtMost("?", ~limit=2) { 114 | | [_, search] => search 115 | | _ => "" 116 | } 117 | } 118 | 119 | let search = (~serverUrlString=?, ()) => 120 | switch (serverUrlString, window) { 121 | | (None, None) => "" 122 | | (Some(serverUrlString), _) => searchParse(serverUrlString) 123 | | (_, Some(window: Dom.window)) => searchParse(window->location->search) 124 | } 125 | 126 | let push = path => 127 | switch (history, window) { 128 | | (None, _) 129 | | (_, None) => () 130 | | (Some(history: Dom.history), Some(window: Dom.window)) => 131 | pushState(history, ~href=path) 132 | dispatchEvent(window, safeMakeEvent("popstate")) 133 | } 134 | 135 | let replace = path => 136 | switch (history, window) { 137 | | (None, _) 138 | | (_, None) => () 139 | | (Some(history: Dom.history), Some(window: Dom.window)) => 140 | replaceState(history, ~href=path) 141 | dispatchEvent(window, safeMakeEvent("popstate")) 142 | } 143 | 144 | type url = { 145 | path: list, 146 | hash: string, 147 | search: string, 148 | } 149 | 150 | let urlNotEqual = (a, b) => { 151 | let rec listNotEqual = (aList, bList) => 152 | switch (aList, bList) { 153 | | (list{}, list{}) => false 154 | | (list{}, list{_, ..._}) 155 | | (list{_, ..._}, list{}) => true 156 | | (list{aHead, ...aRest}, list{bHead, ...bRest}) => 157 | if aHead !== bHead { 158 | true 159 | } else { 160 | listNotEqual(aRest, bRest) 161 | } 162 | } 163 | a.hash !== b.hash || (a.search !== b.search || listNotEqual(a.path, b.path)) 164 | } 165 | 166 | type watcherID = unit => unit 167 | 168 | let url = (~serverUrlString=?, ()) => { 169 | path: path(~serverUrlString?, ()), 170 | hash: hash(), 171 | search: search(~serverUrlString?, ()), 172 | } 173 | 174 | /* alias exposed publicly */ 175 | let dangerouslyGetInitialUrl = url 176 | 177 | let watchUrl = callback => 178 | switch window { 179 | | None => () => () 180 | | Some(window: Dom.window) => 181 | let watcherID = () => callback(url()) 182 | addEventListener(window, "popstate", watcherID) 183 | watcherID 184 | } 185 | 186 | let unwatchUrl = watcherID => 187 | switch window { 188 | | None => () 189 | | Some(window: Dom.window) => removeEventListener(window, "popstate", watcherID) 190 | } 191 | 192 | let useUrl = (~serverUrl=?, ()) => { 193 | let (url, setUrl) = React.useState(() => 194 | switch serverUrl { 195 | | Some(url) => url 196 | | None => dangerouslyGetInitialUrl() 197 | } 198 | ) 199 | 200 | React.useEffect(() => { 201 | let watcherId = watchUrl(url => setUrl(_ => url)) 202 | 203 | // check for updates that may have occured between the initial state and 204 | // the subscribe above 205 | let newUrl = dangerouslyGetInitialUrl() 206 | if urlNotEqual(newUrl, url) { 207 | setUrl(_ => newUrl) 208 | } 209 | 210 | Some(() => unwatchUrl(watcherId)) 211 | }, []) 212 | 213 | url 214 | } 215 | -------------------------------------------------------------------------------- /src/RescriptReactRouter.resi: -------------------------------------------------------------------------------- 1 | /** update the url with the string path. Example: `push(\"/book/1\")`, `push(\"/books#title\")` */ 2 | let push: string => unit 3 | 4 | /** update the url with the string path. modifies the current history entry instead of creating a new one. Example: `replace(\"/book/1\")`, `replace(\"/books#title\")` */ 5 | let replace: string => unit 6 | type watcherID 7 | type url = { 8 | /* path takes window.location.path, like "/book/title/edit" and turns it into `["book", "title", "edit"]` */ 9 | path: list, 10 | /* the url's hash, if any. The # symbol is stripped out for you */ 11 | hash: string, 12 | /* the url's query params, if any. The ? symbol is stripped out for you */ 13 | search: string, 14 | } 15 | 16 | /** start watching for URL changes. Returns a subscription token. Upon url change, calls the callback and passes it the url record */ 17 | let watchUrl: (url => unit) => watcherID 18 | 19 | /** stop watching for URL changes */ 20 | let unwatchUrl: watcherID => unit 21 | 22 | /** this is marked as \"dangerous\" because you technically shouldn't 23 | be accessing the URL outside of watchUrl's callback; you'd read a potentially 24 | stale url, instead of the fresh one inside watchUrl. 25 | 26 | But this helper is sometimes needed, if you'd like to initialize a page 27 | whose display/state depends on the URL, instead of reading from it in 28 | watchUrl's callback, which you'd probably have put inside didMount (aka 29 | too late, the page's already rendered). 30 | 31 | So, the correct (and idiomatic) usage of this helper is to only use it in 32 | a component that's also subscribed to watchUrl. Please see 33 | https://github.com/reasonml-community/reason-react-example/blob/master/src/todomvc/TodoItem.re 34 | for an example. */ 35 | let dangerouslyGetInitialUrl: (~serverUrlString: string=?, unit) => url 36 | 37 | /** hook for watching url changes. 38 | * serverUrl is used for ssr. it allows you to specify the url without relying on browser apis existing/working as expected 39 | */ 40 | let useUrl: (~serverUrl: url=?, unit) => url 41 | --------------------------------------------------------------------------------