@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
@@ -3,12 +3,13 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var path = require('path');
6
- var semver = require('semver');
7
6
  var flatten = require('@tinkoff/utils/array/flatten');
8
7
  var url = require('@tinkoff/url');
9
8
  var tokensChildApp = require('@tramvai/tokens-child-app');
10
9
  var tokensRender = require('@tramvai/tokens-render');
11
10
  var moduleFederation = require('../shared/webpack/moduleFederation.js');
11
+ var utils = require('./module-federation/utils.js');
12
+ var bestLoadedSharedModules = require('./module-federation/best-loaded-shared-modules.js');
12
13
 
13
14
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
14
15
 
@@ -26,14 +27,16 @@ const deferScriptAttrs = {
26
27
  // and Child App initialization logic executed after that, we need to get script loading status
27
28
  const entryAttrs = {
28
29
  onload: `this.setAttribute('loaded', 'true')`,
29
- onerror: `this.setAttribute('loaded', 'false')`,
30
+ onerror: `this.setAttribute('loaded', 'error')`,
30
31
  };
31
32
  // for cases when preloaded chunk loading is failed before webpack add this script loading handlers,
32
33
  // we need to remove script and webpack will try to load it itself https://github.com/webpack/webpack/issues/14874
33
34
  const chunkAttrs = {
34
35
  onerror: `this.remove()`,
35
36
  };
36
- const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, preloadManager, loader, renderMode, resourcesRegistry, }) => async () => {
37
+ const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, preloadManager, loader, renderMode, resourcesRegistry, }) =>
38
+ // eslint-disable-next-line max-statements
39
+ async () => {
37
40
  const log = logger('child-app:render:slots');
38
41
  const result = [];
39
42
  // defer scripts is not suitable for React streaming, we need to ability to run them as early as possible
@@ -72,36 +75,25 @@ const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, pre
72
75
  };
73
76
  const preloadedList = new Set(preloadManager.getPreloadedList());
74
77
  const sharedScope = moduleFederation.getSharedScope();
75
- const mapSharedToChildApp = new Map();
76
- // sharedScope will contain all of the shared chunks that were added
77
- // while server is running
78
- // but on the page we can use only shared chunks that either provided by the root-app
79
- // or one of loaded child-app
80
- // so gather all of the available shared modules, check the ones that are available in the currently
81
- // preloaded child-apps and figure out the best single version of the dep
82
- for (const shareKey in sharedScope) {
83
- for (const version in sharedScope[shareKey]) {
84
- const dep = sharedScope[shareKey][version];
85
- const last = mapSharedToChildApp.get(shareKey);
86
- const { eager, from } = dep;
87
- const [type, name] = from.split(':');
88
- if (!last ||
89
- // module federation will pick the highest available version
90
- // https://github.com/webpack/webpack/blob/b67626c7b4ffed8737d195b27c8cea1e68d58134/lib/sharing/ConsumeSharedRuntimeModule.js#L144
91
- semver.gt(version, last.version) ||
92
- // if versions are equal then module federation will pick
93
- // the dep with eager prop (it's set in root-app) of with the child-app with highest name in alphabetical order
94
- (semver.eq(version, last.version) && (eager !== last.eager ? eager : name > last.name))) {
95
- mapSharedToChildApp.set(shareKey, { version, type, name, eager });
96
- }
97
- }
98
- }
78
+ const preloadedConfigs = Array.from(preloadedList)
79
+ .map((requestConfig) => {
80
+ return resolveFullConfig(requestConfig);
81
+ })
82
+ .filter(Boolean);
83
+ // flat list of all shared items initialized in shared scope
84
+ const sharedScopeItems = utils.getFlatSharedScopeItemsList(sharedScope);
85
+ // flat list of all preloaded Child Apps shared modules
86
+ const sharedModules = utils.getFlatSharedModulesList({
87
+ preloadedConfigs,
88
+ loader: loader,
89
+ });
90
+ // optimal shared dependencies for preloaded Child Apps
91
+ const bestSharedModules = bestLoadedSharedModules.resolveBestLoadedSharedModules({
92
+ sharedModules,
93
+ sharedScopeItems,
94
+ });
99
95
  // eslint-disable-next-line max-statements
100
- preloadedList.forEach((requestConfig) => {
101
- const config = resolveFullConfig(requestConfig);
102
- if (!config) {
103
- return;
104
- }
96
+ preloadedConfigs.forEach((config) => {
105
97
  const stats = 'getStats' in loader ? loader.getStats(config) : undefined;
106
98
  const di = diManager.getChildDi(config);
107
99
  const loadableAssets = di?.get(tokensChildApp.CHILD_APP_INTERNAL_CHUNK_EXTRACTOR)?.getMainAssets();
@@ -131,21 +123,6 @@ const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, pre
131
123
  for (const file of files) {
132
124
  addChunk(url.resolve(config.client.baseUrl, file));
133
125
  }
134
- for (const sharedModule of federatedModule.sharedModules) {
135
- const { shareKey } = sharedModule.provides?.[0] ?? {};
136
- const { chunks } = sharedModule;
137
- const bestShared = mapSharedToChildApp.get(shareKey);
138
- if (!bestShared?.eager && bestShared?.name === config.name) {
139
- for (const chunk of chunks) {
140
- addChunk(url.resolve(config.client.baseUrl, chunk));
141
- }
142
- // in stats.json federated stats could contain 2 sets of chunks for shared modules
143
- // there usual one and fallback. For shared module there could be used any of this
144
- // and the other one will be useless. So delete entry from map after its usage in order
145
- // to add only single set of chunks for the same shared dep
146
- mapSharedToChildApp.delete(shareKey);
147
- }
148
- }
149
126
  }
150
127
  }
151
128
  if (!di) {
@@ -161,13 +138,48 @@ const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, pre
161
138
  log.error({
162
139
  event: 'get-slots-failed',
163
140
  childApp: {
164
- name: requestConfig.name,
165
- version: requestConfig.version,
166
- tag: requestConfig.tag,
141
+ name: config.name,
142
+ version: config.version,
143
+ tag: config.tag,
167
144
  },
168
145
  });
169
146
  }
170
147
  });
148
+ // shared chunks deduplication
149
+ const addedSharedChunks = new Set();
150
+ // preloaded chunks we need to pass to client
151
+ const serializableSharedModules = [];
152
+ bestSharedModules.forEach(({ shareKey, version, chunks, childAppName, childAppVersion }) => {
153
+ const childAppConfig = preloadedConfigs.find((config) => {
154
+ return config.name === childAppName && config.version === childAppVersion;
155
+ });
156
+ // if it is application shared dependency, we don't have any extra chunks
157
+ if (childAppConfig && chunks) {
158
+ for (const chunk of chunks) {
159
+ if (!addedSharedChunks.has(chunk)) {
160
+ addedSharedChunks.add(chunk);
161
+ addChunk(url.resolve(childAppConfig.client.baseUrl, chunk));
162
+ const containerName = `child-app__${childAppConfig.client.entry}`;
163
+ serializableSharedModules.push({
164
+ containerName,
165
+ shareKey,
166
+ version,
167
+ childAppName: childAppName,
168
+ childAppVersion: childAppVersion,
169
+ });
170
+ }
171
+ }
172
+ }
173
+ });
174
+ result.push({
175
+ type: tokensRender.ResourceType.inlineScript,
176
+ slot: tokensRender.ResourceSlot.HEAD_POLYFILLS,
177
+ payload: `window.__webpack_share_preloaded__ = [
178
+ ${serializableSharedModules.map((m) => {
179
+ return `${JSON.stringify(m)}`;
180
+ })}
181
+ ];`,
182
+ });
171
183
  result.map((item) => resourcesRegistry.register(item));
172
184
  };
173
185
 
@@ -1,5 +1,6 @@
1
1
  import type { Container } from '@tinkoff/dippy';
2
- import type { ChildAppDiManager, ChildAppPreloadManager, ChildAppRenderManager, ChildAppRequestConfig, CHILD_APP_RESOLVE_CONFIG_TOKEN } from '@tramvai/tokens-child-app';
2
+ import { SyncTapableHookInstance, TAPABLE_HOOK_FACTORY_TOKEN } from '@tramvai/core';
3
+ import type { ChildAppDiManager, ChildAppPreloadManager, ChildAppRenderManager, ChildAppRequestConfig, CHILD_APP_RESOLVE_CONFIG_TOKEN, ChildAppConfigArgs } from '@tramvai/tokens-child-app';
3
4
  import type { LOGGER_TOKEN } from '@tramvai/tokens-common';
4
5
  export declare class RenderManager implements ChildAppRenderManager {
5
6
  private readonly preloadManager;
@@ -7,7 +8,13 @@ export declare class RenderManager implements ChildAppRenderManager {
7
8
  private readonly resolveFullConfig;
8
9
  private readonly log;
9
10
  private readonly hasRenderedSet;
10
- constructor({ logger, preloadManager, diManager, resolveFullConfig, }: {
11
+ private hookFactory;
12
+ hooks: {
13
+ mounted: SyncTapableHookInstance<ChildAppConfigArgs>;
14
+ mountFailed: SyncTapableHookInstance<ChildAppConfigArgs>;
15
+ };
16
+ constructor({ logger, preloadManager, diManager, resolveFullConfig, hookFactory, }: {
17
+ hookFactory: typeof TAPABLE_HOOK_FACTORY_TOKEN;
11
18
  logger: typeof LOGGER_TOKEN;
12
19
  preloadManager: ChildAppPreloadManager;
13
20
  diManager: ChildAppDiManager;
@@ -1,6 +1,17 @@
1
1
  class RenderManager {
2
- constructor({ logger, preloadManager, diManager, resolveFullConfig, }) {
3
- this.hasRenderedSet = new Set();
2
+ preloadManager;
3
+ diManager;
4
+ resolveFullConfig;
5
+ log;
6
+ hasRenderedSet = new Set();
7
+ hookFactory;
8
+ hooks;
9
+ constructor({ logger, preloadManager, diManager, resolveFullConfig, hookFactory, }) {
10
+ this.hookFactory = hookFactory;
11
+ this.hooks = {
12
+ mounted: this.hookFactory.createSync('childAppMounted'),
13
+ mountFailed: this.hookFactory.createSync('childAppMountFailed'),
14
+ };
4
15
  this.log = logger('child-app:render');
5
16
  this.preloadManager = preloadManager;
6
17
  this.diManager = diManager;
@@ -3,8 +3,19 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  class RenderManager {
6
- constructor({ logger, preloadManager, diManager, resolveFullConfig, }) {
7
- this.hasRenderedSet = new Set();
6
+ preloadManager;
7
+ diManager;
8
+ resolveFullConfig;
9
+ log;
10
+ hasRenderedSet = new Set();
11
+ hookFactory;
12
+ hooks;
13
+ constructor({ logger, preloadManager, diManager, resolveFullConfig, hookFactory, }) {
14
+ this.hookFactory = hookFactory;
15
+ this.hooks = {
16
+ mounted: this.hookFactory.createSync('childAppMounted'),
17
+ mountFailed: this.hookFactory.createSync('childAppMountFailed'),
18
+ };
8
19
  this.log = logger('child-app:render');
9
20
  this.preloadManager = preloadManager;
10
21
  this.diManager = diManager;
@@ -17,8 +17,10 @@ const executeRootStateSubscriptions = ({ store, diManager, }) => {
17
17
  };
18
18
  };
19
19
  class StateManager {
20
+ log;
21
+ diManager;
22
+ state = Object.create(null);
20
23
  constructor({ logger, diManager, }) {
21
- this.state = Object.create(null);
22
24
  this.log = logger('child-app:state-manager');
23
25
  this.diManager = diManager;
24
26
  }
@@ -21,8 +21,10 @@ const executeRootStateSubscriptions = ({ store, diManager, }) => {
21
21
  };
22
22
  };
23
23
  class StateManager {
24
+ log;
25
+ diManager;
26
+ state = Object.create(null);
24
27
  constructor({ logger, diManager, }) {
25
- this.state = Object.create(null);
26
28
  this.log = logger('child-app:state-manager');
27
29
  this.diManager = diManager;
28
30
  }
@@ -3,6 +3,10 @@ import { COMMAND_LINE_RUNNER_TOKEN, provide } from '@tramvai/core';
3
3
  import { ROOT_EXECUTION_CONTEXT_TOKEN } from '@tramvai/tokens-common';
4
4
 
5
5
  class CommandLineRunner {
6
+ log;
7
+ rootCommandLineRunner;
8
+ diManager;
9
+ commandLineExecutionContext;
6
10
  constructor({ logger, rootCommandLineRunner, diManager, commandLineExecutionContext, }) {
7
11
  this.log = logger('child-app:command-line-runner');
8
12
  this.rootCommandLineRunner = rootCommandLineRunner;
@@ -3,6 +3,10 @@ import { COMMAND_LINE_RUNNER_TOKEN, provide } from '@tramvai/core';
3
3
  import { ROOT_EXECUTION_CONTEXT_TOKEN } from '@tramvai/tokens-common';
4
4
 
5
5
  class CommandLineRunner {
6
+ log;
7
+ rootCommandLineRunner;
8
+ diManager;
9
+ commandLineExecutionContext;
6
10
  constructor({ logger, rootCommandLineRunner, diManager, commandLineExecutionContext, }) {
7
11
  this.log = logger('child-app:command-line-runner');
8
12
  this.rootCommandLineRunner = rootCommandLineRunner;
@@ -7,6 +7,10 @@ var core = require('@tramvai/core');
7
7
  var tokensCommon = require('@tramvai/tokens-common');
8
8
 
9
9
  class CommandLineRunner {
10
+ log;
11
+ rootCommandLineRunner;
12
+ diManager;
13
+ commandLineExecutionContext;
10
14
  constructor({ logger, rootCommandLineRunner, diManager, commandLineExecutionContext, }) {
11
15
  this.log = logger('child-app:command-line-runner');
12
16
  this.rootCommandLineRunner = rootCommandLineRunner;
@@ -2,8 +2,12 @@ import { ChildContainer } from '@tinkoff/dippy';
2
2
  import { shouldIsolateDi } from './isolatedDi.browser.js';
3
3
 
4
4
  class DiManager {
5
+ appDi;
6
+ loader;
7
+ singletonDiManager;
8
+ rootDiAccessMode;
9
+ cache = new Map();
5
10
  constructor({ appDi, loader, singletonDiManager, rootDiAccessMode, }) {
6
- this.cache = new Map();
7
11
  this.appDi = appDi;
8
12
  this.loader = loader;
9
13
  this.singletonDiManager = singletonDiManager;
@@ -2,8 +2,12 @@ import { ChildContainer } from '@tinkoff/dippy';
2
2
  import { shouldIsolateDi } from './isolatedDi.es.js';
3
3
 
4
4
  class DiManager {
5
+ appDi;
6
+ loader;
7
+ singletonDiManager;
8
+ rootDiAccessMode;
9
+ cache = new Map();
5
10
  constructor({ appDi, loader, singletonDiManager, rootDiAccessMode, }) {
6
- this.cache = new Map();
7
11
  this.appDi = appDi;
8
12
  this.loader = loader;
9
13
  this.singletonDiManager = singletonDiManager;
package/lib/shared/di.js CHANGED
@@ -6,8 +6,12 @@ var dippy = require('@tinkoff/dippy');
6
6
  var isolatedDi = require('./isolatedDi.js');
7
7
 
8
8
  class DiManager {
9
+ appDi;
10
+ loader;
11
+ singletonDiManager;
12
+ rootDiAccessMode;
13
+ cache = new Map();
9
14
  constructor({ appDi, loader, singletonDiManager, rootDiAccessMode, }) {
10
- this.cache = new Map();
11
15
  this.appDi = appDi;
12
16
  this.loader = loader;
13
17
  this.singletonDiManager = singletonDiManager;
@@ -2,6 +2,10 @@ import isArray from '@tinkoff/utils/is/array';
2
2
  import { resolveLazyComponent } from '@tramvai/react';
3
3
 
4
4
  class ChildAppPageService {
5
+ actionsRegistry;
6
+ config;
7
+ componentRegistry;
8
+ pageService;
5
9
  constructor({ actionsRegistry, config, componentRegistry, pageService, pageComponents, }) {
6
10
  this.actionsRegistry = actionsRegistry;
7
11
  this.config = config;
@@ -11,9 +15,10 @@ class ChildAppPageService {
11
15
  (pageComponents || []).forEach((record) => {
12
16
  Object.keys(record).forEach((pageComponentName) => {
13
17
  const pageComponent = record[pageComponentName];
14
- if (!this.componentRegistry.get(pageComponentName, componentsGroupName)) {
15
- this.componentRegistry.add(pageComponentName, pageComponent, componentsGroupName);
16
- }
18
+ // to prevent memory leaks, always register Child App components,
19
+ // because if Child App server JS is loaded again after eviction from server loader cache,
20
+ // we want to load new instance of components and actions and clear memory references to old server JS
21
+ this.componentRegistry.add(pageComponentName, pageComponent, componentsGroupName);
17
22
  });
18
23
  });
19
24
  }
@@ -2,6 +2,10 @@ import isArray from '@tinkoff/utils/is/array';
2
2
  import { resolveLazyComponent } from '@tramvai/react';
3
3
 
4
4
  class ChildAppPageService {
5
+ actionsRegistry;
6
+ config;
7
+ componentRegistry;
8
+ pageService;
5
9
  constructor({ actionsRegistry, config, componentRegistry, pageService, pageComponents, }) {
6
10
  this.actionsRegistry = actionsRegistry;
7
11
  this.config = config;
@@ -11,9 +15,10 @@ class ChildAppPageService {
11
15
  (pageComponents || []).forEach((record) => {
12
16
  Object.keys(record).forEach((pageComponentName) => {
13
17
  const pageComponent = record[pageComponentName];
14
- if (!this.componentRegistry.get(pageComponentName, componentsGroupName)) {
15
- this.componentRegistry.add(pageComponentName, pageComponent, componentsGroupName);
16
- }
18
+ // to prevent memory leaks, always register Child App components,
19
+ // because if Child App server JS is loaded again after eviction from server loader cache,
20
+ // we want to load new instance of components and actions and clear memory references to old server JS
21
+ this.componentRegistry.add(pageComponentName, pageComponent, componentsGroupName);
17
22
  });
18
23
  });
19
24
  }
@@ -10,6 +10,10 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
10
10
  var isArray__default = /*#__PURE__*/_interopDefaultLegacy(isArray);
11
11
 
12
12
  class ChildAppPageService {
13
+ actionsRegistry;
14
+ config;
15
+ componentRegistry;
16
+ pageService;
13
17
  constructor({ actionsRegistry, config, componentRegistry, pageService, pageComponents, }) {
14
18
  this.actionsRegistry = actionsRegistry;
15
19
  this.config = config;
@@ -19,9 +23,10 @@ class ChildAppPageService {
19
23
  (pageComponents || []).forEach((record) => {
20
24
  Object.keys(record).forEach((pageComponentName) => {
21
25
  const pageComponent = record[pageComponentName];
22
- if (!this.componentRegistry.get(pageComponentName, componentsGroupName)) {
23
- this.componentRegistry.add(pageComponentName, pageComponent, componentsGroupName);
24
- }
26
+ // to prevent memory leaks, always register Child App components,
27
+ // because if Child App server JS is loaded again after eviction from server loader cache,
28
+ // we want to load new instance of components and actions and clear memory references to old server JS
29
+ this.componentRegistry.add(pageComponentName, pageComponent, componentsGroupName);
25
30
  });
26
31
  });
27
32
  }
@@ -1,6 +1,6 @@
1
1
  import { Scope, DI_TOKEN, optional } from '@tinkoff/dippy';
2
- import { provide, commandLineListTokens, COMMAND_LINE_RUNNER_TOKEN } from '@tramvai/core';
3
- import { CHILD_APP_RESOLUTION_CONFIG_MANAGER_TOKEN, CHILD_APP_RESOLUTION_CONFIGS_TOKEN, CHILD_APP_RESOLVE_CONFIG_TOKEN, CHILD_APP_RESOLVE_BASE_URL_TOKEN, CHILD_APP_SINGLETON_DI_MANAGER_TOKEN, CHILD_APP_LOADER_TOKEN, CHILD_APP_ROOT_DI_ACCESS_MODE_TOKEN, CHILD_APP_CONTRACT_MANAGER, CHILD_APP_DI_MANAGER_TOKEN, CHILD_APP_COMMAND_LINE_RUNNER_TOKEN, CHILD_APP_PRELOAD_MANAGER_TOKEN, CHILD_APP_RENDER_MANAGER_TOKEN, HOST_PROVIDED_CONTRACTS, CHILD_APP_ERROR_BOUNDARY_TOKEN } from '@tramvai/tokens-child-app';
2
+ import { provide, commandLineListTokens, TAPABLE_HOOK_FACTORY_TOKEN, COMMAND_LINE_RUNNER_TOKEN } from '@tramvai/core';
3
+ import { CHILD_APP_RESOLUTION_CONFIG_MANAGER_TOKEN, CHILD_APP_CONFIG_RESOLUTION_PLUGIN, CHILD_APP_RESOLUTION_CONFIGS_TOKEN, CHILD_APP_RESOLVE_CONFIG_TOKEN, CHILD_APP_RESOLVE_BASE_URL_TOKEN, CHILD_APP_SINGLETON_DI_MANAGER_TOKEN, CHILD_APP_LOADER_TOKEN, CHILD_APP_ROOT_DI_ACCESS_MODE_TOKEN, CHILD_APP_CONTRACT_MANAGER, CHILD_APP_DI_MANAGER_TOKEN, CHILD_APP_COMMAND_LINE_RUNNER_TOKEN, CHILD_APP_PRELOAD_MANAGER_TOKEN, CHILD_APP_RENDER_MANAGER_TOKEN, HOST_PROVIDED_CONTRACTS, CHILD_APP_ERROR_BOUNDARY_TOKEN } from '@tramvai/tokens-child-app';
4
4
  import { COMBINE_REDUCERS, LOGGER_TOKEN, ENV_MANAGER_TOKEN, COMMAND_LINE_EXECUTION_CONTEXT_TOKEN, REGISTER_CLEAR_CACHE_TOKEN, CLEAR_CACHE_TOKEN, ENV_USED_TOKEN, COMPONENT_REGISTRY_TOKEN, LIMIT_ACTION_GLOBAL_TIME_RUN, ACTION_CONDITIONALS } from '@tramvai/tokens-common';
5
5
  import { EXTEND_RENDER } from '@tramvai/tokens-render';
6
6
  import { ROUTER_TOKEN, PAGE_SERVICE_TOKEN, LINK_PREFETCH_MANAGER_TOKEN, ROUTER_SPA_ACTIONS_RUN_MODE_TOKEN } from '@tramvai/tokens-router';
@@ -18,7 +18,7 @@ const sharedProviders = [
18
18
  provide({
19
19
  provide: COMBINE_REDUCERS,
20
20
  multi: true,
21
- useValue: ChildAppStore,
21
+ useValue: [ChildAppStore],
22
22
  }),
23
23
  provide({
24
24
  provide: commandLineListTokens.init,
@@ -29,6 +29,11 @@ const sharedProviders = [
29
29
  provide: CHILD_APP_RESOLUTION_CONFIG_MANAGER_TOKEN,
30
30
  useClass: ChildAppResolutionConfigManager,
31
31
  deps: {
32
+ hookFactory: TAPABLE_HOOK_FACTORY_TOKEN,
33
+ plugins: {
34
+ optional: true,
35
+ token: CHILD_APP_CONFIG_RESOLUTION_PLUGIN,
36
+ },
32
37
  configs: { token: CHILD_APP_RESOLUTION_CONFIGS_TOKEN, optional: true },
33
38
  logger: LOGGER_TOKEN,
34
39
  },
@@ -1,6 +1,6 @@
1
1
  import { Scope, DI_TOKEN, optional } from '@tinkoff/dippy';
2
- import { provide, commandLineListTokens, COMMAND_LINE_RUNNER_TOKEN } from '@tramvai/core';
3
- import { CHILD_APP_RESOLUTION_CONFIG_MANAGER_TOKEN, CHILD_APP_RESOLUTION_CONFIGS_TOKEN, CHILD_APP_RESOLVE_CONFIG_TOKEN, CHILD_APP_RESOLVE_BASE_URL_TOKEN, CHILD_APP_SINGLETON_DI_MANAGER_TOKEN, CHILD_APP_LOADER_TOKEN, CHILD_APP_ROOT_DI_ACCESS_MODE_TOKEN, CHILD_APP_CONTRACT_MANAGER, CHILD_APP_DI_MANAGER_TOKEN, CHILD_APP_COMMAND_LINE_RUNNER_TOKEN, CHILD_APP_PRELOAD_MANAGER_TOKEN, CHILD_APP_RENDER_MANAGER_TOKEN, HOST_PROVIDED_CONTRACTS, CHILD_APP_ERROR_BOUNDARY_TOKEN } from '@tramvai/tokens-child-app';
2
+ import { provide, commandLineListTokens, TAPABLE_HOOK_FACTORY_TOKEN, COMMAND_LINE_RUNNER_TOKEN } from '@tramvai/core';
3
+ import { CHILD_APP_RESOLUTION_CONFIG_MANAGER_TOKEN, CHILD_APP_CONFIG_RESOLUTION_PLUGIN, CHILD_APP_RESOLUTION_CONFIGS_TOKEN, CHILD_APP_RESOLVE_CONFIG_TOKEN, CHILD_APP_RESOLVE_BASE_URL_TOKEN, CHILD_APP_SINGLETON_DI_MANAGER_TOKEN, CHILD_APP_LOADER_TOKEN, CHILD_APP_ROOT_DI_ACCESS_MODE_TOKEN, CHILD_APP_CONTRACT_MANAGER, CHILD_APP_DI_MANAGER_TOKEN, CHILD_APP_COMMAND_LINE_RUNNER_TOKEN, CHILD_APP_PRELOAD_MANAGER_TOKEN, CHILD_APP_RENDER_MANAGER_TOKEN, HOST_PROVIDED_CONTRACTS, CHILD_APP_ERROR_BOUNDARY_TOKEN } from '@tramvai/tokens-child-app';
4
4
  import { COMBINE_REDUCERS, LOGGER_TOKEN, ENV_MANAGER_TOKEN, COMMAND_LINE_EXECUTION_CONTEXT_TOKEN, REGISTER_CLEAR_CACHE_TOKEN, CLEAR_CACHE_TOKEN, ENV_USED_TOKEN, COMPONENT_REGISTRY_TOKEN, LIMIT_ACTION_GLOBAL_TIME_RUN, ACTION_CONDITIONALS } from '@tramvai/tokens-common';
5
5
  import { EXTEND_RENDER } from '@tramvai/tokens-render';
6
6
  import { ROUTER_TOKEN, PAGE_SERVICE_TOKEN, LINK_PREFETCH_MANAGER_TOKEN, ROUTER_SPA_ACTIONS_RUN_MODE_TOKEN } from '@tramvai/tokens-router';
@@ -18,7 +18,7 @@ const sharedProviders = [
18
18
  provide({
19
19
  provide: COMBINE_REDUCERS,
20
20
  multi: true,
21
- useValue: ChildAppStore,
21
+ useValue: [ChildAppStore],
22
22
  }),
23
23
  provide({
24
24
  provide: commandLineListTokens.init,
@@ -29,6 +29,11 @@ const sharedProviders = [
29
29
  provide: CHILD_APP_RESOLUTION_CONFIG_MANAGER_TOKEN,
30
30
  useClass: ChildAppResolutionConfigManager,
31
31
  deps: {
32
+ hookFactory: TAPABLE_HOOK_FACTORY_TOKEN,
33
+ plugins: {
34
+ optional: true,
35
+ token: CHILD_APP_CONFIG_RESOLUTION_PLUGIN,
36
+ },
32
37
  configs: { token: CHILD_APP_RESOLUTION_CONFIGS_TOKEN, optional: true },
33
38
  logger: LOGGER_TOKEN,
34
39
  },
@@ -22,7 +22,7 @@ const sharedProviders = [
22
22
  core.provide({
23
23
  provide: tokensCommon.COMBINE_REDUCERS,
24
24
  multi: true,
25
- useValue: store.ChildAppStore,
25
+ useValue: [store.ChildAppStore],
26
26
  }),
27
27
  core.provide({
28
28
  provide: core.commandLineListTokens.init,
@@ -33,6 +33,11 @@ const sharedProviders = [
33
33
  provide: tokensChildApp.CHILD_APP_RESOLUTION_CONFIG_MANAGER_TOKEN,
34
34
  useClass: resolutionConfigManager.ChildAppResolutionConfigManager,
35
35
  deps: {
36
+ hookFactory: core.TAPABLE_HOOK_FACTORY_TOKEN,
37
+ plugins: {
38
+ optional: true,
39
+ token: tokensChildApp.CHILD_APP_CONFIG_RESOLUTION_PLUGIN,
40
+ },
36
41
  configs: { token: tokensChildApp.CHILD_APP_RESOLUTION_CONFIGS_TOKEN, optional: true },
37
42
  logger: tokensCommon.LOGGER_TOKEN,
38
43
  },
@@ -0,0 +1,56 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { Component } from 'react';
3
+ import { FallbackError } from './FallbackError.browser.js';
4
+ import { ChildAppFallbackWrapper } from '../ChildAppFallbackWrapper.browser.js';
5
+
6
+ class ChildAppErrorBoundary extends Component {
7
+ constructor(props) {
8
+ super(props);
9
+ this.state = {
10
+ error: props.error || null,
11
+ childAppLoadingStatus: null,
12
+ };
13
+ }
14
+ static displayName = 'ChildAppErrorBoundary';
15
+ static getDerivedStateFromProps(props, state) {
16
+ if (!props.childAppLoadingStatus) {
17
+ return { error: props.error || state.error };
18
+ }
19
+ if (state.childAppLoadingStatus &&
20
+ props.childAppLoadingStatus &&
21
+ props.childAppLoadingStatus !== state.childAppLoadingStatus) {
22
+ return { error: props.error || null, childAppLoadingStatus: props.childAppLoadingStatus };
23
+ }
24
+ return {
25
+ error: props.error || state.error,
26
+ childAppLoadingStatus: props.childAppLoadingStatus,
27
+ };
28
+ }
29
+ static getDerivedStateFromError(error) {
30
+ return { error };
31
+ }
32
+ componentDidCatch(error, errorInfo) {
33
+ const { errorHandlers } = this.props;
34
+ if (errorHandlers) {
35
+ errorHandlers.forEach((handler) => {
36
+ handler(error, errorInfo);
37
+ });
38
+ }
39
+ }
40
+ render() {
41
+ const { children, fallback: Fallback, fallbackFromDi, config } = this.props;
42
+ const { error } = this.state;
43
+ if (!error) {
44
+ return children;
45
+ }
46
+ if (Fallback) {
47
+ return jsx(ChildAppFallbackWrapper, { fallback: Fallback, ...config, error: error });
48
+ }
49
+ if (fallbackFromDi) {
50
+ return fallbackFromDi;
51
+ }
52
+ return jsx(FallbackError, {});
53
+ }
54
+ }
55
+
56
+ export { ChildAppErrorBoundary };
@@ -0,0 +1,45 @@
1
+ import React, { Component } from 'react';
2
+ import type { ExtractDependencyType } from '@tinkoff/dippy';
3
+ import type { ERROR_BOUNDARY_TOKEN } from '@tramvai/react';
4
+ import { ChildAppReactConfig } from '@tramvai/tokens-child-app';
5
+ type AnyError = Error & {
6
+ [key: string]: any;
7
+ };
8
+ export interface ChildAppErrorBoundaryFallbackProps {
9
+ error: AnyError;
10
+ }
11
+ export interface ChildAppErrorBoundaryProps {
12
+ config: ChildAppReactConfig;
13
+ childAppLoadingStatus?: string;
14
+ error?: AnyError | null;
15
+ fallback?: React.ComponentType<ChildAppErrorBoundaryFallbackProps>;
16
+ errorHandlers?: ExtractDependencyType<typeof ERROR_BOUNDARY_TOKEN> | null;
17
+ /**
18
+ * @deprecated
19
+ */
20
+ fallbackFromDi?: React.ReactElement | null;
21
+ children?: React.ReactNode;
22
+ }
23
+ interface State {
24
+ error: AnyError | null;
25
+ childAppLoadingStatus?: string | null;
26
+ }
27
+ type Props = ChildAppErrorBoundaryProps;
28
+ export declare class ChildAppErrorBoundary extends Component<Props, State> {
29
+ constructor(props: Props);
30
+ static displayName: string;
31
+ static getDerivedStateFromProps(props: Props, state: State): {
32
+ error: AnyError | null;
33
+ childAppLoadingStatus?: undefined;
34
+ } | {
35
+ error: AnyError | null;
36
+ childAppLoadingStatus: string;
37
+ };
38
+ static getDerivedStateFromError(error: AnyError): {
39
+ error: AnyError;
40
+ };
41
+ componentDidCatch(error: AnyError, errorInfo: React.ErrorInfo): void;
42
+ render(): React.ReactNode;
43
+ }
44
+ export {};
45
+ //# sourceMappingURL=ChildAppErrorBoundary.d.ts.map
@@ -0,0 +1,56 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { Component } from 'react';
3
+ import { FallbackError } from './FallbackError.es.js';
4
+ import { ChildAppFallbackWrapper } from '../ChildAppFallbackWrapper.es.js';
5
+
6
+ class ChildAppErrorBoundary extends Component {
7
+ constructor(props) {
8
+ super(props);
9
+ this.state = {
10
+ error: props.error || null,
11
+ childAppLoadingStatus: null,
12
+ };
13
+ }
14
+ static displayName = 'ChildAppErrorBoundary';
15
+ static getDerivedStateFromProps(props, state) {
16
+ if (!props.childAppLoadingStatus) {
17
+ return { error: props.error || state.error };
18
+ }
19
+ if (state.childAppLoadingStatus &&
20
+ props.childAppLoadingStatus &&
21
+ props.childAppLoadingStatus !== state.childAppLoadingStatus) {
22
+ return { error: props.error || null, childAppLoadingStatus: props.childAppLoadingStatus };
23
+ }
24
+ return {
25
+ error: props.error || state.error,
26
+ childAppLoadingStatus: props.childAppLoadingStatus,
27
+ };
28
+ }
29
+ static getDerivedStateFromError(error) {
30
+ return { error };
31
+ }
32
+ componentDidCatch(error, errorInfo) {
33
+ const { errorHandlers } = this.props;
34
+ if (errorHandlers) {
35
+ errorHandlers.forEach((handler) => {
36
+ handler(error, errorInfo);
37
+ });
38
+ }
39
+ }
40
+ render() {
41
+ const { children, fallback: Fallback, fallbackFromDi, config } = this.props;
42
+ const { error } = this.state;
43
+ if (!error) {
44
+ return children;
45
+ }
46
+ if (Fallback) {
47
+ return jsx(ChildAppFallbackWrapper, { fallback: Fallback, ...config, error: error });
48
+ }
49
+ if (fallbackFromDi) {
50
+ return fallbackFromDi;
51
+ }
52
+ return jsx(FallbackError, {});
53
+ }
54
+ }
55
+
56
+ export { ChildAppErrorBoundary };