├── .gitignore ├── LICENSE ├── README.md ├── bsconfig.json ├── examples ├── .gitignore ├── README.md ├── bsconfig.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── Admin.re │ ├── App.re │ ├── Home.re │ ├── NotFound.re │ ├── index.css │ └── index.re └── yarn.lock ├── package.json ├── src └── ReRoute.re └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | *.log 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | package-lock.json 7 | 8 | # Dependency directories 9 | node_modules/ 10 | 11 | # Optional npm cache directory 12 | .npm 13 | 14 | # Optional eslint cache 15 | .eslintcache 16 | 17 | # Yarn Integrity file 18 | .yarn-integrity 19 | 20 | .merlin 21 | 22 | lib 23 | *.bs.js 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Callstack 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 | # reroute 2 | 3 | > a fast, declarative microrouter for reason-react 4 | 5 | ## Getting started 6 | 7 | ### Installation 8 | 9 | Open a Terminal in your project's folder and run, 10 | 11 | ``` 12 | $ yarn add reason-reroute 13 | ``` 14 | 15 | After installation, you will need to add this library to your `bsconfig.json` dependencies 16 | 17 | ``` 18 | "bs-dependencies": [ 19 | "reason-react", 20 | "reason-reroute" 21 | ], 22 | ``` 23 | 24 | ## Usage 25 | 26 | ```reason 27 | module RouterConfig = { 28 | type route = 29 | | Admin 30 | | Home; 31 | let routeFromUrl = (url: ReasonReact.Router.url) => 32 | switch url.path { 33 | | ["admin"] => Admin 34 | | [] => Home 35 | }; 36 | let routeToUrl = (route: route) => 37 | switch route { 38 | | Admin => "/admin" 39 | | Home => "/" 40 | }; 41 | }; 42 | 43 | module Router = ReRoute.CreateRouter(RouterConfig); 44 | 45 | let component = ReasonReact.statelessComponent("App"); 46 | 47 | let make = _children => { 48 | ...component, 49 | render: _self => 50 | 51 | ...( 52 | (~currentRoute) => 53 | switch currentRoute { 54 | | RouterConfig.Admin => 55 | | RouterConfig.Home => 56 | } 57 | ) 58 | 59 | }; 60 | ``` 61 | 62 | ## API 63 | 64 | Sections below are under construction. 65 | 66 | ### Link 67 | 68 | ### Container 69 | 70 | ## Rationale 71 | 72 | ReasonReact comes with a router ([`ReasonReact.Router`](https://reasonml.github.io/reason-react/docs/en/router.html)) by default. It offers minimal yet powerful API that is suitable for applications at any scale. However, being just an API, it leaves the routing logic up to the developer. This library builds on top of it to provide an elegant interface for working with routes that is ready to use, predictable and consistent across apps you create. 73 | 74 | ## Credits 75 | 76 | The concept of `reroute` has been highly influenced by [@thangngoc89](https://github.com/thangngoc89) and his [reference implementation](https://gist.github.com/thangngoc89/c9162c0263df5427fe9a36fc7f94ac94). Thank you for pushing this forward! 77 | 78 | ## License 79 | 80 | MIT (c) 2018 Callstack 81 | -------------------------------------------------------------------------------- /bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reason-reroute", 3 | "bsc-flags": ["-bs-no-version-header", "-bs-super-errors"], 4 | "refmt": 3, 5 | "bs-dependencies": ["reason-react"], 6 | "reason": { 7 | "react-jsx": 2 8 | }, 9 | "namespace": false, 10 | "sources": [ 11 | { 12 | "dir": "src", 13 | "public": "all" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # bucklescript 13 | /lib 14 | /types 15 | .merlin 16 | 17 | # misc 18 | .DS_Store 19 | .env.local 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Reroute example 2 | 3 | ### Get started 4 | 5 | 1. Install dependencies: `yarn` or `npm install` 6 | 2. Run the example app: `yarn start` 7 | -------------------------------------------------------------------------------- /examples/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reason-scripts", 3 | "sources": ["src"], 4 | "bs-dependencies": ["reason-react", "bs-jest", "reason-reroute"], 5 | "reason": { 6 | "react-jsx": 2 7 | }, 8 | "bsc-flags": ["-bs-super-errors"], 9 | "refmt": 3 10 | } 11 | -------------------------------------------------------------------------------- /examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.2.0", 7 | "react-dom": "^16.2.0", 8 | "reason-reroute": "../", 9 | "reason-scripts": "0.8.0" 10 | }, 11 | "scripts": { 12 | "start": "react-scripts start", 13 | "build": "react-scripts build", 14 | "test": "react-scripts test --env=jsdom", 15 | "eject": "react-scripts eject", 16 | "prepare": "npm link bs-platform" 17 | }, 18 | "devDependencies": { 19 | "bs-jest": "^0.3.2", 20 | "reason-react": "^0.3.2" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstackincubator/reroute/2af42d862edd9a7e84bd44bf357fc83e01bdf2bc/examples/public/favicon.ico -------------------------------------------------------------------------------- /examples/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /examples/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /examples/src/Admin.re: -------------------------------------------------------------------------------- 1 | let component = ReasonReact.statelessComponent("Admin"); 2 | 3 | let make = _children => { 4 | ...component, 5 | render: _self => 6 |
7 |

(ReasonReact.stringToElement("Admin"))

8 |

(ReasonReact.stringToElement("This is an admin page"))

9 |
10 | }; -------------------------------------------------------------------------------- /examples/src/App.re: -------------------------------------------------------------------------------- 1 | module RouterConfig = { 2 | type route = 3 | | Admin 4 | | Home 5 | | NotFound; 6 | let routeFromUrl = (url: ReasonReact.Router.url) => 7 | switch url.path { 8 | | ["admin"] => Admin 9 | | [] => Home 10 | | _ => NotFound 11 | }; 12 | let routeToUrl = (route: route) => 13 | switch route { 14 | | Admin => "/admin" 15 | | Home => "/" 16 | | NotFound => "" 17 | }; 18 | }; 19 | 20 | module Router = ReRoute.CreateRouter(RouterConfig); 21 | 22 | let component = ReasonReact.statelessComponent("App"); 23 | 24 | let make = _children => { 25 | ...component, 26 | render: _self => 27 | 28 | ...( 29 | (~currentRoute) => 30 |
31 | 40 | ( 41 | switch currentRoute { 42 | | RouterConfig.Admin => 43 | | RouterConfig.Home => 44 | | RouterConfig.NotFound => 45 | } 46 | ) 47 |
48 | ) 49 |
50 | }; -------------------------------------------------------------------------------- /examples/src/Home.re: -------------------------------------------------------------------------------- 1 | let component = ReasonReact.statelessComponent("Home"); 2 | 3 | let make = _children => { 4 | ...component, 5 | render: _self => 6 |
7 |

(ReasonReact.stringToElement("Home"))

8 |

(ReasonReact.stringToElement("Welcome to the home page!"))

9 |
10 | }; -------------------------------------------------------------------------------- /examples/src/NotFound.re: -------------------------------------------------------------------------------- 1 | let component = ReasonReact.statelessComponent("NotFound"); 2 | 3 | let make = _children => { 4 | ...component, 5 | render: _self => 6 |
7 |

(ReasonReact.stringToElement("We are sorry..."))

8 |

9 | ( 10 | ReasonReact.stringToElement( 11 | "Unfortunately we could not find the page you were looking for." 12 | ) 13 | ) 14 |

15 |

16 | ( 17 | ReasonReact.stringToElement( 18 | "Use the above menu to navigate to other pages :)" 19 | ) 20 | ) 21 |

22 |
23 | }; -------------------------------------------------------------------------------- /examples/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /examples/src/index.re: -------------------------------------------------------------------------------- 1 | [%bs.raw {|require('./index.css')|}]; 2 | 3 | ReactDOMRe.renderToElementWithId(, "root"); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reason-reroute", 3 | "version": "0.0.4", 4 | "description": "Tiny wrapper around ReasonReact.Router ", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "bsb -make-world", 8 | "start": "bsb -make-world -w", 9 | "clean": "bsb -clean-world", 10 | "prepublish": "npm run build", 11 | "test": "exit 0" 12 | }, 13 | "keywords": [ 14 | "router", 15 | "react", 16 | "reason", 17 | "reasonml" 18 | ], 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/callstack/reroute.git" 22 | }, 23 | "author": "Mike Grabowski ", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/callstack/reroute/issues" 27 | }, 28 | "homepage": "https://github.com/callstack/reroute#readme", 29 | "devDependencies": { 30 | "bs-platform": "^4.0.3", 31 | "reason-react": "^0.5.3" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/ReRoute.re: -------------------------------------------------------------------------------- 1 | module type RouterConfig = { 2 | type route; 3 | let routeFromUrl: ReasonReact.Router.url => route; 4 | let routeToUrl: route => string; 5 | }; 6 | 7 | module CreateRouter = (Config: RouterConfig) => { 8 | module Container = { 9 | type action = 10 | | ChangeRoute(Config.route); 11 | type state = Config.route; 12 | let component = ReasonReact.reducerComponent("CallstackRerouteRouter"); 13 | let make = children => { 14 | ...component, 15 | initialState: () => 16 | ReasonReact.Router.dangerouslyGetInitialUrl() |> Config.routeFromUrl, 17 | reducer: (action, _state) => 18 | switch (action) { 19 | | ChangeRoute(route) => ReasonReact.Update(route) 20 | }, 21 | didMount: self => { 22 | let watcherID = 23 | ReasonReact.Router.watchUrl(url => 24 | self.send(ChangeRoute(url |> Config.routeFromUrl)) 25 | ); 26 | self.onUnmount(() => ReasonReact.Router.unwatchUrl(watcherID)); 27 | }, 28 | render: self => children(~currentRoute=self.state), 29 | }; 30 | }; 31 | module Link = { 32 | let component = ReasonReact.statelessComponent("CallstackRerouteLink"); 33 | let make = (~route, children) => { 34 | ...component, 35 | render: _self => { 36 | let href = Config.routeToUrl(route); 37 | { 41 | event->ReactEvent.Synthetic.preventDefault; 42 | ReasonReact.Router.push(href); 43 | } 44 | )> 45 | (ReasonReact.array(children)) 46 | ; 47 | }, 48 | }; 49 | }; 50 | }; -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | asap@~2.0.3: 6 | version "2.0.6" 7 | resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" 8 | 9 | bs-platform@^4.0.3: 10 | version "4.0.3" 11 | resolved "https://registry.yarnpkg.com/bs-platform/-/bs-platform-4.0.3.tgz#4510c1c915cc5b169b5717e5c0ada52d3f99ff98" 12 | 13 | core-js@^1.0.0: 14 | version "1.2.7" 15 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" 16 | 17 | encoding@^0.1.11: 18 | version "0.1.12" 19 | resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" 20 | dependencies: 21 | iconv-lite "~0.4.13" 22 | 23 | fbjs@^0.8.16: 24 | version "0.8.16" 25 | resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" 26 | dependencies: 27 | core-js "^1.0.0" 28 | isomorphic-fetch "^2.1.1" 29 | loose-envify "^1.0.0" 30 | object-assign "^4.1.0" 31 | promise "^7.1.1" 32 | setimmediate "^1.0.5" 33 | ua-parser-js "^0.7.9" 34 | 35 | iconv-lite@~0.4.13: 36 | version "0.4.23" 37 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" 38 | dependencies: 39 | safer-buffer ">= 2.1.2 < 3" 40 | 41 | is-stream@^1.0.1: 42 | version "1.1.0" 43 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 44 | 45 | isomorphic-fetch@^2.1.1: 46 | version "2.2.1" 47 | resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" 48 | dependencies: 49 | node-fetch "^1.0.1" 50 | whatwg-fetch ">=0.10.0" 51 | 52 | js-tokens@^3.0.0: 53 | version "3.0.2" 54 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" 55 | 56 | loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: 57 | version "1.3.1" 58 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" 59 | dependencies: 60 | js-tokens "^3.0.0" 61 | 62 | node-fetch@^1.0.1: 63 | version "1.7.3" 64 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" 65 | dependencies: 66 | encoding "^0.1.11" 67 | is-stream "^1.0.1" 68 | 69 | object-assign@^4.1.0, object-assign@^4.1.1: 70 | version "4.1.1" 71 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 72 | 73 | promise@^7.1.1: 74 | version "7.3.1" 75 | resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" 76 | dependencies: 77 | asap "~2.0.3" 78 | 79 | prop-types@^15.6.0: 80 | version "15.6.1" 81 | resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" 82 | dependencies: 83 | fbjs "^0.8.16" 84 | loose-envify "^1.3.1" 85 | object-assign "^4.1.1" 86 | 87 | "react-dom@>=15.0.0 || >=16.0.0": 88 | version "16.3.2" 89 | resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.2.tgz#cb90f107e09536d683d84ed5d4888e9640e0e4df" 90 | dependencies: 91 | fbjs "^0.8.16" 92 | loose-envify "^1.1.0" 93 | object-assign "^4.1.1" 94 | prop-types "^15.6.0" 95 | 96 | "react@>=15.0.0 || >=16.0.0": 97 | version "16.3.2" 98 | resolved "https://registry.yarnpkg.com/react/-/react-16.3.2.tgz#fdc8420398533a1e58872f59091b272ce2f91ea9" 99 | dependencies: 100 | fbjs "^0.8.16" 101 | loose-envify "^1.1.0" 102 | object-assign "^4.1.1" 103 | prop-types "^15.6.0" 104 | 105 | reason-react@^0.5.3: 106 | version "0.5.3" 107 | resolved "https://registry.yarnpkg.com/reason-react/-/reason-react-0.5.3.tgz#10601809742fd991109ec9d69ad4baf2c3f17540" 108 | dependencies: 109 | react ">=15.0.0 || >=16.0.0" 110 | react-dom ">=15.0.0 || >=16.0.0" 111 | 112 | "safer-buffer@>= 2.1.2 < 3": 113 | version "2.1.2" 114 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 115 | 116 | setimmediate@^1.0.5: 117 | version "1.0.5" 118 | resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" 119 | 120 | ua-parser-js@^0.7.9: 121 | version "0.7.18" 122 | resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed" 123 | 124 | whatwg-fetch@>=0.10.0: 125 | version "2.0.4" 126 | resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" 127 | --------------------------------------------------------------------------------