space-router 0.8.4 → 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 CHANGED
@@ -1,3 +1,8 @@
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
+
1
6
  ## 0.8.4
2
7
 
3
8
  - fix the `navigate(url: string)` implementation where `"".replace` was being tested instead of `to.replace`
@@ -3,10 +3,11 @@
3
3
  exports.__esModule = true;
4
4
  exports.createHistory = createHistory;
5
5
 
6
- function createHistory(onChange, options) {
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
- if (mode !== 'memory') {
30
- off = on(window, mode === 'history' ? 'popstate' : 'hashchange', onPop);
31
- }
30
+ function listen(onChange) {
31
+ onPop = function onPop() {
32
+ return onChange(getUrl());
33
+ };
32
34
 
33
- function onPop() {
34
- onChange(getUrl());
35
- }
35
+ if (mode !== 'memory') {
36
+ off = on(window, mode === 'history' ? 'popstate' : 'hashchange', onPop);
37
+ raf(onPop);
38
+ }
36
39
 
37
- if (mode !== 'memory') {
38
- onChange(getUrl());
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
 
@@ -40,29 +40,27 @@ 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
- history.destroy();
51
+ dispose();
54
52
  history = null;
55
53
  routes = [];
56
54
  };
57
55
  },
58
- navigate: function navigate(to) {
56
+ navigate: function navigate(to, curr) {
59
57
  if (typeof to === 'string') {
60
58
  to = {
61
59
  url: to
62
60
  };
63
61
  }
64
62
 
65
- var url = router.href(to);
63
+ var url = router.href(to, curr);
66
64
 
67
65
  if (to.replace) {
68
66
  history.replace(url);
@@ -70,7 +68,7 @@ function createRouter(options) {
70
68
  history.push(url);
71
69
  }
72
70
  },
73
- href: function href(to) {
71
+ href: function href(to, curr) {
74
72
  // already a url
75
73
  if (typeof to === 'string') {
76
74
  return to;
@@ -82,7 +80,7 @@ function createRouter(options) {
82
80
  }
83
81
 
84
82
  if (to.merge) {
85
- var curr = to.merge === true ? router.match(router.getUrl()) : to.merge;
83
+ curr = curr || router.match(router.getUrl());
86
84
  to = merge(curr, to);
87
85
  }
88
86
 
@@ -195,7 +193,7 @@ function merge(curr, to) {
195
193
  curr = {};
196
194
  }
197
195
 
198
- var pathname = to.pathname || curr.pattern;
196
+ var pathname = to.pathname || curr.pattern || curr.pathname;
199
197
  var params = Object.assign({}, curr.params, to.params);
200
198
  var query = to.query === null ? null : Object.assign({}, curr.query, to.query);
201
199
  var hash = to.hash === null ? null : to.hash || curr.hash || '';
@@ -1,7 +1,8 @@
1
- export function createHistory(onChange, options) {
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
- if (mode !== 'memory') {
21
- off = on(window, mode === 'history' ? 'popstate' : 'hashchange', onPop);
22
- }
21
+ function listen(onChange) {
22
+ onPop = () => onChange(getUrl());
23
23
 
24
- function onPop() {
25
- onChange(getUrl());
26
- }
24
+ if (mode !== 'memory') {
25
+ off = on(window, mode === 'history' ? 'popstate' : 'hashchange', onPop);
26
+ raf(onPop);
27
+ }
27
28
 
28
- if (mode !== 'memory') {
29
- onChange(getUrl());
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
  };
@@ -14,28 +14,26 @@ 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
- history.destroy();
23
+ dispose();
26
24
  history = null;
27
25
  routes = [];
28
26
  };
29
27
  },
30
28
 
31
- navigate(to) {
29
+ navigate(to, curr) {
32
30
  if (typeof to === 'string') {
33
31
  to = {
34
32
  url: to
35
33
  };
36
34
  }
37
35
 
38
- const url = router.href(to);
36
+ const url = router.href(to, curr);
39
37
 
40
38
  if (to.replace) {
41
39
  history.replace(url);
@@ -44,7 +42,7 @@ export function createRouter(options = {}) {
44
42
  }
45
43
  },
46
44
 
47
- href(to) {
45
+ href(to, curr) {
48
46
  // already a url
49
47
  if (typeof to === 'string') {
50
48
  return to;
@@ -56,7 +54,7 @@ export function createRouter(options = {}) {
56
54
  }
57
55
 
58
56
  if (to.merge) {
59
- const curr = to.merge === true ? router.match(router.getUrl()) : to.merge;
57
+ curr = curr || router.match(router.getUrl());
60
58
  to = merge(curr, to);
61
59
  }
62
60
 
@@ -164,7 +162,7 @@ function data(routes, matchingRoute) {
164
162
  }
165
163
 
166
164
  export function merge(curr = {}, to) {
167
- const pathname = to.pathname || curr.pattern;
165
+ const pathname = to.pathname || curr.pattern || curr.pathname;
168
166
  const params = Object.assign({}, curr.params, to.params);
169
167
  const query = to.query === null ? null : Object.assign({}, curr.query, to.query);
170
168
  const hash = to.hash === null ? null : to.hash || curr.hash || '';
@@ -107,9 +107,18 @@ Note, calling listen will immediately call `onChange` based on the current url w
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 `(route)`
111
- - `route` is an object of shape `{ pattern, href, pathname, params, query, search, hash }`
112
- - `data` is an array of datas associated with this route
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,9 +126,17 @@ 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
- Navigate to a url. Navigating will update the browser's location bar, depending no the mode the router is in and will call the router listener callback with the newly matched route.
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
141
  - `to` - a `string` url or an `object` with the following properties
125
142
  - `url` a relative url string or a route pattern
@@ -140,19 +157,26 @@ 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 string against the routes and return the matching route object. Useful in server side rendering to translate the request url to a matching route.
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
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
 
155
- Note: `to` can also be a string, in which case href simply returns the input. This is to align the function signature with that of `navigate` so the two can be used interchangeably.
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.
156
180
 
157
181
  ### `getUrl`
158
182
 
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",
4
+ "version": "0.8.5",
5
5
  "main": "dist/cjs",
6
6
  "module": "dist/esm",
7
7
  "license": "ICS",
@@ -32,16 +32,16 @@
32
32
  "release:docs": "hugo -s docs && gh-pages -d docs/public"
33
33
  },
34
34
  "devDependencies": {
35
- "@babel/cli": "^7.16.0",
36
- "@babel/preset-env": "^7.16.0",
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": "^4.0.0",
42
- "np": "^7.5.0",
41
+ "healthier": "^5.0.1",
42
+ "np": "^7.6.1",
43
43
  "nyc": "^15.1.0",
44
- "prettier": "^2.4.1"
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(onChange, options) {
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
- if (mode !== 'memory') {
21
- off = on(window, mode === 'history' ? 'popstate' : 'hashchange', onPop)
22
- }
21
+ function listen(onChange) {
22
+ onPop = () => onChange(getUrl())
23
23
 
24
- function onPop() {
25
- onChange(getUrl())
26
- }
24
+ if (mode !== 'memory') {
25
+ off = on(window, mode === 'history' ? 'popstate' : 'hashchange', onPop)
26
+ raf(onPop)
27
+ }
27
28
 
28
- if (mode !== 'memory') {
29
- onChange(getUrl())
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,21 +16,21 @@ export function createRouter(options = {}) {
16
16
  }
17
17
 
18
18
  routes = flatten(routeMap)
19
- const onHistoryChange = (url) => transition(router, url, cb)
20
- history = createHistory(onHistoryChange, { mode, sync })
19
+ history = createHistory({ mode, sync })
20
+ const dispose = history.listen((url) => transition(router, url, cb))
21
21
 
22
22
  return () => {
23
- history.destroy()
23
+ dispose()
24
24
  history = null
25
25
  routes = []
26
26
  }
27
27
  },
28
28
 
29
- navigate(to) {
29
+ navigate(to, curr) {
30
30
  if (typeof to === 'string') {
31
31
  to = { url: to }
32
32
  }
33
- const url = router.href(to)
33
+ const url = router.href(to, curr)
34
34
  if (to.replace) {
35
35
  history.replace(url)
36
36
  } else {
@@ -38,7 +38,7 @@ export function createRouter(options = {}) {
38
38
  }
39
39
  },
40
40
 
41
- href(to) {
41
+ href(to, curr) {
42
42
  // already a url
43
43
  if (typeof to === 'string') {
44
44
  return to
@@ -50,7 +50,7 @@ export function createRouter(options = {}) {
50
50
  }
51
51
 
52
52
  if (to.merge) {
53
- const curr = to.merge === true ? router.match(router.getUrl()) : to.merge
53
+ curr = curr || router.match(router.getUrl())
54
54
  to = merge(curr, to)
55
55
  }
56
56
 
@@ -139,7 +139,7 @@ function data(routes, matchingRoute) {
139
139
  }
140
140
 
141
141
  export function merge(curr = {}, to) {
142
- const pathname = to.pathname || curr.pattern
142
+ const pathname = to.pathname || curr.pattern || curr.pathname
143
143
  const params = Object.assign({}, curr.params, to.params)
144
144
  const query = to.query === null ? null : Object.assign({}, curr.query, to.query)
145
145
  const hash = to.hash === null ? null : to.hash || curr.hash || ''
@@ -20,6 +20,7 @@ test('createRouter, listen, navigate and dispose', (t) => {
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(options)', (t) => {
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 } }))