@tramvai/module-child-app 4.9.0 → 4.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { loadModule } from '@tinkoff/module-loader-client';
1
+ import { loadModule, waitModule } from '@tinkoff/module-loader-client';
2
2
  import { Loader } from '../shared/loader.browser.js';
3
3
  import { initModuleFederation, getModuleFederation } from '../shared/webpack/moduleFederation.browser.js';
4
4
 
@@ -52,6 +52,10 @@ class BrowserLoader extends Loader {
52
52
  async init(config) {
53
53
  const container = getModuleFromGlobal(config.client.entry);
54
54
  if (container) {
55
+ this.log.debug({
56
+ event: 'init',
57
+ moduleName: config.name,
58
+ });
55
59
  await initModuleFederation(container);
56
60
  const factory = (await getModuleFederation(container, 'entry'));
57
61
  const entry = factory();
@@ -63,6 +67,14 @@ class BrowserLoader extends Loader {
63
67
  const entry = container && this.initializedMap.get(container);
64
68
  return entry && this.resolve(entry);
65
69
  }
70
+ async waitFor(config) {
71
+ this.log.debug({
72
+ event: 'wait-for',
73
+ moduleName: config.name,
74
+ });
75
+ // wait for entry chunk
76
+ await waitModule(config.client.entry);
77
+ }
66
78
  }
67
79
 
68
80
  export { BrowserLoader, getModuleFromGlobal };
@@ -13,5 +13,6 @@ export declare class BrowserLoader extends Loader {
13
13
  load(config: ChildAppFinalConfig): Promise<ChildApp | void>;
14
14
  init(config: ChildAppFinalConfig): Promise<void>;
15
15
  get(config: ChildAppFinalConfig): ChildApp | void;
16
+ waitFor(config: ChildAppFinalConfig): Promise<void>;
16
17
  }
17
18
  //# sourceMappingURL=loader.d.ts.map
@@ -77,13 +77,24 @@ class PreloadManager {
77
77
  const promises = [];
78
78
  this.currentlyPreloaded.forEach((config) => {
79
79
  promises.push((async () => {
80
+ // double check that preloaded Child App entry chunk is already loaded on the client.
81
+ // in streaming mode with async scripts it may not be loaded yet, and we need to wait this script.
82
+ // we don't need to load this script, because for server preloaded Child Apps it already been in HTML,
83
+ // for not preloaded Child Apps we don't want to delay hydration
84
+ if (!getModuleFromGlobal(config.client.entry)) {
85
+ // TODO: current test cases work with `loader.load`, need to be sure that new `loader.waitFor` method is necessary
86
+ await this.loader.waitFor(config).catch((e) => {
87
+ // it is expected case if entry chunk is not existed or failed
88
+ });
89
+ }
90
+ // if entry chunk is already loaded, here other Child App chunks will be waited or loaded
80
91
  await this.loader.init(config);
81
92
  // case for client initialization of Child App
82
93
  await this.resolveComponent(config);
83
94
  await this.run('customer', config);
84
95
  })());
85
96
  });
86
- await Promise.all(promises);
97
+ await Promise.allSettled(promises);
87
98
  }
88
99
  pageRender() {
89
100
  this.pageHasRendered = true;
@@ -112,13 +123,8 @@ class PreloadManager {
112
123
  preloaded.forEach((request) => {
113
124
  const config = this.resolveExternalConfig(request);
114
125
  if (config) {
115
- // double check that Child App script is already loaded.
116
- // in streaming mode with async scripts it may not be loaded yet,
117
- // and we will run client initialization flow for this Child App, like it was not preloaded
118
- if (getModuleFromGlobal(config.client.entry)) {
119
- this.currentlyPreloaded.set(config.key, config);
120
- this.hasPreloadBefore.add(config.key);
121
- }
126
+ this.currentlyPreloaded.set(config.key, config);
127
+ this.hasPreloadBefore.add(config.key);
122
128
  }
123
129
  });
124
130
  this.hasInitialized = true;
@@ -126,7 +132,7 @@ class PreloadManager {
126
132
  }
127
133
  async init() {
128
134
  await this.resolutionConfigManager.init();
129
- this.initServerPreloaded();
135
+ await this.initServerPreloaded();
130
136
  }
131
137
  async run(status, config) {
132
138
  const childApp = this.loader.get(config);
@@ -48,17 +48,24 @@ const browserProviders = [
48
48
  provide({
49
49
  provide: commandLineListTokens.resolvePageDeps,
50
50
  multi: true,
51
- useFactory: ({ preloader }) => {
51
+ useFactory: ({ preloader, logger }) => {
52
52
  let isSpaNavigation = false;
53
53
  return function childAppRunPreloaded() {
54
54
  if (isSpaNavigation)
55
55
  return;
56
56
  isSpaNavigation = true;
57
- return preloader.runPreloaded();
57
+ return preloader.runPreloaded().catch((error) => {
58
+ const log = logger('child-app:run-preloaded');
59
+ log.error({
60
+ event: 'client-failed',
61
+ error,
62
+ });
63
+ });
58
64
  };
59
65
  },
60
66
  deps: {
61
67
  preloader: CHILD_APP_PRELOAD_MANAGER_TOKEN,
68
+ logger: LOGGER_TOKEN,
62
69
  },
63
70
  }),
64
71
  provide({
@@ -78,7 +85,7 @@ const browserProviders = [
78
85
  provide({
79
86
  provide: commandLineListTokens.spaTransition,
80
87
  multi: true,
81
- useFactory: ({ preloader, runner, diManager, pageService }) => {
88
+ useFactory: ({ preloader, runner, diManager, pageService, logger }) => {
82
89
  return async function childAppRunPreloaded() {
83
90
  await runCommand({
84
91
  preloader,
@@ -87,6 +94,7 @@ const browserProviders = [
87
94
  pageService,
88
95
  status: 'spa',
89
96
  forcePreload: false,
97
+ logger,
90
98
  });
91
99
  };
92
100
  },
@@ -95,12 +103,13 @@ const browserProviders = [
95
103
  runner: CHILD_APP_COMMAND_LINE_RUNNER_TOKEN,
96
104
  diManager: CHILD_APP_DI_MANAGER_TOKEN,
97
105
  pageService: PAGE_SERVICE_TOKEN,
106
+ logger: LOGGER_TOKEN,
98
107
  },
99
108
  }),
100
109
  provide({
101
110
  provide: commandLineListTokens.afterSpaTransition,
102
111
  multi: true,
103
- useFactory: ({ preloader, runner, diManager, pageService }) => {
112
+ useFactory: ({ preloader, runner, diManager, pageService, logger }) => {
104
113
  return async function childAppRunPreloaded() {
105
114
  await runCommand({
106
115
  preloader,
@@ -109,6 +118,7 @@ const browserProviders = [
109
118
  pageService,
110
119
  status: 'afterSpa',
111
120
  forcePreload: true,
121
+ logger,
112
122
  });
113
123
  };
114
124
  },
@@ -117,6 +127,7 @@ const browserProviders = [
117
127
  runner: CHILD_APP_COMMAND_LINE_RUNNER_TOKEN,
118
128
  diManager: CHILD_APP_DI_MANAGER_TOKEN,
119
129
  pageService: PAGE_SERVICE_TOKEN,
130
+ logger: LOGGER_TOKEN,
120
131
  },
121
132
  }),
122
133
  ];
@@ -1,4 +1,4 @@
1
- const runCommand = async ({ status, forcePreload, runner, preloader, diManager, pageService, }) => {
1
+ const runCommand = async ({ status, forcePreload, runner, preloader, diManager, pageService, logger, }) => {
2
2
  const childApps = preloader.getPreloadedList();
3
3
  await Promise.all(childApps.map(async (config) => {
4
4
  if (forcePreload) {
@@ -6,7 +6,13 @@ const runCommand = async ({ status, forcePreload, runner, preloader, diManager,
6
6
  await preloader.preload(config, pageService.getCurrentRoute());
7
7
  }
8
8
  return runner.run('client', status, config);
9
- }));
9
+ })).catch((error) => {
10
+ const log = logger('child-app:run-preloaded');
11
+ log.error({
12
+ event: 'spa-failed',
13
+ error,
14
+ });
15
+ });
10
16
  };
11
17
 
12
18
  export { runCommand };
@@ -1,12 +1,14 @@
1
1
  import type { CHILD_APP_DI_MANAGER_TOKEN } from '@tramvai/tokens-child-app';
2
2
  import { type CHILD_APP_COMMAND_LINE_RUNNER_TOKEN, type CHILD_APP_PRELOAD_MANAGER_TOKEN } from '@tramvai/tokens-child-app';
3
+ import type { LOGGER_TOKEN } from '@tramvai/tokens-common';
3
4
  import type { PAGE_SERVICE_TOKEN } from '@tramvai/tokens-router';
4
- export declare const runCommand: ({ status, forcePreload, runner, preloader, diManager, pageService, }: {
5
+ export declare const runCommand: ({ status, forcePreload, runner, preloader, diManager, pageService, logger, }: {
5
6
  status: string;
6
7
  forcePreload: boolean;
7
8
  runner: typeof CHILD_APP_COMMAND_LINE_RUNNER_TOKEN;
8
9
  preloader: typeof CHILD_APP_PRELOAD_MANAGER_TOKEN;
9
10
  diManager: typeof CHILD_APP_DI_MANAGER_TOKEN;
10
11
  pageService: typeof PAGE_SERVICE_TOKEN;
12
+ logger: typeof LOGGER_TOKEN;
11
13
  }) => Promise<void>;
12
14
  //# sourceMappingURL=runCommand.d.ts.map
@@ -18,5 +18,6 @@ export declare class ServerLoader extends Loader {
18
18
  get(config: ChildAppFinalConfig): ChildApp | void;
19
19
  getStats(config: ChildAppFinalConfig): ModuleFederationStats | void;
20
20
  getLoadableStats(config: ChildAppFinalConfig): LoadableStats | void;
21
+ waitFor(): Promise<void>;
21
22
  }
22
23
  //# sourceMappingURL=loader.d.ts.map
@@ -90,6 +90,9 @@ class ServerLoader extends Loader {
90
90
  }
91
91
  return this.loader.getByUrl(config.client.statsLoadable);
92
92
  }
93
+ async waitFor() {
94
+ throw Error('Method "waitFor" is not implemented for server loader');
95
+ }
93
96
  }
94
97
 
95
98
  export { ServerLoader };
@@ -98,6 +98,9 @@ class ServerLoader extends loader.Loader {
98
98
  }
99
99
  return this.loader.getByUrl(config.client.statsLoadable);
100
100
  }
101
+ async waitFor() {
102
+ throw Error('Method "waitFor" is not implemented for server loader');
103
+ }
101
104
  }
102
105
 
103
106
  exports.ServerLoader = ServerLoader;
@@ -110,13 +110,20 @@ const serverProviders = [
110
110
  provide({
111
111
  provide: commandLineListTokens.resolvePageDeps,
112
112
  multi: true,
113
- useFactory: ({ preloader }) => {
113
+ useFactory: ({ preloader, logger }) => {
114
114
  return function childAppRunPreloaded() {
115
- return preloader.runPreloaded();
115
+ return preloader.runPreloaded().catch((error) => {
116
+ const log = logger('child-app:run-preloaded');
117
+ log.error({
118
+ event: 'server-failed',
119
+ error,
120
+ });
121
+ });
116
122
  };
117
123
  },
118
124
  deps: {
119
125
  preloader: CHILD_APP_PRELOAD_MANAGER_TOKEN,
126
+ logger: LOGGER_TOKEN,
120
127
  },
121
128
  }),
122
129
  provide({
@@ -114,13 +114,20 @@ const serverProviders = [
114
114
  core.provide({
115
115
  provide: core.commandLineListTokens.resolvePageDeps,
116
116
  multi: true,
117
- useFactory: ({ preloader }) => {
117
+ useFactory: ({ preloader, logger }) => {
118
118
  return function childAppRunPreloaded() {
119
- return preloader.runPreloaded();
119
+ return preloader.runPreloaded().catch((error) => {
120
+ const log = logger('child-app:run-preloaded');
121
+ log.error({
122
+ event: 'server-failed',
123
+ error,
124
+ });
125
+ });
120
126
  };
121
127
  },
122
128
  deps: {
123
129
  preloader: tokensChildApp.CHILD_APP_PRELOAD_MANAGER_TOKEN,
130
+ logger: tokensCommon.LOGGER_TOKEN,
124
131
  },
125
132
  }),
126
133
  core.provide({
@@ -20,6 +20,11 @@ const entryAttrs = {
20
20
  onload: `this.setAttribute('loaded', 'true')`,
21
21
  onerror: `this.setAttribute('loaded', 'false')`,
22
22
  };
23
+ // for cases when preloaded chunk loading is failed before webpack add this script loading handlers,
24
+ // we need to remove script and webpack will try to load it itself https://github.com/webpack/webpack/issues/14874
25
+ const chunkAttrs = {
26
+ onerror: `this.remove()`,
27
+ };
23
28
  const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, preloadManager, loader, renderMode, resourcesRegistry, }) => async () => {
24
29
  const log = logger('child-app:render:slots');
25
30
  const result = [];
@@ -41,7 +46,7 @@ const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, pre
41
46
  attrs: {
42
47
  'data-critical': 'true',
43
48
  ...scriptTypeAttr,
44
- ...(isEntry ? entryAttrs : {}),
49
+ ...(isEntry ? entryAttrs : chunkAttrs),
45
50
  },
46
51
  });
47
52
  break;
@@ -28,6 +28,11 @@ const entryAttrs = {
28
28
  onload: `this.setAttribute('loaded', 'true')`,
29
29
  onerror: `this.setAttribute('loaded', 'false')`,
30
30
  };
31
+ // for cases when preloaded chunk loading is failed before webpack add this script loading handlers,
32
+ // we need to remove script and webpack will try to load it itself https://github.com/webpack/webpack/issues/14874
33
+ const chunkAttrs = {
34
+ onerror: `this.remove()`,
35
+ };
31
36
  const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, preloadManager, loader, renderMode, resourcesRegistry, }) => async () => {
32
37
  const log = logger('child-app:render:slots');
33
38
  const result = [];
@@ -49,7 +54,7 @@ const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, pre
49
54
  attrs: {
50
55
  'data-critical': 'true',
51
56
  ...scriptTypeAttr,
52
- ...(isEntry ? entryAttrs : {}),
57
+ ...(isEntry ? entryAttrs : chunkAttrs),
53
58
  },
54
59
  });
55
60
  break;
@@ -5,6 +5,7 @@ export declare abstract class Loader implements ChildAppLoader {
5
5
  abstract get(config: ChildAppFinalConfig): ChildApp | void;
6
6
  abstract load(config: ChildAppFinalConfig): Promise<ChildApp | void>;
7
7
  abstract init(config: ChildAppFinalConfig): Promise<void>;
8
+ abstract waitFor(config: ChildAppFinalConfig): Promise<void>;
8
9
  protected resolve(entry: ChildAppModuleWrapper): ChildApp;
9
10
  }
10
11
  //# sourceMappingURL=loader.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tramvai/module-child-app",
3
- "version": "4.9.0",
3
+ "version": "4.10.1",
4
4
  "description": "Module for child apps",
5
5
  "browser": {
6
6
  "./lib/server.js": "./lib/browser.js",
@@ -31,25 +31,25 @@
31
31
  "dependencies": {
32
32
  "@loadable/server": "^5.15.0",
33
33
  "@tinkoff/env-validators": "0.3.1",
34
- "@tinkoff/module-loader-client": "0.6.2",
34
+ "@tinkoff/module-loader-client": "0.6.3",
35
35
  "@tinkoff/module-loader-server": "0.7.1",
36
- "@tramvai/module-common": "4.9.0",
36
+ "@tramvai/module-common": "4.10.1",
37
37
  "@tinkoff/url": "0.10.1",
38
- "@tramvai/child-app-core": "4.9.0",
38
+ "@tramvai/child-app-core": "4.10.1",
39
39
  "@tramvai/safe-strings": "0.7.1",
40
- "@tramvai/tokens-child-app": "4.9.0"
40
+ "@tramvai/tokens-child-app": "4.10.1"
41
41
  },
42
42
  "devDependencies": {},
43
43
  "peerDependencies": {
44
44
  "@tinkoff/dippy": "0.10.2",
45
- "@tinkoff/router": "0.4.31",
45
+ "@tinkoff/router": "0.4.34",
46
46
  "@tinkoff/utils": "^2.1.2",
47
- "@tramvai/core": "4.9.0",
48
- "@tramvai/state": "4.9.0",
49
- "@tramvai/react": "4.9.0",
50
- "@tramvai/tokens-common": "4.9.0",
51
- "@tramvai/tokens-render": "4.9.0",
52
- "@tramvai/tokens-router": "4.9.0",
47
+ "@tramvai/core": "4.10.1",
48
+ "@tramvai/state": "4.10.1",
49
+ "@tramvai/react": "4.10.1",
50
+ "@tramvai/tokens-common": "4.10.1",
51
+ "@tramvai/tokens-render": "4.10.1",
52
+ "@tramvai/tokens-router": "4.10.1",
53
53
  "react": ">=16.14.0",
54
54
  "react-dom": ">=16.14.0",
55
55
  "object-assign": "^4.1.1",