├── .codeclimate.yml
├── .eslintrc.yaml
├── .github
└── workflows
│ └── test.yml
├── .gitignore
├── .prettierrc.cjs
├── LICENSE
├── README.md
├── demos
├── components
│ ├── app.riot
│ └── user.riot
├── riot-history.html
├── standalone-hash.html
└── standalone-history.html
├── index.d.ts
├── package-lock.json
├── package.json
├── rollup.config.js
├── src
├── components
│ ├── route-hoc.js
│ ├── route-hoc.riot
│ ├── router-hoc.js
│ └── router-hoc.riot
├── constants.js
├── dom.js
├── get-current-route.js
├── index.js
├── set-base.js
└── util.js
└── test
├── components.spec.js
├── components
├── computed-routes.riot
├── history-router-app.riot
├── nested-updates.riot
├── recursive-updates-bug-router.riot
├── recursive-updates-bug148.riot
├── same-route-matches.riot
├── spred-props-router.riot
├── static-base-path.riot
└── user.riot
├── misc.spec.js
├── setup.js
├── standalone-hash-dom.spec.js
├── standalone-history-dom.spec.js
└── util.js
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | ratings:
2 | paths:
3 | - 'src/**.js'
4 |
5 | exclude_paths:
6 | - 'route.js'
7 | - 'route.esm.js'
8 | - 'route.standalone.js'
9 | - 'test/**'
10 | - 'demos/**'
11 |
--------------------------------------------------------------------------------
/.eslintrc.yaml:
--------------------------------------------------------------------------------
1 | extends: eslint-config-riot
2 |
3 | rules:
4 | fp/no-mutating-methods: 0
5 | fp/no-rest-parameters: 0
6 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: test
2 |
3 | on:
4 | push:
5 | branches: [main, dev]
6 | pull_request:
7 | branches: [dev]
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 |
13 | strategy:
14 | matrix:
15 | node-version: [18.x, 20.x]
16 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
17 | steps:
18 | - uses: actions/checkout@v2
19 | - name: Local Unit Test ${{ matrix.node-version }}
20 | uses: actions/setup-node@v1
21 | with:
22 | node-version: ${{ matrix.node-version }}
23 | - run: npm i
24 | - run: npm test
25 | - name: Generate Coverage
26 | if: ${{ success() && github.event_name != 'pull_request' && matrix.node-version == '20.x' }}
27 | run: npm run cov
28 | - name: Coveralls
29 | if: ${{ success() && github.event_name != 'pull_request' && matrix.node-version == '20.x' }}
30 | uses: coverallsapp/github-action@master
31 | with:
32 | path-to-lcov: ./coverage/lcov.info
33 | github-token: ${{ secrets.GITHUB_TOKEN }}
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # editor files
21 | .idea
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | # mac useless file
40 | .DS_Store
41 |
42 | # Generated files
43 | index.js
44 | index.umd.js
45 | index.standalone.js
46 | index.standalone.umd.js
47 | demos/components/*.js
48 |
--------------------------------------------------------------------------------
/.prettierrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = require('@riotjs/prettier-config')
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Gianluca Guarini
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 | # Riot Router
2 |
3 | [](https://github.com/riot/route/)
4 |
5 | [![Build Status][ci-image]][ci-url] [![Code Quality][codeclimate-image]][codeclimate-url] [![NPM version][npm-version-image]][npm-url] [![NPM downloads][npm-downloads-image]][npm-url] [![MIT License][license-image]][license-url] [![Coverage Status][coverage-image]][coverage-url]
6 |
7 | > Simple isomorphic router
8 |
9 | The Riot.js Router is the minimal router implementation with such technologies:
10 |
11 | - compatible with the DOM pushState and history API
12 | - isomorphic functional API
13 | - [erre.js streams](https://github.com/GianlucaGuarini/erre) and javascript async generators
14 | - [rawth.js](https://github.com/GianlucaGuarini/rawth) urls parsing
15 |
16 | It doesn't need Riot.js to work and can be used as standalone module.
17 |
18 | **For Riot.js 3 and the older route version please check the [v3 branch](https://github.com/riot/route/tree/v3)**
19 |
20 | ## Table of Contents
21 |
22 | - [Install](#install)
23 | - [Documentation](#documentation)
24 | - [Demos](https://github.com/riot/examples)
25 |
26 | ## Install
27 |
28 | We have 2 editions:
29 |
30 | | edition | file |
31 | | :------------------------ | :------------------------ |
32 | | **ESM Module** | `index.js` |
33 | | **UMD Version** | `index.umd.js` |
34 | | **Standalone ESM Module** | `index.standalone.js` |
35 | | **Standalone UMD Module** | `index.standalone.umd.js` |
36 |
37 | ### Script injection
38 |
39 | ```html
40 |
41 | ```
42 |
43 | _Note_: change the part `x.x.x` to the version numbers what you want to use: ex. `4.5.0` or `4.7.0`.
44 |
45 | ### ESM module
46 |
47 | ```js
48 | import { route } from 'https://unpkg.com/@riotjs/route/index.js'
49 | ```
50 |
51 | ### npm
52 |
53 | ```bash
54 | $ npm i -S @riotjs/route
55 | ```
56 |
57 | ### Download by yourself
58 |
59 | - [Standalone](https://unpkg.com/@riotjs/route/route.js)
60 | - [ESM](https://unpkg.com/@riotjs/route/route.esm.js)
61 |
62 | ## Documentation
63 |
64 | ### With Riot.js
65 |
66 | You can import the `` and `` components in your application and use them as it follows:
67 |
68 | ```html
69 |
70 |
71 |
72 |
77 |
78 |
79 | Home page
80 | About
81 | Hello dear { route.params.person }
82 |
83 |
84 |
91 |
92 | ```
93 |
94 | You can also use the `riot.register` method to register them globally
95 |
96 | ```js
97 | import { Route, Router } from '@riotjs/route'
98 | import { register } from 'riot'
99 |
100 | // now the Router and Route components are globally available
101 | register('router', Router)
102 | register('route', Route)
103 | ```
104 |
105 | #### Router
106 |
107 | The `` component should wrap your application markup and will detect automatically all the clicks on links that should trigger a route event.
108 |
109 | ```html
110 |
111 |
112 | Link
113 |
114 |
115 | Link
116 | ```
117 |
118 | You can also specify the base of your application via component attributes:
119 |
120 | ```html
121 |
122 |
123 | Link
124 |
125 | ```
126 |
127 | The router component has also an `onStarted` callback that will be called asynchronously after the first route event will be called
128 |
129 | ```html
130 |
131 | ```
132 |
133 | #### Route
134 |
135 | The `` component provides the `route` property to its children (it's simply a [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL) object) allowing you to detect the url params and queries.
136 |
137 | ```html
138 | {JSON.stringify(route.params)}
139 |
140 |
141 |
142 |
143 | {route.searchParams.get('q')}
144 |
145 | ```
146 |
147 | Each `` component has its own lifecycle attributes in order to let you know when it gets mounted or unmounted.
148 |
149 | ```riot
150 |
151 |
152 |
160 |
161 |
162 | ```
163 |
164 | ### Standalone
165 |
166 | This module was not only designed to be used with Riot.js but also as standalone module.
167 | Without importing the Riot.js components in your application you can use the core methods exported to build and customize your own router compatible with any kind of frontend setup.
168 |
169 | Depending on your project setup you might import it as follows:
170 |
171 | ```js
172 | // in a Riot.js application
173 | import { route } from '@riotjs/route'
174 |
175 | // in a standalone context
176 | import { route } from '@riotjs/route/standalone'
177 | ```
178 |
179 | #### Fundamentals
180 |
181 | This module works on node and on any modern browser, it exports the `router` and `router` property exposed by [rawth](https://github.com/GianlucaGuarini/rawth)
182 |
183 | ```js
184 | import { route, router, setBase } from '@riotjs/route'
185 |
186 | // required to set base first
187 | setBase('/')
188 |
189 | // create a route stream
190 | const aboutStream = route('/about')
191 |
192 | aboutStream.on.value((url) => {
193 | console.log(url) // URL object
194 | })
195 |
196 | aboutStream.on.value(() => {
197 | console.log('just log that the about route was triggered')
198 | })
199 |
200 | // triggered on each route event
201 | router.on.value((path) => {
202 | // path is always a string in this function
203 | console.log(path)
204 | })
205 |
206 | // trigger a route change manually
207 | router.push('/about')
208 |
209 | // end the stream
210 | aboutStream.end()
211 | ```
212 |
213 | #### Base path
214 |
215 | Before using the router in your browser you will need to set your application base path.
216 | This setting can be configured simply via `setBase` method:
217 |
218 | ```js
219 | import { setBase } from '@riotjs/route'
220 |
221 | // in case you want to use the HTML5 history navigation
222 | setBase(`/`)
223 |
224 | // in case you use the hash navigation
225 | setBase(`#`)
226 | ```
227 |
228 | Setting the base path of your application route is mandatory and is the first you probably are going to do before creating your route listeners.
229 |
230 | #### DOM binding
231 |
232 | The example above is not really practical in case you are working in a browser environment. In that case you might want to bind your router to the DOM listening all the click events that might trigger a route change event.
233 | Window history `popstate` events should be also connected to the router.
234 | With the `initDomListeners` method you can automatically achieve all the features above:
235 |
236 | ```js
237 | import { initDomListeners } from '@riotjs/route'
238 |
239 | const unsubscribe = initDomListeners()
240 | // the router is connected to the page DOM
241 |
242 | // ...tear down and disconnect the router from the DOM
243 | unsubscribe()
244 | ```
245 |
246 | The `initDomListeners` will intercept any link click on your application. However it can also receive a HTMLElement or a list of HTMLElements as argument to scope the click listener only to a specific DOM region of your application
247 |
248 | ```js
249 | import { initDomListeners } from '@riotjs/route'
250 |
251 | initDomListeners(document.querySelector('.main-navigation'))
252 | ```
253 |
254 | [ci-image]: https://img.shields.io/github/actions/workflow/status/riot/route/test.yml?style=flat-square
255 | [ci-url]: https://github.com/riot/route/actions
256 | [license-image]: http://img.shields.io/badge/license-MIT-000000.svg?style=flat-square
257 | [license-url]: LICENSE.txt
258 | [npm-version-image]: http://img.shields.io/npm/v/@riotjs/route.svg?style=flat-square
259 | [npm-downloads-image]: http://img.shields.io/npm/dm/@riotjs/route.svg?style=flat-square
260 | [npm-url]: https://npmjs.org/package/@riotjs/route
261 | [coverage-image]: https://img.shields.io/coveralls/riot/route/main.svg?style=flat-square
262 | [coverage-url]: https://coveralls.io/github/riot/route/?branch=main
263 | [codeclimate-image]: https://api.codeclimate.com/v1/badges/1487b171ba4409b5c302/maintainability
264 | [codeclimate-url]: https://codeclimate.com/github/riot/route
265 |
--------------------------------------------------------------------------------
/demos/components/app.riot:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 | Every route :)
13 |
14 | hello
15 | user
16 |
17 |
18 |
19 | goodbye
20 |
21 |
22 |
29 |
30 |
--------------------------------------------------------------------------------
/demos/components/user.riot:
--------------------------------------------------------------------------------
1 |
2 | User {JSON.stringify(props)}
3 |
4 |