@tinkoff/router 0.1.77 → 0.1.80

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.
@@ -1,6 +1,12 @@
1
- import React from 'react';
1
+ import type { FunctionComponent, ReactNode } from 'react';
2
+ import type { Url } from '@tinkoff/url';
2
3
  import type { AbstractRouter } from '../../router/abstract';
3
- export declare const Provider: React.FunctionComponent<{
4
+ import type { NavigationRoute } from '../../types';
5
+ export declare const Provider: FunctionComponent<{
4
6
  router: AbstractRouter;
5
- children?: React.ReactNode;
7
+ serverState?: {
8
+ currentRoute: NavigationRoute;
9
+ currentUrl: Url;
10
+ };
11
+ children?: ReactNode;
6
12
  }>;
@@ -8,8 +8,9 @@ import each from '@tinkoff/utils/array/each';
8
8
  import find from '@tinkoff/utils/array/find';
9
9
  import findIndex from '@tinkoff/utils/array/findIndex';
10
10
  import { jsx } from 'react/jsx-runtime';
11
- import { createContext, useState, useContext, useCallback, isValidElement, cloneElement } from 'react';
12
- import { useIsomorphicLayoutEffect, useShallowEqual } from '@tinkoff/react-hooks';
11
+ import { useSyncExternalStore } from 'use-sync-external-store/shim';
12
+ import { createContext, useContext, useCallback, isValidElement, cloneElement } from 'react';
13
+ import { useShallowEqual } from '@tinkoff/react-hooks';
13
14
 
14
15
  const PARAMETER_DELIMITER = ':';
15
16
  const WILDCARD_REGEXP = /\*/;
@@ -173,6 +174,14 @@ class AbstractRouter {
173
174
  // same as getCurrentRoute
174
175
  return (_b = (_a = this.currentNavigation) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : (_c = this.lastNavigation) === null || _c === void 0 ? void 0 : _c.url;
175
176
  }
177
+ getLastRoute() {
178
+ var _a;
179
+ return (_a = this.lastNavigation) === null || _a === void 0 ? void 0 : _a.to;
180
+ }
181
+ getLastUrl() {
182
+ var _a;
183
+ return (_a = this.lastNavigation) === null || _a === void 0 ? void 0 : _a.url;
184
+ }
176
185
  commitNavigation(navigation) {
177
186
  logger.debug({
178
187
  event: 'commit-navigation',
@@ -182,9 +191,9 @@ class AbstractRouter {
182
191
  // in case we came from history do not history back to prevent infinity recursive calls
183
192
  this.history.save(navigation);
184
193
  }
185
- this.runSyncHooks('change', navigation);
186
194
  this.lastNavigation = navigation;
187
195
  this.currentNavigation = null;
196
+ this.runSyncHooks('change', navigation);
188
197
  }
189
198
  async updateCurrentRoute(updateRouteOptions) {
190
199
  return this.internalUpdateCurrentRoute(updateRouteOptions, {});
@@ -221,17 +230,20 @@ class AbstractRouter {
221
230
  await this.runHooks('afterUpdateCurrent', navigation);
222
231
  }
223
232
  async navigate(navigateOptions) {
224
- return this.internalNavigate(typeof navigateOptions === 'string' ? { url: navigateOptions } : navigateOptions, {});
233
+ return this.internalNavigate(makeNavigateOptions(navigateOptions), {});
225
234
  }
226
- async internalNavigate(navigateOptions, { history }) {
235
+ async internalNavigate(navigateOptions, { history, redirect }) {
227
236
  var _a;
228
237
  const { url, replace, params, navigateState, code } = navigateOptions;
229
- const prevNavigation = (_a = this.currentNavigation) !== null && _a !== void 0 ? _a : this.lastNavigation;
238
+ const prevNavigation = redirect
239
+ ? this.lastNavigation
240
+ : (_a = this.currentNavigation) !== null && _a !== void 0 ? _a : this.lastNavigation;
230
241
  if (!url && !prevNavigation) {
231
242
  throw new Error('Navigate url should be specified and cannot be omitted for first navigation');
232
243
  }
233
244
  const resolvedUrl = this.resolveUrl(navigateOptions);
234
245
  const { to: from, url: fromUrl } = prevNavigation !== null && prevNavigation !== void 0 ? prevNavigation : {};
246
+ const redirectFrom = redirect ? this.currentNavigation.to : undefined;
235
247
  let navigation = {
236
248
  type: 'navigate',
237
249
  from,
@@ -241,6 +253,8 @@ class AbstractRouter {
241
253
  history,
242
254
  navigateState,
243
255
  code,
256
+ redirect,
257
+ redirectFrom,
244
258
  };
245
259
  await this.runHooks('beforeResolve', navigation);
246
260
  const to = this.resolveRoute({ url: resolvedUrl, params, navigateState }, { wildcard: true });
@@ -792,7 +806,12 @@ class ClientRouter extends AbstractRouter {
792
806
  }
793
807
  async redirect(navigation, target) {
794
808
  await super.redirect(navigation, target);
795
- return this.navigate({ ...target, replace: target.replace || navigation.replace });
809
+ return this.internalNavigate({
810
+ ...target,
811
+ replace: target.replace || navigation.replace,
812
+ }, {
813
+ redirect: true,
814
+ });
796
815
  }
797
816
  }
798
817
 
@@ -1116,17 +1135,10 @@ const RouterContext = createContext(null);
1116
1135
  const RouteContext = createContext(null);
1117
1136
  const UrlContext = createContext(null);
1118
1137
 
1119
- const Provider = ({ router, children }) => {
1120
- const [state, setState] = useState({
1121
- route: router.getCurrentRoute(),
1122
- url: router.getCurrentUrl(),
1123
- });
1124
- useIsomorphicLayoutEffect(() => {
1125
- return router.registerSyncHook('change', ({ to, url }) => {
1126
- setState({ route: to, url });
1127
- });
1128
- }, [router]);
1129
- return (jsx(RouterContext.Provider, Object.assign({ value: router }, { children: jsx(RouteContext.Provider, Object.assign({ value: state.route }, { children: jsx(UrlContext.Provider, Object.assign({ value: state.url }, { children: children }), void 0) }), void 0) }), void 0));
1138
+ const Provider = ({ router, serverState, children }) => {
1139
+ const route = useSyncExternalStore((cb) => router.registerSyncHook('change', cb), () => router.getLastRoute(), serverState ? () => serverState.currentRoute : () => router.getLastRoute());
1140
+ const url = useSyncExternalStore((cb) => router.registerSyncHook('change', cb), () => router.getLastUrl(), serverState ? () => serverState.currentUrl : () => router.getLastUrl());
1141
+ return (jsx(RouterContext.Provider, Object.assign({ value: router }, { children: jsx(RouteContext.Provider, Object.assign({ value: route }, { children: jsx(UrlContext.Provider, Object.assign({ value: url }, { children: children }), void 0) }), void 0) }), void 0));
1130
1142
  };
1131
1143
  Provider.displayName = 'Provider';
1132
1144
 
package/lib/index.es.js CHANGED
@@ -8,8 +8,9 @@ import each from '@tinkoff/utils/array/each';
8
8
  import find from '@tinkoff/utils/array/find';
9
9
  import findIndex from '@tinkoff/utils/array/findIndex';
10
10
  import { jsx } from 'react/jsx-runtime';
11
- import { createContext, useState, useContext, useCallback, isValidElement, cloneElement } from 'react';
12
- import { useIsomorphicLayoutEffect, useShallowEqual } from '@tinkoff/react-hooks';
11
+ import { useSyncExternalStore } from 'use-sync-external-store/shim';
12
+ import { createContext, useContext, useCallback, isValidElement, cloneElement } from 'react';
13
+ import { useShallowEqual } from '@tinkoff/react-hooks';
13
14
 
14
15
  const PARAMETER_DELIMITER = ':';
15
16
  const WILDCARD_REGEXP = /\*/;
@@ -173,6 +174,14 @@ class AbstractRouter {
173
174
  // same as getCurrentRoute
174
175
  return (_b = (_a = this.currentNavigation) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : (_c = this.lastNavigation) === null || _c === void 0 ? void 0 : _c.url;
175
176
  }
177
+ getLastRoute() {
178
+ var _a;
179
+ return (_a = this.lastNavigation) === null || _a === void 0 ? void 0 : _a.to;
180
+ }
181
+ getLastUrl() {
182
+ var _a;
183
+ return (_a = this.lastNavigation) === null || _a === void 0 ? void 0 : _a.url;
184
+ }
176
185
  commitNavigation(navigation) {
177
186
  logger.debug({
178
187
  event: 'commit-navigation',
@@ -182,9 +191,9 @@ class AbstractRouter {
182
191
  // in case we came from history do not history back to prevent infinity recursive calls
183
192
  this.history.save(navigation);
184
193
  }
185
- this.runSyncHooks('change', navigation);
186
194
  this.lastNavigation = navigation;
187
195
  this.currentNavigation = null;
196
+ this.runSyncHooks('change', navigation);
188
197
  }
189
198
  async updateCurrentRoute(updateRouteOptions) {
190
199
  return this.internalUpdateCurrentRoute(updateRouteOptions, {});
@@ -221,17 +230,20 @@ class AbstractRouter {
221
230
  await this.runHooks('afterUpdateCurrent', navigation);
222
231
  }
223
232
  async navigate(navigateOptions) {
224
- return this.internalNavigate(typeof navigateOptions === 'string' ? { url: navigateOptions } : navigateOptions, {});
233
+ return this.internalNavigate(makeNavigateOptions(navigateOptions), {});
225
234
  }
226
- async internalNavigate(navigateOptions, { history }) {
235
+ async internalNavigate(navigateOptions, { history, redirect }) {
227
236
  var _a;
228
237
  const { url, replace, params, navigateState, code } = navigateOptions;
229
- const prevNavigation = (_a = this.currentNavigation) !== null && _a !== void 0 ? _a : this.lastNavigation;
238
+ const prevNavigation = redirect
239
+ ? this.lastNavigation
240
+ : (_a = this.currentNavigation) !== null && _a !== void 0 ? _a : this.lastNavigation;
230
241
  if (!url && !prevNavigation) {
231
242
  throw new Error('Navigate url should be specified and cannot be omitted for first navigation');
232
243
  }
233
244
  const resolvedUrl = this.resolveUrl(navigateOptions);
234
245
  const { to: from, url: fromUrl } = prevNavigation !== null && prevNavigation !== void 0 ? prevNavigation : {};
246
+ const redirectFrom = redirect ? this.currentNavigation.to : undefined;
235
247
  let navigation = {
236
248
  type: 'navigate',
237
249
  from,
@@ -241,6 +253,8 @@ class AbstractRouter {
241
253
  history,
242
254
  navigateState,
243
255
  code,
256
+ redirect,
257
+ redirectFrom,
244
258
  };
245
259
  await this.runHooks('beforeResolve', navigation);
246
260
  const to = this.resolveRoute({ url: resolvedUrl, params, navigateState }, { wildcard: true });
@@ -1022,7 +1036,12 @@ class ClientRouter extends AbstractRouter {
1022
1036
  }
1023
1037
  async redirect(navigation, target) {
1024
1038
  await super.redirect(navigation, target);
1025
- return this.navigate({ ...target, replace: target.replace || navigation.replace });
1039
+ return this.internalNavigate({
1040
+ ...target,
1041
+ replace: target.replace || navigation.replace,
1042
+ }, {
1043
+ redirect: true,
1044
+ });
1026
1045
  }
1027
1046
  }
1028
1047
 
@@ -1044,17 +1063,10 @@ const RouterContext = createContext(null);
1044
1063
  const RouteContext = createContext(null);
1045
1064
  const UrlContext = createContext(null);
1046
1065
 
1047
- const Provider = ({ router, children }) => {
1048
- const [state, setState] = useState({
1049
- route: router.getCurrentRoute(),
1050
- url: router.getCurrentUrl(),
1051
- });
1052
- useIsomorphicLayoutEffect(() => {
1053
- return router.registerSyncHook('change', ({ to, url }) => {
1054
- setState({ route: to, url });
1055
- });
1056
- }, [router]);
1057
- return (jsx(RouterContext.Provider, Object.assign({ value: router }, { children: jsx(RouteContext.Provider, Object.assign({ value: state.route }, { children: jsx(UrlContext.Provider, Object.assign({ value: state.url }, { children: children }), void 0) }), void 0) }), void 0));
1066
+ const Provider = ({ router, serverState, children }) => {
1067
+ const route = useSyncExternalStore((cb) => router.registerSyncHook('change', cb), () => router.getLastRoute(), serverState ? () => serverState.currentRoute : () => router.getLastRoute());
1068
+ const url = useSyncExternalStore((cb) => router.registerSyncHook('change', cb), () => router.getLastUrl(), serverState ? () => serverState.currentUrl : () => router.getLastUrl());
1069
+ return (jsx(RouterContext.Provider, Object.assign({ value: router }, { children: jsx(RouteContext.Provider, Object.assign({ value: route }, { children: jsx(UrlContext.Provider, Object.assign({ value: url }, { children: children }), void 0) }), void 0) }), void 0));
1058
1070
  };
1059
1071
  Provider.displayName = 'Provider';
1060
1072
 
package/lib/index.js CHANGED
@@ -12,6 +12,7 @@ var each = require('@tinkoff/utils/array/each');
12
12
  var find = require('@tinkoff/utils/array/find');
13
13
  var findIndex = require('@tinkoff/utils/array/findIndex');
14
14
  var jsxRuntime = require('react/jsx-runtime');
15
+ var shim = require('use-sync-external-store/shim');
15
16
  var react = require('react');
16
17
  var reactHooks = require('@tinkoff/react-hooks');
17
18
 
@@ -188,6 +189,14 @@ class AbstractRouter {
188
189
  // same as getCurrentRoute
189
190
  return (_b = (_a = this.currentNavigation) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : (_c = this.lastNavigation) === null || _c === void 0 ? void 0 : _c.url;
190
191
  }
192
+ getLastRoute() {
193
+ var _a;
194
+ return (_a = this.lastNavigation) === null || _a === void 0 ? void 0 : _a.to;
195
+ }
196
+ getLastUrl() {
197
+ var _a;
198
+ return (_a = this.lastNavigation) === null || _a === void 0 ? void 0 : _a.url;
199
+ }
191
200
  commitNavigation(navigation) {
192
201
  exports.logger.debug({
193
202
  event: 'commit-navigation',
@@ -197,9 +206,9 @@ class AbstractRouter {
197
206
  // in case we came from history do not history back to prevent infinity recursive calls
198
207
  this.history.save(navigation);
199
208
  }
200
- this.runSyncHooks('change', navigation);
201
209
  this.lastNavigation = navigation;
202
210
  this.currentNavigation = null;
211
+ this.runSyncHooks('change', navigation);
203
212
  }
204
213
  async updateCurrentRoute(updateRouteOptions) {
205
214
  return this.internalUpdateCurrentRoute(updateRouteOptions, {});
@@ -236,17 +245,20 @@ class AbstractRouter {
236
245
  await this.runHooks('afterUpdateCurrent', navigation);
237
246
  }
238
247
  async navigate(navigateOptions) {
239
- return this.internalNavigate(typeof navigateOptions === 'string' ? { url: navigateOptions } : navigateOptions, {});
248
+ return this.internalNavigate(makeNavigateOptions(navigateOptions), {});
240
249
  }
241
- async internalNavigate(navigateOptions, { history }) {
250
+ async internalNavigate(navigateOptions, { history, redirect }) {
242
251
  var _a;
243
252
  const { url, replace, params, navigateState, code } = navigateOptions;
244
- const prevNavigation = (_a = this.currentNavigation) !== null && _a !== void 0 ? _a : this.lastNavigation;
253
+ const prevNavigation = redirect
254
+ ? this.lastNavigation
255
+ : (_a = this.currentNavigation) !== null && _a !== void 0 ? _a : this.lastNavigation;
245
256
  if (!url && !prevNavigation) {
246
257
  throw new Error('Navigate url should be specified and cannot be omitted for first navigation');
247
258
  }
248
259
  const resolvedUrl = this.resolveUrl(navigateOptions);
249
260
  const { to: from, url: fromUrl } = prevNavigation !== null && prevNavigation !== void 0 ? prevNavigation : {};
261
+ const redirectFrom = redirect ? this.currentNavigation.to : undefined;
250
262
  let navigation = {
251
263
  type: 'navigate',
252
264
  from,
@@ -256,6 +268,8 @@ class AbstractRouter {
256
268
  history,
257
269
  navigateState,
258
270
  code,
271
+ redirect,
272
+ redirectFrom,
259
273
  };
260
274
  await this.runHooks('beforeResolve', navigation);
261
275
  const to = this.resolveRoute({ url: resolvedUrl, params, navigateState }, { wildcard: true });
@@ -1037,7 +1051,12 @@ class ClientRouter extends AbstractRouter {
1037
1051
  }
1038
1052
  async redirect(navigation, target) {
1039
1053
  await super.redirect(navigation, target);
1040
- return this.navigate({ ...target, replace: target.replace || navigation.replace });
1054
+ return this.internalNavigate({
1055
+ ...target,
1056
+ replace: target.replace || navigation.replace,
1057
+ }, {
1058
+ redirect: true,
1059
+ });
1041
1060
  }
1042
1061
  }
1043
1062
 
@@ -1059,17 +1078,10 @@ const RouterContext = react.createContext(null);
1059
1078
  const RouteContext = react.createContext(null);
1060
1079
  const UrlContext = react.createContext(null);
1061
1080
 
1062
- const Provider = ({ router, children }) => {
1063
- const [state, setState] = react.useState({
1064
- route: router.getCurrentRoute(),
1065
- url: router.getCurrentUrl(),
1066
- });
1067
- reactHooks.useIsomorphicLayoutEffect(() => {
1068
- return router.registerSyncHook('change', ({ to, url }) => {
1069
- setState({ route: to, url });
1070
- });
1071
- }, [router]);
1072
- return (jsxRuntime.jsx(RouterContext.Provider, Object.assign({ value: router }, { children: jsxRuntime.jsx(RouteContext.Provider, Object.assign({ value: state.route }, { children: jsxRuntime.jsx(UrlContext.Provider, Object.assign({ value: state.url }, { children: children }), void 0) }), void 0) }), void 0));
1081
+ const Provider = ({ router, serverState, children }) => {
1082
+ const route = shim.useSyncExternalStore((cb) => router.registerSyncHook('change', cb), () => router.getLastRoute(), serverState ? () => serverState.currentRoute : () => router.getLastRoute());
1083
+ const url = shim.useSyncExternalStore((cb) => router.registerSyncHook('change', cb), () => router.getLastUrl(), serverState ? () => serverState.currentUrl : () => router.getLastUrl());
1084
+ return (jsxRuntime.jsx(RouterContext.Provider, Object.assign({ value: router }, { children: jsxRuntime.jsx(RouteContext.Provider, Object.assign({ value: route }, { children: jsxRuntime.jsx(UrlContext.Provider, Object.assign({ value: url }, { children: children }), void 0) }), void 0) }), void 0));
1073
1085
  };
1074
1086
  Provider.displayName = 'Provider';
1075
1087
 
@@ -20,6 +20,7 @@ export interface Options {
20
20
  }
21
21
  interface InternalOptions {
22
22
  history?: boolean;
23
+ redirect?: boolean;
23
24
  }
24
25
  export declare abstract class AbstractRouter {
25
26
  protected started: boolean;
@@ -40,12 +41,14 @@ export declare abstract class AbstractRouter {
40
41
  start(): Promise<void>;
41
42
  getCurrentRoute(): import("../types").NavigationRoute;
42
43
  getCurrentUrl(): Url;
44
+ getLastRoute(): import("../types").NavigationRoute;
45
+ getLastUrl(): Url;
43
46
  protected commitNavigation(navigation: Navigation): void;
44
47
  updateCurrentRoute(updateRouteOptions: UpdateCurrentRouteOptions): Promise<void>;
45
48
  protected internalUpdateCurrentRoute(updateRouteOptions: UpdateCurrentRouteOptions, { history }: InternalOptions): Promise<void>;
46
49
  protected runUpdateCurrentRoute(navigation: Navigation): Promise<void>;
47
50
  navigate(navigateOptions: NavigateOptions | string): Promise<void>;
48
- protected internalNavigate(navigateOptions: NavigateOptions, { history }: InternalOptions): Promise<void>;
51
+ protected internalNavigate(navigateOptions: NavigateOptions, { history, redirect }: InternalOptions): Promise<void>;
49
52
  protected runNavigate(navigation: Navigation): Promise<void>;
50
53
  protected run(navigation: Navigation): Promise<void>;
51
54
  resolve(resolveOptions: NavigateOptions | string, options?: Parameters<AbstractRouter['resolveRoute']>[1]): import("../types").NavigationRoute;
package/lib/types.d.ts CHANGED
@@ -40,6 +40,8 @@ export interface Navigation {
40
40
  history?: boolean;
41
41
  cancelled?: boolean;
42
42
  code?: number;
43
+ redirect?: boolean;
44
+ redirectFrom?: NavigationRoute;
43
45
  }
44
46
  export declare type NavigationGuard = (navigation: Navigation) => Promise<void | NavigateOptions | string | boolean>;
45
47
  export declare type NavigationHook = (navigation: Navigation) => Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinkoff/router",
3
- "version": "0.1.77",
3
+ "version": "0.1.80",
4
4
  "description": "router",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
@@ -22,9 +22,10 @@
22
22
  "build-for-publish": "true"
23
23
  },
24
24
  "dependencies": {
25
- "@tinkoff/react-hooks": "0.0.26",
26
- "@tinkoff/url": "0.7.38",
27
- "@tinkoff/utils": "^2.1.2"
25
+ "@tinkoff/react-hooks": "0.0.27",
26
+ "@tinkoff/url": "0.7.39",
27
+ "@tinkoff/utils": "^2.1.2",
28
+ "use-sync-external-store": "^1.2.0"
28
29
  },
29
30
  "peerDependencies": {
30
31
  "react": ">=16.14.0",
@@ -32,6 +33,7 @@
32
33
  },
33
34
  "sideEffects": false,
34
35
  "devDependencies": {
36
+ "@types/use-sync-external-store": "^0.0.3",
35
37
  "object-sizeof": "^1.6.1"
36
38
  },
37
39
  "license": "Apache-2.0"