@tramvai/module-child-app 5.49.1 → 6.59.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 (97) hide show
  1. package/lib/browser/loader.browser.js +18 -5
  2. package/lib/browser/loader.d.ts +10 -2
  3. package/lib/browser/preload.browser.js +85 -29
  4. package/lib/browser/preload.d.ts +14 -2
  5. package/lib/browser/providers.browser.js +17 -2
  6. package/lib/browser/render.browser.js +15 -1
  7. package/lib/browser/render.d.ts +10 -2
  8. package/lib/browser/runCommand.browser.js +1 -1
  9. package/lib/browser/runCommand.d.ts +1 -1
  10. package/lib/browser/timing.browser.js +100 -0
  11. package/lib/browser/timing.d.ts +4 -0
  12. package/lib/browser.js +2 -1
  13. package/lib/contracts/contractManager.base.browser.js +4 -0
  14. package/lib/contracts/contractManager.base.es.js +4 -0
  15. package/lib/contracts/contractManager.base.js +4 -0
  16. package/lib/contracts/contractManager.server.es.js +1 -0
  17. package/lib/contracts/contractManager.server.js +1 -0
  18. package/lib/server/cache/cache.d.ts +39 -0
  19. package/lib/server/cache/cache.es.js +76 -0
  20. package/lib/server/cache/cache.js +80 -0
  21. package/lib/server/cache/cacheCleanup.d.ts +4 -0
  22. package/lib/server/cache/cacheCleanup.es.js +16 -0
  23. package/lib/server/cache/cacheCleanup.js +21 -0
  24. package/lib/server/loader.d.ts +14 -7
  25. package/lib/server/loader.es.js +34 -19
  26. package/lib/server/loader.js +34 -19
  27. package/lib/server/module-federation/best-loaded-shared-modules.d.ts +15 -0
  28. package/lib/server/module-federation/best-loaded-shared-modules.es.js +26 -0
  29. package/lib/server/module-federation/best-loaded-shared-modules.js +30 -0
  30. package/lib/server/module-federation/best-shared-modules.d.ts +15 -0
  31. package/lib/server/module-federation/utils.d.ts +18 -0
  32. package/lib/server/module-federation/utils.es.js +38 -0
  33. package/lib/server/module-federation/utils.js +43 -0
  34. package/lib/server/preload.d.ts +13 -2
  35. package/lib/server/preload.es.js +58 -19
  36. package/lib/server/preload.js +58 -19
  37. package/lib/server/providers.es.js +17 -4
  38. package/lib/server/providers.js +14 -1
  39. package/lib/server/render-slots.es.js +62 -50
  40. package/lib/server/render-slots.js +62 -50
  41. package/lib/server/render.d.ts +9 -2
  42. package/lib/server/render.es.js +13 -2
  43. package/lib/server/render.js +13 -2
  44. package/lib/server/stateManager.es.js +3 -1
  45. package/lib/server/stateManager.js +3 -1
  46. package/lib/shared/command.browser.js +4 -0
  47. package/lib/shared/command.es.js +4 -0
  48. package/lib/shared/command.js +4 -0
  49. package/lib/shared/di.browser.js +5 -1
  50. package/lib/shared/di.es.js +5 -1
  51. package/lib/shared/di.js +5 -1
  52. package/lib/shared/pageService.browser.js +8 -3
  53. package/lib/shared/pageService.es.js +8 -3
  54. package/lib/shared/pageService.js +8 -3
  55. package/lib/shared/providers.browser.js +8 -3
  56. package/lib/shared/providers.es.js +8 -3
  57. package/lib/shared/providers.js +6 -1
  58. package/lib/shared/react/ChildAppErrorBoundary/ChildAppErrorBoundary.browser.js +56 -0
  59. package/lib/shared/react/ChildAppErrorBoundary/ChildAppErrorBoundary.d.ts +45 -0
  60. package/lib/shared/react/ChildAppErrorBoundary/ChildAppErrorBoundary.es.js +56 -0
  61. package/lib/shared/react/ChildAppErrorBoundary/ChildAppErrorBoundary.js +60 -0
  62. package/lib/shared/react/ChildAppErrorBoundary/FallbackError.browser.js +21 -0
  63. package/lib/shared/react/ChildAppErrorBoundary/FallbackError.d.ts +5 -0
  64. package/lib/shared/react/ChildAppErrorBoundary/FallbackError.es.js +21 -0
  65. package/lib/shared/react/ChildAppErrorBoundary/FallbackError.js +25 -0
  66. package/lib/shared/react/ChildAppFallbackWrapper.browser.js +14 -0
  67. package/lib/shared/react/ChildAppFallbackWrapper.d.ts +10 -0
  68. package/lib/shared/react/ChildAppFallbackWrapper.es.js +14 -0
  69. package/lib/shared/react/ChildAppFallbackWrapper.js +18 -0
  70. package/lib/shared/react/childAppErrorBoundaryWrapper.browser.js +22 -0
  71. package/lib/shared/react/childAppErrorBoundaryWrapper.d.ts +8 -0
  72. package/lib/shared/react/childAppErrorBoundaryWrapper.es.js +22 -0
  73. package/lib/shared/react/childAppErrorBoundaryWrapper.js +26 -0
  74. package/lib/shared/react/component.browser.js +31 -6
  75. package/lib/shared/react/component.es.js +31 -6
  76. package/lib/shared/react/component.js +29 -4
  77. package/lib/shared/resolutionConfigManager.browser.js +35 -15
  78. package/lib/shared/resolutionConfigManager.d.ts +8 -2
  79. package/lib/shared/resolutionConfigManager.es.js +35 -15
  80. package/lib/shared/resolutionConfigManager.js +35 -15
  81. package/lib/shared/singletonDi.browser.js +18 -2
  82. package/lib/shared/singletonDi.d.ts +1 -0
  83. package/lib/shared/singletonDi.es.js +18 -2
  84. package/lib/shared/singletonDi.js +18 -2
  85. package/lib/shared/store.browser.js +15 -2
  86. package/lib/shared/store.d.ts +6 -0
  87. package/lib/shared/store.es.js +15 -2
  88. package/lib/shared/store.js +15 -1
  89. package/lib/shared/webpack/moduleFederation.browser.js +42 -0
  90. package/lib/shared/webpack/moduleFederation.d.ts +27 -18
  91. package/lib/shared/webpack/moduleFederation.es.js +42 -0
  92. package/lib/shared/webpack/moduleFederation.js +42 -0
  93. package/package.json +22 -23
  94. package/lib/shared/react/childAppErrorBoundary.browser.js +0 -14
  95. package/lib/shared/react/childAppErrorBoundary.d.ts +0 -8
  96. package/lib/shared/react/childAppErrorBoundary.es.js +0 -14
  97. package/lib/shared/react/childAppErrorBoundary.js +0 -18
@@ -1,5 +1,6 @@
1
1
  import type { Route } from '@tinkoff/router';
2
- import { type ChildAppCommandLineRunner, type ChildAppRequestConfig, type ChildAppLoader, type ChildAppPreloadManager, type ChildAppStateManager, type CHILD_APP_RESOLVE_CONFIG_TOKEN, type ChildAppFinalConfig, type CHILD_APP_RESOLUTION_CONFIG_MANAGER_TOKEN, type CHILD_APP_DI_MANAGER_TOKEN } from '@tramvai/tokens-child-app';
2
+ import { AsyncTapableHookInstance, TAPABLE_HOOK_FACTORY_TOKEN } from '@tramvai/core';
3
+ import { type ChildAppCommandLineRunner, type ChildAppRequestConfig, type ChildAppLoader, type ChildAppPreloadManager, type ChildAppStateManager, type CHILD_APP_RESOLVE_CONFIG_TOKEN, type ChildAppFinalConfig, type CHILD_APP_RESOLUTION_CONFIG_MANAGER_TOKEN, type CHILD_APP_DI_MANAGER_TOKEN, CHILD_APP_PRELOAD_MANAGER_PLUGIN, PreloadArgs, RunChildAppCommandLineArgs } from '@tramvai/tokens-child-app';
3
4
  export declare class PreloadManager implements ChildAppPreloadManager {
4
5
  private loader;
5
6
  private runner;
@@ -10,14 +11,24 @@ export declare class PreloadManager implements ChildAppPreloadManager {
10
11
  private shouldRunImmediately;
11
12
  private map;
12
13
  private preloadMap;
13
- constructor({ loader, runner, stateManager, resolutionConfigManager, resolveFullConfig, diManager, }: {
14
+ private hookFactory;
15
+ hooks: {
16
+ preloadChildApp: AsyncTapableHookInstance<PreloadArgs>;
17
+ prefetchChildApp: AsyncTapableHookInstance<PreloadArgs>;
18
+ runChildAppCommandLine: AsyncTapableHookInstance<RunChildAppCommandLineArgs>;
19
+ };
20
+ constructor({ loader, runner, stateManager, resolutionConfigManager, resolveFullConfig, diManager, hookFactory, plugins, }: {
14
21
  loader: ChildAppLoader;
15
22
  runner: ChildAppCommandLineRunner;
16
23
  stateManager: ChildAppStateManager;
17
24
  resolutionConfigManager: typeof CHILD_APP_RESOLUTION_CONFIG_MANAGER_TOKEN;
18
25
  resolveFullConfig: typeof CHILD_APP_RESOLVE_CONFIG_TOKEN;
19
26
  diManager: typeof CHILD_APP_DI_MANAGER_TOKEN;
27
+ hookFactory: typeof TAPABLE_HOOK_FACTORY_TOKEN;
28
+ plugins: (typeof CHILD_APP_PRELOAD_MANAGER_PLUGIN)[] | null;
20
29
  });
30
+ private runChildAppCommandLineHook;
31
+ private preloadChildAppHook;
21
32
  preload(request: ChildAppRequestConfig, route?: Route): Promise<void>;
22
33
  prefetch(request: ChildAppRequestConfig, route?: Route): Promise<void>;
23
34
  isPreloaded(request: ChildAppRequestConfig): boolean;
@@ -2,28 +2,53 @@ import { optional } from '@tinkoff/dippy';
2
2
  import { CHILD_APP_PAGE_SERVICE_TOKEN } from '@tramvai/tokens-child-app';
3
3
 
4
4
  class PreloadManager {
5
- constructor({ loader, runner, stateManager, resolutionConfigManager, resolveFullConfig, diManager, }) {
6
- this.shouldRunImmediately = false;
7
- this.map = new Map();
8
- this.preloadMap = new Map();
5
+ loader;
6
+ runner;
7
+ stateManager;
8
+ resolutionConfigManager;
9
+ resolveFullConfig;
10
+ diManager;
11
+ shouldRunImmediately = false;
12
+ map = new Map();
13
+ preloadMap = new Map();
14
+ hookFactory;
15
+ hooks;
16
+ constructor({ loader, runner, stateManager, resolutionConfigManager, resolveFullConfig, diManager, hookFactory, plugins, }) {
9
17
  this.loader = loader;
10
18
  this.runner = runner;
11
19
  this.stateManager = stateManager;
12
20
  this.resolutionConfigManager = resolutionConfigManager;
13
21
  this.resolveFullConfig = resolveFullConfig;
14
22
  this.diManager = diManager;
23
+ this.hookFactory = hookFactory;
24
+ this.hooks = {
25
+ preloadChildApp: this.hookFactory.createAsync('preloadChildApp'),
26
+ prefetchChildApp: this.hookFactory.createAsync('prefetchChildApp'),
27
+ runChildAppCommandLine: this.hookFactory.createAsync('runChildAppCommandLine'),
28
+ };
29
+ this.hooks.preloadChildApp.tapPromise('childAppPreload', async (_, payload) => {
30
+ await this.preloadChildAppHook(payload);
31
+ });
32
+ this.hooks.prefetchChildApp.tapPromise('childAppPrefetch', async () => {
33
+ // do nothing at server side
34
+ });
35
+ this.hooks.runChildAppCommandLine.tapPromise('childAppCommandRun', async (_, payload) => {
36
+ await this.runChildAppCommandLineHook(payload);
37
+ });
38
+ plugins?.forEach((plugin) => {
39
+ plugin.apply(this.hooks);
40
+ });
15
41
  }
16
- async preload(request, route) {
17
- await this.resolutionConfigManager.init();
18
- const config = this.resolveFullConfig(request);
19
- if (!config) {
42
+ async runChildAppCommandLineHook({ config, status, }) {
43
+ const childApp = this.loader.get(config);
44
+ if (!childApp) {
20
45
  return;
21
46
  }
47
+ await this.runner.run('server', status, config);
48
+ await this.stateManager.registerChildApp(config);
49
+ }
50
+ async preloadChildAppHook({ config }) {
22
51
  const { key } = config;
23
- if (this.map.has(key)) {
24
- await this.map.get(key);
25
- return;
26
- }
27
52
  const promise = this.loader
28
53
  .load(config)
29
54
  .catch(() => {
@@ -47,8 +72,23 @@ class PreloadManager {
47
72
  await promise;
48
73
  }
49
74
  }
75
+ async preload(request, route) {
76
+ await this.resolutionConfigManager.init();
77
+ const config = this.resolveFullConfig(request);
78
+ if (!config) {
79
+ return;
80
+ }
81
+ const { key } = config;
82
+ if (this.map.has(key)) {
83
+ await this.map.get(key);
84
+ }
85
+ await this.hooks.preloadChildApp.callPromise({
86
+ config,
87
+ route,
88
+ });
89
+ }
50
90
  async prefetch(request, route) {
51
- // do nothing at server-side
91
+ // do nothing on server side
52
92
  }
53
93
  isPreloaded(request) {
54
94
  const config = this.resolveFullConfig(request);
@@ -78,12 +118,11 @@ class PreloadManager {
78
118
  return Array.from(this.preloadMap.values());
79
119
  }
80
120
  async run(status, config) {
81
- const childApp = this.loader.get(config);
82
- if (!childApp) {
83
- return;
84
- }
85
- await this.runner.run('server', status, config);
86
- await this.stateManager.registerChildApp(config);
121
+ await this.hooks.runChildAppCommandLine.callPromise({
122
+ config,
123
+ status,
124
+ line: 'server',
125
+ });
87
126
  }
88
127
  saveNotPreloadedForSpaNavigation(request) {
89
128
  // do nothing at server-side
@@ -6,28 +6,53 @@ var dippy = require('@tinkoff/dippy');
6
6
  var tokensChildApp = require('@tramvai/tokens-child-app');
7
7
 
8
8
  class PreloadManager {
9
- constructor({ loader, runner, stateManager, resolutionConfigManager, resolveFullConfig, diManager, }) {
10
- this.shouldRunImmediately = false;
11
- this.map = new Map();
12
- this.preloadMap = new Map();
9
+ loader;
10
+ runner;
11
+ stateManager;
12
+ resolutionConfigManager;
13
+ resolveFullConfig;
14
+ diManager;
15
+ shouldRunImmediately = false;
16
+ map = new Map();
17
+ preloadMap = new Map();
18
+ hookFactory;
19
+ hooks;
20
+ constructor({ loader, runner, stateManager, resolutionConfigManager, resolveFullConfig, diManager, hookFactory, plugins, }) {
13
21
  this.loader = loader;
14
22
  this.runner = runner;
15
23
  this.stateManager = stateManager;
16
24
  this.resolutionConfigManager = resolutionConfigManager;
17
25
  this.resolveFullConfig = resolveFullConfig;
18
26
  this.diManager = diManager;
27
+ this.hookFactory = hookFactory;
28
+ this.hooks = {
29
+ preloadChildApp: this.hookFactory.createAsync('preloadChildApp'),
30
+ prefetchChildApp: this.hookFactory.createAsync('prefetchChildApp'),
31
+ runChildAppCommandLine: this.hookFactory.createAsync('runChildAppCommandLine'),
32
+ };
33
+ this.hooks.preloadChildApp.tapPromise('childAppPreload', async (_, payload) => {
34
+ await this.preloadChildAppHook(payload);
35
+ });
36
+ this.hooks.prefetchChildApp.tapPromise('childAppPrefetch', async () => {
37
+ // do nothing at server side
38
+ });
39
+ this.hooks.runChildAppCommandLine.tapPromise('childAppCommandRun', async (_, payload) => {
40
+ await this.runChildAppCommandLineHook(payload);
41
+ });
42
+ plugins?.forEach((plugin) => {
43
+ plugin.apply(this.hooks);
44
+ });
19
45
  }
20
- async preload(request, route) {
21
- await this.resolutionConfigManager.init();
22
- const config = this.resolveFullConfig(request);
23
- if (!config) {
46
+ async runChildAppCommandLineHook({ config, status, }) {
47
+ const childApp = this.loader.get(config);
48
+ if (!childApp) {
24
49
  return;
25
50
  }
51
+ await this.runner.run('server', status, config);
52
+ await this.stateManager.registerChildApp(config);
53
+ }
54
+ async preloadChildAppHook({ config }) {
26
55
  const { key } = config;
27
- if (this.map.has(key)) {
28
- await this.map.get(key);
29
- return;
30
- }
31
56
  const promise = this.loader
32
57
  .load(config)
33
58
  .catch(() => {
@@ -51,8 +76,23 @@ class PreloadManager {
51
76
  await promise;
52
77
  }
53
78
  }
79
+ async preload(request, route) {
80
+ await this.resolutionConfigManager.init();
81
+ const config = this.resolveFullConfig(request);
82
+ if (!config) {
83
+ return;
84
+ }
85
+ const { key } = config;
86
+ if (this.map.has(key)) {
87
+ await this.map.get(key);
88
+ }
89
+ await this.hooks.preloadChildApp.callPromise({
90
+ config,
91
+ route,
92
+ });
93
+ }
54
94
  async prefetch(request, route) {
55
- // do nothing at server-side
95
+ // do nothing on server side
56
96
  }
57
97
  isPreloaded(request) {
58
98
  const config = this.resolveFullConfig(request);
@@ -82,12 +122,11 @@ class PreloadManager {
82
122
  return Array.from(this.preloadMap.values());
83
123
  }
84
124
  async run(status, config) {
85
- const childApp = this.loader.get(config);
86
- if (!childApp) {
87
- return;
88
- }
89
- await this.runner.run('server', status, config);
90
- await this.stateManager.registerChildApp(config);
125
+ await this.hooks.runChildAppCommandLine.callPromise({
126
+ config,
127
+ status,
128
+ line: 'server',
129
+ });
91
130
  }
92
131
  saveNotPreloadedForSpaNavigation(request) {
93
132
  // do nothing at server-side
@@ -1,9 +1,9 @@
1
1
  import { combineValidators, isUrl, endsWith } from '@tinkoff/env-validators';
2
2
  import { Scope, optional, DI_TOKEN } from '@tinkoff/dippy';
3
- import { provide, commandLineListTokens } from '@tramvai/core';
4
- import { ENV_USED_TOKEN, LOGGER_TOKEN, CREATE_CACHE_TOKEN, ENV_MANAGER_TOKEN, STORE_TOKEN, ASYNC_LOCAL_STORAGE_TOKEN } from '@tramvai/tokens-common';
3
+ import { provide, TAPABLE_HOOK_FACTORY_TOKEN, commandLineListTokens } from '@tramvai/core';
4
+ import { ENV_USED_TOKEN, LOGGER_TOKEN, ENV_MANAGER_TOKEN, STORE_TOKEN, ASYNC_LOCAL_STORAGE_TOKEN } from '@tramvai/tokens-common';
5
5
  import { RESOURCES_REGISTRY, RENDER_SLOTS, ResourceType, ResourceSlot, RENDER_FLOW_AFTER_TOKEN, REACT_SERVER_RENDER_MODE, EXTEND_RENDER } from '@tramvai/tokens-render';
6
- import { CHILD_APP_LOADER_TOKEN, CHILD_APP_STATE_MANAGER_TOKEN, CHILD_APP_DI_MANAGER_TOKEN, CHILD_APP_PRELOAD_MANAGER_TOKEN, CHILD_APP_COMMAND_LINE_RUNNER_TOKEN, CHILD_APP_RESOLUTION_CONFIG_MANAGER_TOKEN, CHILD_APP_RESOLVE_CONFIG_TOKEN, CHILD_APP_RENDER_MANAGER_TOKEN, CHILD_APP_CONTRACT_MANAGER, HOST_PROVIDED_CONTRACTS, HOST_REQUIRED_CONTRACTS } from '@tramvai/tokens-child-app';
6
+ import { CHILD_APP_LOADER_TOKEN, CHILD_APP_LOADER_PLUGIN, CHILD_APP_LOADER_CACHE_TOKEN, CHILD_APP_STATE_MANAGER_TOKEN, CHILD_APP_DI_MANAGER_TOKEN, CHILD_APP_PRELOAD_MANAGER_TOKEN, CHILD_APP_PRELOAD_MANAGER_PLUGIN, CHILD_APP_COMMAND_LINE_RUNNER_TOKEN, CHILD_APP_RESOLUTION_CONFIG_MANAGER_TOKEN, CHILD_APP_RESOLVE_CONFIG_TOKEN, CHILD_APP_RENDER_MANAGER_TOKEN, CHILD_APP_CONTRACT_MANAGER, HOST_PROVIDED_CONTRACTS, HOST_REQUIRED_CONTRACTS } from '@tramvai/tokens-child-app';
7
7
  import { safeStringify } from '@tramvai/safe-strings';
8
8
  import { ServerLoader } from './loader.es.js';
9
9
  import { PreloadManager } from './preload.es.js';
@@ -13,8 +13,10 @@ import { RenderManager } from './render.es.js';
13
13
  import { registerChildAppRenderSlots } from './render-slots.es.js';
14
14
  import { GLOBAL_CHILD_STATE } from '../shared/constants.es.js';
15
15
  import { ChildAppContractManager } from '../contracts/contractManager.server.es.js';
16
+ import { cache } from './cache/cache.es.js';
16
17
 
17
18
  const serverProviders = [
19
+ ...cache,
18
20
  provide({
19
21
  provide: ENV_USED_TOKEN,
20
22
  multi: true,
@@ -31,8 +33,13 @@ const serverProviders = [
31
33
  useClass: ServerLoader,
32
34
  scope: Scope.SINGLETON,
33
35
  deps: {
36
+ plugins: {
37
+ optional: true,
38
+ token: CHILD_APP_LOADER_PLUGIN,
39
+ },
40
+ hookFactory: TAPABLE_HOOK_FACTORY_TOKEN,
34
41
  logger: LOGGER_TOKEN,
35
- createCache: CREATE_CACHE_TOKEN,
42
+ cache: CHILD_APP_LOADER_CACHE_TOKEN,
36
43
  envManager: ENV_MANAGER_TOKEN,
37
44
  },
38
45
  }),
@@ -49,6 +56,11 @@ const serverProviders = [
49
56
  provide: CHILD_APP_PRELOAD_MANAGER_TOKEN,
50
57
  useClass: PreloadManager,
51
58
  deps: {
59
+ hookFactory: TAPABLE_HOOK_FACTORY_TOKEN,
60
+ plugins: {
61
+ optional: true,
62
+ token: CHILD_APP_PRELOAD_MANAGER_PLUGIN,
63
+ },
52
64
  loader: CHILD_APP_LOADER_TOKEN,
53
65
  runner: CHILD_APP_COMMAND_LINE_RUNNER_TOKEN,
54
66
  stateManager: CHILD_APP_STATE_MANAGER_TOKEN,
@@ -102,6 +114,7 @@ const serverProviders = [
102
114
  provide: CHILD_APP_RENDER_MANAGER_TOKEN,
103
115
  useClass: RenderManager,
104
116
  deps: {
117
+ hookFactory: TAPABLE_HOOK_FACTORY_TOKEN,
105
118
  logger: LOGGER_TOKEN,
106
119
  diManager: CHILD_APP_DI_MANAGER_TOKEN,
107
120
  preloadManager: CHILD_APP_PRELOAD_MANAGER_TOKEN,
@@ -17,8 +17,10 @@ var render = require('./render.js');
17
17
  var renderSlots = require('./render-slots.js');
18
18
  var constants = require('../shared/constants.js');
19
19
  var contractManager_server = require('../contracts/contractManager.server.js');
20
+ var cache = require('./cache/cache.js');
20
21
 
21
22
  const serverProviders = [
23
+ ...cache.cache,
22
24
  core.provide({
23
25
  provide: tokensCommon.ENV_USED_TOKEN,
24
26
  multi: true,
@@ -35,8 +37,13 @@ const serverProviders = [
35
37
  useClass: loader.ServerLoader,
36
38
  scope: dippy.Scope.SINGLETON,
37
39
  deps: {
40
+ plugins: {
41
+ optional: true,
42
+ token: tokensChildApp.CHILD_APP_LOADER_PLUGIN,
43
+ },
44
+ hookFactory: core.TAPABLE_HOOK_FACTORY_TOKEN,
38
45
  logger: tokensCommon.LOGGER_TOKEN,
39
- createCache: tokensCommon.CREATE_CACHE_TOKEN,
46
+ cache: tokensChildApp.CHILD_APP_LOADER_CACHE_TOKEN,
40
47
  envManager: tokensCommon.ENV_MANAGER_TOKEN,
41
48
  },
42
49
  }),
@@ -53,6 +60,11 @@ const serverProviders = [
53
60
  provide: tokensChildApp.CHILD_APP_PRELOAD_MANAGER_TOKEN,
54
61
  useClass: preload.PreloadManager,
55
62
  deps: {
63
+ hookFactory: core.TAPABLE_HOOK_FACTORY_TOKEN,
64
+ plugins: {
65
+ optional: true,
66
+ token: tokensChildApp.CHILD_APP_PRELOAD_MANAGER_PLUGIN,
67
+ },
56
68
  loader: tokensChildApp.CHILD_APP_LOADER_TOKEN,
57
69
  runner: tokensChildApp.CHILD_APP_COMMAND_LINE_RUNNER_TOKEN,
58
70
  stateManager: tokensChildApp.CHILD_APP_STATE_MANAGER_TOKEN,
@@ -106,6 +118,7 @@ const serverProviders = [
106
118
  provide: tokensChildApp.CHILD_APP_RENDER_MANAGER_TOKEN,
107
119
  useClass: render.RenderManager,
108
120
  deps: {
121
+ hookFactory: core.TAPABLE_HOOK_FACTORY_TOKEN,
109
122
  logger: tokensCommon.LOGGER_TOKEN,
110
123
  diManager: tokensChildApp.CHILD_APP_DI_MANAGER_TOKEN,
111
124
  preloadManager: tokensChildApp.CHILD_APP_PRELOAD_MANAGER_TOKEN,
@@ -1,10 +1,11 @@
1
1
  import { extname } from 'path';
2
- import { gt, eq } from 'semver';
3
2
  import flatten from '@tinkoff/utils/array/flatten';
4
3
  import { resolve } from '@tinkoff/url';
5
4
  import { CHILD_APP_INTERNAL_CHUNK_EXTRACTOR } from '@tramvai/tokens-child-app';
6
5
  import { RENDER_SLOTS, ResourceType, ResourceSlot } from '@tramvai/tokens-render';
7
6
  import { getSharedScope } from '../shared/webpack/moduleFederation.es.js';
7
+ import { getFlatSharedScopeItemsList, getFlatSharedModulesList } from './module-federation/utils.es.js';
8
+ import { resolveBestLoadedSharedModules } from './module-federation/best-loaded-shared-modules.es.js';
8
9
 
9
10
  const asyncScriptAttrs = {
10
11
  defer: null,
@@ -18,14 +19,16 @@ const deferScriptAttrs = {
18
19
  // and Child App initialization logic executed after that, we need to get script loading status
19
20
  const entryAttrs = {
20
21
  onload: `this.setAttribute('loaded', 'true')`,
21
- onerror: `this.setAttribute('loaded', 'false')`,
22
+ onerror: `this.setAttribute('loaded', 'error')`,
22
23
  };
23
24
  // for cases when preloaded chunk loading is failed before webpack add this script loading handlers,
24
25
  // we need to remove script and webpack will try to load it itself https://github.com/webpack/webpack/issues/14874
25
26
  const chunkAttrs = {
26
27
  onerror: `this.remove()`,
27
28
  };
28
- const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, preloadManager, loader, renderMode, resourcesRegistry, }) => async () => {
29
+ const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, preloadManager, loader, renderMode, resourcesRegistry, }) =>
30
+ // eslint-disable-next-line max-statements
31
+ async () => {
29
32
  const log = logger('child-app:render:slots');
30
33
  const result = [];
31
34
  // defer scripts is not suitable for React streaming, we need to ability to run them as early as possible
@@ -64,36 +67,25 @@ const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, pre
64
67
  };
65
68
  const preloadedList = new Set(preloadManager.getPreloadedList());
66
69
  const sharedScope = getSharedScope();
67
- const mapSharedToChildApp = new Map();
68
- // sharedScope will contain all of the shared chunks that were added
69
- // while server is running
70
- // but on the page we can use only shared chunks that either provided by the root-app
71
- // or one of loaded child-app
72
- // so gather all of the available shared modules, check the ones that are available in the currently
73
- // preloaded child-apps and figure out the best single version of the dep
74
- for (const shareKey in sharedScope) {
75
- for (const version in sharedScope[shareKey]) {
76
- const dep = sharedScope[shareKey][version];
77
- const last = mapSharedToChildApp.get(shareKey);
78
- const { eager, from } = dep;
79
- const [type, name] = from.split(':');
80
- if (!last ||
81
- // module federation will pick the highest available version
82
- // https://github.com/webpack/webpack/blob/b67626c7b4ffed8737d195b27c8cea1e68d58134/lib/sharing/ConsumeSharedRuntimeModule.js#L144
83
- gt(version, last.version) ||
84
- // if versions are equal then module federation will pick
85
- // the dep with eager prop (it's set in root-app) of with the child-app with highest name in alphabetical order
86
- (eq(version, last.version) && (eager !== last.eager ? eager : name > last.name))) {
87
- mapSharedToChildApp.set(shareKey, { version, type, name, eager });
88
- }
89
- }
90
- }
70
+ const preloadedConfigs = Array.from(preloadedList)
71
+ .map((requestConfig) => {
72
+ return resolveFullConfig(requestConfig);
73
+ })
74
+ .filter(Boolean);
75
+ // flat list of all shared items initialized in shared scope
76
+ const sharedScopeItems = getFlatSharedScopeItemsList(sharedScope);
77
+ // flat list of all preloaded Child Apps shared modules
78
+ const sharedModules = getFlatSharedModulesList({
79
+ preloadedConfigs,
80
+ loader: loader,
81
+ });
82
+ // optimal shared dependencies for preloaded Child Apps
83
+ const bestSharedModules = resolveBestLoadedSharedModules({
84
+ sharedModules,
85
+ sharedScopeItems,
86
+ });
91
87
  // eslint-disable-next-line max-statements
92
- preloadedList.forEach((requestConfig) => {
93
- const config = resolveFullConfig(requestConfig);
94
- if (!config) {
95
- return;
96
- }
88
+ preloadedConfigs.forEach((config) => {
97
89
  const stats = 'getStats' in loader ? loader.getStats(config) : undefined;
98
90
  const di = diManager.getChildDi(config);
99
91
  const loadableAssets = di?.get(CHILD_APP_INTERNAL_CHUNK_EXTRACTOR)?.getMainAssets();
@@ -123,21 +115,6 @@ const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, pre
123
115
  for (const file of files) {
124
116
  addChunk(resolve(config.client.baseUrl, file));
125
117
  }
126
- for (const sharedModule of federatedModule.sharedModules) {
127
- const { shareKey } = sharedModule.provides?.[0] ?? {};
128
- const { chunks } = sharedModule;
129
- const bestShared = mapSharedToChildApp.get(shareKey);
130
- if (!bestShared?.eager && bestShared?.name === config.name) {
131
- for (const chunk of chunks) {
132
- addChunk(resolve(config.client.baseUrl, chunk));
133
- }
134
- // in stats.json federated stats could contain 2 sets of chunks for shared modules
135
- // there usual one and fallback. For shared module there could be used any of this
136
- // and the other one will be useless. So delete entry from map after its usage in order
137
- // to add only single set of chunks for the same shared dep
138
- mapSharedToChildApp.delete(shareKey);
139
- }
140
- }
141
118
  }
142
119
  }
143
120
  if (!di) {
@@ -153,13 +130,48 @@ const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, pre
153
130
  log.error({
154
131
  event: 'get-slots-failed',
155
132
  childApp: {
156
- name: requestConfig.name,
157
- version: requestConfig.version,
158
- tag: requestConfig.tag,
133
+ name: config.name,
134
+ version: config.version,
135
+ tag: config.tag,
159
136
  },
160
137
  });
161
138
  }
162
139
  });
140
+ // shared chunks deduplication
141
+ const addedSharedChunks = new Set();
142
+ // preloaded chunks we need to pass to client
143
+ const serializableSharedModules = [];
144
+ bestSharedModules.forEach(({ shareKey, version, chunks, childAppName, childAppVersion }) => {
145
+ const childAppConfig = preloadedConfigs.find((config) => {
146
+ return config.name === childAppName && config.version === childAppVersion;
147
+ });
148
+ // if it is application shared dependency, we don't have any extra chunks
149
+ if (childAppConfig && chunks) {
150
+ for (const chunk of chunks) {
151
+ if (!addedSharedChunks.has(chunk)) {
152
+ addedSharedChunks.add(chunk);
153
+ addChunk(resolve(childAppConfig.client.baseUrl, chunk));
154
+ const containerName = `child-app__${childAppConfig.client.entry}`;
155
+ serializableSharedModules.push({
156
+ containerName,
157
+ shareKey,
158
+ version,
159
+ childAppName: childAppName,
160
+ childAppVersion: childAppVersion,
161
+ });
162
+ }
163
+ }
164
+ }
165
+ });
166
+ result.push({
167
+ type: ResourceType.inlineScript,
168
+ slot: ResourceSlot.HEAD_POLYFILLS,
169
+ payload: `window.__webpack_share_preloaded__ = [
170
+ ${serializableSharedModules.map((m) => {
171
+ return `${JSON.stringify(m)}`;
172
+ })}
173
+ ];`,
174
+ });
163
175
  result.map((item) => resourcesRegistry.register(item));
164
176
  };
165
177