@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
@@ -0,0 +1,76 @@
1
+ import { provide, commandLineListTokens } from '@tramvai/core';
2
+ import { optional, Scope } from '@tinkoff/dippy';
3
+ import { CHILD_APP_LOADER_CACHE_CLEANUP_CONFIG_TOKEN, CHILD_APP_LOADER_CACHE_TOKEN, CHILD_APP_LOADER_CACHE_OPTIONS_TOKEN } from '@tramvai/tokens-child-app';
4
+ import { ASYNC_LOCAL_STORAGE_TOKEN, CREATE_CACHE_TOKEN } from '@tramvai/tokens-common';
5
+ import { setCacheCleanupInterval } from './cacheCleanup.es.js';
6
+
7
+ const cache = [
8
+ provide({
9
+ provide: commandLineListTokens.listen,
10
+ useFactory: ({ childAppLoaderCache, cacheCleanupConfig }) => {
11
+ return () => {
12
+ setCacheCleanupInterval(childAppLoaderCache, cacheCleanupConfig?.cleanupIntervalMs);
13
+ };
14
+ },
15
+ deps: {
16
+ cacheCleanupConfig: optional(CHILD_APP_LOADER_CACHE_CLEANUP_CONFIG_TOKEN),
17
+ childAppLoaderCache: CHILD_APP_LOADER_CACHE_TOKEN,
18
+ },
19
+ }),
20
+ provide({
21
+ provide: CHILD_APP_LOADER_CACHE_TOKEN,
22
+ scope: Scope.SINGLETON,
23
+ useFactory: ({ createCache, cacheOptions, asyncLocalStorage }) => {
24
+ const inMemoryCache = createCache('memory', {
25
+ name: 'child-app-loader',
26
+ // When Child App script is evicted from server loader cache, we get a small memory leak,
27
+ // because providers in singleton child DI, page components / actions, will store a reference to removed script,
28
+ // and server loader cache will contain a new instance of the same script.
29
+ //
30
+ // So, it is better to have bigger cache size to prevent evicting from cache,
31
+ // also for one Child App we need to save 3 elements - server JS, stats JSON and loadable stats JSON.
32
+ max: 100,
33
+ ttl: 1000 * 60 * 60 * 24 * 5,
34
+ ...cacheOptions,
35
+ });
36
+ let childAppLoaderCache = inMemoryCache;
37
+ // Cache is not compatible with HMR (Hot Module Replacement) because after HMR and page reload,
38
+ // we get the page from the cache. To solve this, we use Async Local Storage to ensure the
39
+ // cache is only valid within the context of a single request.
40
+ if (process.env.NODE_ENV === 'development' && !!asyncLocalStorage) {
41
+ childAppLoaderCache = {
42
+ get(key) {
43
+ if (key?.startsWith('__DEBUG__')) {
44
+ const store = asyncLocalStorage.getStore();
45
+ if (store) {
46
+ return store[key];
47
+ }
48
+ }
49
+ else {
50
+ return inMemoryCache.get(key);
51
+ }
52
+ },
53
+ set(key, module) {
54
+ if (key?.startsWith('__DEBUG__')) {
55
+ const store = asyncLocalStorage.getStore();
56
+ if (store) {
57
+ store[key] = module;
58
+ }
59
+ }
60
+ else {
61
+ inMemoryCache.set(key, module);
62
+ }
63
+ },
64
+ };
65
+ }
66
+ return childAppLoaderCache;
67
+ },
68
+ deps: {
69
+ asyncLocalStorage: optional(ASYNC_LOCAL_STORAGE_TOKEN),
70
+ cacheOptions: optional(CHILD_APP_LOADER_CACHE_OPTIONS_TOKEN),
71
+ createCache: CREATE_CACHE_TOKEN,
72
+ },
73
+ }),
74
+ ];
75
+
76
+ export { cache };
@@ -0,0 +1,80 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var core = require('@tramvai/core');
6
+ var dippy = require('@tinkoff/dippy');
7
+ var tokensChildApp = require('@tramvai/tokens-child-app');
8
+ var tokensCommon = require('@tramvai/tokens-common');
9
+ var cacheCleanup = require('./cacheCleanup.js');
10
+
11
+ const cache = [
12
+ core.provide({
13
+ provide: core.commandLineListTokens.listen,
14
+ useFactory: ({ childAppLoaderCache, cacheCleanupConfig }) => {
15
+ return () => {
16
+ cacheCleanup.setCacheCleanupInterval(childAppLoaderCache, cacheCleanupConfig?.cleanupIntervalMs);
17
+ };
18
+ },
19
+ deps: {
20
+ cacheCleanupConfig: dippy.optional(tokensChildApp.CHILD_APP_LOADER_CACHE_CLEANUP_CONFIG_TOKEN),
21
+ childAppLoaderCache: tokensChildApp.CHILD_APP_LOADER_CACHE_TOKEN,
22
+ },
23
+ }),
24
+ core.provide({
25
+ provide: tokensChildApp.CHILD_APP_LOADER_CACHE_TOKEN,
26
+ scope: dippy.Scope.SINGLETON,
27
+ useFactory: ({ createCache, cacheOptions, asyncLocalStorage }) => {
28
+ const inMemoryCache = createCache('memory', {
29
+ name: 'child-app-loader',
30
+ // When Child App script is evicted from server loader cache, we get a small memory leak,
31
+ // because providers in singleton child DI, page components / actions, will store a reference to removed script,
32
+ // and server loader cache will contain a new instance of the same script.
33
+ //
34
+ // So, it is better to have bigger cache size to prevent evicting from cache,
35
+ // also for one Child App we need to save 3 elements - server JS, stats JSON and loadable stats JSON.
36
+ max: 100,
37
+ ttl: 1000 * 60 * 60 * 24 * 5,
38
+ ...cacheOptions,
39
+ });
40
+ let childAppLoaderCache = inMemoryCache;
41
+ // Cache is not compatible with HMR (Hot Module Replacement) because after HMR and page reload,
42
+ // we get the page from the cache. To solve this, we use Async Local Storage to ensure the
43
+ // cache is only valid within the context of a single request.
44
+ if (process.env.NODE_ENV === 'development' && !!asyncLocalStorage) {
45
+ childAppLoaderCache = {
46
+ get(key) {
47
+ if (key?.startsWith('__DEBUG__')) {
48
+ const store = asyncLocalStorage.getStore();
49
+ if (store) {
50
+ return store[key];
51
+ }
52
+ }
53
+ else {
54
+ return inMemoryCache.get(key);
55
+ }
56
+ },
57
+ set(key, module) {
58
+ if (key?.startsWith('__DEBUG__')) {
59
+ const store = asyncLocalStorage.getStore();
60
+ if (store) {
61
+ store[key] = module;
62
+ }
63
+ }
64
+ else {
65
+ inMemoryCache.set(key, module);
66
+ }
67
+ },
68
+ };
69
+ }
70
+ return childAppLoaderCache;
71
+ },
72
+ deps: {
73
+ asyncLocalStorage: dippy.optional(tokensCommon.ASYNC_LOCAL_STORAGE_TOKEN),
74
+ cacheOptions: dippy.optional(tokensChildApp.CHILD_APP_LOADER_CACHE_OPTIONS_TOKEN),
75
+ createCache: tokensCommon.CREATE_CACHE_TOKEN,
76
+ },
77
+ }),
78
+ ];
79
+
80
+ exports.cache = cache;
@@ -0,0 +1,4 @@
1
+ import { Cache } from '@tramvai/tokens-common';
2
+ export declare function cleanupStaleElementsInCache<T>(cache: Cache<T>): void;
3
+ export declare function setCacheCleanupInterval<T>(cache: Cache<T>, intervalMs?: number): void;
4
+ //# sourceMappingURL=cacheCleanup.d.ts.map
@@ -0,0 +1,16 @@
1
+ function cleanupStaleElementsInCache(cache) {
2
+ const entries = cache.dump();
3
+ const now = Date.now();
4
+ for (const [key, value] of entries) {
5
+ if (value.start + value.ttl < now) {
6
+ cache.delete(key);
7
+ }
8
+ }
9
+ }
10
+ function setCacheCleanupInterval(cache, intervalMs = 1000 * 60 * 60 * 24) {
11
+ setInterval(() => {
12
+ cleanupStaleElementsInCache(cache);
13
+ }, intervalMs);
14
+ }
15
+
16
+ export { cleanupStaleElementsInCache, setCacheCleanupInterval };
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ function cleanupStaleElementsInCache(cache) {
6
+ const entries = cache.dump();
7
+ const now = Date.now();
8
+ for (const [key, value] of entries) {
9
+ if (value.start + value.ttl < now) {
10
+ cache.delete(key);
11
+ }
12
+ }
13
+ }
14
+ function setCacheCleanupInterval(cache, intervalMs = 1000 * 60 * 60 * 24) {
15
+ setInterval(() => {
16
+ cleanupStaleElementsInCache(cache);
17
+ }, intervalMs);
18
+ }
19
+
20
+ exports.cleanupStaleElementsInCache = cleanupStaleElementsInCache;
21
+ exports.setCacheCleanupInterval = setCacheCleanupInterval;
@@ -1,21 +1,28 @@
1
- import type { ChildApp } from '@tramvai/child-app-core';
2
- import type { ChildAppFinalConfig } from '@tramvai/tokens-child-app';
3
- import type { CREATE_CACHE_TOKEN, LOGGER_TOKEN, ENV_MANAGER_TOKEN } from '@tramvai/tokens-common';
1
+ import type { CHILD_APP_LOADER_PLUGIN, ChildApp } from '@tramvai/child-app-core';
2
+ import { CHILD_APP_LOADER_CACHE_TOKEN, type ChildAppFinalConfig } from '@tramvai/tokens-child-app';
3
+ import type { LOGGER_TOKEN, ENV_MANAGER_TOKEN } from '@tramvai/tokens-common';
4
+ import { AsyncTapableHookInstance, TAPABLE_HOOK_FACTORY_TOKEN } from '@tramvai/core';
4
5
  import { Loader } from '../shared/loader';
5
6
  import type { LoadableStats, ModuleFederationStats } from '../shared/webpack/moduleFederation';
6
7
  export declare class ServerLoader extends Loader {
7
8
  private readonly loader;
8
9
  private readonly initializedMap;
9
- private internalLoadCache;
10
10
  private log;
11
- constructor({ logger, createCache, envManager, }: {
11
+ private hookFactory;
12
+ loadModuleHook: AsyncTapableHookInstance<{
13
+ config: ChildAppFinalConfig;
14
+ }, ChildApp | undefined>;
15
+ constructor({ logger, envManager, cache, hookFactory, plugins, }: {
16
+ hookFactory: typeof TAPABLE_HOOK_FACTORY_TOKEN;
12
17
  logger: typeof LOGGER_TOKEN;
13
- createCache: typeof CREATE_CACHE_TOKEN;
14
18
  envManager: typeof ENV_MANAGER_TOKEN;
19
+ cache: typeof CHILD_APP_LOADER_CACHE_TOKEN;
20
+ plugins: (typeof CHILD_APP_LOADER_PLUGIN)[] | null;
15
21
  });
22
+ private loadModule;
16
23
  load(config: ChildAppFinalConfig): Promise<ChildApp | void>;
17
24
  init(config: ChildAppFinalConfig): Promise<void>;
18
- get(config: ChildAppFinalConfig): ChildApp | void;
25
+ get(config: ChildAppFinalConfig): ChildApp | undefined;
19
26
  getStats(config: ChildAppFinalConfig): ModuleFederationStats | void;
20
27
  getLoadableStats(config: ChildAppFinalConfig): LoadableStats | void;
21
28
  waitFor(): Promise<void>;
@@ -4,15 +4,14 @@ import { Loader } from '../shared/loader.es.js';
4
4
  import { initModuleFederation, getModuleFederation } from '../shared/webpack/moduleFederation.es.js';
5
5
 
6
6
  class ServerLoader extends Loader {
7
- constructor({ logger, createCache, envManager, }) {
7
+ loader;
8
+ initializedMap = new WeakMap();
9
+ log;
10
+ hookFactory;
11
+ loadModuleHook;
12
+ constructor({ logger, envManager, cache, hookFactory, plugins, }) {
8
13
  super();
9
- this.initializedMap = new WeakMap();
10
- const cache = createCache('memory', {
11
- name: 'child-app-loader',
12
- ttl: 1000 * 60 * 60 * 24 * 5,
13
- max: 20,
14
- });
15
- this.internalLoadCache = cache;
14
+ this.hookFactory = hookFactory;
16
15
  this.log = logger('child-app:loader');
17
16
  this.loader = new ServerLoader$1({
18
17
  cache,
@@ -21,13 +20,21 @@ class ServerLoader extends Loader {
21
20
  circuitBreakerEnabled: isNil(envManager.get('HTTP_CLIENT_CIRCUIT_BREAKER_DISABLED')),
22
21
  },
23
22
  });
23
+ this.loadModuleHook = this.hookFactory.createAsync('childAppLoadModule');
24
+ this.loadModuleHook.tapPromise('childAppLoadModule', async (_, payload) => {
25
+ return this.loadModule(payload);
26
+ });
27
+ plugins?.forEach((plugin) => {
28
+ plugin.apply({ loadModule: this.loadModuleHook });
29
+ });
24
30
  }
25
- async load(config) {
31
+ async loadModule({ config }) {
26
32
  const promises = [
27
33
  this.loader.resolveByUrl(config.server.entry, {
28
34
  codePrefix: `var ASSETS_PREFIX="${config.client.baseUrl}";`,
29
35
  displayName: config.name,
30
36
  kind: 'child-app',
37
+ debug: config.tag === 'debug',
31
38
  }),
32
39
  this.loader
33
40
  .resolveByUrl(config.client.stats, {
@@ -35,6 +42,7 @@ class ServerLoader extends Loader {
35
42
  kind: 'child-app stats',
36
43
  displayName: config.name,
37
44
  silent: true,
45
+ debug: config.tag === 'debug',
38
46
  })
39
47
  // we can live without stats
40
48
  .catch(() => { }),
@@ -46,6 +54,7 @@ class ServerLoader extends Loader {
46
54
  kind: 'child-app loadable stats',
47
55
  displayName: config.name,
48
56
  silent: true,
57
+ debug: config.tag === 'debug',
49
58
  })
50
59
  // we can live without loadable stats, for backward compatibility is ok
51
60
  // but hydration errors will occur when lazy component will loaded at client-side at demand
@@ -53,17 +62,16 @@ class ServerLoader extends Loader {
53
62
  }
54
63
  await Promise.all(promises);
55
64
  await this.init(config);
56
- if (config.tag === 'debug') {
57
- setTimeout(() => {
58
- this.internalLoadCache.set(config.server.entry, null);
59
- this.internalLoadCache.set(config.client.stats, null);
60
- this.internalLoadCache.set(config.client.statsLoadable, null);
61
- }, 10000);
62
- }
63
65
  return this.get(config);
64
66
  }
67
+ async load(config) {
68
+ const childApp = await this.loadModuleHook.callPromise({ config });
69
+ return childApp;
70
+ }
65
71
  async init(config) {
66
- const container = this.loader.getByUrl(config.server.entry);
72
+ const container = this.loader.getByUrl(config.server.entry, {
73
+ debug: config.tag === 'debug',
74
+ });
67
75
  if (!container) {
68
76
  return;
69
77
  }
@@ -86,12 +94,19 @@ class ServerLoader extends Loader {
86
94
  }
87
95
  }
88
96
  get(config) {
89
- const container = this.loader.getByUrl(config.server.entry);
97
+ const container = this.loader.getByUrl(config.server.entry, {
98
+ debug: config.tag === 'debug',
99
+ });
100
+ if (!container) {
101
+ return undefined;
102
+ }
90
103
  const entry = container && this.initializedMap.get(container);
91
104
  return entry && this.resolve(entry);
92
105
  }
93
106
  getStats(config) {
94
- return this.loader.getByUrl(config.client.stats);
107
+ return this.loader.getByUrl(config.client.stats, {
108
+ debug: config.tag === 'debug',
109
+ });
95
110
  }
96
111
  getLoadableStats(config) {
97
112
  if (!config.client.statsLoadable) {
@@ -12,15 +12,14 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
12
12
  var isNil__default = /*#__PURE__*/_interopDefaultLegacy(isNil);
13
13
 
14
14
  class ServerLoader extends loader.Loader {
15
- constructor({ logger, createCache, envManager, }) {
15
+ loader;
16
+ initializedMap = new WeakMap();
17
+ log;
18
+ hookFactory;
19
+ loadModuleHook;
20
+ constructor({ logger, envManager, cache, hookFactory, plugins, }) {
16
21
  super();
17
- this.initializedMap = new WeakMap();
18
- const cache = createCache('memory', {
19
- name: 'child-app-loader',
20
- ttl: 1000 * 60 * 60 * 24 * 5,
21
- max: 20,
22
- });
23
- this.internalLoadCache = cache;
22
+ this.hookFactory = hookFactory;
24
23
  this.log = logger('child-app:loader');
25
24
  this.loader = new moduleLoaderServer.ServerLoader({
26
25
  cache,
@@ -29,13 +28,21 @@ class ServerLoader extends loader.Loader {
29
28
  circuitBreakerEnabled: isNil__default["default"](envManager.get('HTTP_CLIENT_CIRCUIT_BREAKER_DISABLED')),
30
29
  },
31
30
  });
31
+ this.loadModuleHook = this.hookFactory.createAsync('childAppLoadModule');
32
+ this.loadModuleHook.tapPromise('childAppLoadModule', async (_, payload) => {
33
+ return this.loadModule(payload);
34
+ });
35
+ plugins?.forEach((plugin) => {
36
+ plugin.apply({ loadModule: this.loadModuleHook });
37
+ });
32
38
  }
33
- async load(config) {
39
+ async loadModule({ config }) {
34
40
  const promises = [
35
41
  this.loader.resolveByUrl(config.server.entry, {
36
42
  codePrefix: `var ASSETS_PREFIX="${config.client.baseUrl}";`,
37
43
  displayName: config.name,
38
44
  kind: 'child-app',
45
+ debug: config.tag === 'debug',
39
46
  }),
40
47
  this.loader
41
48
  .resolveByUrl(config.client.stats, {
@@ -43,6 +50,7 @@ class ServerLoader extends loader.Loader {
43
50
  kind: 'child-app stats',
44
51
  displayName: config.name,
45
52
  silent: true,
53
+ debug: config.tag === 'debug',
46
54
  })
47
55
  // we can live without stats
48
56
  .catch(() => { }),
@@ -54,6 +62,7 @@ class ServerLoader extends loader.Loader {
54
62
  kind: 'child-app loadable stats',
55
63
  displayName: config.name,
56
64
  silent: true,
65
+ debug: config.tag === 'debug',
57
66
  })
58
67
  // we can live without loadable stats, for backward compatibility is ok
59
68
  // but hydration errors will occur when lazy component will loaded at client-side at demand
@@ -61,17 +70,16 @@ class ServerLoader extends loader.Loader {
61
70
  }
62
71
  await Promise.all(promises);
63
72
  await this.init(config);
64
- if (config.tag === 'debug') {
65
- setTimeout(() => {
66
- this.internalLoadCache.set(config.server.entry, null);
67
- this.internalLoadCache.set(config.client.stats, null);
68
- this.internalLoadCache.set(config.client.statsLoadable, null);
69
- }, 10000);
70
- }
71
73
  return this.get(config);
72
74
  }
75
+ async load(config) {
76
+ const childApp = await this.loadModuleHook.callPromise({ config });
77
+ return childApp;
78
+ }
73
79
  async init(config) {
74
- const container = this.loader.getByUrl(config.server.entry);
80
+ const container = this.loader.getByUrl(config.server.entry, {
81
+ debug: config.tag === 'debug',
82
+ });
75
83
  if (!container) {
76
84
  return;
77
85
  }
@@ -94,12 +102,19 @@ class ServerLoader extends loader.Loader {
94
102
  }
95
103
  }
96
104
  get(config) {
97
- const container = this.loader.getByUrl(config.server.entry);
105
+ const container = this.loader.getByUrl(config.server.entry, {
106
+ debug: config.tag === 'debug',
107
+ });
108
+ if (!container) {
109
+ return undefined;
110
+ }
98
111
  const entry = container && this.initializedMap.get(container);
99
112
  return entry && this.resolve(entry);
100
113
  }
101
114
  getStats(config) {
102
- return this.loader.getByUrl(config.client.stats);
115
+ return this.loader.getByUrl(config.client.stats, {
116
+ debug: config.tag === 'debug',
117
+ });
103
118
  }
104
119
  getLoadableStats(config) {
105
120
  if (!config.client.statsLoadable) {
@@ -0,0 +1,15 @@
1
+ import { SharedModule, SharedScopeItem } from './utils';
2
+ export type SharedModuleWithChunks = {
3
+ shareKey: string;
4
+ from: string;
5
+ version: string;
6
+ eager: boolean;
7
+ childAppName?: string;
8
+ childAppVersion?: string;
9
+ chunks?: string[];
10
+ };
11
+ export declare function resolveBestLoadedSharedModules({ sharedModules, sharedScopeItems, }: {
12
+ sharedModules: SharedModule[];
13
+ sharedScopeItems: SharedScopeItem[];
14
+ }): SharedModuleWithChunks[];
15
+ //# sourceMappingURL=best-loaded-shared-modules.d.ts.map
@@ -0,0 +1,26 @@
1
+ function resolveBestLoadedSharedModules({ sharedModules, sharedScopeItems, }) {
2
+ const result = [];
3
+ for (const sharedScopeItem of sharedScopeItems) {
4
+ const { shareKey, from, eager, loaded } = sharedScopeItem;
5
+ // we need only Child Apps deps, host app shared modules is always loaded on the client
6
+ // and we need to get only used shared modules, `loaded` flag means that module was required by MF runtime
7
+ // https://github.com/webpack/webpack/blob/97d4961cd1de9c69dba0f050a63f3b56bb64fab2/lib/sharing/ConsumeSharedRuntimeModule.js#L104
8
+ if (from.startsWith('child-app') && !!loaded) {
9
+ const sharedModule = sharedModules.find((m) => m.from === from && m.provides?.[0]?.shareKey === shareKey);
10
+ if (sharedModule) {
11
+ result.push({
12
+ shareKey,
13
+ from: sharedScopeItem.from,
14
+ eager,
15
+ version: sharedScopeItem.version,
16
+ childAppName: sharedModule?.childAppName,
17
+ childAppVersion: sharedModule?.childAppVersion,
18
+ chunks: sharedModule?.chunks,
19
+ });
20
+ }
21
+ }
22
+ }
23
+ return result;
24
+ }
25
+
26
+ export { resolveBestLoadedSharedModules };
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ function resolveBestLoadedSharedModules({ sharedModules, sharedScopeItems, }) {
6
+ const result = [];
7
+ for (const sharedScopeItem of sharedScopeItems) {
8
+ const { shareKey, from, eager, loaded } = sharedScopeItem;
9
+ // we need only Child Apps deps, host app shared modules is always loaded on the client
10
+ // and we need to get only used shared modules, `loaded` flag means that module was required by MF runtime
11
+ // https://github.com/webpack/webpack/blob/97d4961cd1de9c69dba0f050a63f3b56bb64fab2/lib/sharing/ConsumeSharedRuntimeModule.js#L104
12
+ if (from.startsWith('child-app') && !!loaded) {
13
+ const sharedModule = sharedModules.find((m) => m.from === from && m.provides?.[0]?.shareKey === shareKey);
14
+ if (sharedModule) {
15
+ result.push({
16
+ shareKey,
17
+ from: sharedScopeItem.from,
18
+ eager,
19
+ version: sharedScopeItem.version,
20
+ childAppName: sharedModule?.childAppName,
21
+ childAppVersion: sharedModule?.childAppVersion,
22
+ chunks: sharedModule?.chunks,
23
+ });
24
+ }
25
+ }
26
+ }
27
+ return result;
28
+ }
29
+
30
+ exports.resolveBestLoadedSharedModules = resolveBestLoadedSharedModules;
@@ -0,0 +1,15 @@
1
+ import { SharedModule, SharedScopeItem } from './utils';
2
+ export type SharedModuleWithChunks = {
3
+ shareKey: string;
4
+ from: string;
5
+ version: string;
6
+ eager: boolean;
7
+ childAppName?: string;
8
+ childAppVersion?: string;
9
+ chunks?: string[];
10
+ };
11
+ export declare function resolveBestSharedModules({ sharedModules, sharedScopeItems, }: {
12
+ sharedModules: SharedModule[];
13
+ sharedScopeItems: SharedScopeItem[];
14
+ }): SharedModuleWithChunks[];
15
+ //# sourceMappingURL=best-shared-modules.d.ts.map
@@ -0,0 +1,18 @@
1
+ import { ChildAppFinalConfig } from '@tramvai/tokens-child-app';
2
+ import { ModuleFederationSharedModule, ModuleFederationSharedScope, ModuleFederationSharedScopeItem } from '../../shared/webpack/moduleFederation';
3
+ import { ServerLoader } from '../loader';
4
+ export type SharedScopeItem = ModuleFederationSharedScopeItem & {
5
+ shareKey: string;
6
+ version: string;
7
+ };
8
+ export type SharedModule = ModuleFederationSharedModule & {
9
+ from: string;
10
+ childAppName: string;
11
+ childAppVersion: string;
12
+ };
13
+ export declare function getFlatSharedScopeItemsList(sharedScope: ModuleFederationSharedScope): SharedScopeItem[];
14
+ export declare function getFlatSharedModulesList({ preloadedConfigs, loader, }: {
15
+ preloadedConfigs: ChildAppFinalConfig[];
16
+ loader: ServerLoader;
17
+ }): SharedModule[];
18
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1,38 @@
1
+ function getFlatSharedScopeItemsList(sharedScope) {
2
+ // flat list of all shared items initialized in shared scope
3
+ const sharedScopeItems = [];
4
+ for (const shareKey in sharedScope) {
5
+ const sharedDependency = sharedScope[shareKey];
6
+ for (const version in sharedDependency) {
7
+ const sharedScopeItem = sharedDependency[version];
8
+ sharedScopeItems.push({
9
+ ...sharedScopeItem,
10
+ shareKey,
11
+ version,
12
+ });
13
+ }
14
+ }
15
+ return sharedScopeItems;
16
+ }
17
+ function getFlatSharedModulesList({ preloadedConfigs, loader, }) {
18
+ const sharedModules = [];
19
+ for (const config of preloadedConfigs) {
20
+ const stats = 'getStats' in loader ? loader.getStats(config) : undefined;
21
+ const from = `child-app:${config.name}:${config.version}`;
22
+ if (stats && stats.federatedModules) {
23
+ for (const federatedModule of stats.federatedModules) {
24
+ for (const sharedModule of federatedModule.sharedModules) {
25
+ sharedModules.push({
26
+ ...sharedModule,
27
+ from,
28
+ childAppName: config.name,
29
+ childAppVersion: config.version,
30
+ });
31
+ }
32
+ }
33
+ }
34
+ }
35
+ return sharedModules;
36
+ }
37
+
38
+ export { getFlatSharedModulesList, getFlatSharedScopeItemsList };
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ function getFlatSharedScopeItemsList(sharedScope) {
6
+ // flat list of all shared items initialized in shared scope
7
+ const sharedScopeItems = [];
8
+ for (const shareKey in sharedScope) {
9
+ const sharedDependency = sharedScope[shareKey];
10
+ for (const version in sharedDependency) {
11
+ const sharedScopeItem = sharedDependency[version];
12
+ sharedScopeItems.push({
13
+ ...sharedScopeItem,
14
+ shareKey,
15
+ version,
16
+ });
17
+ }
18
+ }
19
+ return sharedScopeItems;
20
+ }
21
+ function getFlatSharedModulesList({ preloadedConfigs, loader, }) {
22
+ const sharedModules = [];
23
+ for (const config of preloadedConfigs) {
24
+ const stats = 'getStats' in loader ? loader.getStats(config) : undefined;
25
+ const from = `child-app:${config.name}:${config.version}`;
26
+ if (stats && stats.federatedModules) {
27
+ for (const federatedModule of stats.federatedModules) {
28
+ for (const sharedModule of federatedModule.sharedModules) {
29
+ sharedModules.push({
30
+ ...sharedModule,
31
+ from,
32
+ childAppName: config.name,
33
+ childAppVersion: config.version,
34
+ });
35
+ }
36
+ }
37
+ }
38
+ }
39
+ return sharedModules;
40
+ }
41
+
42
+ exports.getFlatSharedModulesList = getFlatSharedModulesList;
43
+ exports.getFlatSharedScopeItemsList = getFlatSharedScopeItemsList;