@tinkoff/router 0.1.62 → 0.1.66

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/README.md CHANGED
@@ -66,8 +66,24 @@ export const myGuard: NavigationGuard = async ({ to }) => {
66
66
 
67
67
  // if nothing is returned, the transition will be performed as usual
68
68
  };
69
+
70
+ router.registerGuard(myGuard);
69
71
  ```
70
72
 
73
+ #### Rules
74
+
75
+ - guards are asynchronous and it execution will be awaited inside routing
76
+ - all guards are running in parallel and they are all awaited
77
+ - if several guars return something then the result from a guard that was registered early will be used
78
+
79
+ #### Possible result
80
+
81
+ The behaviour of routing depends on the result of executing guards functions and there result might be next:
82
+
83
+ - if all of the guards returns `undefined` than navigation will continue executing
84
+ - if any of the guards returns `false` than navigation is getting blocked and next action differs on server and client
85
+ - if any of the guards returns `string` it is considered as url to which redirect should be happen
86
+
71
87
  ### Transitions hooks
72
88
 
73
89
  Transition hooks allow you to perform your asynchronous actions at different stages of the transition.
@@ -78,8 +94,27 @@ import { NavigationHook } from '@tinkoff/router';
78
94
  export const myHook: NavigationHook = async ({ from, to, url, fromUrl }) => {
79
95
  console.log(`navigating from ${from} to route ${to}`);
80
96
  };
97
+
98
+ router.registerHook('beforeNavigate', myHook);
81
99
  ```
82
100
 
101
+ #### Rules
102
+
103
+ - all hooks from the same event are running in parallel
104
+ - most of the hooks are asynchronous and are awaited inside router
105
+ - if some error happens when running hook it will be logged to console but wont affect navigation (except for the `beforeResolve` hook - error for it will be rethrown)
106
+
107
+ #### List of available hooks
108
+
109
+ Async hooks:
110
+
111
+ - [navigate hooks](#navigate-hooks) - asynchronous hooks only for navigate calls
112
+ - [updateCurrentRoute hooks](#updatecurrentroute-hooks) - asynchronous hooks only for updateCurrentRoute calls
113
+
114
+ Sync hooks:
115
+
116
+ - `change` - runs when any of changes to current route\url happens
117
+
83
118
  ## API
84
119
 
85
120
  ### Getting data about the current route or url
@@ -102,12 +137,20 @@ router.navigate('/test');
102
137
  router.navigate({ url: './test', query: { a: '1' } });
103
138
  ```
104
139
 
105
- Transition hooks:
140
+ ##### navigate hooks
106
141
 
107
142
  - beforeResolve
108
143
  - beforeNavigate
109
144
  - afterNavigate
110
145
 
146
+ ##### navigate workflow
147
+
148
+ 1. `beforeResolve` hook
149
+ 2. [guards](#router-guards)
150
+ 3. `beforeNavigate`
151
+ 4. `change`
152
+ 5. `afterNavigate`
153
+
111
154
  #### updateCurrentRoute
112
155
 
113
156
  The transition is based on the current route (therefore this method cannot be called on the server) and allows you to simply update some data for the current page
@@ -117,11 +160,17 @@ router.updateCurrentRoute({ params: { id: 'abc' } });
117
160
  router.updateCurrentRoute({ query: { a: '1' } });
118
161
  ```
119
162
 
120
- Hooks:
163
+ ##### updateCurrentRoute hooks
121
164
 
122
165
  - beforeUpdateCurrent
123
166
  - afterUpdateCurrent
124
167
 
168
+ ##### updateCurrentRoute workflow
169
+
170
+ 1. `beforeUpdateCurrent`
171
+ 2. `change`
172
+ 3. `afterUpdateCurrent`
173
+
125
174
  ### Working with query
126
175
 
127
176
  #### query option
@@ -161,6 +210,11 @@ router.updateCurrentRoute({ query: { a: undefined, c: 'c' }, preserveQuery: true
161
210
  router.getCurrentUrl().query; // { b: 'b', c: 'c' }
162
211
  ```
163
212
 
213
+ ### Constructor options
214
+
215
+ - `trailingSlash` - do router should force all urls to end with slash. If `true` - force trailing slash for every path, `false` - force no trailing slash, `undefined` - trailing slash is specified by request and both trailing and not trailing slashes are used. By default value if `undefined`
216
+ - `mergeSlashes` - replace several consecutive slashes by single slashes (slashes after protocol are still be with `//` after protocol name). By default is `false` - no merge for slashes.
217
+
164
218
  ### Integration with React
165
219
 
166
220
  Library has some useful React hooks and components for working with routing
@@ -229,3 +283,26 @@ export const WrapLink = () => {
229
283
  return <Link url="/test/">Click me</Link>;
230
284
  };
231
285
  ```
286
+
287
+ ## How to
288
+
289
+ ### Load route config from external api
290
+
291
+ Use [transition hook](#transitions-hooks) `beforeResolve` and load routes config based on url.
292
+
293
+ ```ts
294
+ router.registerHook('beforeResolve', async (navigation) => {
295
+ const route = await routeResolve(navigation);
296
+
297
+ if (route) {
298
+ router.addRoute(routeTransform(route));
299
+ }
300
+ });
301
+ ```
302
+
303
+ ### App behind proxy
304
+
305
+ Router doesn't support proxy setup directly. But proxy still can be used with some limitations:
306
+
307
+ - setup proxy server to pass requests to app with rewriting request and response paths. (E.g. for [nginx](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_redirect))
308
+ - it wont work as expected on spa navigation on client, so only option in this case is use the `NoSpaRouter`
@@ -183,6 +183,7 @@ class AbstractRouter {
183
183
  }
184
184
  this.runSyncHooks('change', navigation);
185
185
  this.lastNavigation = navigation;
186
+ this.currentNavigation = null;
186
187
  }
187
188
  async updateCurrentRoute(updateRouteOptions) {
188
189
  return this.internalUpdateCurrentRoute(updateRouteOptions, {});
@@ -276,7 +277,6 @@ class AbstractRouter {
276
277
  if (navigation.type === 'updateCurrentRoute') {
277
278
  await this.runUpdateCurrentRoute(navigation);
278
279
  }
279
- this.currentNavigation = null;
280
280
  }
281
281
  resolve(resolveOptions, options) {
282
282
  const opts = typeof resolveOptions === 'string' ? { url: resolveOptions } : resolveOptions;
package/lib/index.es.js CHANGED
@@ -183,6 +183,7 @@ class AbstractRouter {
183
183
  }
184
184
  this.runSyncHooks('change', navigation);
185
185
  this.lastNavigation = navigation;
186
+ this.currentNavigation = null;
186
187
  }
187
188
  async updateCurrentRoute(updateRouteOptions) {
188
189
  return this.internalUpdateCurrentRoute(updateRouteOptions, {});
@@ -276,7 +277,6 @@ class AbstractRouter {
276
277
  if (navigation.type === 'updateCurrentRoute') {
277
278
  await this.runUpdateCurrentRoute(navigation);
278
279
  }
279
- this.currentNavigation = null;
280
280
  }
281
281
  resolve(resolveOptions, options) {
282
282
  const opts = typeof resolveOptions === 'string' ? { url: resolveOptions } : resolveOptions;
@@ -668,9 +668,11 @@ class RouteTree {
668
668
 
669
669
  class Router extends AbstractRouter {
670
670
  constructor(options) {
671
+ var _a;
671
672
  super(options);
672
673
  this.blocked = false;
673
674
  this.tree = new RouteTree(options.routes);
675
+ this.defaultRedirectCode = (_a = options.defaultRedirectCode) !== null && _a !== void 0 ? _a : 308;
674
676
  this.history = new ServerHistory();
675
677
  }
676
678
  async dehydrate() {
@@ -713,13 +715,13 @@ class Router extends AbstractRouter {
713
715
  normalizePathname(pathname) {
714
716
  const normalized = super.normalizePathname(pathname);
715
717
  if (normalized !== pathname) {
716
- this.redirectCode = 308;
718
+ this.redirectCode = this.defaultRedirectCode;
717
719
  }
718
720
  return normalized;
719
721
  }
720
722
  resolveUrl(options) {
721
723
  if (options.url && isInvalidUrl(options.url)) {
722
- this.redirectCode = 308;
724
+ this.redirectCode = this.defaultRedirectCode;
723
725
  }
724
726
  return super.resolveUrl(options);
725
727
  }
package/lib/index.js CHANGED
@@ -199,6 +199,7 @@ class AbstractRouter {
199
199
  }
200
200
  this.runSyncHooks('change', navigation);
201
201
  this.lastNavigation = navigation;
202
+ this.currentNavigation = null;
202
203
  }
203
204
  async updateCurrentRoute(updateRouteOptions) {
204
205
  return this.internalUpdateCurrentRoute(updateRouteOptions, {});
@@ -292,7 +293,6 @@ class AbstractRouter {
292
293
  if (navigation.type === 'updateCurrentRoute') {
293
294
  await this.runUpdateCurrentRoute(navigation);
294
295
  }
295
- this.currentNavigation = null;
296
296
  }
297
297
  resolve(resolveOptions, options) {
298
298
  const opts = typeof resolveOptions === 'string' ? { url: resolveOptions } : resolveOptions;
@@ -684,9 +684,11 @@ class RouteTree {
684
684
 
685
685
  class Router extends AbstractRouter {
686
686
  constructor(options) {
687
+ var _a;
687
688
  super(options);
688
689
  this.blocked = false;
689
690
  this.tree = new RouteTree(options.routes);
691
+ this.defaultRedirectCode = (_a = options.defaultRedirectCode) !== null && _a !== void 0 ? _a : 308;
690
692
  this.history = new ServerHistory();
691
693
  }
692
694
  async dehydrate() {
@@ -729,13 +731,13 @@ class Router extends AbstractRouter {
729
731
  normalizePathname(pathname) {
730
732
  const normalized = super.normalizePathname(pathname);
731
733
  if (normalized !== pathname) {
732
- this.redirectCode = 308;
734
+ this.redirectCode = this.defaultRedirectCode;
733
735
  }
734
736
  return normalized;
735
737
  }
736
738
  resolveUrl(options) {
737
739
  if (options.url && url.isInvalidUrl(options.url)) {
738
- this.redirectCode = 308;
740
+ this.redirectCode = this.defaultRedirectCode;
739
741
  }
740
742
  return super.resolveUrl(options);
741
743
  }
@@ -16,6 +16,7 @@ export interface Options {
16
16
  afterUpdateCurrent?: NavigationHook[];
17
17
  guards?: NavigationGuard[];
18
18
  onChange?: NavigationSyncHook[];
19
+ defaultRedirectCode?: number;
19
20
  }
20
21
  interface InternalOptions {
21
22
  history?: boolean;
@@ -1,14 +1,11 @@
1
1
  import type { Options } from './abstract';
2
2
  import { AbstractRouter } from './abstract';
3
- import type { Navigation, NavigateOptions, HookName, NavigationHook } from '../types';
3
+ import type { Navigation, NavigateOptions, HookName } from '../types';
4
4
  export declare class Router extends AbstractRouter {
5
+ protected defaultRedirectCode: number;
5
6
  protected blocked: boolean;
6
7
  protected redirectCode?: number;
7
- constructor(options: Options & {
8
- onRedirect: (navigation: Navigation) => Promise<void>;
9
- onNotFound: NavigationHook;
10
- onBlock: NavigationHook;
11
- });
8
+ constructor(options: Options);
12
9
  protected onRedirect: (navigation: Navigation) => Promise<void>;
13
10
  dehydrate(): Promise<Navigation>;
14
11
  protected run(navigation: Navigation): Promise<void>;
package/lib/types.d.ts CHANGED
@@ -13,8 +13,8 @@ export interface NavigationRoute extends Route {
13
13
  navigateState?: any;
14
14
  }
15
15
  export interface BaseNavigateOptions {
16
- params?: Params;
17
- query?: Query;
16
+ params?: Partial<Params>;
17
+ query?: Partial<Query>;
18
18
  preserveQuery?: boolean;
19
19
  replace?: boolean;
20
20
  hash?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinkoff/router",
3
- "version": "0.1.62",
3
+ "version": "0.1.66",
4
4
  "description": "router",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -14,7 +14,7 @@
14
14
  ],
15
15
  "repository": {
16
16
  "type": "git",
17
- "url": "git@github.com:TinkoffCreditSystems/tramvai.git"
17
+ "url": "git@github.com:Tinkoff/tramvai.git"
18
18
  },
19
19
  "scripts": {
20
20
  "build": "tramvai-build --for-publish",