space-router 0.8.2 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/cjs/history.js +15 -13
- package/dist/cjs/router.js +26 -21
- package/dist/esm/history.js +13 -14
- package/dist/esm/router.js +24 -19
- package/docs/assets/styles/main.scss +5 -2
- package/docs/content/_index.md +43 -17
- package/package.json +8 -8
- package/src/history.js +13 -13
- package/src/router.js +22 -18
- package/test/router.test.js +11 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## 0.8.5
|
|
2
|
+
|
|
3
|
+
- fix redirects - the history was not ready by the time initial transition was kicked off by redirecting
|
|
4
|
+
- remove the possibility to pass the current route via the `merge` option directly, instead set `merge: true` (truthy value works now), and instead optionally pass the current route as the second argument to `navigate(to, curr)` or `href(to, curr)`, this is a breaking change, but also an undocumented API as it it's still being refined and is used for correctly merging urls during async navigations.
|
|
5
|
+
|
|
6
|
+
## 0.8.4
|
|
7
|
+
|
|
8
|
+
- fix the `navigate(url: string)` implementation where `"".replace` was being tested instead of `to.replace`
|
|
9
|
+
|
|
10
|
+
## 0.8.3
|
|
11
|
+
|
|
12
|
+
- re-introduce `navigate(url: string)` since it's nice and convenient
|
|
13
|
+
|
|
1
14
|
## 0.8.2
|
|
2
15
|
|
|
3
16
|
- refer to `setImmediate` via `global.setImmediate` to fix a webpack compilation error
|
package/dist/cjs/history.js
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
4
|
exports.createHistory = createHistory;
|
|
5
5
|
|
|
6
|
-
function createHistory(
|
|
6
|
+
function createHistory(options) {
|
|
7
7
|
var sync = options.sync;
|
|
8
8
|
var mode = options.mode;
|
|
9
9
|
var raf;
|
|
10
|
+
var onPop;
|
|
10
11
|
var memory = [];
|
|
11
12
|
var off;
|
|
12
13
|
var destroyed = false;
|
|
@@ -26,29 +27,30 @@ function createHistory(onChange, options) {
|
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
function listen(onChange) {
|
|
31
|
+
onPop = function onPop() {
|
|
32
|
+
return onChange(getUrl());
|
|
33
|
+
};
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
if (mode !== 'memory') {
|
|
36
|
+
off = on(window, mode === 'history' ? 'popstate' : 'hashchange', onPop);
|
|
37
|
+
raf(onPop);
|
|
38
|
+
}
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
return function () {
|
|
41
|
+
destroyed = true;
|
|
42
|
+
off && off();
|
|
43
|
+
};
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
return {
|
|
47
|
+
listen: listen,
|
|
42
48
|
getUrl: getUrl,
|
|
43
49
|
push: function push(url) {
|
|
44
50
|
go(url);
|
|
45
51
|
},
|
|
46
52
|
replace: function replace(url) {
|
|
47
53
|
go(url, true);
|
|
48
|
-
},
|
|
49
|
-
destroy: function destroy() {
|
|
50
|
-
destroyed = true;
|
|
51
|
-
off && off();
|
|
52
54
|
}
|
|
53
55
|
};
|
|
54
56
|
|
package/dist/cjs/router.js
CHANGED
|
@@ -40,42 +40,47 @@ function createRouter(options) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
routes = flatten(routeMap);
|
|
43
|
-
|
|
44
|
-
var onHistoryChange = function onHistoryChange(url) {
|
|
45
|
-
return transition(router, url, cb);
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
history = (0, _history.createHistory)(onHistoryChange, {
|
|
43
|
+
history = (0, _history.createHistory)({
|
|
49
44
|
mode: mode,
|
|
50
45
|
sync: sync
|
|
51
46
|
});
|
|
47
|
+
var dispose = history.listen(function (url) {
|
|
48
|
+
return transition(router, url, cb);
|
|
49
|
+
});
|
|
52
50
|
return function () {
|
|
53
|
-
|
|
51
|
+
dispose();
|
|
54
52
|
history = null;
|
|
55
53
|
routes = [];
|
|
56
54
|
};
|
|
57
55
|
},
|
|
58
|
-
navigate: function navigate(to) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
url = to.url;
|
|
64
|
-
} else {
|
|
65
|
-
url = router.href(to);
|
|
56
|
+
navigate: function navigate(to, curr) {
|
|
57
|
+
if (typeof to === 'string') {
|
|
58
|
+
to = {
|
|
59
|
+
url: to
|
|
60
|
+
};
|
|
66
61
|
}
|
|
67
62
|
|
|
68
|
-
|
|
63
|
+
var url = router.href(to, curr);
|
|
69
64
|
|
|
70
|
-
if (replace) {
|
|
65
|
+
if (to.replace) {
|
|
71
66
|
history.replace(url);
|
|
72
67
|
} else {
|
|
73
68
|
history.push(url);
|
|
74
69
|
}
|
|
75
70
|
},
|
|
76
|
-
href: function href(to) {
|
|
71
|
+
href: function href(to, curr) {
|
|
72
|
+
// already a url
|
|
73
|
+
if (typeof to === 'string') {
|
|
74
|
+
return to;
|
|
75
|
+
} // align with navigate API
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
if (to.url) {
|
|
79
|
+
return to.url;
|
|
80
|
+
}
|
|
81
|
+
|
|
77
82
|
if (to.merge) {
|
|
78
|
-
|
|
83
|
+
curr = curr || router.match(router.getUrl());
|
|
79
84
|
to = merge(curr, to);
|
|
80
85
|
}
|
|
81
86
|
|
|
@@ -172,7 +177,7 @@ function redirectUrl(router, redirect, matchingRoute) {
|
|
|
172
177
|
redirect = redirect(matchingRoute);
|
|
173
178
|
}
|
|
174
179
|
|
|
175
|
-
return
|
|
180
|
+
return router.href(redirect);
|
|
176
181
|
}
|
|
177
182
|
|
|
178
183
|
function data(routes, matchingRoute) {
|
|
@@ -188,7 +193,7 @@ function merge(curr, to) {
|
|
|
188
193
|
curr = {};
|
|
189
194
|
}
|
|
190
195
|
|
|
191
|
-
var pathname = to.pathname || curr.pattern;
|
|
196
|
+
var pathname = to.pathname || curr.pattern || curr.pathname;
|
|
192
197
|
var params = Object.assign({}, curr.params, to.params);
|
|
193
198
|
var query = to.query === null ? null : Object.assign({}, curr.query, to.query);
|
|
194
199
|
var hash = to.hash === null ? null : to.hash || curr.hash || '';
|
package/dist/esm/history.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export function createHistory(
|
|
1
|
+
export function createHistory(options) {
|
|
2
2
|
const sync = options.sync;
|
|
3
3
|
let mode = options.mode;
|
|
4
4
|
let raf;
|
|
5
|
+
let onPop;
|
|
5
6
|
const memory = [];
|
|
6
7
|
let off;
|
|
7
8
|
let destroyed = false;
|
|
@@ -17,19 +18,22 @@ export function createHistory(onChange, options) {
|
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
21
|
+
function listen(onChange) {
|
|
22
|
+
onPop = () => onChange(getUrl());
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
if (mode !== 'memory') {
|
|
25
|
+
off = on(window, mode === 'history' ? 'popstate' : 'hashchange', onPop);
|
|
26
|
+
raf(onPop);
|
|
27
|
+
}
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
return () => {
|
|
30
|
+
destroyed = true;
|
|
31
|
+
off && off();
|
|
32
|
+
};
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
return {
|
|
36
|
+
listen,
|
|
33
37
|
getUrl,
|
|
34
38
|
|
|
35
39
|
push(url) {
|
|
@@ -38,11 +42,6 @@ export function createHistory(onChange, options) {
|
|
|
38
42
|
|
|
39
43
|
replace(url) {
|
|
40
44
|
go(url, true);
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
destroy() {
|
|
44
|
-
destroyed = true;
|
|
45
|
-
off && off();
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
};
|
package/dist/esm/router.js
CHANGED
|
@@ -14,42 +14,47 @@ export function createRouter(options = {}) {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
routes = flatten(routeMap);
|
|
17
|
-
|
|
18
|
-
const onHistoryChange = url => transition(router, url, cb);
|
|
19
|
-
|
|
20
|
-
history = createHistory(onHistoryChange, {
|
|
17
|
+
history = createHistory({
|
|
21
18
|
mode,
|
|
22
19
|
sync
|
|
23
20
|
});
|
|
21
|
+
const dispose = history.listen(url => transition(router, url, cb));
|
|
24
22
|
return () => {
|
|
25
|
-
|
|
23
|
+
dispose();
|
|
26
24
|
history = null;
|
|
27
25
|
routes = [];
|
|
28
26
|
};
|
|
29
27
|
},
|
|
30
28
|
|
|
31
|
-
navigate(to) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
url = to.url;
|
|
37
|
-
} else {
|
|
38
|
-
url = router.href(to);
|
|
29
|
+
navigate(to, curr) {
|
|
30
|
+
if (typeof to === 'string') {
|
|
31
|
+
to = {
|
|
32
|
+
url: to
|
|
33
|
+
};
|
|
39
34
|
}
|
|
40
35
|
|
|
41
|
-
|
|
36
|
+
const url = router.href(to, curr);
|
|
42
37
|
|
|
43
|
-
if (replace) {
|
|
38
|
+
if (to.replace) {
|
|
44
39
|
history.replace(url);
|
|
45
40
|
} else {
|
|
46
41
|
history.push(url);
|
|
47
42
|
}
|
|
48
43
|
},
|
|
49
44
|
|
|
50
|
-
href(to) {
|
|
45
|
+
href(to, curr) {
|
|
46
|
+
// already a url
|
|
47
|
+
if (typeof to === 'string') {
|
|
48
|
+
return to;
|
|
49
|
+
} // align with navigate API
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
if (to.url) {
|
|
53
|
+
return to.url;
|
|
54
|
+
}
|
|
55
|
+
|
|
51
56
|
if (to.merge) {
|
|
52
|
-
|
|
57
|
+
curr = curr || router.match(router.getUrl());
|
|
53
58
|
to = merge(curr, to);
|
|
54
59
|
}
|
|
55
60
|
|
|
@@ -145,7 +150,7 @@ function redirectUrl(router, redirect, matchingRoute) {
|
|
|
145
150
|
redirect = redirect(matchingRoute);
|
|
146
151
|
}
|
|
147
152
|
|
|
148
|
-
return
|
|
153
|
+
return router.href(redirect);
|
|
149
154
|
}
|
|
150
155
|
|
|
151
156
|
function data(routes, matchingRoute) {
|
|
@@ -157,7 +162,7 @@ function data(routes, matchingRoute) {
|
|
|
157
162
|
}
|
|
158
163
|
|
|
159
164
|
export function merge(curr = {}, to) {
|
|
160
|
-
const pathname = to.pathname || curr.pattern;
|
|
165
|
+
const pathname = to.pathname || curr.pattern || curr.pathname;
|
|
161
166
|
const params = Object.assign({}, curr.params, to.params);
|
|
162
167
|
const query = to.query === null ? null : Object.assign({}, curr.query, to.query);
|
|
163
168
|
const hash = to.hash === null ? null : to.hash || curr.hash || '';
|
|
@@ -204,6 +204,7 @@ h1 {
|
|
|
204
204
|
|
|
205
205
|
#TOC {
|
|
206
206
|
padding: 32px;
|
|
207
|
+
margin-top: 24px;
|
|
207
208
|
|
|
208
209
|
@media (min-width: 768px) {
|
|
209
210
|
color: rgb(26, 26, 26);
|
|
@@ -231,8 +232,9 @@ h1 {
|
|
|
231
232
|
margin: 0;
|
|
232
233
|
list-style: none;
|
|
233
234
|
}
|
|
234
|
-
#TOC ul
|
|
235
|
+
#TOC ul ul {
|
|
235
236
|
margin: 0;
|
|
237
|
+
margin-top: 12px;
|
|
236
238
|
}
|
|
237
239
|
#TOC ul {
|
|
238
240
|
list-style: none;
|
|
@@ -259,7 +261,8 @@ h1 {
|
|
|
259
261
|
}
|
|
260
262
|
|
|
261
263
|
#TOC > nav > ul > li > a {
|
|
262
|
-
font-
|
|
264
|
+
font-size: 20px;
|
|
265
|
+
font-weight: 500;
|
|
263
266
|
margin-top: 12px;
|
|
264
267
|
}
|
|
265
268
|
|
package/docs/content/_index.md
CHANGED
|
@@ -59,8 +59,8 @@ const routes = [
|
|
|
59
59
|
// create the router
|
|
60
60
|
const router = createRouter()
|
|
61
61
|
|
|
62
|
-
// start listening to the
|
|
63
|
-
// based on the current
|
|
62
|
+
// start listening to the url changes and kick of the initial render
|
|
63
|
+
// based on the current url
|
|
64
64
|
export const dispose = router.listen(routes, render)
|
|
65
65
|
|
|
66
66
|
// every time the route changes, do something, e.g. re-render your app
|
|
@@ -86,7 +86,7 @@ function render(route) {
|
|
|
86
86
|
const router = createRouter(options)
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
Create the router object.
|
|
90
90
|
|
|
91
91
|
- `options` object
|
|
92
92
|
- `mode` - one of `history`, `hash`, `memory`, default is `history`
|
|
@@ -98,18 +98,27 @@ Creates the router object.
|
|
|
98
98
|
const dispose = router.listen(routes, onChange)
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
Start listening to url changes. Every time the url changes via back/forward button or by performing programmatic navigations, the `onChange` callback will get called with the matched `route` object.
|
|
102
102
|
|
|
103
|
-
Note, calling listen will immediately call `onChange` based on the current
|
|
103
|
+
Note, calling listen will immediately call `onChange` based on the current url when in `history` or `hash` modes. This does not happen in `memory` mode so that you could perform the initial navigation yourself since there is no url to read from in that case.
|
|
104
104
|
|
|
105
105
|
- `routes` an array of arrays of route definitions, where each route is an object of shape `{ path, redirect, routes, ...metadata }`
|
|
106
|
-
- `path` is the
|
|
106
|
+
- `path` is the url pattern to match that can include named parameters as segments
|
|
107
107
|
- `redirect` can be a string or a function that redirects upon entering that route
|
|
108
108
|
- `routes` is a nested object of nested route definitions
|
|
109
109
|
- `...metadata` all other other keys can be chosen by you
|
|
110
|
-
- `onChange` is called with `
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
- `onChange` is called with the `route` object
|
|
111
|
+
|
|
112
|
+
`route` is an object of shape `{ url, pathname, params, query, search, hash, pattern, data }`:
|
|
113
|
+
|
|
114
|
+
- `url` full relative url string including query string and hash if any
|
|
115
|
+
- `pathname` the pathname portion of the target url, which can include named segments
|
|
116
|
+
- `params` params extracted from the named pathname segments
|
|
117
|
+
- `query` query object that was parsed with `qs.parse`
|
|
118
|
+
- `search` full unparsed query string
|
|
119
|
+
- `hash` hash fragment
|
|
120
|
+
- `pattern` the matched route pattern as defined in the route config
|
|
121
|
+
- `data` an array of nested matched route objects with any metadata found in the route config
|
|
113
122
|
|
|
114
123
|
Listen returns a `dispose` function that stops listening to url changes.
|
|
115
124
|
|
|
@@ -117,22 +126,30 @@ Listen returns a `dispose` function that stops listening to url changes.
|
|
|
117
126
|
|
|
118
127
|
```js
|
|
119
128
|
router.navigate(to)
|
|
129
|
+
|
|
130
|
+
// examples
|
|
131
|
+
router.navigate('/shows')
|
|
132
|
+
router.navigate({ url: '/show/1' })
|
|
133
|
+
router.navigate({ url: '/show/2', replace: true })
|
|
134
|
+
router.navigate({ pathname: '/shows', query: { 'most-recent': 1 } })
|
|
135
|
+
router.navigate({ query: { 'top-rated': 1 }, merge: true })
|
|
136
|
+
router.navigate({ query: { 'top-rated': undefined }, merge: true })
|
|
120
137
|
```
|
|
121
138
|
|
|
122
|
-
|
|
139
|
+
Navigate to a url. Navigating will update the browser's location bar (unless in `memory` mode) and will call the router listener callback with the newly matched route.
|
|
123
140
|
|
|
124
|
-
- `to` -
|
|
141
|
+
- `to` - a `string` url or an `object` with the following properties
|
|
125
142
|
- `url` a relative url string or a route pattern
|
|
126
143
|
- `pathname` the pathname portion of the target url, which can include named segments
|
|
127
144
|
- `params` params to interpolate into the named pathname segments
|
|
128
145
|
- `query` the query object that will be passed through `qs.stringify`
|
|
129
146
|
- `hash` the hash fragment to append to the url of the url
|
|
130
147
|
- `replace` set to true to replace the current entry in the navigation stack instead of pushing
|
|
131
|
-
- `merge` set to true to merge in the params from the current
|
|
148
|
+
- `merge` set to true to merge in the params from the current url, alternatively set to the current route object to use that as the current route to be used in merging
|
|
132
149
|
|
|
133
150
|
Note, if `url` option is provided, the `pathname`, `params`, `query` and `hash` will be ignored.
|
|
134
151
|
|
|
135
|
-
Note, be careful when using `merge` as this reads the location's current
|
|
152
|
+
Note, be careful when using `merge` as this reads the location's current url which might be different from the one you store in your application's state in case you're performing async logic in the listen callback.
|
|
136
153
|
|
|
137
154
|
### `match`
|
|
138
155
|
|
|
@@ -140,24 +157,33 @@ Note, be careful when using `merge` as this reads the location's current URL whi
|
|
|
140
157
|
const route = router.match(url)
|
|
141
158
|
```
|
|
142
159
|
|
|
143
|
-
Match the url against the routes and return the matching route object.
|
|
160
|
+
Match the url string against the routes and return the matching route object.
|
|
144
161
|
|
|
145
162
|
### `href`
|
|
146
163
|
|
|
147
164
|
```js
|
|
148
165
|
const url = router.href(to)
|
|
166
|
+
|
|
167
|
+
// examples
|
|
168
|
+
router.href('/shows')
|
|
169
|
+
router.href({ url: '/show/1' })
|
|
170
|
+
router.href({ pathname: '/shows', query: { 'most-recent': 1 } })
|
|
171
|
+
router.href({ query: { 'top-rated': 1 }, merge: true })
|
|
172
|
+
router.href({ query: { 'top-rated': undefined }, merge: true })
|
|
149
173
|
```
|
|
150
174
|
|
|
151
|
-
Create a relative
|
|
175
|
+
Create a relative url string to use in `<a href>` attribute.
|
|
152
176
|
|
|
153
177
|
- `to` object of shape `{ pathname, params, query, hash }`. The `params` will be interpolated into the pathname if the pathname contains any parametrised segments. The `query` is an object that will be passed through `qs.stringify`.
|
|
154
178
|
|
|
179
|
+
Note: `to` can be a string, in which case `href` simply returns the input. Similarly, the to can contain `{ url }` key in which case `href` returns that url. This is to align the function signature with that of `navigate` so the two can be used interchangeably.
|
|
180
|
+
|
|
155
181
|
### `getUrl`
|
|
156
182
|
|
|
157
183
|
```js
|
|
158
184
|
const url = router.getUrl()
|
|
159
185
|
```
|
|
160
186
|
|
|
161
|
-
Get the current
|
|
187
|
+
Get the current url string. Note, this only includes the path and does not not include the protocol and host.
|
|
162
188
|
|
|
163
|
-
You shouldn't need to read this most of the time since the updates to
|
|
189
|
+
You shouldn't need to read this most of the time since the updates to url changes and the matching route will be provided in the `listen` callback. Be especially careful if you're performing asynchronous logic in your callback, such as lazily importing some modules, where you're then constructing links based on the current url - use route provided to your listener instead of calling `getUrl` as the url might already have been updated to another value in the meantime.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "space-router",
|
|
3
3
|
"description": "All the routing essentials.",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.5",
|
|
5
5
|
"main": "dist/cjs",
|
|
6
6
|
"module": "dist/esm",
|
|
7
7
|
"license": "ICS",
|
|
@@ -27,21 +27,21 @@
|
|
|
27
27
|
"build": "node ./tasks/build.js",
|
|
28
28
|
"watch": "node ./tasks/build.js -w",
|
|
29
29
|
"version": "npm run build",
|
|
30
|
-
"release": "np",
|
|
31
|
-
"release:beta": "np --tag=beta",
|
|
30
|
+
"release": "np --no-release-draft",
|
|
31
|
+
"release:beta": "np --tag=beta --no-release-draft",
|
|
32
32
|
"release:docs": "hugo -s docs && gh-pages -d docs/public"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@babel/cli": "^7.
|
|
36
|
-
"@babel/preset-env": "^7.16.
|
|
35
|
+
"@babel/cli": "^7.17.6",
|
|
36
|
+
"@babel/preset-env": "^7.16.11",
|
|
37
37
|
"ava": "^3.15.0",
|
|
38
38
|
"esm": "^3.2.25",
|
|
39
39
|
"execa": "^5.1.1",
|
|
40
40
|
"gh-pages": "^3.2.3",
|
|
41
|
-
"healthier": "^
|
|
42
|
-
"np": "^7.
|
|
41
|
+
"healthier": "^5.0.1",
|
|
42
|
+
"np": "^7.6.1",
|
|
43
43
|
"nyc": "^15.1.0",
|
|
44
|
-
"prettier": "^2.
|
|
44
|
+
"prettier": "^2.6.2"
|
|
45
45
|
},
|
|
46
46
|
"healthier": {
|
|
47
47
|
"global": [
|
package/src/history.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export function createHistory(
|
|
1
|
+
export function createHistory(options) {
|
|
2
2
|
const sync = options.sync
|
|
3
3
|
let mode = options.mode
|
|
4
4
|
let raf
|
|
5
|
+
let onPop
|
|
5
6
|
|
|
6
7
|
const memory = []
|
|
7
8
|
let off
|
|
@@ -17,19 +18,22 @@ export function createHistory(onChange, options) {
|
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
21
|
+
function listen(onChange) {
|
|
22
|
+
onPop = () => onChange(getUrl())
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
if (mode !== 'memory') {
|
|
25
|
+
off = on(window, mode === 'history' ? 'popstate' : 'hashchange', onPop)
|
|
26
|
+
raf(onPop)
|
|
27
|
+
}
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
return () => {
|
|
30
|
+
destroyed = true
|
|
31
|
+
off && off()
|
|
32
|
+
}
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
return {
|
|
36
|
+
listen,
|
|
33
37
|
getUrl,
|
|
34
38
|
push(url) {
|
|
35
39
|
go(url)
|
|
@@ -37,10 +41,6 @@ export function createHistory(onChange, options) {
|
|
|
37
41
|
replace(url) {
|
|
38
42
|
go(url, true)
|
|
39
43
|
},
|
|
40
|
-
destroy() {
|
|
41
|
-
destroyed = true
|
|
42
|
-
off && off()
|
|
43
|
-
},
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
function go(url, replace) {
|
package/src/router.js
CHANGED
|
@@ -16,37 +16,41 @@ export function createRouter(options = {}) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
routes = flatten(routeMap)
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
history = createHistory({ mode, sync })
|
|
20
|
+
const dispose = history.listen((url) => transition(router, url, cb))
|
|
21
21
|
|
|
22
22
|
return () => {
|
|
23
|
-
|
|
23
|
+
dispose()
|
|
24
24
|
history = null
|
|
25
25
|
routes = []
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
|
|
29
|
-
navigate(to) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (to.url) {
|
|
34
|
-
url = to.url
|
|
35
|
-
} else {
|
|
36
|
-
url = router.href(to)
|
|
29
|
+
navigate(to, curr) {
|
|
30
|
+
if (typeof to === 'string') {
|
|
31
|
+
to = { url: to }
|
|
37
32
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (replace) {
|
|
33
|
+
const url = router.href(to, curr)
|
|
34
|
+
if (to.replace) {
|
|
41
35
|
history.replace(url)
|
|
42
36
|
} else {
|
|
43
37
|
history.push(url)
|
|
44
38
|
}
|
|
45
39
|
},
|
|
46
40
|
|
|
47
|
-
href(to) {
|
|
41
|
+
href(to, curr) {
|
|
42
|
+
// already a url
|
|
43
|
+
if (typeof to === 'string') {
|
|
44
|
+
return to
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// align with navigate API
|
|
48
|
+
if (to.url) {
|
|
49
|
+
return to.url
|
|
50
|
+
}
|
|
51
|
+
|
|
48
52
|
if (to.merge) {
|
|
49
|
-
|
|
53
|
+
curr = curr || router.match(router.getUrl())
|
|
50
54
|
to = merge(curr, to)
|
|
51
55
|
}
|
|
52
56
|
|
|
@@ -123,7 +127,7 @@ function redirectUrl(router, redirect, matchingRoute) {
|
|
|
123
127
|
if (typeof redirect === 'function') {
|
|
124
128
|
redirect = redirect(matchingRoute)
|
|
125
129
|
}
|
|
126
|
-
return
|
|
130
|
+
return router.href(redirect)
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
function data(routes, matchingRoute) {
|
|
@@ -135,7 +139,7 @@ function data(routes, matchingRoute) {
|
|
|
135
139
|
}
|
|
136
140
|
|
|
137
141
|
export function merge(curr = {}, to) {
|
|
138
|
-
const pathname = to.pathname || curr.pattern
|
|
142
|
+
const pathname = to.pathname || curr.pattern || curr.pathname
|
|
139
143
|
const params = Object.assign({}, curr.params, to.params)
|
|
140
144
|
const query = to.query === null ? null : Object.assign({}, curr.query, to.query)
|
|
141
145
|
const hash = to.hash === null ? null : to.hash || curr.hash || ''
|
package/test/router.test.js
CHANGED
|
@@ -13,13 +13,14 @@ test('createRouter, listen, navigate and dispose', (t) => {
|
|
|
13
13
|
router.navigate({ url: '/user/7/friends' })
|
|
14
14
|
router.navigate({ url: '/user/7' })
|
|
15
15
|
router.navigate({ url: '/user/8/posts' })
|
|
16
|
-
router.navigate(
|
|
17
|
-
router.navigate(
|
|
16
|
+
router.navigate('/bar')
|
|
17
|
+
router.navigate('/user/1')
|
|
18
18
|
router.navigate({ pathname: '/user/:id', params: { id: 2 } })
|
|
19
19
|
router.navigate({ pathname: '/user/2', query: { a: 1, b: 2 } })
|
|
20
20
|
router.navigate({ query: { a: 11, b: 22 }, merge: true })
|
|
21
21
|
router.navigate({ query: { b: undefined, c: 'bla' }, hash: 'test', merge: true })
|
|
22
22
|
router.navigate({ params: { id: 3 }, merge: true })
|
|
23
|
+
router.navigate({ query: { curr: 'test' }, merge: true }, { pathname: '/user/curr' })
|
|
23
24
|
|
|
24
25
|
t.deepEqual(
|
|
25
26
|
[
|
|
@@ -35,6 +36,7 @@ test('createRouter, listen, navigate and dispose', (t) => {
|
|
|
35
36
|
'user=2?a=11&b=22',
|
|
36
37
|
'user=2?a=11&c=bla#test',
|
|
37
38
|
'user=3?a=11&c=bla#test',
|
|
39
|
+
'user=curr?curr=test',
|
|
38
40
|
],
|
|
39
41
|
calls
|
|
40
42
|
)
|
|
@@ -42,7 +44,7 @@ test('createRouter, listen, navigate and dispose', (t) => {
|
|
|
42
44
|
dispose()
|
|
43
45
|
})
|
|
44
46
|
|
|
45
|
-
test('.href(
|
|
47
|
+
test('.href(to)', (t) => {
|
|
46
48
|
const { router, dispose } = createTestRouter()
|
|
47
49
|
|
|
48
50
|
t.deepEqual('/user/7/friends?a=1&b=2', router.href({ pathname: '/user/7/friends', query: { a: 1, b: 2 } }))
|
|
@@ -107,7 +109,7 @@ test('.match(url) without catch all', (t) => {
|
|
|
107
109
|
test('.getUrl()', (t) => {
|
|
108
110
|
const { router } = createTestRouter()
|
|
109
111
|
|
|
110
|
-
router.navigate(
|
|
112
|
+
router.navigate('/user/8')
|
|
111
113
|
t.is(router.getUrl(), '/user/8')
|
|
112
114
|
})
|
|
113
115
|
|
|
@@ -118,10 +120,10 @@ test('redirects', (t) => {
|
|
|
118
120
|
calls.push(route.data[0].render(route.params, route.query))
|
|
119
121
|
})
|
|
120
122
|
|
|
121
|
-
router.navigate(
|
|
122
|
-
router.navigate(
|
|
123
|
-
router.navigate(
|
|
124
|
-
router.navigate(
|
|
123
|
+
router.navigate('/redirect-via-obj-1')
|
|
124
|
+
router.navigate('/redirect-via-obj-2')
|
|
125
|
+
router.navigate('/redirect-via-fn-1/2')
|
|
126
|
+
router.navigate('/redirect-via-fn-2/2')
|
|
125
127
|
|
|
126
128
|
t.deepEqual(['bar', 'user=1', 'user=2', 'foo'], calls)
|
|
127
129
|
|
|
@@ -134,7 +136,7 @@ function createTestRouter(cb, { withoutCatchAll = false } = {}) {
|
|
|
134
136
|
[
|
|
135
137
|
{ path: '/foo', render: () => 'foo' },
|
|
136
138
|
{ path: '/bar', render: () => 'bar' },
|
|
137
|
-
{ path: '/redirect-via-obj-1', redirect:
|
|
139
|
+
{ path: '/redirect-via-obj-1', redirect: 'bar' },
|
|
138
140
|
{ path: '/redirect-via-obj-2', redirect: { pathname: '/user/:id', params: { id: 1 } } },
|
|
139
141
|
{ path: '/redirect-via-fn-1/:id', redirect: ({ params }) => ({ pathname: '/user/:id', params }) },
|
|
140
142
|
{ path: '/redirect-via-fn-2/:id', redirect: ({ params }) => ({ url: '/foo' }) },
|