space-router 0.8.4 → 0.9.0

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/dist/esm/match.js CHANGED
@@ -1,90 +1,76 @@
1
1
  export function match(routes, url, qs) {
2
- if (!url) {
3
- return;
4
- }
5
-
6
- for (let i = 0; i < routes.length; i++) {
7
- const m = matchOne(routes[i].pattern, url, qs);
8
-
9
- if (m) {
10
- return m;
2
+ if (!url) {
3
+ return;
4
+ }
5
+ for(var i = 0; i < routes.length; i++){
6
+ var m = matchOne(routes[i].pattern, url, qs);
7
+ if (m) {
8
+ return m;
9
+ }
11
10
  }
12
- }
13
11
  }
14
12
  export function matchOne(pattern, url, qs) {
15
- if (!pattern) {
16
- return false;
17
- }
18
-
19
- const re = /(?:\?([^#]*))?(#.*)?$/;
20
- const originalUrl = url;
21
- const originalPattern = pattern;
22
- const c = url.match(re);
23
- const params = {};
24
- let query = {};
25
- let search = '';
26
- let hash = '';
27
- let ret;
28
-
29
- if (c && c[1]) {
30
- search = '?' + c[1];
31
- query = qs.parse(c[1]);
32
- }
33
-
34
- if (c && c[2]) {
35
- hash = c[2] || '';
36
- }
37
-
38
- if (pattern !== '*') {
39
- url = segmentize(url.replace(re, ''));
40
- pattern = segmentize(pattern || '');
41
- const max = Math.max(url.length, pattern.length);
42
-
43
- for (let i = 0; i < max; i++) {
44
- if (pattern[i] && pattern[i].charAt(0) === ':') {
45
- const param = pattern[i].replace(/(^:|[+*?]+$)/g, '');
46
- const flags = (pattern[i].match(/[+*?]+$/) || {})[0] || '';
47
- const plus = flags.indexOf('+') > -1;
48
- const star = flags.indexOf('*') > -1;
49
- const val = url[i] || '';
50
-
51
- if (!val && !star && (flags.indexOf('?') < 0 || plus)) {
52
- ret = false;
53
- break;
13
+ if (!pattern) {
14
+ return false;
15
+ }
16
+ var re = /(?:\?([^#]*))?(#.*)?$/;
17
+ var originalUrl = url;
18
+ var originalPattern = pattern;
19
+ var c = url.match(re);
20
+ var params = {};
21
+ var query = {};
22
+ var search = "";
23
+ var hash = "";
24
+ var ret;
25
+ if (c && c[1]) {
26
+ search = "?" + c[1];
27
+ query = qs.parse(c[1]);
28
+ }
29
+ if (c && c[2]) {
30
+ hash = c[2] || "";
31
+ }
32
+ if (pattern !== "*") {
33
+ url = segmentize(url.replace(re, ""));
34
+ pattern = segmentize(pattern || "");
35
+ var max = Math.max(url.length, pattern.length);
36
+ for(var i = 0; i < max; i++){
37
+ if (pattern[i] && pattern[i].charAt(0) === ":") {
38
+ var param = pattern[i].replace(/(^:|[+*?]+$)/g, "");
39
+ var flags = (pattern[i].match(/[+*?]+$/) || {})[0] || "";
40
+ var plus = flags.indexOf("+") > -1;
41
+ var star = flags.indexOf("*") > -1;
42
+ var val = url[i] || "";
43
+ if (!val && !star && (flags.indexOf("?") < 0 || plus)) {
44
+ ret = false;
45
+ break;
46
+ }
47
+ params[param] = decodeURIComponent(val);
48
+ if (plus || star) {
49
+ params[param] = url.slice(i).map(decodeURIComponent).join("/");
50
+ break;
51
+ }
52
+ } else if (pattern[i] !== url[i]) {
53
+ ret = false;
54
+ break;
55
+ }
54
56
  }
55
-
56
- params[param] = decodeURIComponent(val);
57
-
58
- if (plus || star) {
59
- params[param] = url.slice(i).map(decodeURIComponent).join('/');
60
- break;
57
+ if (ret === false) {
58
+ return false;
61
59
  }
62
- } else if (pattern[i] !== url[i]) {
63
- ret = false;
64
- break;
65
- }
66
- }
67
-
68
- if (ret === false) {
69
- return false;
70
60
  }
71
- }
72
-
73
- return {
74
- pattern: originalPattern,
75
- url: originalUrl,
76
- pathname: originalUrl.replace(re, ''),
77
- params,
78
- query,
79
- search,
80
- hash
81
- };
61
+ return {
62
+ pattern: originalPattern,
63
+ url: originalUrl,
64
+ pathname: originalUrl.replace(re, ""),
65
+ params: params,
66
+ query: query,
67
+ search: search,
68
+ hash: hash
69
+ };
82
70
  }
83
-
84
71
  function segmentize(url) {
85
- return strip(url).split('/');
72
+ return strip(url).split("/");
86
73
  }
87
-
88
74
  function strip(url) {
89
- return url.replace(/(^\/+|\/+$)/g, '');
90
- }
75
+ return url.replace(/(^\/+|\/+$)/g, "");
76
+ }
package/dist/esm/qs.js CHANGED
@@ -1,20 +1,17 @@
1
- export const qs = {
2
- parse(queryString) {
3
- return queryString.split('&').reduce((acc, pair) => {
4
- const parts = pair.split('=');
5
- acc[parts[0]] = decodeURIComponent(parts[1]);
6
- return acc;
7
- }, {});
8
- },
9
-
10
- stringify(query) {
11
- return Object.keys(query).reduce((acc, key) => {
12
- if (query[key] !== undefined) {
13
- acc.push(key + '=' + encodeURIComponent(query[key]));
14
- }
15
-
16
- return acc;
17
- }, []).join('&');
18
- }
19
-
20
- };
1
+ export var qs = {
2
+ parse: function parse(queryString) {
3
+ return queryString.split("&").reduce(function(acc, pair) {
4
+ var parts = pair.split("=");
5
+ acc[parts[0]] = decodeURIComponent(parts[1]);
6
+ return acc;
7
+ }, {});
8
+ },
9
+ stringify: function stringify(query) {
10
+ return Object.keys(query).reduce(function(acc, key) {
11
+ if (query[key] !== undefined) {
12
+ acc.push(key + "=" + encodeURIComponent(query[key]));
13
+ }
14
+ return acc;
15
+ }, []).join("&");
16
+ }
17
+ };
@@ -1,177 +1,249 @@
1
- import { match as findMatch } from './match';
2
- import { createHistory } from './history';
3
- import { qs as defaultQs } from './qs';
4
- export function createRouter(options = {}) {
5
- let history = null;
6
- let routes = [];
7
- const mode = options.mode || 'history';
8
- const qs = options.qs || defaultQs;
9
- const sync = options.sync || false;
10
- const router = {
11
- listen(routeMap, cb) {
12
- if (history) {
13
- throw new Error('Already listening');
14
- }
15
-
16
- routes = flatten(routeMap);
17
-
18
- const onHistoryChange = url => transition(router, url, cb);
19
-
20
- history = createHistory(onHistoryChange, {
21
- mode,
22
- sync
23
- });
24
- return () => {
25
- history.destroy();
26
- history = null;
27
- routes = [];
28
- };
29
- },
30
-
31
- navigate(to) {
32
- if (typeof to === 'string') {
33
- to = {
34
- url: to
35
- };
36
- }
37
-
38
- const url = router.href(to);
39
-
40
- if (to.replace) {
41
- history.replace(url);
42
- } else {
43
- history.push(url);
44
- }
45
- },
46
-
47
- href(to) {
48
- // already a url
49
- if (typeof to === 'string') {
50
- return to;
51
- } // align with navigate API
52
-
53
-
54
- if (to.url) {
55
- return to.url;
56
- }
57
-
58
- if (to.merge) {
59
- const curr = to.merge === true ? router.match(router.getUrl()) : to.merge;
60
- to = merge(curr, to);
61
- }
62
-
63
- let url = to.pathname || '/';
64
-
65
- if (to.params) {
66
- Object.keys(to.params).forEach(param => {
67
- url = url.replace(':' + param, to.params[param]);
1
+ function _define_property(obj, key, value) {
2
+ if (key in obj) {
3
+ Object.defineProperty(obj, key, {
4
+ value: value,
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true
68
8
  });
69
- }
70
-
71
- if (to.query && Object.keys(to.query).length) {
72
- const query = qs.stringify(to.query);
73
-
74
- if (query) {
75
- url = url + '?' + query;
9
+ } else {
10
+ obj[key] = value;
11
+ }
12
+ return obj;
13
+ }
14
+ function _object_spread(target) {
15
+ for(var i = 1; i < arguments.length; i++){
16
+ var source = arguments[i] != null ? arguments[i] : {};
17
+ var ownKeys = Object.keys(source);
18
+ if (typeof Object.getOwnPropertySymbols === "function") {
19
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
20
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
21
+ }));
22
+ }
23
+ ownKeys.forEach(function(key) {
24
+ _define_property(target, key, source[key]);
25
+ });
26
+ }
27
+ return target;
28
+ }
29
+ function ownKeys(object, enumerableOnly) {
30
+ var keys = Object.keys(object);
31
+ if (Object.getOwnPropertySymbols) {
32
+ var symbols = Object.getOwnPropertySymbols(object);
33
+ if (enumerableOnly) {
34
+ symbols = symbols.filter(function(sym) {
35
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
36
+ });
37
+ }
38
+ keys.push.apply(keys, symbols);
39
+ }
40
+ return keys;
41
+ }
42
+ function _object_spread_props(target, source) {
43
+ source = source != null ? source : {};
44
+ if (Object.getOwnPropertyDescriptors) {
45
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
46
+ } else {
47
+ ownKeys(Object(source)).forEach(function(key) {
48
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
49
+ });
50
+ }
51
+ return target;
52
+ }
53
+ function _object_without_properties(source, excluded) {
54
+ if (source == null) return {};
55
+ var target = _object_without_properties_loose(source, excluded);
56
+ var key, i;
57
+ if (Object.getOwnPropertySymbols) {
58
+ var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
59
+ for(i = 0; i < sourceSymbolKeys.length; i++){
60
+ key = sourceSymbolKeys[i];
61
+ if (excluded.indexOf(key) >= 0) continue;
62
+ if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
63
+ target[key] = source[key];
76
64
  }
77
- }
78
-
79
- if (to.hash) {
80
- const prefix = to.hash.startsWith('#') ? '' : '#';
81
- url = url + prefix + to.hash;
82
- }
83
-
84
- return url;
85
- },
86
-
87
- match(url) {
88
- const route = findMatch(routes, url, qs);
89
-
90
- if (route) {
91
- return { ...route,
92
- data: data(routes, route)
93
- };
94
- }
95
- },
96
-
97
- getUrl() {
98
- return history.getUrl();
99
65
  }
100
-
101
- };
102
- return router;
66
+ return target;
67
+ }
68
+ function _object_without_properties_loose(source, excluded) {
69
+ if (source == null) return {};
70
+ var target = {};
71
+ var sourceKeys = Object.keys(source);
72
+ var key, i;
73
+ for(i = 0; i < sourceKeys.length; i++){
74
+ key = sourceKeys[i];
75
+ if (excluded.indexOf(key) >= 0) continue;
76
+ target[key] = source[key];
77
+ }
78
+ return target;
79
+ }
80
+ import { match as findMatch } from "./match.js";
81
+ import { createHistory } from "./history.js";
82
+ import { qs as defaultQs } from "./qs.js";
83
+ export function createRouter() {
84
+ var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
85
+ var history = null;
86
+ var routes = [];
87
+ var mode = options.mode || "history";
88
+ var qs = options.qs || defaultQs;
89
+ var sync = options.sync || false;
90
+ var router = {
91
+ listen: function listen(routeMap, cb) {
92
+ if (history) {
93
+ throw new Error("Already listening");
94
+ }
95
+ routes = flatten(routeMap);
96
+ history = createHistory({
97
+ mode: mode,
98
+ sync: sync
99
+ });
100
+ var dispose = history.listen(function(url) {
101
+ return transition(router, url, cb);
102
+ });
103
+ return function() {
104
+ dispose();
105
+ history = null;
106
+ routes = [];
107
+ };
108
+ },
109
+ navigate: function navigate(to, curr) {
110
+ if (typeof to === "string") {
111
+ to = {
112
+ url: to
113
+ };
114
+ }
115
+ var url = router.href(to, curr);
116
+ if (to.replace) {
117
+ history.replace(url);
118
+ } else {
119
+ history.push(url);
120
+ }
121
+ },
122
+ href: function href(to, curr) {
123
+ // already a url
124
+ if (typeof to === "string") {
125
+ return to;
126
+ }
127
+ // align with navigate API
128
+ if (to.url) {
129
+ return to.url;
130
+ }
131
+ if (to.merge) {
132
+ curr = curr || router.match(router.getUrl());
133
+ to = merge(curr, to);
134
+ }
135
+ var url = to.pathname || "/";
136
+ if (to.params) {
137
+ Object.keys(to.params).forEach(function(param) {
138
+ url = url.replace(":" + param, to.params[param]);
139
+ });
140
+ }
141
+ if (to.query && Object.keys(to.query).length) {
142
+ var query = qs.stringify(to.query);
143
+ if (query) {
144
+ url = url + "?" + query;
145
+ }
146
+ }
147
+ if (to.hash) {
148
+ var prefix = to.hash.startsWith("#") ? "" : "#";
149
+ url = url + prefix + to.hash;
150
+ }
151
+ return url;
152
+ },
153
+ match: function match(url) {
154
+ var route = findMatch(routes, url, qs);
155
+ if (route) {
156
+ return _object_spread_props(_object_spread({}, route), {
157
+ data: data(routes, route)
158
+ });
159
+ }
160
+ },
161
+ getUrl: function getUrl() {
162
+ return history.getUrl();
163
+ }
164
+ };
165
+ return router;
103
166
  }
104
167
  export function flatten(routeMap) {
105
- const routes = [];
106
- const parentData = [];
107
-
108
- function addLevel(level) {
109
- level.forEach(route => {
110
- const {
111
- path = '',
112
- routes: children,
113
- ...routeData
114
- } = route;
115
- routes.push({
116
- pattern: path,
117
- data: parentData.concat([routeData])
118
- });
119
-
120
- if (children) {
121
- parentData.push(routeData);
122
- addLevel(children);
123
- parentData.pop();
124
- }
125
- });
126
- }
127
-
128
- addLevel(routeMap);
129
- return routes;
168
+ var routes = [];
169
+ var parentData = [];
170
+ function addLevel(level) {
171
+ level.forEach(function(route) {
172
+ var _route_path = route.path, path = _route_path === void 0 ? "" : _route_path, children = route.routes, routeData = _object_without_properties(route, [
173
+ "path",
174
+ "routes"
175
+ ]);
176
+ routes.push({
177
+ pattern: path,
178
+ data: parentData.concat([
179
+ routeData
180
+ ])
181
+ });
182
+ if (children) {
183
+ parentData.push(routeData);
184
+ addLevel(children);
185
+ parentData.pop();
186
+ }
187
+ });
188
+ }
189
+ addLevel(routeMap);
190
+ return routes;
130
191
  }
131
-
132
192
  function transition(router, url, onNavigated) {
133
- const route = router.match(url);
134
-
135
- if (route) {
136
- for (const r of route.data) {
137
- if (r.redirect) {
138
- const url = redirectUrl(router, r.redirect, route);
139
- return router.navigate({
140
- url,
141
- replace: true
142
- });
143
- }
193
+ var route = router.match(url);
194
+ if (route) {
195
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
196
+ try {
197
+ for(var _iterator = route.data[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
198
+ var r = _step.value;
199
+ if (r.redirect) {
200
+ var _$url = redirectUrl(router, r.redirect, route);
201
+ return router.navigate({
202
+ url: _$url,
203
+ replace: true
204
+ });
205
+ }
206
+ }
207
+ } catch (err) {
208
+ _didIteratorError = true;
209
+ _iteratorError = err;
210
+ } finally{
211
+ try {
212
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
213
+ _iterator.return();
214
+ }
215
+ } finally{
216
+ if (_didIteratorError) {
217
+ throw _iteratorError;
218
+ }
219
+ }
220
+ }
221
+ onNavigated && onNavigated(route);
144
222
  }
145
-
146
- onNavigated && onNavigated(route);
147
- }
148
223
  }
149
-
150
224
  function redirectUrl(router, redirect, matchingRoute) {
151
- if (typeof redirect === 'function') {
152
- redirect = redirect(matchingRoute);
153
- }
154
-
155
- return router.href(redirect);
225
+ if (typeof redirect === "function") {
226
+ redirect = redirect(matchingRoute);
227
+ }
228
+ return router.href(redirect);
156
229
  }
157
-
158
230
  function data(routes, matchingRoute) {
159
- for (let i = 0; i < routes.length; i++) {
160
- if (routes[i].pattern === matchingRoute.pattern) {
161
- return routes[i].data;
231
+ for(var i = 0; i < routes.length; i++){
232
+ if (routes[i].pattern === matchingRoute.pattern) {
233
+ return routes[i].data;
234
+ }
162
235
  }
163
- }
164
236
  }
165
-
166
- export function merge(curr = {}, to) {
167
- const pathname = to.pathname || curr.pattern;
168
- const params = Object.assign({}, curr.params, to.params);
169
- const query = to.query === null ? null : Object.assign({}, curr.query, to.query);
170
- const hash = to.hash === null ? null : to.hash || curr.hash || '';
171
- return {
172
- pathname,
173
- params,
174
- query,
175
- hash
176
- };
177
- }
237
+ export function merge() {
238
+ var curr = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}, to = arguments.length > 1 ? arguments[1] : void 0;
239
+ var pathname = to.pathname || curr.pattern || curr.pathname;
240
+ var params = Object.assign({}, curr.params, to.params);
241
+ var query = to.query === null ? null : Object.assign({}, curr.query, to.query);
242
+ var hash = to.hash === null ? null : to.hash || curr.hash || "";
243
+ return {
244
+ pathname: pathname,
245
+ params: params,
246
+ query: query,
247
+ hash: hash
248
+ };
249
+ }
@@ -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