@tramvai/module-child-app 2.76.2 → 2.78.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.
- package/README.md +117 -1
- package/lib/server/loader.d.ts +3 -0
- package/lib/server/loader.es.js +33 -8
- package/lib/server/loader.js +33 -8
- package/lib/server/providers.es.js +1 -0
- package/lib/server/providers.js +1 -0
- package/lib/server/render-slots.d.ts +6 -3
- package/lib/server/render-slots.es.js +98 -21
- package/lib/server/render-slots.js +101 -20
- package/lib/shared/providers.browser.js +1 -0
- package/lib/shared/providers.es.js +1 -0
- package/lib/shared/providers.js +1 -0
- package/lib/shared/webpack/moduleFederation.browser.js +10 -5
- package/lib/shared/webpack/moduleFederation.d.ts +37 -4
- package/lib/shared/webpack/moduleFederation.es.js +14 -6
- package/lib/shared/webpack/moduleFederation.js +14 -5
- package/package.json +13 -11
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ This section will explain how the child-app are loaded and executed.
|
|
|
62
62
|
|
|
63
63
|
### DI
|
|
64
64
|
|
|
65
|
-
Every child-app has its own DI-hierarchy which is isolated from other child app and partially from root-app. The only way communicate
|
|
65
|
+
Every child-app has its own DI-hierarchy which is isolated from other child app and partially from root-app. The only way communicate for DIs it's getting providers from root-app DI inside child-app.
|
|
66
66
|
|
|
67
67
|
Next picture shows connection between DI-containers in `root-app` and `child-app`s
|
|
68
68
|
|
|
@@ -80,6 +80,16 @@ How does it work when we trying to get provider from DI in `child-app`:
|
|
|
80
80
|
1. If it exists then return it
|
|
81
81
|
2. Throw error otherwise
|
|
82
82
|
|
|
83
|
+
There is a list of providers - exceptions, for which only factories will be borrowed, not instances, and new instances will be created in current Child-App DI:
|
|
84
|
+
- `COMMAND_LINE_RUNNER_TOKEN`
|
|
85
|
+
- `ACTION_EXECUTION_TOKEN`
|
|
86
|
+
- `ACTION_PAGE_RUNNER_TOKEN`
|
|
87
|
+
- `DISPATCHER_TOKEN`
|
|
88
|
+
- `STORE_TOKEN`
|
|
89
|
+
- `CONTEXT_TOKEN`
|
|
90
|
+
- `CREATE_CACHE_TOKEN`
|
|
91
|
+
- `CLEAR_CACHE_TOKEN`
|
|
92
|
+
|
|
83
93
|
### CommandLineRunner
|
|
84
94
|
|
|
85
95
|
Each `child-app` has its own CommandLineRunner instance which allows to `child-app` make some preparations before the actual page render. This CommandLineRunner has almost identical lines as `root-app` to simplicity, but it is actually completely other line which are independent from lines in `root-app`
|
|
@@ -164,6 +174,39 @@ This token is considered undesirable to use as it leads to high coupling with st
|
|
|
164
174
|
|
|
165
175
|
:::
|
|
166
176
|
|
|
177
|
+
### Module Federation sharing dependencies
|
|
178
|
+
|
|
179
|
+
Child-apps utilizes [Module Federation](https://webpack.js.org/concepts/module-federation/) feature of webpack.
|
|
180
|
+
|
|
181
|
+
That allows child-apps:
|
|
182
|
+
- share dependencies between child-apps and root-app
|
|
183
|
+
- fallbacks to loading dependencies on request if implementation for dependency was not provided before or version of the dependency not satisfies request
|
|
184
|
+
|
|
185
|
+
The list of default shared dependencies is very short as it can increase bundle size in cases when child-apps are not used.
|
|
186
|
+
|
|
187
|
+
The following dependencies are shared by default:
|
|
188
|
+
- react core packages (react, react-dom, react/jsx-runtime)
|
|
189
|
+
- @tramvai/react
|
|
190
|
+
- @tinkoff/dippy
|
|
191
|
+
- @tramvai/core
|
|
192
|
+
|
|
193
|
+
To add additional dependency follow [instructions](#add-dependency-to-shared-list)
|
|
194
|
+
|
|
195
|
+
#### FAQ about shared dependencies
|
|
196
|
+
|
|
197
|
+
- **How shared dependencies look like?**. It mostly the implementation details but some info below might be useful for understanding:
|
|
198
|
+
- if shared dependency is provided in root-app the dependency will built in the initial chunk of root-app and dependency will be available without any additional network requests (these dependencies are marked as `eager` in moduleFederation config)
|
|
199
|
+
- if shared dependency is missing in the root-app then additional network request will be executed to some of child-app static files to load dependency code (the highest available version of dependency from all of child-apps will be loaded) i.e. additional js file with the name of shared dependency will be loaded on child-app usage.
|
|
200
|
+
- **How does shared dependencies affects root-app build?**. Using shared dependency slightly increases the generated bundle size. So it is preferred to make the list of shared dependencies as small as possible.
|
|
201
|
+
- **How versions of shared dependencies are resolved?**. Module federation will prefer to use the highest available version for the dependency but only if it satisfies the semver constraints of the all consumers. So it is preferred to use higher versions of the dependencies in the root-app and do not upgrade dependency versions in the child-apps without special need.
|
|
202
|
+
- **Dependency is added to list of shared but is not used by the app code**. Such dependency will not be provided and will not be available for consumption by other apps in that case.
|
|
203
|
+
- **How css is shared?**. Currently css are fully separated between root-app and child-app and child-app buid generates only single css file for the whole child-app
|
|
204
|
+
- **If two modules are using same shared dependency and root-app doesn't provide this dependency will the code for dep be loaded twice?**. It depends. On the client-side module federation will try to make only single network request, but with SSR it becomes a little more complicated and it is hard to resolve everything properly on server-side so sometimes it may lead to two network requests for different versions of the same dependency.
|
|
205
|
+
- **If version in child-app and root-app are not semver compatible**. Then child-app will load it's own version in that case as root-app cannot provide compatible version
|
|
206
|
+
- **Can I make sure the shared dependency is initialized only once across consumers?**. Yes, you can pass an object with `singleton` property instead of bare string in the tramvai.json config for shared dependency.
|
|
207
|
+
- **Should I add only high level wrapper of the dependencies I need to provide the list of all dependencies that I want to share?**. Better try different setups and see the output bundle size as it depends. The main rule is provide all of modules that might be imported by app code and that use the same low-level libraries. E.g. to share react-query integration add `@tramvai/module-react-query` and `@tramvai/react-query` to the shared dependencies
|
|
208
|
+
- **When building child-app I see two chunks related to the same package**. It happens due to some of caveats how module federation works. But anyway most of the time only single chunk will be used for the package, so just ignore the fact that in generated files you see two chunks.
|
|
209
|
+
|
|
167
210
|
### Error handling
|
|
168
211
|
|
|
169
212
|
#### Error while loading child-app configs
|
|
@@ -359,6 +402,56 @@ const PageCmp: PageComponent = () => {
|
|
|
359
402
|
PageCmp.childApps = [{ name: '[name]' }];
|
|
360
403
|
```
|
|
361
404
|
|
|
405
|
+
### Add dependency to shared list
|
|
406
|
+
|
|
407
|
+
:::tip
|
|
408
|
+
|
|
409
|
+
To get most of the sharing dependencies add dependency both for root-apps that uses child-apps with the dependency and child-apps that uses the dependency
|
|
410
|
+
|
|
411
|
+
:::
|
|
412
|
+
|
|
413
|
+
In tramvai.json add new `shared` field
|
|
414
|
+
|
|
415
|
+
```json
|
|
416
|
+
{
|
|
417
|
+
"projects": {
|
|
418
|
+
"root-app": {
|
|
419
|
+
"name": "root-app",
|
|
420
|
+
"root": "root-app",
|
|
421
|
+
"type": "application",
|
|
422
|
+
"hotRefresh": {
|
|
423
|
+
"enabled": true
|
|
424
|
+
},
|
|
425
|
+
"shared": {
|
|
426
|
+
"deps": [
|
|
427
|
+
"@tramvai/react-query",
|
|
428
|
+
"@tramvai/module-react-query",
|
|
429
|
+
{ "name": "@tramvai/state", "singleton": true }
|
|
430
|
+
]
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
"child-app": {
|
|
434
|
+
"name": "child-app",
|
|
435
|
+
"root": "child-app",
|
|
436
|
+
"type": "child-app",
|
|
437
|
+
"shared": {
|
|
438
|
+
"deps": [
|
|
439
|
+
"@tramvai/react-query",
|
|
440
|
+
"@tramvai/module-react-query",
|
|
441
|
+
{ "name": "@tramvai/state", "singleton": true },
|
|
442
|
+
]
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
In order to choose what dependencies should be shared:
|
|
450
|
+
- use `tramvai analyze` command to explore the output bundle and how different options affects it
|
|
451
|
+
- try different dependencies and see what is loading on the page when child-app is used
|
|
452
|
+
- validate how adding shared dependency affects root-app bundle size through `trmavai analyze`
|
|
453
|
+
|
|
454
|
+
|
|
362
455
|
### Debug child-app problems
|
|
363
456
|
|
|
364
457
|
If your are facing any problems while developing or using child-app use next instructions first.
|
|
@@ -441,3 +534,26 @@ Few advices to avoid this problem:
|
|
|
441
534
|
|
|
442
535
|
- Memoize object, passed to child-app `props` property
|
|
443
536
|
- Prevent pass to child-app properties, which can be changed during hydration, for example at client-side in page actions
|
|
537
|
+
|
|
538
|
+
### Shared dependency are still loaded although the root-app shares it
|
|
539
|
+
|
|
540
|
+
Refer to the [FAQ](#faq-about-shared-dependencies) about the details. In summary:
|
|
541
|
+
- it is more reliable to provide shared dependency from the root-app than relying on sharing between several child-apps
|
|
542
|
+
- make sure all versions of the shared dependencies are semver compatible
|
|
543
|
+
|
|
544
|
+
### Token with name already created!
|
|
545
|
+
|
|
546
|
+
The issue happens when `@tinkoff/dippy` library is shared due to fact that root-app and child-apps will have separate instances of the same tokens packages with the same naming.
|
|
547
|
+
|
|
548
|
+
For now, just ignore that kind of warnings during development. In producation these warnings won't be shown
|
|
549
|
+
|
|
550
|
+
### Possible problems with shared dependency
|
|
551
|
+
|
|
552
|
+
#### react-query: No QueryClient set, use QueryClientProvider to set one
|
|
553
|
+
|
|
554
|
+
The issue may happen if there are different instances of `@tramvai/module-react-query` and `@tramvai/react-query` and therefore internal code inside `@tramvai/react-query` resolves React Context that differs from the QueryClient Provided inside `@tramvai/module-react-query`
|
|
555
|
+
|
|
556
|
+
To resolve the issue:
|
|
557
|
+
- when defining shared dependencies add both `@tramvai/module-react-query` and `@tramvai/module-react-query`
|
|
558
|
+
- make sure that both packages are used in the root-app (or none) as both instances should resolve to one place and if it isn't apply then for example `@tramvai/react-query` might instantiate with different React Context
|
|
559
|
+
- another option would be to add underlying library `@tanstack/react-query` to both child-app and root-app shared dependencies to make sure that required React Context is created only in single instance
|
package/lib/server/loader.d.ts
CHANGED
|
@@ -2,10 +2,12 @@ import type { ChildApp } from '@tramvai/child-app-core';
|
|
|
2
2
|
import type { ChildAppFinalConfig } from '@tramvai/tokens-child-app';
|
|
3
3
|
import type { CREATE_CACHE_TOKEN, LOGGER_TOKEN } from '@tramvai/tokens-common';
|
|
4
4
|
import { Loader } from '../shared/loader';
|
|
5
|
+
import type { ModuleFederationStats } from '../shared/webpack/moduleFederation';
|
|
5
6
|
export declare class ServerLoader extends Loader {
|
|
6
7
|
private readonly loader;
|
|
7
8
|
private readonly initializedMap;
|
|
8
9
|
private internalLoadCache;
|
|
10
|
+
private log;
|
|
9
11
|
constructor({ logger, createCache, }: {
|
|
10
12
|
logger: typeof LOGGER_TOKEN;
|
|
11
13
|
createCache: typeof CREATE_CACHE_TOKEN;
|
|
@@ -13,4 +15,5 @@ export declare class ServerLoader extends Loader {
|
|
|
13
15
|
load(config: ChildAppFinalConfig): Promise<ChildApp | void>;
|
|
14
16
|
init(config: ChildAppFinalConfig): Promise<void>;
|
|
15
17
|
get(config: ChildAppFinalConfig): ChildApp | void;
|
|
18
|
+
getStats(config: ChildAppFinalConfig): ModuleFederationStats | void;
|
|
16
19
|
}
|
package/lib/server/loader.es.js
CHANGED
|
@@ -11,17 +11,28 @@ class ServerLoader extends Loader {
|
|
|
11
11
|
max: 20,
|
|
12
12
|
});
|
|
13
13
|
this.internalLoadCache = cache;
|
|
14
|
+
this.log = logger('child-app:loader');
|
|
14
15
|
this.loader = new ServerLoader$1({
|
|
15
16
|
cache,
|
|
16
|
-
log:
|
|
17
|
+
log: this.log,
|
|
17
18
|
});
|
|
18
19
|
}
|
|
19
20
|
async load(config) {
|
|
20
|
-
await
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
await Promise.all([
|
|
22
|
+
this.loader.resolveByUrl(config.server.entry, {
|
|
23
|
+
codePrefix: `var ASSETS_PREFIX="${config.client.baseUrl}";`,
|
|
24
|
+
displayName: config.name,
|
|
25
|
+
kind: 'child-app',
|
|
26
|
+
}),
|
|
27
|
+
this.loader
|
|
28
|
+
.resolveByUrl(config.client.stats, {
|
|
29
|
+
type: 'json',
|
|
30
|
+
kind: 'child-app stats',
|
|
31
|
+
displayName: config.name,
|
|
32
|
+
})
|
|
33
|
+
// we can live without stats
|
|
34
|
+
.catch(() => { }),
|
|
35
|
+
]);
|
|
25
36
|
await this.init(config);
|
|
26
37
|
if (config.tag === 'debug') {
|
|
27
38
|
setTimeout(() => {
|
|
@@ -32,16 +43,30 @@ class ServerLoader extends Loader {
|
|
|
32
43
|
}
|
|
33
44
|
async init(config) {
|
|
34
45
|
const container = this.loader.getByUrl(config.server.entry);
|
|
35
|
-
if (container) {
|
|
46
|
+
if (!container) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
36
50
|
await initModuleFederation(container, 'default');
|
|
51
|
+
// copy some logic from https://github.com/module-federation/universe/blob/02221527aa684d2a37773c913bf341748fd34ecf/packages/node/src/plugins/loadScript.ts#L66
|
|
52
|
+
// to implement the same logic for loading child-app as UniversalModuleFederation
|
|
53
|
+
global.__remote_scope__._config[config.name] = config.server.entry;
|
|
37
54
|
const factory = (await getModuleFederation(container, 'entry'));
|
|
38
55
|
const entry = factory();
|
|
39
56
|
this.initializedMap.set(container, entry);
|
|
40
57
|
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
this.log.error(err);
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
41
62
|
}
|
|
42
63
|
get(config) {
|
|
43
64
|
const container = this.loader.getByUrl(config.server.entry);
|
|
44
|
-
|
|
65
|
+
const entry = container && this.initializedMap.get(container);
|
|
66
|
+
return entry && this.resolve(entry);
|
|
67
|
+
}
|
|
68
|
+
getStats(config) {
|
|
69
|
+
return this.loader.getByUrl(config.client.stats);
|
|
45
70
|
}
|
|
46
71
|
}
|
|
47
72
|
|
package/lib/server/loader.js
CHANGED
|
@@ -15,17 +15,28 @@ class ServerLoader extends loader.Loader {
|
|
|
15
15
|
max: 20,
|
|
16
16
|
});
|
|
17
17
|
this.internalLoadCache = cache;
|
|
18
|
+
this.log = logger('child-app:loader');
|
|
18
19
|
this.loader = new moduleLoaderServer.ServerLoader({
|
|
19
20
|
cache,
|
|
20
|
-
log:
|
|
21
|
+
log: this.log,
|
|
21
22
|
});
|
|
22
23
|
}
|
|
23
24
|
async load(config) {
|
|
24
|
-
await
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
await Promise.all([
|
|
26
|
+
this.loader.resolveByUrl(config.server.entry, {
|
|
27
|
+
codePrefix: `var ASSETS_PREFIX="${config.client.baseUrl}";`,
|
|
28
|
+
displayName: config.name,
|
|
29
|
+
kind: 'child-app',
|
|
30
|
+
}),
|
|
31
|
+
this.loader
|
|
32
|
+
.resolveByUrl(config.client.stats, {
|
|
33
|
+
type: 'json',
|
|
34
|
+
kind: 'child-app stats',
|
|
35
|
+
displayName: config.name,
|
|
36
|
+
})
|
|
37
|
+
// we can live without stats
|
|
38
|
+
.catch(() => { }),
|
|
39
|
+
]);
|
|
29
40
|
await this.init(config);
|
|
30
41
|
if (config.tag === 'debug') {
|
|
31
42
|
setTimeout(() => {
|
|
@@ -36,16 +47,30 @@ class ServerLoader extends loader.Loader {
|
|
|
36
47
|
}
|
|
37
48
|
async init(config) {
|
|
38
49
|
const container = this.loader.getByUrl(config.server.entry);
|
|
39
|
-
if (container) {
|
|
50
|
+
if (!container) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
40
54
|
await moduleFederation.initModuleFederation(container, 'default');
|
|
55
|
+
// copy some logic from https://github.com/module-federation/universe/blob/02221527aa684d2a37773c913bf341748fd34ecf/packages/node/src/plugins/loadScript.ts#L66
|
|
56
|
+
// to implement the same logic for loading child-app as UniversalModuleFederation
|
|
57
|
+
global.__remote_scope__._config[config.name] = config.server.entry;
|
|
41
58
|
const factory = (await moduleFederation.getModuleFederation(container, 'entry'));
|
|
42
59
|
const entry = factory();
|
|
43
60
|
this.initializedMap.set(container, entry);
|
|
44
61
|
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
this.log.error(err);
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
45
66
|
}
|
|
46
67
|
get(config) {
|
|
47
68
|
const container = this.loader.getByUrl(config.server.entry);
|
|
48
|
-
|
|
69
|
+
const entry = container && this.initializedMap.get(container);
|
|
70
|
+
return entry && this.resolve(entry);
|
|
71
|
+
}
|
|
72
|
+
getStats(config) {
|
|
73
|
+
return this.loader.getByUrl(config.client.stats);
|
|
49
74
|
}
|
|
50
75
|
}
|
|
51
76
|
|
package/lib/server/providers.js
CHANGED
|
@@ -83,6 +83,7 @@ const serverProviders = [
|
|
|
83
83
|
logger: tokensCommon.LOGGER_TOKEN,
|
|
84
84
|
diManager: tokensChildApp.CHILD_APP_DI_MANAGER_TOKEN,
|
|
85
85
|
resolveFullConfig: tokensChildApp.CHILD_APP_RESOLVE_CONFIG_TOKEN,
|
|
86
|
+
loader: tokensChildApp.CHILD_APP_LOADER_TOKEN,
|
|
86
87
|
preloadManager: tokensChildApp.CHILD_APP_PRELOAD_MANAGER_TOKEN,
|
|
87
88
|
},
|
|
88
89
|
}),
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { ExtractDependencyType } from '@tinkoff/dippy';
|
|
2
|
-
import type { ChildAppDiManager, ChildAppPreloadManager, CHILD_APP_RESOLVE_CONFIG_TOKEN } from '@tramvai/tokens-child-app';
|
|
2
|
+
import type { ChildAppDiManager, ChildAppLoader, ChildAppPreloadManager, CHILD_APP_RESOLVE_CONFIG_TOKEN } from '@tramvai/tokens-child-app';
|
|
3
3
|
import type { LOGGER_TOKEN } from '@tramvai/tokens-common';
|
|
4
|
-
|
|
4
|
+
import type { PageResource } from '@tramvai/tokens-render';
|
|
5
|
+
import type { ServerLoader } from './loader';
|
|
6
|
+
export declare const registerChildAppRenderSlots: ({ logger, diManager, resolveFullConfig, preloadManager, loader, }: {
|
|
5
7
|
logger: ExtractDependencyType<typeof LOGGER_TOKEN>;
|
|
6
8
|
diManager: ChildAppDiManager;
|
|
7
9
|
resolveFullConfig: ExtractDependencyType<typeof CHILD_APP_RESOLVE_CONFIG_TOKEN>;
|
|
8
10
|
preloadManager: ChildAppPreloadManager;
|
|
9
|
-
|
|
11
|
+
loader: ChildAppLoader | ServerLoader;
|
|
12
|
+
}) => PageResource[];
|
|
@@ -1,32 +1,109 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { extname } from 'path';
|
|
2
|
+
import { gt, eq } from 'semver';
|
|
3
|
+
import flatten from '@tinkoff/utils/array/flatten';
|
|
4
|
+
import { resolve } from '@tinkoff/url';
|
|
5
|
+
import { RENDER_SLOTS, ResourceType, ResourceSlot } from '@tramvai/tokens-render';
|
|
6
|
+
import { getSharedScope } from '../shared/webpack/moduleFederation.es.js';
|
|
2
7
|
|
|
3
|
-
const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, preloadManager, }) => {
|
|
8
|
+
const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, preloadManager, loader, }) => {
|
|
4
9
|
const log = logger('child-app:render:slots');
|
|
5
10
|
const result = [];
|
|
6
|
-
|
|
7
|
-
|
|
11
|
+
const addChunk = (entry) => {
|
|
12
|
+
if (!entry) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const extension = extname(entry);
|
|
16
|
+
switch (extension) {
|
|
17
|
+
case '.js':
|
|
18
|
+
result.push({
|
|
19
|
+
type: ResourceType.script,
|
|
20
|
+
slot: ResourceSlot.HEAD_CORE_SCRIPTS,
|
|
21
|
+
payload: entry,
|
|
22
|
+
attrs: {
|
|
23
|
+
'data-critical': 'true',
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
break;
|
|
27
|
+
case '.css':
|
|
28
|
+
result.push({
|
|
29
|
+
type: ResourceType.style,
|
|
30
|
+
slot: ResourceSlot.HEAD_CORE_STYLES,
|
|
31
|
+
payload: entry,
|
|
32
|
+
attrs: {
|
|
33
|
+
'data-critical': 'true',
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const preloadedList = new Set(preloadManager.getPreloadedList());
|
|
40
|
+
const sharedScope = getSharedScope();
|
|
41
|
+
const mapSharedToChildApp = new Map();
|
|
42
|
+
// sharedScope will contain all of the shared chunks that were added
|
|
43
|
+
// while server is running
|
|
44
|
+
// but on the page we can use only shared chunks that either provided by the root-app
|
|
45
|
+
// or one of loaded child-app
|
|
46
|
+
// so gather all of the available shared modules, check the ones that are available in the currently
|
|
47
|
+
// preloaded child-apps and figure out the best single version of the dep
|
|
48
|
+
for (const shareKey in sharedScope) {
|
|
49
|
+
for (const version in sharedScope[shareKey]) {
|
|
50
|
+
const dep = sharedScope[shareKey][version];
|
|
51
|
+
const last = mapSharedToChildApp.get(shareKey);
|
|
52
|
+
const { eager, from } = dep;
|
|
53
|
+
const [type, name] = from.split(':');
|
|
54
|
+
if (!last ||
|
|
55
|
+
// module federation will pick the highest available version
|
|
56
|
+
// https://github.com/webpack/webpack/blob/b67626c7b4ffed8737d195b27c8cea1e68d58134/lib/sharing/ConsumeSharedRuntimeModule.js#L144
|
|
57
|
+
gt(version, last.version) ||
|
|
58
|
+
// if versions are equal then module federation will pick
|
|
59
|
+
// the dep with eager prop (it's set in root-app) of with the child-app with highest name in alphabetical order
|
|
60
|
+
(eq(version, last.version) && (eager !== last.eager ? eager : name > last.name))) {
|
|
61
|
+
mapSharedToChildApp.set(shareKey, { version, type, name, eager });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// eslint-disable-next-line max-statements
|
|
66
|
+
preloadedList.forEach((requestConfig) => {
|
|
67
|
+
var _a, _b, _c;
|
|
8
68
|
const config = resolveFullConfig(requestConfig);
|
|
9
69
|
if (!config) {
|
|
10
70
|
return;
|
|
11
71
|
}
|
|
72
|
+
const stats = 'getStats' in loader ? loader.getStats(config) : undefined;
|
|
12
73
|
const di = diManager.getChildDi(config);
|
|
13
|
-
|
|
14
|
-
type: ResourceType.script,
|
|
15
|
-
slot: ResourceSlot.HEAD_CORE_SCRIPTS,
|
|
16
|
-
payload: config.client.entry,
|
|
17
|
-
attrs: {
|
|
18
|
-
'data-critical': 'true',
|
|
19
|
-
},
|
|
20
|
-
});
|
|
74
|
+
addChunk(config.client.entry);
|
|
21
75
|
if (config.css) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
76
|
+
addChunk(config.css.entry);
|
|
77
|
+
}
|
|
78
|
+
if (stats) {
|
|
79
|
+
for (const federatedModule of stats.federatedModules) {
|
|
80
|
+
// entries are duplicated in the `exposes` field of federated stats for some reason
|
|
81
|
+
// for now there anyway should be only one exposed entry so took the first available
|
|
82
|
+
const files = new Set();
|
|
83
|
+
(_b = (_a = federatedModule === null || federatedModule === void 0 ? void 0 : federatedModule.exposes) === null || _a === void 0 ? void 0 : _a.entry) === null || _b === void 0 ? void 0 : _b.forEach((entry) => {
|
|
84
|
+
for (const key in entry) {
|
|
85
|
+
entry[key].forEach((file) => files.add(file));
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
for (const file of files) {
|
|
89
|
+
addChunk(resolve(config.client.baseUrl, file));
|
|
90
|
+
}
|
|
91
|
+
for (const sharedModule of federatedModule.sharedModules) {
|
|
92
|
+
const { shareKey } = (_c = sharedModule.provides) === null || _c === void 0 ? void 0 : _c[0];
|
|
93
|
+
const { chunks } = sharedModule;
|
|
94
|
+
const bestShared = mapSharedToChildApp.get(shareKey);
|
|
95
|
+
if (!(bestShared === null || bestShared === void 0 ? void 0 : bestShared.eager) && (bestShared === null || bestShared === void 0 ? void 0 : bestShared.name) === config.name) {
|
|
96
|
+
for (const chunk of chunks) {
|
|
97
|
+
addChunk(resolve(config.client.baseUrl, chunk));
|
|
98
|
+
}
|
|
99
|
+
// in stats.json federated stats could contain 2 sets of chunks for shared modules
|
|
100
|
+
// there usual one and fallback. For shared module there could be used any of this
|
|
101
|
+
// and the other one will be useless. So delete entry from map after its usage in order
|
|
102
|
+
// to add only single set of chunks for the same shared dep
|
|
103
|
+
mapSharedToChildApp.delete(shareKey);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
30
107
|
}
|
|
31
108
|
if (!di) {
|
|
32
109
|
return;
|
|
@@ -34,7 +111,7 @@ const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, pre
|
|
|
34
111
|
try {
|
|
35
112
|
const renderSlots = di.get({ token: RENDER_SLOTS, optional: true });
|
|
36
113
|
if (renderSlots) {
|
|
37
|
-
result.push(...renderSlots);
|
|
114
|
+
result.push(...flatten(renderSlots));
|
|
38
115
|
}
|
|
39
116
|
}
|
|
40
117
|
catch (error) {
|
|
@@ -2,35 +2,116 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
+
var path = require('path');
|
|
6
|
+
var semver = require('semver');
|
|
7
|
+
var flatten = require('@tinkoff/utils/array/flatten');
|
|
8
|
+
var url = require('@tinkoff/url');
|
|
5
9
|
var tokensRender = require('@tramvai/tokens-render');
|
|
10
|
+
var moduleFederation = require('../shared/webpack/moduleFederation.js');
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
13
|
+
|
|
14
|
+
var flatten__default = /*#__PURE__*/_interopDefaultLegacy(flatten);
|
|
15
|
+
|
|
16
|
+
const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, preloadManager, loader, }) => {
|
|
8
17
|
const log = logger('child-app:render:slots');
|
|
9
18
|
const result = [];
|
|
10
|
-
|
|
11
|
-
|
|
19
|
+
const addChunk = (entry) => {
|
|
20
|
+
if (!entry) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const extension = path.extname(entry);
|
|
24
|
+
switch (extension) {
|
|
25
|
+
case '.js':
|
|
26
|
+
result.push({
|
|
27
|
+
type: tokensRender.ResourceType.script,
|
|
28
|
+
slot: tokensRender.ResourceSlot.HEAD_CORE_SCRIPTS,
|
|
29
|
+
payload: entry,
|
|
30
|
+
attrs: {
|
|
31
|
+
'data-critical': 'true',
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
break;
|
|
35
|
+
case '.css':
|
|
36
|
+
result.push({
|
|
37
|
+
type: tokensRender.ResourceType.style,
|
|
38
|
+
slot: tokensRender.ResourceSlot.HEAD_CORE_STYLES,
|
|
39
|
+
payload: entry,
|
|
40
|
+
attrs: {
|
|
41
|
+
'data-critical': 'true',
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const preloadedList = new Set(preloadManager.getPreloadedList());
|
|
48
|
+
const sharedScope = moduleFederation.getSharedScope();
|
|
49
|
+
const mapSharedToChildApp = new Map();
|
|
50
|
+
// sharedScope will contain all of the shared chunks that were added
|
|
51
|
+
// while server is running
|
|
52
|
+
// but on the page we can use only shared chunks that either provided by the root-app
|
|
53
|
+
// or one of loaded child-app
|
|
54
|
+
// so gather all of the available shared modules, check the ones that are available in the currently
|
|
55
|
+
// preloaded child-apps and figure out the best single version of the dep
|
|
56
|
+
for (const shareKey in sharedScope) {
|
|
57
|
+
for (const version in sharedScope[shareKey]) {
|
|
58
|
+
const dep = sharedScope[shareKey][version];
|
|
59
|
+
const last = mapSharedToChildApp.get(shareKey);
|
|
60
|
+
const { eager, from } = dep;
|
|
61
|
+
const [type, name] = from.split(':');
|
|
62
|
+
if (!last ||
|
|
63
|
+
// module federation will pick the highest available version
|
|
64
|
+
// https://github.com/webpack/webpack/blob/b67626c7b4ffed8737d195b27c8cea1e68d58134/lib/sharing/ConsumeSharedRuntimeModule.js#L144
|
|
65
|
+
semver.gt(version, last.version) ||
|
|
66
|
+
// if versions are equal then module federation will pick
|
|
67
|
+
// the dep with eager prop (it's set in root-app) of with the child-app with highest name in alphabetical order
|
|
68
|
+
(semver.eq(version, last.version) && (eager !== last.eager ? eager : name > last.name))) {
|
|
69
|
+
mapSharedToChildApp.set(shareKey, { version, type, name, eager });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// eslint-disable-next-line max-statements
|
|
74
|
+
preloadedList.forEach((requestConfig) => {
|
|
75
|
+
var _a, _b, _c;
|
|
12
76
|
const config = resolveFullConfig(requestConfig);
|
|
13
77
|
if (!config) {
|
|
14
78
|
return;
|
|
15
79
|
}
|
|
80
|
+
const stats = 'getStats' in loader ? loader.getStats(config) : undefined;
|
|
16
81
|
const di = diManager.getChildDi(config);
|
|
17
|
-
|
|
18
|
-
type: tokensRender.ResourceType.script,
|
|
19
|
-
slot: tokensRender.ResourceSlot.HEAD_CORE_SCRIPTS,
|
|
20
|
-
payload: config.client.entry,
|
|
21
|
-
attrs: {
|
|
22
|
-
'data-critical': 'true',
|
|
23
|
-
},
|
|
24
|
-
});
|
|
82
|
+
addChunk(config.client.entry);
|
|
25
83
|
if (config.css) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
84
|
+
addChunk(config.css.entry);
|
|
85
|
+
}
|
|
86
|
+
if (stats) {
|
|
87
|
+
for (const federatedModule of stats.federatedModules) {
|
|
88
|
+
// entries are duplicated in the `exposes` field of federated stats for some reason
|
|
89
|
+
// for now there anyway should be only one exposed entry so took the first available
|
|
90
|
+
const files = new Set();
|
|
91
|
+
(_b = (_a = federatedModule === null || federatedModule === void 0 ? void 0 : federatedModule.exposes) === null || _a === void 0 ? void 0 : _a.entry) === null || _b === void 0 ? void 0 : _b.forEach((entry) => {
|
|
92
|
+
for (const key in entry) {
|
|
93
|
+
entry[key].forEach((file) => files.add(file));
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
for (const file of files) {
|
|
97
|
+
addChunk(url.resolve(config.client.baseUrl, file));
|
|
98
|
+
}
|
|
99
|
+
for (const sharedModule of federatedModule.sharedModules) {
|
|
100
|
+
const { shareKey } = (_c = sharedModule.provides) === null || _c === void 0 ? void 0 : _c[0];
|
|
101
|
+
const { chunks } = sharedModule;
|
|
102
|
+
const bestShared = mapSharedToChildApp.get(shareKey);
|
|
103
|
+
if (!(bestShared === null || bestShared === void 0 ? void 0 : bestShared.eager) && (bestShared === null || bestShared === void 0 ? void 0 : bestShared.name) === config.name) {
|
|
104
|
+
for (const chunk of chunks) {
|
|
105
|
+
addChunk(url.resolve(config.client.baseUrl, chunk));
|
|
106
|
+
}
|
|
107
|
+
// in stats.json federated stats could contain 2 sets of chunks for shared modules
|
|
108
|
+
// there usual one and fallback. For shared module there could be used any of this
|
|
109
|
+
// and the other one will be useless. So delete entry from map after its usage in order
|
|
110
|
+
// to add only single set of chunks for the same shared dep
|
|
111
|
+
mapSharedToChildApp.delete(shareKey);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
34
115
|
}
|
|
35
116
|
if (!di) {
|
|
36
117
|
return;
|
|
@@ -38,7 +119,7 @@ const registerChildAppRenderSlots = ({ logger, diManager, resolveFullConfig, pre
|
|
|
38
119
|
try {
|
|
39
120
|
const renderSlots = di.get({ token: tokensRender.RENDER_SLOTS, optional: true });
|
|
40
121
|
if (renderSlots) {
|
|
41
|
-
result.push(...renderSlots);
|
|
122
|
+
result.push(...flatten__default["default"](renderSlots));
|
|
42
123
|
}
|
|
43
124
|
}
|
|
44
125
|
catch (error) {
|
package/lib/shared/providers.js
CHANGED
|
@@ -3,16 +3,21 @@ const initModuleFederation = async (container, scope = 'default') => {
|
|
|
3
3
|
await container.init(__webpack_share_scopes__[scope]);
|
|
4
4
|
return;
|
|
5
5
|
}
|
|
6
|
+
if (typeof window === 'undefined') {
|
|
7
|
+
// copy some logic from https://github.com/module-federation/universe/blob/02221527aa684d2a37773c913bf341748fd34ecf/packages/node/src/plugins/loadScript.ts#L66
|
|
8
|
+
// to implement the same logic for loading child-app as UniversalModuleFederation
|
|
9
|
+
global.__remote_scope__ = global.__remote_scope__ || { _config: {} };
|
|
10
|
+
}
|
|
6
11
|
await __webpack_init_sharing__('default');
|
|
7
|
-
// currently module federation has problems with external modules
|
|
8
|
-
// and
|
|
12
|
+
// currently module federation has problems with external modules (they are marked as externals in the dev build)
|
|
13
|
+
// and unfortunately react and react-dom are marked as externals in defaults
|
|
9
14
|
// fill sharedScope manually here
|
|
10
15
|
const shareScope = __webpack_share_scopes__[scope];
|
|
11
16
|
if (!shareScope.react) {
|
|
12
17
|
shareScope.react = {
|
|
13
18
|
'*': {
|
|
14
19
|
get: () => () => require('react'),
|
|
15
|
-
from: 'tramvai-mf-fix',
|
|
20
|
+
from: 'application:tramvai-mf-fix',
|
|
16
21
|
eager: true,
|
|
17
22
|
loaded: true,
|
|
18
23
|
},
|
|
@@ -22,7 +27,7 @@ const initModuleFederation = async (container, scope = 'default') => {
|
|
|
22
27
|
shareScope['react-dom'] = {
|
|
23
28
|
'*': {
|
|
24
29
|
get: () => () => require('react-dom'),
|
|
25
|
-
from: 'tramvai-mf-fix',
|
|
30
|
+
from: 'application:tramvai-mf-fix',
|
|
26
31
|
eager: true,
|
|
27
32
|
loaded: true,
|
|
28
33
|
},
|
|
@@ -34,7 +39,7 @@ const initModuleFederation = async (container, scope = 'default') => {
|
|
|
34
39
|
shareScope['react/jsx-runtime'] = {
|
|
35
40
|
'*': {
|
|
36
41
|
get: () => () => require('react/jsx-runtime'),
|
|
37
|
-
from: 'tramvai-mf-fix',
|
|
42
|
+
from: 'application:tramvai-mf-fix',
|
|
38
43
|
eager: true,
|
|
39
44
|
loaded: true,
|
|
40
45
|
},
|
|
@@ -1,15 +1,48 @@
|
|
|
1
1
|
declare global {
|
|
2
2
|
var __webpack_init_sharing__: (name: string) => Promise<void>;
|
|
3
|
-
var __webpack_share_scopes__:
|
|
3
|
+
var __webpack_share_scopes__: ModuleFederationSharedScopes;
|
|
4
|
+
var __remote_scope__: {
|
|
5
|
+
_config: Record<string, string>;
|
|
6
|
+
};
|
|
4
7
|
}
|
|
5
8
|
interface ModuleFederationSharedScope {
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
[packageName: string]: {
|
|
10
|
+
[version: string]: {
|
|
11
|
+
get: Function;
|
|
12
|
+
from: string;
|
|
13
|
+
eager: boolean;
|
|
14
|
+
loaded: 0 | 1 | boolean;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
interface ModuleFederationSharedScopes {
|
|
19
|
+
default: ModuleFederationSharedScope;
|
|
20
|
+
[scope: string]: ModuleFederationSharedScope;
|
|
8
21
|
}
|
|
9
22
|
export interface ModuleFederationContainer {
|
|
10
|
-
init(scope:
|
|
23
|
+
init(scope: ModuleFederationSharedScopes[string]): Promise<void>;
|
|
11
24
|
get<T = unknown>(name: string): Promise<T>;
|
|
12
25
|
}
|
|
26
|
+
export interface ModuleFederationStats {
|
|
27
|
+
sharedModules: any[];
|
|
28
|
+
federatedModules: Array<{
|
|
29
|
+
remote: string;
|
|
30
|
+
entry: string;
|
|
31
|
+
sharedModules: Array<{
|
|
32
|
+
chunks: string[];
|
|
33
|
+
provides: Array<{
|
|
34
|
+
shareScope: string;
|
|
35
|
+
shareKey: string;
|
|
36
|
+
requiredVersion: string;
|
|
37
|
+
strictVersion: boolean;
|
|
38
|
+
singleton: boolean;
|
|
39
|
+
eager: boolean;
|
|
40
|
+
}>;
|
|
41
|
+
}>;
|
|
42
|
+
exposes: Record<string, Array<Record<string, string[]>>>;
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
45
|
+
export declare const getSharedScope: (scope?: string) => ModuleFederationSharedScope;
|
|
13
46
|
export declare const initModuleFederation: (container?: ModuleFederationContainer, scope?: string) => Promise<void>;
|
|
14
47
|
export declare const getModuleFederation: (container: ModuleFederationContainer, name?: string) => Promise<unknown>;
|
|
15
48
|
export {};
|
|
@@ -1,18 +1,26 @@
|
|
|
1
|
+
const getSharedScope = (scope = 'default') => {
|
|
2
|
+
return __webpack_share_scopes__[scope];
|
|
3
|
+
};
|
|
1
4
|
const initModuleFederation = async (container, scope = 'default') => {
|
|
2
5
|
if (container) {
|
|
3
6
|
await container.init(__webpack_share_scopes__[scope]);
|
|
4
7
|
return;
|
|
5
8
|
}
|
|
9
|
+
if (typeof window === 'undefined') {
|
|
10
|
+
// copy some logic from https://github.com/module-federation/universe/blob/02221527aa684d2a37773c913bf341748fd34ecf/packages/node/src/plugins/loadScript.ts#L66
|
|
11
|
+
// to implement the same logic for loading child-app as UniversalModuleFederation
|
|
12
|
+
global.__remote_scope__ = global.__remote_scope__ || { _config: {} };
|
|
13
|
+
}
|
|
6
14
|
await __webpack_init_sharing__('default');
|
|
7
|
-
// currently module federation has problems with external modules
|
|
8
|
-
// and
|
|
15
|
+
// currently module federation has problems with external modules (they are marked as externals in the dev build)
|
|
16
|
+
// and unfortunately react and react-dom are marked as externals in defaults
|
|
9
17
|
// fill sharedScope manually here
|
|
10
18
|
const shareScope = __webpack_share_scopes__[scope];
|
|
11
19
|
if (!shareScope.react) {
|
|
12
20
|
shareScope.react = {
|
|
13
21
|
'*': {
|
|
14
22
|
get: () => () => require('react'),
|
|
15
|
-
from: 'tramvai-mf-fix',
|
|
23
|
+
from: 'application:tramvai-mf-fix',
|
|
16
24
|
eager: true,
|
|
17
25
|
loaded: true,
|
|
18
26
|
},
|
|
@@ -22,7 +30,7 @@ const initModuleFederation = async (container, scope = 'default') => {
|
|
|
22
30
|
shareScope['react-dom'] = {
|
|
23
31
|
'*': {
|
|
24
32
|
get: () => () => require('react-dom'),
|
|
25
|
-
from: 'tramvai-mf-fix',
|
|
33
|
+
from: 'application:tramvai-mf-fix',
|
|
26
34
|
eager: true,
|
|
27
35
|
loaded: true,
|
|
28
36
|
},
|
|
@@ -34,7 +42,7 @@ const initModuleFederation = async (container, scope = 'default') => {
|
|
|
34
42
|
shareScope['react/jsx-runtime'] = {
|
|
35
43
|
'*': {
|
|
36
44
|
get: () => () => require('react/jsx-runtime'),
|
|
37
|
-
from: 'tramvai-mf-fix',
|
|
45
|
+
from: 'application:tramvai-mf-fix',
|
|
38
46
|
eager: true,
|
|
39
47
|
loaded: true,
|
|
40
48
|
},
|
|
@@ -46,4 +54,4 @@ const getModuleFederation = async (container, name = 'entry') => {
|
|
|
46
54
|
return container.get(name);
|
|
47
55
|
};
|
|
48
56
|
|
|
49
|
-
export { getModuleFederation, initModuleFederation };
|
|
57
|
+
export { getModuleFederation, getSharedScope, initModuleFederation };
|
|
@@ -2,21 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
+
const getSharedScope = (scope = 'default') => {
|
|
6
|
+
return __webpack_share_scopes__[scope];
|
|
7
|
+
};
|
|
5
8
|
const initModuleFederation = async (container, scope = 'default') => {
|
|
6
9
|
if (container) {
|
|
7
10
|
await container.init(__webpack_share_scopes__[scope]);
|
|
8
11
|
return;
|
|
9
12
|
}
|
|
13
|
+
if (typeof window === 'undefined') {
|
|
14
|
+
// copy some logic from https://github.com/module-federation/universe/blob/02221527aa684d2a37773c913bf341748fd34ecf/packages/node/src/plugins/loadScript.ts#L66
|
|
15
|
+
// to implement the same logic for loading child-app as UniversalModuleFederation
|
|
16
|
+
global.__remote_scope__ = global.__remote_scope__ || { _config: {} };
|
|
17
|
+
}
|
|
10
18
|
await __webpack_init_sharing__('default');
|
|
11
|
-
// currently module federation has problems with external modules
|
|
12
|
-
// and
|
|
19
|
+
// currently module federation has problems with external modules (they are marked as externals in the dev build)
|
|
20
|
+
// and unfortunately react and react-dom are marked as externals in defaults
|
|
13
21
|
// fill sharedScope manually here
|
|
14
22
|
const shareScope = __webpack_share_scopes__[scope];
|
|
15
23
|
if (!shareScope.react) {
|
|
16
24
|
shareScope.react = {
|
|
17
25
|
'*': {
|
|
18
26
|
get: () => () => require('react'),
|
|
19
|
-
from: 'tramvai-mf-fix',
|
|
27
|
+
from: 'application:tramvai-mf-fix',
|
|
20
28
|
eager: true,
|
|
21
29
|
loaded: true,
|
|
22
30
|
},
|
|
@@ -26,7 +34,7 @@ const initModuleFederation = async (container, scope = 'default') => {
|
|
|
26
34
|
shareScope['react-dom'] = {
|
|
27
35
|
'*': {
|
|
28
36
|
get: () => () => require('react-dom'),
|
|
29
|
-
from: 'tramvai-mf-fix',
|
|
37
|
+
from: 'application:tramvai-mf-fix',
|
|
30
38
|
eager: true,
|
|
31
39
|
loaded: true,
|
|
32
40
|
},
|
|
@@ -38,7 +46,7 @@ const initModuleFederation = async (container, scope = 'default') => {
|
|
|
38
46
|
shareScope['react/jsx-runtime'] = {
|
|
39
47
|
'*': {
|
|
40
48
|
get: () => () => require('react/jsx-runtime'),
|
|
41
|
-
from: 'tramvai-mf-fix',
|
|
49
|
+
from: 'application:tramvai-mf-fix',
|
|
42
50
|
eager: true,
|
|
43
51
|
loaded: true,
|
|
44
52
|
},
|
|
@@ -51,4 +59,5 @@ const getModuleFederation = async (container, name = 'entry') => {
|
|
|
51
59
|
};
|
|
52
60
|
|
|
53
61
|
exports.getModuleFederation = getModuleFederation;
|
|
62
|
+
exports.getSharedScope = getSharedScope;
|
|
54
63
|
exports.initModuleFederation = initModuleFederation;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tramvai/module-child-app",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.78.1",
|
|
4
4
|
"description": "Module for child apps",
|
|
5
5
|
"browser": {
|
|
6
6
|
"./lib/server.js": "./lib/browser.js",
|
|
@@ -29,25 +29,27 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@tinkoff/env-validators": "0.1.5",
|
|
31
31
|
"@tinkoff/module-loader-client": "0.4.5",
|
|
32
|
-
"@tinkoff/module-loader-server": "0.5.
|
|
33
|
-
"@
|
|
34
|
-
"@tramvai/
|
|
32
|
+
"@tinkoff/module-loader-server": "0.5.8",
|
|
33
|
+
"@tinkoff/url": "0.8.5",
|
|
34
|
+
"@tramvai/child-app-core": "2.78.1",
|
|
35
|
+
"@tramvai/module-router": "2.78.1",
|
|
35
36
|
"@tramvai/safe-strings": "0.5.7",
|
|
36
|
-
"@tramvai/tokens-child-app": "2.
|
|
37
|
+
"@tramvai/tokens-child-app": "2.78.1"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {},
|
|
39
40
|
"peerDependencies": {
|
|
40
41
|
"@tinkoff/dippy": "0.8.13",
|
|
41
42
|
"@tinkoff/utils": "^2.1.2",
|
|
42
|
-
"@tramvai/core": "2.
|
|
43
|
-
"@tramvai/state": "2.
|
|
44
|
-
"@tramvai/react": "2.
|
|
45
|
-
"@tramvai/tokens-common": "2.
|
|
46
|
-
"@tramvai/tokens-render": "2.
|
|
47
|
-
"@tramvai/tokens-router": "2.
|
|
43
|
+
"@tramvai/core": "2.78.1",
|
|
44
|
+
"@tramvai/state": "2.78.1",
|
|
45
|
+
"@tramvai/react": "2.78.1",
|
|
46
|
+
"@tramvai/tokens-common": "2.78.1",
|
|
47
|
+
"@tramvai/tokens-render": "2.78.1",
|
|
48
|
+
"@tramvai/tokens-router": "2.78.1",
|
|
48
49
|
"react": ">=16.14.0",
|
|
49
50
|
"react-dom": ">=16.14.0",
|
|
50
51
|
"object-assign": "^4.1.1",
|
|
52
|
+
"semver": "^7.3.8",
|
|
51
53
|
"tslib": "^2.4.0"
|
|
52
54
|
},
|
|
53
55
|
"module": "lib/server.es.js"
|