@tramvai/module-router 2.70.1 → 2.72.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.
Files changed (91) hide show
  1. package/lib/components/link.browser.js +55 -0
  2. package/lib/components/link.es.js +49 -0
  3. package/lib/components/link.js +53 -0
  4. package/lib/hooks/usePageService.browser.js +10 -0
  5. package/lib/hooks/usePageService.es.js +10 -0
  6. package/lib/hooks/usePageService.js +14 -0
  7. package/lib/hooks/usePrefetch.browser.browser.js +42 -0
  8. package/lib/index.browser.js +7 -918
  9. package/lib/index.es.js +6 -764
  10. package/lib/index.js +34 -788
  11. package/lib/modules/child-app.browser.js +31 -0
  12. package/lib/modules/child-app.es.js +31 -0
  13. package/lib/modules/child-app.js +33 -0
  14. package/lib/modules/clientCommon.browser.js +70 -0
  15. package/lib/modules/common.browser.js +137 -0
  16. package/lib/modules/common.es.js +137 -0
  17. package/lib/modules/common.js +145 -0
  18. package/lib/modules/fileSystemPages.browser.js +27 -0
  19. package/lib/modules/fileSystemPages.es.js +27 -0
  20. package/lib/modules/fileSystemPages.js +31 -0
  21. package/lib/modules/guards/common/index.browser.js +32 -0
  22. package/lib/modules/guards/common/index.es.js +32 -0
  23. package/lib/modules/guards/common/index.js +36 -0
  24. package/lib/modules/guards/common/internalError.browser.js +14 -0
  25. package/lib/modules/guards/common/internalError.es.js +14 -0
  26. package/lib/modules/guards/common/internalError.js +18 -0
  27. package/lib/modules/guards/common/loadBundle.browser.js +67 -0
  28. package/lib/modules/guards/common/loadBundle.es.js +67 -0
  29. package/lib/modules/guards/common/loadBundle.js +75 -0
  30. package/lib/modules/guards/server/httpMethod.es.js +20 -0
  31. package/lib/modules/guards/server/httpMethod.js +24 -0
  32. package/lib/modules/guards/server/index.es.js +18 -0
  33. package/lib/modules/guards/server/index.js +22 -0
  34. package/lib/modules/hooks/common/fillRouterStore.browser.js +9 -0
  35. package/lib/modules/hooks/common/fillRouterStore.es.js +9 -0
  36. package/lib/modules/hooks/common/fillRouterStore.js +13 -0
  37. package/lib/modules/hooks/common/index.browser.js +16 -0
  38. package/lib/modules/hooks/common/index.es.js +16 -0
  39. package/lib/modules/hooks/common/index.js +20 -0
  40. package/lib/modules/hooks/runActions.browser.js +34 -0
  41. package/lib/modules/hooks/runActions.es.js +34 -0
  42. package/lib/modules/hooks/runActions.js +42 -0
  43. package/lib/modules/hooks/server/httpStatus.es.js +7 -0
  44. package/lib/modules/hooks/server/httpStatus.js +11 -0
  45. package/lib/modules/hooks/server/index.es.js +17 -0
  46. package/lib/modules/hooks/server/index.js +21 -0
  47. package/lib/modules/hooks/spa/index.browser.js +70 -0
  48. package/lib/modules/hooks/spa/runCommands.browser.js +12 -0
  49. package/lib/modules/no-spa.browser.js +21 -0
  50. package/lib/modules/server.es.js +59 -0
  51. package/lib/modules/server.js +63 -0
  52. package/lib/modules/spa.browser.js +23 -0
  53. package/lib/modules/tokens/browser/index.browser.js +5 -0
  54. package/lib/modules/tokens/browser/prefetch.browser.js +112 -0
  55. package/lib/modules/tokens/common/index.browser.js +40 -0
  56. package/lib/modules/tokens/common/index.es.js +40 -0
  57. package/lib/modules/tokens/common/index.js +44 -0
  58. package/lib/modules/tokens/common/render.browser.js +14 -0
  59. package/lib/modules/tokens/common/render.es.js +14 -0
  60. package/lib/modules/tokens/common/render.js +18 -0
  61. package/lib/modules/tokens/common/routeNormalize.browser.js +10 -0
  62. package/lib/modules/tokens/common/routeNormalize.es.js +10 -0
  63. package/lib/modules/tokens/common/routeNormalize.js +18 -0
  64. package/lib/modules/tokens/common/routeSetDefaults.browser.js +13 -0
  65. package/lib/modules/tokens/common/routeSetDefaults.es.js +13 -0
  66. package/lib/modules/tokens/common/routeSetDefaults.js +17 -0
  67. package/lib/modules/tokens/common/routeTransform.browser.js +12 -0
  68. package/lib/modules/tokens/common/routeTransform.es.js +12 -0
  69. package/lib/modules/tokens/common/routeTransform.js +22 -0
  70. package/lib/modules/tokens/server/bundleInfo.es.js +35 -0
  71. package/lib/modules/tokens/server/bundleInfo.js +44 -0
  72. package/lib/modules/tokens/server/index.es.js +25 -0
  73. package/lib/modules/tokens/server/index.js +29 -0
  74. package/lib/modules/tokens/server/prefetch.es.js +15 -0
  75. package/lib/modules/tokens/server/prefetch.js +19 -0
  76. package/lib/modules/tokens/server/routerOptions.es.js +37 -0
  77. package/lib/modules/tokens/server/routerOptions.js +41 -0
  78. package/lib/modules/tokens.browser.js +22 -0
  79. package/lib/modules/tokens.es.js +22 -0
  80. package/lib/modules/tokens.js +35 -0
  81. package/lib/modules/utils/forRoot.browser.js +22 -0
  82. package/lib/modules/utils/forRoot.es.js +22 -0
  83. package/lib/modules/utils/forRoot.js +26 -0
  84. package/lib/services/page.browser.js +63 -0
  85. package/lib/services/page.es.js +63 -0
  86. package/lib/services/page.js +67 -0
  87. package/lib/stores/RouterStore.browser.js +29 -0
  88. package/lib/stores/RouterStore.es.js +29 -0
  89. package/lib/stores/RouterStore.js +35 -0
  90. package/lib/utils/requestIdleCallback.browser.js +10 -0
  91. package/package.json +22 -23
@@ -1,920 +1,9 @@
1
- import { provide, commandLineListTokens, createToken as createToken$1, Module, DI_TOKEN, COMMAND_LINE_RUNNER_TOKEN } from '@tramvai/core';
2
- import { Provider, setLogger, NoSpaRouter, Router, useNavigate, useRoute } from '@tinkoff/router';
1
+ export { NoSpaRouterModule } from './modules/no-spa.browser.js';
2
+ export { SpaRouterModule } from './modules/spa.browser.js';
3
+ export { RouterChildAppModule } from './modules/child-app.browser.js';
3
4
  export { Provider, useNavigate, useRoute, useRouter, useUrl } from '@tinkoff/router';
4
- import noop from '@tinkoff/utils/function/noop';
5
- import { LOGGER_TOKEN, BUNDLE_MANAGER_TOKEN, ACTION_REGISTRY_TOKEN, RESPONSE_MANAGER_TOKEN, DISPATCHER_TOKEN, DISPATCHER_CONTEXT_TOKEN, CONTEXT_TOKEN, COMPONENT_REGISTRY_TOKEN, COMBINE_REDUCERS, STORE_TOKEN, ACTION_PAGE_RUNNER_TOKEN } from '@tramvai/tokens-common';
6
- import { ROUTER_GUARD_TOKEN, PAGE_SERVICE_TOKEN, ROUTE_TRANSFORM_TOKEN, ROUTER_TOKEN, ROUTES_TOKEN, ROUTE_RESOLVE_TOKEN, ROUTER_SPA_ACTIONS_RUN_MODE_TOKEN, LINK_PREFETCH_MANAGER_TOKEN } from '@tramvai/tokens-router';
5
+ export { Link } from './components/link.browser.js';
7
6
  export * from '@tramvai/tokens-router';
8
- import { EXTEND_RENDER, TRAMVAI_RENDER_MODE } from '@tramvai/tokens-render';
9
- import { createEvent, createReducer } from '@tramvai/state';
10
- import flatten from '@tinkoff/utils/array/flatten';
11
- import { createToken, optional } from '@tinkoff/dippy';
12
- import isArray from '@tinkoff/utils/is/array';
13
- import { resolveLazyComponent, useDi } from '@tramvai/react';
14
- import identity from '@tinkoff/utils/function/identity';
15
- import compose from '@tinkoff/utils/function/compose';
16
- import replace from '@tinkoff/utils/string/replace';
17
- import { jsx } from 'react/jsx-runtime';
18
- import { isFileSystemPageComponent, fileSystemPagesEnabled, getStaticFileSystemPages, fileSystemPageToRoute } from '@tramvai/experiments';
19
- import { addQuery } from '@tinkoff/url';
20
- import uniq from '@tinkoff/utils/array/uniq';
21
- import { isRedirectFoundError, isNotFoundError } from '@tinkoff/errors';
22
- import { __decorate } from 'tslib';
23
- import { CHILD_APP_INTERNAL_ROOT_STATE_ALLOWED_STORE_TOKEN } from '@tramvai/tokens-child-app';
24
- import { useEffect, useState, useCallback, isValidElement, cloneElement } from 'react';
25
-
26
- const setCurrentNavigation = createEvent('SET_CURRENT_ROUTE');
27
- const setUrlOnRehydrate = createEvent('SET_URL_ON_REHYDRATE');
28
- const initialState = {
29
- currentRoute: null,
30
- previousRoute: null,
31
- currentUrl: null,
32
- previousUrl: null,
33
- };
34
- const RouterStore = createReducer('router', initialState)
35
- .on(setCurrentNavigation, (state, { to, from, url, fromUrl, replace }) => {
36
- // TODO: убедиться что все данные нам нужны из роутинга,
37
- // возможно всякие meta, content не нужны при дегидрации
38
- return {
39
- currentRoute: to,
40
- previousRoute: replace ? state.previousRoute : from,
41
- currentUrl: url,
42
- previousUrl: replace ? state.previousUrl : fromUrl,
43
- };
44
- })
45
- .on(setUrlOnRehydrate, (state, url) => {
46
- return {
47
- ...state,
48
- currentUrl: url,
49
- };
50
- });
51
-
52
- const routerClassToken = createToken('router routerClassToken');
53
- const additionalRouterParameters = createToken('router additionalParameters');
54
- const onChangeHooksToken = createToken('router onChangeHooks', {
55
- multi: true,
56
- });
57
- const beforeResolveHooksToken = createToken('router beforeResolveHooks', {
58
- multi: true,
59
- });
60
- const beforeNavigateHooksToken = createToken('router beforeNavigateHooks', {
61
- multi: true,
62
- });
63
- const afterNavigateHooksToken = createToken('router afterNavigateHooks', {
64
- multi: true,
65
- });
66
- const beforeUpdateCurrentHooksToken = createToken('router beforeUpdateCurrentHooks', { multi: true });
67
- const afterUpdateCurrentHooksToken = createToken('router afterUpdateCurrentHooks', { multi: true });
68
- const routeTransformToken = createToken('router finalRouteTransform');
69
- createToken('router bundleInfoAdditional');
70
-
71
- const loadBundle = ({ bundleManager, logger, actionRegistry, responseManager, dispatcher, dispatcherContext, pageService, }) => {
72
- const log = logger('route:load-bundles');
73
- return async ({ to }) => {
74
- var _a;
75
- log.debug({
76
- event: 'load-bundle',
77
- route: to,
78
- });
79
- const { bundle, pageComponent, nestedLayoutComponent } = to.config;
80
- if (!bundleManager.has(bundle, pageComponent)) {
81
- log.info({
82
- event: 'load-bundle-not-found',
83
- bundle,
84
- pageComponent,
85
- });
86
- // если бандл не найден, то всё ок и мы должны вернуть 404 на сервере,
87
- // а на клиенте просто загрузить новую страницу
88
- responseManager.setStatus(404);
89
- return false;
90
- }
91
- // Пытаемся загрузить и в случае инициализации на клиенте, т.к.
92
- // несмотря на наличие бандла в разметке, все равно мы должны дождаться
93
- // его загрузки перед рендером
94
- try {
95
- const promises = [];
96
- // for file-system pages bundle is virtual, so it will be loaded in parallel with nested layout
97
- // for classic bundles, page component will be loaded after bundle loading
98
- promises.push(bundleManager.get(bundle, pageComponent));
99
- if (nestedLayoutComponent) {
100
- promises.push(resolveLazyComponent(pageService.resolveComponentFromConfig('nestedLayout')));
101
- }
102
- const [resolvedBundle, resolvedLayout] = await Promise.all(promises);
103
- const { components, reducers } = resolvedBundle;
104
- const component = components[pageComponent];
105
- // pageComponent should have required fields even if it is lazy,
106
- // thanks to `git log 9466cb32bfb71ba49144ef839d3d5bce246e213c -L90,103:packages/modules/common/src/bundleManager/bundleManager.ts`
107
- (_a = component === null || component === void 0 ? void 0 : component.reducers) === null || _a === void 0 ? void 0 : _a.forEach((reducer) => dispatcherContext.getStore(reducer));
108
- reducers === null || reducers === void 0 ? void 0 : reducers.forEach((reducer) => dispatcherContext.getStore(reducer));
109
- // @todo: reuse logic from bundleManager?
110
- // register nested layout actions and reducers for current page
111
- if (resolvedLayout) {
112
- if ('actions' in resolvedLayout && isArray(resolvedLayout.actions)) {
113
- actionRegistry.add(pageComponent, resolvedLayout.actions);
114
- }
115
- if ('reducers' in resolvedLayout && isArray(resolvedLayout.reducers)) {
116
- resolvedLayout.reducers.forEach((reducer) => {
117
- dispatcher.registerStore(reducer);
118
- dispatcherContext.getStore(reducer);
119
- });
120
- }
121
- }
122
- }
123
- catch (error) {
124
- log.error({
125
- event: 'load-bundle-failed',
126
- error,
127
- });
128
- // блокируем переход если не удалось загрузить бандл
129
- return false;
130
- }
131
- };
132
- };
133
-
134
- const internalError = ({ responseManager, logger, }) => {
135
- const log = logger('route:guard-check-error');
136
- return async () => {
137
- if (responseManager.getStatus() >= 500) {
138
- log.error({
139
- event: 'status-is-error',
140
- status: responseManager.getStatus(),
141
- });
142
- return false;
143
- }
144
- };
145
- };
146
-
147
- const commonGuards = [
148
- {
149
- provide: ROUTER_GUARD_TOKEN,
150
- multi: true,
151
- useFactory: loadBundle,
152
- deps: {
153
- logger: LOGGER_TOKEN,
154
- bundleManager: BUNDLE_MANAGER_TOKEN,
155
- actionRegistry: ACTION_REGISTRY_TOKEN,
156
- responseManager: RESPONSE_MANAGER_TOKEN,
157
- dispatcher: DISPATCHER_TOKEN,
158
- dispatcherContext: DISPATCHER_CONTEXT_TOKEN,
159
- pageService: PAGE_SERVICE_TOKEN,
160
- },
161
- },
162
- {
163
- provide: ROUTER_GUARD_TOKEN,
164
- multi: true,
165
- useFactory: internalError,
166
- deps: {
167
- logger: LOGGER_TOKEN,
168
- responseManager: RESPONSE_MANAGER_TOKEN,
169
- },
170
- },
171
- ];
172
-
173
- const fillRouterStore = ({ context, }) => {
174
- return (navigation) => {
175
- context.dispatch(setCurrentNavigation(navigation));
176
- };
177
- };
178
-
179
- const commonHooks = [
180
- {
181
- provide: onChangeHooksToken,
182
- multi: true,
183
- useFactory: fillRouterStore,
184
- deps: {
185
- context: CONTEXT_TOKEN,
186
- },
187
- },
188
- ];
189
-
190
- const routeTransform = ({ transformers, }) => {
191
- if (!transformers || transformers.length === 0) {
192
- return identity;
193
- }
194
- return compose(...flatten(transformers));
195
- };
196
-
197
- const routeSetDefaults = (route) => {
198
- const config = {
199
- bundle: 'mainDefault',
200
- pageComponent: 'pageDefault',
201
- ...route.config,
202
- };
203
- return {
204
- ...route,
205
- config,
206
- };
207
- };
208
-
209
- const routeNormalize = (route) => {
210
- return {
211
- ...route,
212
- path: replace(/\/?$/, '/', route.path),
213
- };
214
- };
215
-
216
- const provideRouter = ({ router }) => {
217
- const serverState = {
218
- currentRoute: router.getCurrentRoute(),
219
- currentUrl: router.getCurrentUrl(),
220
- };
221
- return (render) => {
222
- return (jsx(Provider, { router: router, serverState: serverState, children: render }));
223
- };
224
- };
225
-
226
- const commonTokens = [
227
- {
228
- provide: routeTransformToken,
229
- useFactory: routeTransform,
230
- deps: {
231
- transformers: {
232
- token: ROUTE_TRANSFORM_TOKEN,
233
- optional: true,
234
- },
235
- },
236
- },
237
- {
238
- provide: ROUTE_TRANSFORM_TOKEN,
239
- multi: true,
240
- useValue: routeSetDefaults,
241
- },
242
- {
243
- provide: ROUTE_TRANSFORM_TOKEN,
244
- multi: true,
245
- useValue: routeNormalize,
246
- },
247
- {
248
- provide: EXTEND_RENDER,
249
- multi: true,
250
- useFactory: provideRouter,
251
- deps: {
252
- router: ROUTER_TOKEN,
253
- },
254
- },
255
- ];
256
-
257
- class PageService {
258
- constructor({ router, componentRegistry }) {
259
- this.router = router;
260
- this.componentRegistry = componentRegistry;
261
- }
262
- getCurrentRoute() {
263
- return this.router.getCurrentRoute();
264
- }
265
- getCurrentUrl() {
266
- return this.router.getCurrentUrl();
267
- }
268
- getConfig() {
269
- var _a, _b;
270
- return (_b = (_a = this.getCurrentRoute()) === null || _a === void 0 ? void 0 : _a.config) !== null && _b !== void 0 ? _b : {};
271
- }
272
- getContent() {
273
- var _a;
274
- return (_a = this.getConfig().content) !== null && _a !== void 0 ? _a : {};
275
- }
276
- getMeta() {
277
- var _a;
278
- return (_a = this.getConfig().meta) !== null && _a !== void 0 ? _a : {};
279
- }
280
- navigate(options) {
281
- return this.router.navigate(options);
282
- }
283
- updateCurrentRoute(options) {
284
- return this.router.updateCurrentRoute(options);
285
- }
286
- back(options) {
287
- return this.router.back(options);
288
- }
289
- forward() {
290
- return this.router.forward();
291
- }
292
- go(to, options) {
293
- return this.router.go(to, options);
294
- }
295
- addComponent(name, component) {
296
- const group = this.getComponentsGroupName();
297
- return this.componentRegistry.add(name, component, group);
298
- }
299
- getComponent(name) {
300
- const group = this.getComponentsGroupName();
301
- return this.componentRegistry.get(name, group);
302
- }
303
- resolveComponentFromConfig(property) {
304
- var _a;
305
- const configName = `${property}Component`;
306
- const defaultComponent = `${property}Default`;
307
- const componentName = (_a = this.getConfig()[configName]) !== null && _a !== void 0 ? _a : defaultComponent;
308
- return this.getComponent(componentName) || this.getComponent(defaultComponent);
309
- }
310
- getComponentsGroupName() {
311
- const { bundle, pageComponent } = this.getConfig();
312
- const group = isFileSystemPageComponent(pageComponent) ? pageComponent : bundle;
313
- return group;
314
- }
315
- }
316
-
317
- const providers$2 = [
318
- fileSystemPagesEnabled() &&
319
- provide({
320
- provide: ROUTES_TOKEN,
321
- multi: true,
322
- useFactory: ({ logger }) => {
323
- const log = logger('file-system-pages:route');
324
- const pagesNames = Object.keys(getStaticFileSystemPages());
325
- const routes = pagesNames.map(fileSystemPageToRoute);
326
- log.debug({
327
- event: 'create static routes from file-system pages',
328
- routes,
329
- });
330
- return routes;
331
- },
332
- deps: {
333
- logger: LOGGER_TOKEN,
334
- },
335
- }),
336
- ].filter(Boolean);
337
-
338
- const providers$1 = [
339
- ...commonGuards,
340
- ...commonHooks,
341
- ...commonTokens,
342
- ...providers$2,
343
- {
344
- provide: ROUTER_TOKEN,
345
- useFactory: ({ RouterClass, routes, routeTransform, routeResolve, additionalParameters, }) => {
346
- const router = new RouterClass({
347
- ...additionalParameters,
348
- trailingSlash: true,
349
- mergeSlashes: true,
350
- routes: flatten(routes !== null && routes !== void 0 ? routes : []).map(routeTransform),
351
- });
352
- if (routeResolve) {
353
- router.registerHook('beforeResolve', async (navigation) => {
354
- if (!router.resolve(navigation.url.href)) {
355
- const route = await routeResolve(navigation);
356
- if (route) {
357
- router.addRoute(routeTransform(route));
358
- }
359
- }
360
- });
361
- }
362
- return router;
363
- },
364
- deps: {
365
- RouterClass: routerClassToken,
366
- additionalParameters: {
367
- token: additionalRouterParameters,
368
- optional: true,
369
- },
370
- routes: {
371
- token: ROUTES_TOKEN,
372
- optional: true,
373
- },
374
- routeTransform: routeTransformToken,
375
- routeResolve: {
376
- token: ROUTE_RESOLVE_TOKEN,
377
- optional: true,
378
- },
379
- },
380
- },
381
- {
382
- provide: commandLineListTokens.customerStart,
383
- multi: true,
384
- useFactory: ({ router, guards, onChange, beforeResolve, beforeNavigate, afterNavigate, beforeUpdateCurrent, afterUpdateCurrent, }) => {
385
- return function routerInit() {
386
- flatten(guards !== null && guards !== void 0 ? guards : []).forEach((guard) => router.registerGuard(guard));
387
- flatten(onChange !== null && onChange !== void 0 ? onChange : []).forEach((hook) => router.registerSyncHook('change', hook));
388
- flatten(beforeResolve !== null && beforeResolve !== void 0 ? beforeResolve : []).forEach((hook) => router.registerHook('beforeResolve', hook));
389
- flatten(beforeNavigate !== null && beforeNavigate !== void 0 ? beforeNavigate : []).forEach((hook) => router.registerHook('beforeNavigate', hook));
390
- flatten(afterNavigate !== null && afterNavigate !== void 0 ? afterNavigate : []).forEach((hook) => router.registerHook('afterNavigate', hook));
391
- flatten(beforeUpdateCurrent !== null && beforeUpdateCurrent !== void 0 ? beforeUpdateCurrent : []).forEach((hook) => router.registerHook('beforeUpdateCurrent', hook));
392
- flatten(afterUpdateCurrent !== null && afterUpdateCurrent !== void 0 ? afterUpdateCurrent : []).forEach((hook) => router.registerHook('afterUpdateCurrent', hook));
393
- };
394
- },
395
- deps: {
396
- router: ROUTER_TOKEN,
397
- guards: {
398
- token: ROUTER_GUARD_TOKEN,
399
- optional: true,
400
- },
401
- onChange: {
402
- token: onChangeHooksToken,
403
- optional: true,
404
- },
405
- beforeResolve: {
406
- token: beforeResolveHooksToken,
407
- optional: true,
408
- },
409
- beforeNavigate: {
410
- token: beforeNavigateHooksToken,
411
- optional: true,
412
- },
413
- afterNavigate: {
414
- token: afterNavigateHooksToken,
415
- optional: true,
416
- },
417
- beforeUpdateCurrent: {
418
- token: beforeUpdateCurrentHooksToken,
419
- optional: true,
420
- },
421
- afterUpdateCurrent: {
422
- token: afterUpdateCurrentHooksToken,
423
- optional: true,
424
- },
425
- },
426
- },
427
- {
428
- provide: commandLineListTokens.init,
429
- multi: true,
430
- useFactory: ({ logger }) => {
431
- return function routerSetLogger() {
432
- setLogger(logger('route:router'));
433
- };
434
- },
435
- deps: {
436
- logger: LOGGER_TOKEN,
437
- },
438
- },
439
- {
440
- provide: ROUTER_SPA_ACTIONS_RUN_MODE_TOKEN,
441
- // TODO: пока делаем after т.к. на это рассчитывает большинство приложений и у нас пока нет механизма контроля выполнения экшенов, чтобы
442
- // избежать случаев зависания перехода из-за долгих экшенов
443
- // рассмотреть возможность замены после доработок экшенов
444
- useValue: 'after',
445
- },
446
- provide({
447
- provide: PAGE_SERVICE_TOKEN,
448
- useClass: PageService,
449
- deps: {
450
- router: ROUTER_TOKEN,
451
- componentRegistry: COMPONENT_REGISTRY_TOKEN,
452
- },
453
- }),
454
- {
455
- provide: COMBINE_REDUCERS,
456
- multi: true,
457
- useValue: RouterStore,
458
- },
459
- ];
460
-
461
- const PREFETCHED_LINKS_CACHE_TOKEN = createToken$1();
462
- const PREFETCHED_LINKS_QUEUE_TOKEN = createToken$1();
463
- const prefetchProviders = [
464
- provide({
465
- provide: PREFETCHED_LINKS_CACHE_TOKEN,
466
- useValue: new Set(),
467
- }),
468
- provide({
469
- provide: PREFETCHED_LINKS_QUEUE_TOKEN,
470
- useFactory: () => {
471
- let queue = Promise.resolve();
472
- return {
473
- add(run) {
474
- queue = queue.then(run);
475
- return queue;
476
- },
477
- };
478
- },
479
- }),
480
- provide({
481
- provide: LINK_PREFETCH_MANAGER_TOKEN,
482
- useFactory: ({ router, routeTransform, routeResolve, componentRegistry, bundleManager, logger, prefetchedLinksCache, prefetchedLinksQueue, }) => {
483
- const log = logger('link-prefetch-manager');
484
- const prefetch = async (url) => {
485
- // prefetch needed for cache assets by browser, so we want to prefetch resources for any page only once.
486
- // cache will work only for SPA-router, but anyway resources loading deduplication logic already included in @loadable
487
- if (prefetchedLinksCache.has(url)) {
488
- return;
489
- }
490
- prefetchedLinksCache.add(url);
491
- log.info({ event: 'prefetch-route-init', url });
492
- // first, try to find static or resolved dynamic route
493
- let route = router.resolve(url);
494
- // if route not found, try to resolve dynamic route,
495
- // logic from `ROUTER_TOKEN` provider factory, without `router.addRoute` method call
496
- if (!route && routeResolve) {
497
- // add query `prefetchRoute` to indicate to any `routeResolve` implementation
498
- // that this route request no need to be cached or have any personalization
499
- const parsedUrl = addQuery(url, { prefetchRoute: 'true' });
500
- route = await routeResolve({
501
- url: parsedUrl,
502
- type: 'navigate',
503
- });
504
- if (route) {
505
- route = routeTransform(route);
506
- }
507
- }
508
- if (!route) {
509
- log.info({ event: 'prefetch-route-not-found', url });
510
- return;
511
- }
512
- // @todo: очередь запросов!
513
- // get name of everything what we need to prefetch,
514
- // @loadable will load all scripts and styles for us
515
- const { config: { pageComponent, bundle, nestedLayoutComponent }, } = route;
516
- const promises = [];
517
- // no need for bundle and page component actions amd reducers initialization,
518
- // so copy only part of `modules/guards/common/loadBundle.ts` logic,
519
- // all relative assets will be fetched
520
- if (bundleManager.has(bundle, pageComponent)) {
521
- promises.push(bundleManager.get(bundle, pageComponent));
522
- }
523
- // no need for nestedLayoutComponent actions amd reducers initialization,
524
- // so copy only part of `modules/guards/common/loadBundle.ts` logic,
525
- // all relative assets will be fetched
526
- if (nestedLayoutComponent) {
527
- promises.push(resolveLazyComponent(componentRegistry.get(nestedLayoutComponent, isFileSystemPageComponent(pageComponent) ? '__default' : bundle)));
528
- }
529
- await Promise.all(promises)
530
- .then(() => {
531
- log.info({ event: 'prefetch-route-success', url });
532
- })
533
- .catch((error) => {
534
- log.warn({
535
- event: 'prefetch-fail',
536
- url,
537
- error,
538
- });
539
- });
540
- };
541
- return {
542
- prefetch: (url) => {
543
- // fetch assets sequentially
544
- return prefetchedLinksQueue.add(() => prefetch(url));
545
- },
546
- };
547
- },
548
- deps: {
549
- router: ROUTER_TOKEN,
550
- routeTransform: routeTransformToken,
551
- routeResolve: {
552
- token: ROUTE_RESOLVE_TOKEN,
553
- optional: true,
554
- },
555
- componentRegistry: COMPONENT_REGISTRY_TOKEN,
556
- bundleManager: BUNDLE_MANAGER_TOKEN,
557
- logger: LOGGER_TOKEN,
558
- prefetchedLinksCache: PREFETCHED_LINKS_CACHE_TOKEN,
559
- prefetchedLinksQueue: PREFETCHED_LINKS_QUEUE_TOKEN,
560
- },
561
- }),
562
- ];
563
-
564
- const clientTokens = [...prefetchProviders];
565
-
566
- const stopRunAtError = (error) => {
567
- if (isNotFoundError(error) || isRedirectFoundError(error)) {
568
- return true;
569
- }
570
- };
571
- const runActionsFactory = ({ store, router, actionRegistry, actionPageRunner, }) => {
572
- return function runActions() {
573
- const route = router.getCurrentRoute();
574
- const { config: { bundle, pageComponent } = {} } = route;
575
- if (!bundle || !pageComponent) {
576
- throw new Error(`bundle and pageComponent should be defined, but got ${route}`);
577
- }
578
- const actions = uniq([
579
- ...(actionRegistry.getGlobal() || []),
580
- ...(actionRegistry.get(bundle) || []),
581
- ...(actionRegistry.get(pageComponent) || []),
582
- ]);
583
- return actionPageRunner.runActions(actions, stopRunAtError).catch((err) => {
584
- if (isRedirectFoundError(err)) {
585
- return router.navigate({
586
- url: err.nextUrl,
587
- replace: true,
588
- code: err.httpStatus,
589
- });
590
- }
591
- throw err.error || err;
592
- });
593
- };
594
- };
595
-
596
- const providers = [
597
- ...clientTokens,
598
- ...providers$1,
599
- provide({
600
- provide: commandLineListTokens.customerStart,
601
- multi: true,
602
- useFactory: ({ router, store }) => {
603
- return async function routerInit() {
604
- const currentRoute = store.getState(RouterStore).currentRoute;
605
- return router.rehydrate({
606
- to: currentRoute,
607
- });
608
- };
609
- },
610
- deps: {
611
- router: ROUTER_TOKEN,
612
- store: STORE_TOKEN,
613
- },
614
- }),
615
- provide({
616
- // после выполнения надо включить исполнение роутинга, чтобы регидрация рендера реакта прошла успешно
617
- // после этого уже можно будет делать переходы и т.д.
618
- provide: commandLineListTokens.clear,
619
- multi: true,
620
- useFactory: ({ router, store }) => {
621
- return function routerStart() {
622
- const routerState = store.getState().router;
623
- if (routerState.currentUrl.href !== window.location.href) {
624
- store.dispatch(setUrlOnRehydrate(router.getCurrentUrl()));
625
- }
626
- return router.start();
627
- };
628
- },
629
- deps: {
630
- router: ROUTER_TOKEN,
631
- store: STORE_TOKEN,
632
- },
633
- }),
634
- provide({
635
- // при первой загрузке страницы мы должны выполнить экшены уже после того произошла регидрация
636
- // рендера реакта, т.к. экшены могут повлиять на рендер
637
- provide: commandLineListTokens.clear,
638
- multi: true,
639
- useFactory: (deps) => {
640
- const currentRoute = deps.store.getState(RouterStore).currentRoute;
641
- // in client-side rendering mode, action will be executed on first navigation
642
- if (deps.renderMode === 'client' &&
643
- (!currentRoute || (currentRoute && currentRoute.actualPath !== window.location.pathname))) {
644
- return noop;
645
- }
646
- return runActionsFactory(deps);
647
- },
648
- deps: {
649
- store: STORE_TOKEN,
650
- router: ROUTER_TOKEN,
651
- actionRegistry: ACTION_REGISTRY_TOKEN,
652
- actionPageRunner: ACTION_PAGE_RUNNER_TOKEN,
653
- renderMode: TRAMVAI_RENDER_MODE,
654
- },
655
- }),
656
- provide({
657
- provide: commandLineListTokens.clear,
658
- multi: true,
659
- useFactory: ({ store, router, renderMode }) => {
660
- return async function csrFirstNavigation() {
661
- const currentRoute = store.getState(RouterStore).currentRoute;
662
- // in client-side rendering mode, if current route inconsistent with current location,
663
- // run navigation to current location.
664
- if (renderMode === 'client' &&
665
- (!currentRoute || (currentRoute && currentRoute.actualPath !== window.location.pathname))) {
666
- // replace because otherwice we will push in the history the same url twice,
667
- // and history.back will return to the same url
668
- return router.navigate({ url: window.location.href, replace: true });
669
- }
670
- };
671
- },
672
- deps: {
673
- store: STORE_TOKEN,
674
- router: ROUTER_TOKEN,
675
- renderMode: TRAMVAI_RENDER_MODE,
676
- },
677
- }),
678
- ];
679
-
680
- const generateForRoot = (mainModule) => {
681
- return (routes, options = {}) => {
682
- return {
683
- mainModule,
684
- providers: [
685
- {
686
- provide: ROUTES_TOKEN,
687
- multi: true,
688
- useValue: routes,
689
- },
690
- options.spaActionsMode && {
691
- provide: ROUTER_SPA_ACTIONS_RUN_MODE_TOKEN,
692
- useValue: options.spaActionsMode,
693
- },
694
- ].filter(Boolean),
695
- };
696
- };
697
- };
698
-
699
- var _a$1;
700
- const NoSpaRouterModule = /* @__PURE__ */ Module({
701
- providers: [
702
- ...providers,
703
- {
704
- provide: routerClassToken,
705
- useValue: NoSpaRouter,
706
- },
707
- ],
708
- })((_a$1 = class NoSpaRouterModule {
709
- },
710
- _a$1.forRoot = generateForRoot(_a$1),
711
- _a$1));
712
-
713
- const runCommandsSpa = ({ commandLineRunner, di, }) => {
714
- return async () => {
715
- await commandLineRunner.run('client', 'spa', [], di);
716
- };
717
- };
718
- const runCommandsAfterSpa = ({ commandLineRunner, di, }) => {
719
- return async () => {
720
- await commandLineRunner.run('client', 'afterSpa', [], di);
721
- };
722
- };
723
-
724
- const spaHooks = [
725
- {
726
- provide: beforeNavigateHooksToken,
727
- multi: true,
728
- useFactory: runCommandsSpa,
729
- deps: {
730
- di: DI_TOKEN,
731
- commandLineRunner: COMMAND_LINE_RUNNER_TOKEN,
732
- },
733
- },
734
- {
735
- provide: afterNavigateHooksToken,
736
- multi: true,
737
- useFactory: runCommandsAfterSpa,
738
- deps: {
739
- di: DI_TOKEN,
740
- commandLineRunner: COMMAND_LINE_RUNNER_TOKEN,
741
- },
742
- },
743
- {
744
- provide: commandLineListTokens.spaTransition,
745
- multi: true,
746
- useFactory: ({ spaMode, ...deps }) => {
747
- if (spaMode !== 'after') {
748
- return runActionsFactory(deps);
749
- }
750
- return noop;
751
- },
752
- deps: {
753
- store: STORE_TOKEN,
754
- router: ROUTER_TOKEN,
755
- actionRegistry: ACTION_REGISTRY_TOKEN,
756
- actionPageRunner: ACTION_PAGE_RUNNER_TOKEN,
757
- spaMode: {
758
- token: ROUTER_SPA_ACTIONS_RUN_MODE_TOKEN,
759
- optional: true,
760
- },
761
- },
762
- },
763
- {
764
- provide: commandLineListTokens.afterSpaTransition,
765
- multi: true,
766
- useFactory: ({ spaMode, ...deps }) => {
767
- if (spaMode === 'after') {
768
- return runActionsFactory(deps);
769
- }
770
- return noop;
771
- },
772
- deps: {
773
- store: STORE_TOKEN,
774
- router: ROUTER_TOKEN,
775
- actionRegistry: ACTION_REGISTRY_TOKEN,
776
- actionPageRunner: ACTION_PAGE_RUNNER_TOKEN,
777
- spaMode: {
778
- token: ROUTER_SPA_ACTIONS_RUN_MODE_TOKEN,
779
- optional: true,
780
- },
781
- },
782
- },
783
- ];
784
-
785
- var _a;
786
- const SpaRouterModule = /* @__PURE__ */ Module({
787
- providers: [
788
- ...providers,
789
- ...spaHooks,
790
- {
791
- provide: routerClassToken,
792
- useValue: Router,
793
- },
794
- ],
795
- })((_a = class SpaRouterModule {
796
- },
797
- _a.forRoot = generateForRoot(_a),
798
- _a));
799
-
800
- let RouterChildAppModule = class RouterChildAppModule {
801
- };
802
- RouterChildAppModule = __decorate([
803
- Module({
804
- providers: [
805
- provide({
806
- provide: EXTEND_RENDER,
807
- multi: true,
808
- useFactory: provideRouter,
809
- deps: {
810
- router: ROUTER_TOKEN,
811
- },
812
- }),
813
- {
814
- provide: CHILD_APP_INTERNAL_ROOT_STATE_ALLOWED_STORE_TOKEN,
815
- multi: true,
816
- useValue: RouterStore,
817
- },
818
- ],
819
- })
820
- ], RouterChildAppModule);
821
-
822
- const requestIdleCallback = window.requestIdleCallback ||
823
- function (callback) {
824
- return window.setTimeout(callback, 1);
825
- };
826
- const cancelIdleCallback = window.cancelIdleCallback ||
827
- function (timer) {
828
- return window.clearTimeout(timer);
829
- };
830
-
831
- const isUserNetworkConditionsSuitableForPrefetch = () => {
832
- var _a;
833
- const { saveData, effectiveType = '' } = (_a = navigator.connection) !== null && _a !== void 0 ? _a : {};
834
- return !saveData && !(effectiveType === '2g' || effectiveType === 'slow-2g');
835
- };
836
- const usePrefetch = ({ url, target, prefetch, }) => {
837
- const linkPrefetchManager = useDi(optional(LINK_PREFETCH_MANAGER_TOKEN));
838
- useEffect(() => {
839
- if (!target || !prefetch || !linkPrefetchManager) {
840
- return;
841
- }
842
- let idleId = null;
843
- const observer = new IntersectionObserver((entries) => {
844
- entries.forEach((entry) => {
845
- // when `Link` element in viewport
846
- if (entry.isIntersecting && entry.intersectionRatio > 0) {
847
- // only once
848
- observer.disconnect();
849
- if (isUserNetworkConditionsSuitableForPrefetch()) {
850
- // trigger prefetching when browser is idle
851
- idleId = requestIdleCallback(() => {
852
- linkPrefetchManager.prefetch(url);
853
- });
854
- }
855
- }
856
- });
857
- });
858
- observer.observe(target);
859
- return () => {
860
- observer.disconnect();
861
- cancelIdleCallback(idleId);
862
- };
863
- }, [url, target, prefetch, linkPrefetchManager]);
864
- };
865
-
866
- function Link(props) {
867
- const { children, onClick, url, query, replace, target, navigateOptions, prefetch = true, ...otherProps } = props;
868
- const navigate = useNavigate({ url, query, replace, ...navigateOptions });
869
- const [linkElement, setLinkElement] = useState(null);
870
- usePrefetch({
871
- url,
872
- prefetch,
873
- target: linkElement,
874
- });
875
- const handleClick = useCallback((event) => {
876
- // ignores the navigation when clicked using right mouse button or
877
- // by holding a special modifier key: ctrl, command, win, alt, shift
878
- if (target ||
879
- event.ctrlKey ||
880
- event.metaKey ||
881
- event.altKey ||
882
- event.shiftKey ||
883
- event.button !== 0) {
884
- return;
885
- }
886
- event.preventDefault();
887
- navigate();
888
- onClick && onClick(event);
889
- }, [navigate, target, onClick]);
890
- const extraProps = { href: url, onClick: handleClick, target };
891
- if (isValidElement(children)) {
892
- return cloneElement(children, {
893
- // @ts-expect-error
894
- ref: (element) => {
895
- // @ts-expect-error
896
- const { ref } = children;
897
- // preserve original ref
898
- if (typeof ref === 'function') {
899
- ref(element);
900
- }
901
- else if (typeof ref === 'object' && ref !== null) {
902
- ref.current = element;
903
- }
904
- setLinkElement(element);
905
- },
906
- ...extraProps,
907
- });
908
- }
909
- return (
910
- // eslint-disable-next-line react/jsx-props-no-spreading
911
- jsx("a", { ref: (element) => setLinkElement(element), ...otherProps, ...extraProps, children: children }));
912
- }
913
- Link.displayName = 'Link';
914
-
915
- const usePageService = () => {
916
- useRoute();
917
- return useDi(PAGE_SERVICE_TOKEN);
918
- };
919
-
920
- export { Link, NoSpaRouterModule, RouterChildAppModule, RouterStore, SpaRouterModule, generateForRoot, setCurrentNavigation, setUrlOnRehydrate, usePageService };
7
+ export { generateForRoot } from './modules/utils/forRoot.browser.js';
8
+ export { RouterStore, setCurrentNavigation, setUrlOnRehydrate } from './stores/RouterStore.browser.js';
9
+ export { usePageService } from './hooks/usePageService.browser.js';