arui-scripts 14.5.0-feat-modules.2 → 14.5.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.
- package/CHANGELOG.md +1 -17
- package/build/commands/build/build-wrapper.d.ts +2 -2
- package/build/commands/build/client.js +3 -3
- package/build/commands/bundle-analyze/index.js +7 -5
- package/build/commands/changelog/index.js +2 -2
- package/build/commands/start/client.js +2 -2
- package/build/commands/start-prod/client.js +2 -2
- package/build/commands/util/{run-client-dev-derver.d.ts → run-client-dev-server.d.ts} +1 -1
- package/build/commands/util/{run-client-dev-derver.js → run-client-dev-server.js} +4 -1
- package/build/configs/app-configs/available-package-settings.d.ts +8 -0
- package/build/configs/app-configs/available-package-settings.js +32 -0
- package/build/configs/app-configs/calculate-dependent-config.d.ts +8 -48
- package/build/configs/app-configs/calculate-dependent-config.js +13 -11
- package/build/configs/app-configs/get-defaults.d.ts +2 -3
- package/build/configs/app-configs/get-defaults.js +34 -49
- package/build/configs/app-configs/index.d.ts +3 -5
- package/build/configs/app-configs/index.js +7 -13
- package/build/configs/app-configs/types.d.ts +26 -60
- package/build/configs/app-configs/update-with-config-file.d.ts +2 -2
- package/build/configs/app-configs/update-with-config-file.js +7 -5
- package/build/configs/app-configs/update-with-env.js +8 -3
- package/build/configs/app-configs/update-with-package.d.ts +2 -2
- package/build/configs/app-configs/update-with-package.js +10 -5
- package/build/configs/app-configs/update-with-presets.d.ts +2 -2
- package/build/configs/app-configs/update-with-presets.js +15 -8
- package/build/configs/app-configs/validate-settings-keys.d.ts +5 -2
- package/build/configs/app-configs/validate-settings-keys.js +7 -6
- package/build/configs/babel-client.d.ts +1 -33
- package/build/configs/babel-dependencies.d.ts +1 -14
- package/build/configs/babel-server.d.ts +1 -26
- package/build/configs/dev-server.d.ts +2 -15
- package/build/configs/jest/index.d.ts +1 -33
- package/build/configs/server-externals-exemptions.d.ts +1 -1
- package/build/configs/server-externals-exemptions.js +0 -1
- package/build/configs/util/apply-overrides.d.ts +6 -6
- package/build/configs/util/get-entry.d.ts +1 -2
- package/build/configs/webpack.client.d.ts +2 -5
- package/build/configs/webpack.client.dev.d.ts +1 -1
- package/build/configs/webpack.client.js +17 -71
- package/build/configs/webpack.client.prod.d.ts +1 -1
- package/build/index.d.ts +1 -1
- package/build/tsconfig-local.tsbuildinfo +1 -1
- package/package.json +3 -4
- package/build/configs/modules.d.ts +0 -15
- package/build/configs/modules.js +0 -78
- package/docs/modules.md +0 -423
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arui-scripts",
|
|
3
|
-
"version": "14.5.0
|
|
3
|
+
"version": "14.5.0",
|
|
4
4
|
"main": "./build/index.js",
|
|
5
5
|
"typings": "./build/index.d.ts",
|
|
6
6
|
"license": "MPL-2.0",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"expose-loader": "3.0.0",
|
|
56
56
|
"file-loader": "6.2.0",
|
|
57
57
|
"filesize": "^6.1.0",
|
|
58
|
-
"fork-ts-checker-webpack-plugin": "6.3
|
|
58
|
+
"fork-ts-checker-webpack-plugin": "6.5.3",
|
|
59
59
|
"fs-extra": "6.0.1",
|
|
60
60
|
"gzip-size": "5.1.1",
|
|
61
61
|
"jest": "28.1.3",
|
|
@@ -79,7 +79,6 @@
|
|
|
79
79
|
"postcss-mixins": "^6.2.0",
|
|
80
80
|
"postcss-nested": "^3.0.0",
|
|
81
81
|
"postcss-omit-import-tilde": "^1.0.1",
|
|
82
|
-
"postcss-prefix-selector": "^1.16.0",
|
|
83
82
|
"postcss-strip-units": "^2.0.1",
|
|
84
83
|
"postcss-url": "7.3.1",
|
|
85
84
|
"react-dev-utils": "11.0.4",
|
|
@@ -123,7 +122,6 @@
|
|
|
123
122
|
"@types/lodash.merge": "^4.6.6",
|
|
124
123
|
"@types/mini-css-extract-plugin": "1.2.2",
|
|
125
124
|
"@types/node": "12",
|
|
126
|
-
"@types/postcss-prefix-selector": "^1.15.0",
|
|
127
125
|
"@types/react-dev-utils": "9.0.8",
|
|
128
126
|
"@types/semver": "^7.3.13",
|
|
129
127
|
"@types/shelljs": "^0.8.11",
|
|
@@ -131,6 +129,7 @@
|
|
|
131
129
|
"@types/strip-ansi": "^5.2.1",
|
|
132
130
|
"@types/tar": "^4.0.3",
|
|
133
131
|
"@types/terser-webpack-plugin": "5.2.0",
|
|
132
|
+
"@types/webpack": "5.28.0",
|
|
134
133
|
"@types/webpack-bundle-analyzer": "4.4.1",
|
|
135
134
|
"@types/webpack-dev-server": "4.3.1",
|
|
136
135
|
"@types/webpack-manifest-plugin": "3.0.3",
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Assets } from 'assets-webpack-plugin';
|
|
2
|
-
import { EmbeddedModuleConfig } from './app-configs/types';
|
|
3
|
-
export declare function processAssetsPluginOutput(assets: Assets): string;
|
|
4
|
-
export declare function haveExposedMfModules(): Record<string, string> | undefined;
|
|
5
|
-
export declare function getCssPrefixForModule(module: EmbeddedModuleConfig): string | undefined;
|
|
6
|
-
export declare function getChunkNamePrefix(module?: EmbeddedModuleConfig): string;
|
|
7
|
-
export declare function getExposeLoadersFormEmbeddedModules(module?: EmbeddedModuleConfig): {
|
|
8
|
-
test: string;
|
|
9
|
-
use: {
|
|
10
|
-
loader: string;
|
|
11
|
-
options: {
|
|
12
|
-
exposes: string[];
|
|
13
|
-
};
|
|
14
|
-
}[];
|
|
15
|
-
}[];
|
package/build/configs/modules.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getExposeLoadersFormEmbeddedModules = exports.getChunkNamePrefix = exports.getCssPrefixForModule = exports.haveExposedMfModules = exports.processAssetsPluginOutput = void 0;
|
|
7
|
-
const app_configs_1 = __importDefault(require("./app-configs"));
|
|
8
|
-
function processAssetsPluginOutput(assets) {
|
|
9
|
-
var _a;
|
|
10
|
-
let adjustedAssets = assets;
|
|
11
|
-
if (haveExposedMfModules()) { // для mf модулей мы делаем publicPath 'auto', но в самом манифесте нам все равно хочется видеть нормальный путь
|
|
12
|
-
Object.keys(adjustedAssets).forEach((key) => {
|
|
13
|
-
adjustedAssets[key] = {
|
|
14
|
-
css: replaceAutoPath(adjustedAssets[key].css),
|
|
15
|
-
js: replaceAutoPath(adjustedAssets[key].js),
|
|
16
|
-
};
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
const result = Object.assign(Object.assign({}, adjustedAssets), { __metadata__: {
|
|
20
|
-
version: app_configs_1.default.version,
|
|
21
|
-
name: ((_a = app_configs_1.default.mfModules) === null || _a === void 0 ? void 0 : _a.name) || app_configs_1.default.normalizedName,
|
|
22
|
-
} });
|
|
23
|
-
return JSON.stringify(result);
|
|
24
|
-
}
|
|
25
|
-
exports.processAssetsPluginOutput = processAssetsPluginOutput;
|
|
26
|
-
function replaceAutoPath(assets) {
|
|
27
|
-
if (!assets) {
|
|
28
|
-
return assets;
|
|
29
|
-
}
|
|
30
|
-
if (Array.isArray(assets)) {
|
|
31
|
-
return assets.map((asset) => asset.replace(/^auto\//, app_configs_1.default.publicPath));
|
|
32
|
-
}
|
|
33
|
-
return assets.replace(/^auto\//, app_configs_1.default.publicPath);
|
|
34
|
-
}
|
|
35
|
-
function haveExposedMfModules() {
|
|
36
|
-
var _a;
|
|
37
|
-
return (_a = app_configs_1.default.mfModules) === null || _a === void 0 ? void 0 : _a.exposes;
|
|
38
|
-
}
|
|
39
|
-
exports.haveExposedMfModules = haveExposedMfModules;
|
|
40
|
-
function getCssPrefixForModule(module) {
|
|
41
|
-
if (module.cssPrefix) {
|
|
42
|
-
return module.cssPrefix;
|
|
43
|
-
}
|
|
44
|
-
if (module.cssPrefix === false) {
|
|
45
|
-
return undefined;
|
|
46
|
-
}
|
|
47
|
-
return `.module-${module.name}`;
|
|
48
|
-
}
|
|
49
|
-
exports.getCssPrefixForModule = getCssPrefixForModule;
|
|
50
|
-
function getChunkNamePrefix(module) {
|
|
51
|
-
if (!module) {
|
|
52
|
-
return '';
|
|
53
|
-
}
|
|
54
|
-
return `${module.name}-`;
|
|
55
|
-
}
|
|
56
|
-
exports.getChunkNamePrefix = getChunkNamePrefix;
|
|
57
|
-
function getExposeLoadersFormEmbeddedModules(module) {
|
|
58
|
-
var _a;
|
|
59
|
-
const shared = (_a = app_configs_1.default.embeddedModules) === null || _a === void 0 ? void 0 : _a.shared;
|
|
60
|
-
if (!shared || module) {
|
|
61
|
-
return [];
|
|
62
|
-
}
|
|
63
|
-
return Object.keys(shared).map((libraryName) => {
|
|
64
|
-
const globalVarName = shared[libraryName];
|
|
65
|
-
return {
|
|
66
|
-
test: require.resolve(libraryName),
|
|
67
|
-
use: [
|
|
68
|
-
{
|
|
69
|
-
loader: require.resolve('expose-loader'),
|
|
70
|
-
options: {
|
|
71
|
-
exposes: [globalVarName],
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
};
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
exports.getExposeLoadersFormEmbeddedModules = getExposeLoadersFormEmbeddedModules;
|
package/docs/modules.md
DELETED
|
@@ -1,423 +0,0 @@
|
|
|
1
|
-
# Модули приложений
|
|
2
|
-
|
|
3
|
-
## Мотивация
|
|
4
|
-
Модули приложений предназначены для решения простой проблемы - переиспользование фронтового кода между приложениями.
|
|
5
|
-
|
|
6
|
-
В целом, для того чтобы переиспользовать код у нас есть много разных способов, например:
|
|
7
|
-
- копировать код из одного приложения в другое. Быстро, но неудобно и неэффективно.
|
|
8
|
-
- выносить код в отдельный пакет и подключать его через npm. Понятный и достаточно удобный вариант,
|
|
9
|
-
но ограничивает нас в скорости изменений. Если мы хотим заменить обновить код в пакете, процесс
|
|
10
|
-
раскатки этого обновления на все приложения может занять много времени.
|
|
11
|
-
- Сделать так, чтобы мы могли подключать код из других приложений в свое. При этом мы получаем
|
|
12
|
-
возможность быстро изменить общий код только в одном месте, а все приложения автоматически получат
|
|
13
|
-
обновления.
|
|
14
|
-
|
|
15
|
-
Модули позволяют реализовать именно последний вариант. Если вы знакомы с концепцией [module federation](https://webpack.js.org/concepts/module-federation/),
|
|
16
|
-
то модули приложений - это его реализация в рамках arui-scripts, с дополнительным уровнем абстракции, который, в том числе,
|
|
17
|
-
позволяет использовать модули без самого module-federation.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
## Общие принципы
|
|
21
|
-
Модуль - это некоторая совокупность js и css файлов, которые можно подключить в приложение. У модуля есть
|
|
22
|
-
входная точка - js файл, код которого будет являться его публичным api.
|
|
23
|
-
|
|
24
|
-
Для подключения модулей в приложение предоставляется набор библиотек, которые позволяют указать, какой модуль
|
|
25
|
-
и куда надо подключить.
|
|
26
|
-
|
|
27
|
-
## Типы модулей
|
|
28
|
-
Несмотря на кажущуюся простоту концепции, можно представить разные сценарии использования модулей. В ряде случаев
|
|
29
|
-
мы хотим просто предоставить доступ к коду из другого приложения, в других - мы хотим иметь возможность
|
|
30
|
-
настраивать поведение модуля в зависимости от приложения, в котором он используется. Или мы хотим сделать так, чтобы
|
|
31
|
-
наш модуль мог сразу же получить какие-то данные с сервера, потому что его поведение зависит от этих данных.
|
|
32
|
-
|
|
33
|
-
Из-за того, что эти сценарии различаются, мы выделили два типа модулей:
|
|
34
|
-
|
|
35
|
-
- Клиентские модули. Это модули, которые имеют только клиентскую часть.
|
|
36
|
-
- Модули с серверной частью. Это модули, которые имеют как клиентскую, так и серверную часть. Серверная часть может
|
|
37
|
-
реализовывать какую-то логику, которая не может быть реализована на клиенте, например отдавать разные модули в зависимости
|
|
38
|
-
от пользователя, получать предподготовленные данные с сервера и т.д.
|
|
39
|
-
|
|
40
|
-
### Сравнение
|
|
41
|
-
|
|
42
|
-
Клиентские модули:
|
|
43
|
-
- Можно реализовать в любом приложении, даже если у него нет серверной части.
|
|
44
|
-
- Меньше кода, меньше проблем с поддержкой.
|
|
45
|
-
- Немного проще подключение модуля в приложение-хост.
|
|
46
|
-
|
|
47
|
-
Модули с серверной частью:
|
|
48
|
-
- Можно реализовать дополнительную логику, которая не может быть реализована на клиенте.
|
|
49
|
-
- Возможность изменить режим подключения модуля без изменений на приложениях-хостах.
|
|
50
|
-
|
|
51
|
-
## Режимы подключения модулей
|
|
52
|
-
|
|
53
|
-
Кроме того, модули делятся еще и по способу их подключения:
|
|
54
|
-
- `mf` модули. Это модули, которые подключаются с помощью [webpack module federation](https://webpack.js.org/concepts/module-federation/).
|
|
55
|
-
- `embedded` модули. Такие модули подключаются просто через добавление нужных скриптов на страницу приложения.
|
|
56
|
-
|
|
57
|
-
Основная проблема, которую решает ModuleFederation - это возможность не загружать на хост-приложение код библиотек уже подключенных в него.
|
|
58
|
-
Например, хост-приложение уже использует `react`, модуль так же написан на `react`. ModuleFederation дает нам легко "переиспользовать"
|
|
59
|
-
уже загруженный в браузер код `react` в модуле, не загружая его еще раз.
|
|
60
|
-
|
|
61
|
-
### Сравнение
|
|
62
|
-
|
|
63
|
-
`mf` модули:
|
|
64
|
-
- **+++** Простой способ для переиспользования библиотек между модулем и приложением-хостом.
|
|
65
|
-
- **+++** Возможность использовать разные версии общих библиотек в разных модулях/хостах (речь про те библиотеки, которые будут шарится).
|
|
66
|
-
- **---** Нет встроенной изоляции стилей. Стили модуля будут применены к хост-приложению.
|
|
67
|
-
- **---** Нет возможности использовать модуль в приложении, которое не использует webpack.
|
|
68
|
-
|
|
69
|
-
Проблема изоляции стилей может быть решена с помощью [shadow dom](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM),
|
|
70
|
-
или с помощью css modules. Но это накладывает некоторые ограничения либо на поддерживаемые браузеры (shadow dom), либо на
|
|
71
|
-
существующую кодовую базу (css modules должны использоваться везде, если у вас будет две версии arui-feather на странице - будет не очень приятно).
|
|
72
|
-
|
|
73
|
-
`embedded` модули:
|
|
74
|
-
- **+++** Встроенная изоляция стилей. Стили модуля не будут применены к хост-приложению, если только вы не захотите этого.
|
|
75
|
-
- **---** Нет возможности использовать разные версии общих библиотек в разных модулях/хостах, если вы хотите их шарить.
|
|
76
|
-
|
|
77
|
-
## Как создать модуль
|
|
78
|
-
|
|
79
|
-
### Описать модуль в настройках arui-scripts
|
|
80
|
-
Для того чтобы ваше приложение начало предоставлять модули, вам необходимо добавить настройки в `arui-scripts.config.ts`:
|
|
81
|
-
|
|
82
|
-
```ts
|
|
83
|
-
import { PackageSettings } from 'arui-scripts';
|
|
84
|
-
|
|
85
|
-
const aruiScriptsConfig: PackageSettings = {
|
|
86
|
-
embeddedModules: {
|
|
87
|
-
exposes: {
|
|
88
|
-
'ClientModuleEmbedded': {
|
|
89
|
-
entry: './src/modules/client-module-embedded/index',
|
|
90
|
-
},
|
|
91
|
-
'ServerModuleEmbedded': {
|
|
92
|
-
entry: './src/modules/server-module-embedded/index',
|
|
93
|
-
// этот модуль будет ожидать на странице глобальные переменные react и reactDOM,
|
|
94
|
-
// он будет использовать их вместо библиотек из своего node_modules
|
|
95
|
-
embeddedConfig: {
|
|
96
|
-
react: 'react',
|
|
97
|
-
'react-dom': 'reactDOM',
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
mfModules: {
|
|
103
|
-
// MF модули тут смогут переиспользовать react и react-dom из хост-приложения,
|
|
104
|
-
// если хост приложение шарит эти библиотеки, и они совпадают по версии
|
|
105
|
-
shared: {
|
|
106
|
-
'react': '^17.0.0',
|
|
107
|
-
'react-dom': '^17.0.0',
|
|
108
|
-
},
|
|
109
|
-
exposes: {
|
|
110
|
-
'ClientModuleMF': './src/modules/client-module-mf/index',
|
|
111
|
-
'ServerModuleMF': './src/modules/server-module-mf/index',
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export default aruiScriptsConfig;
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
Все параметры конфигурации описаны [ниже](#Конфигурация-модулей).
|
|
120
|
-
|
|
121
|
-
### Создать модуль
|
|
122
|
-
Модуль является простым js/ts файлом, который следует простой договоренности:
|
|
123
|
-
|
|
124
|
-
`embedded` модули должны писать в window свой объект, который содержит функции для монтирования и демонтирования модуля.
|
|
125
|
-
|
|
126
|
-
```tsx
|
|
127
|
-
import type { // Обратите внимание на `import type` - наш в модель никак не использует код библиотеки, нам нужны только типы
|
|
128
|
-
ModuleMountFunction,
|
|
129
|
-
ModuleUnmountFunction,
|
|
130
|
-
WindowWithMountableModule
|
|
131
|
-
} from '@alfalab/scripts-modules';
|
|
132
|
-
|
|
133
|
-
const mountModule: ModuleMountFunction = (moduleId, params, targetNode) => {
|
|
134
|
-
// здесь мы можем отрендерить наш модуль в targetNode
|
|
135
|
-
// например:
|
|
136
|
-
ReactDOM.render(<App />, targetNode);
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
const unmountModule: ModuleUnmountFunction = (targetNode) => {
|
|
140
|
-
// здесь мы можем отключить наш модуль от targetNode
|
|
141
|
-
// например:
|
|
142
|
-
ReactDOM.unmountComponentAtNode(targetNode);
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
// ClientModuleEmbedded - имя модуля, которое мы указали в настройках arui-scripts
|
|
147
|
-
(window as WindowWithMountableModule).ClientModuleEmbedded = {
|
|
148
|
-
mount: mountModule,
|
|
149
|
-
unmount: unmountModule,
|
|
150
|
-
};
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
Если у вас возникает вопрос "фу, разве не плохо писать в window?". Ответ: да, плохо. Но вы думаете что ModuleFederation
|
|
154
|
-
работает как то иначе? По сути мы просто воспроизводим то же самое поведение, только в более ручном режиме.
|
|
155
|
-
|
|
156
|
-
`mf` модули не должны ничего писать в window (на самом деле за них то же самое делает webpack). Они должны просто
|
|
157
|
-
экспортировать функции `mount` и `unmount`:
|
|
158
|
-
|
|
159
|
-
```tsx
|
|
160
|
-
import type { ModuleMountFunction, ModuleUnmountFunction } from '@alfalab/scripts-modules';
|
|
161
|
-
|
|
162
|
-
export const mount: ModuleMountFunction = (moduleId, params, targetNode) => {
|
|
163
|
-
// здесь мы можем отрендерить наш модуль в targetNode
|
|
164
|
-
// например:
|
|
165
|
-
ReactDOM.render(<App />, targetNode);
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
export const unmount: ModuleUnmountFunction = (targetNode) => {
|
|
169
|
-
// здесь мы можем отключить наш модуль от targetNode
|
|
170
|
-
// например:
|
|
171
|
-
ReactDOM.unmountComponentAtNode(targetNode);
|
|
172
|
-
};
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### (Опционально) Определить серверный эндпоинт для модуля
|
|
176
|
-
Если вы хотите, чтобы ваш модуль имел серверную часть, которая сможет подготовить данные для модуля, то вам необходимо
|
|
177
|
-
определить серверный эндпоинт для модуля. Для этого вам нужно определить объект, описывающий ваши модули:
|
|
178
|
-
|
|
179
|
-
```ts
|
|
180
|
-
import type { ModulesConfig } from '@alfalab/scripts-server';
|
|
181
|
-
|
|
182
|
-
const modules: ModulesConfig = {
|
|
183
|
-
'ServerModuleEmbedded': {
|
|
184
|
-
mountMode: 'embedded',
|
|
185
|
-
version: '1.0.0',
|
|
186
|
-
getRunParams: async (getResourcesRequest) => ({
|
|
187
|
-
// getResouresRequest - это объект, который будет передан из хост-приложения
|
|
188
|
-
// данные, которые вернет эта функция будут переданы в mount функцию модуля
|
|
189
|
-
paramFromServer: 'This can be any data from server',
|
|
190
|
-
asyncData: 'It can be constructed from async data, so you may perform some service calls here',
|
|
191
|
-
contextRoot: 'http://localhost:8081',
|
|
192
|
-
}),
|
|
193
|
-
},
|
|
194
|
-
'ServerModuleMF': {
|
|
195
|
-
mountMode: 'mf',
|
|
196
|
-
version: '1.0.0',
|
|
197
|
-
getRunParams: async () => ({
|
|
198
|
-
paramFromServer: 'This can be any data from server',
|
|
199
|
-
asyncData: 'It can be constructed from async data, so you may perform some service calls here',
|
|
200
|
-
contextRoot: 'http://localhost:8081',
|
|
201
|
-
}),
|
|
202
|
-
},
|
|
203
|
-
};
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
Далее, в зависимости от того, какой серверный фреймворк вы используете, вам нужно будет подключить ваши модули в
|
|
207
|
-
соответствующий хендлер. Например, для express это будет выглядеть так:
|
|
208
|
-
|
|
209
|
-
```ts
|
|
210
|
-
import { createGetModulesExpress } from '@alfalab/scripts-server/build/express';
|
|
211
|
-
|
|
212
|
-
const modulesRouter = createGetModulesExpress(modules);
|
|
213
|
-
|
|
214
|
-
app.use(modulesRouter);
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
Для hapi@16:
|
|
218
|
-
|
|
219
|
-
```ts
|
|
220
|
-
import { createGetModulesHapi16Plugin } from '@alfalab/scripts-server/build/hapi16';
|
|
221
|
-
|
|
222
|
-
const modulesPlugin = createGetModulesHapi16Plugin(modules);
|
|
223
|
-
|
|
224
|
-
server.register(modulesPlugin);
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
Для hapi@20:
|
|
228
|
-
|
|
229
|
-
```ts
|
|
230
|
-
import { createGetModulesHapi20Plugin } from '@alfalab/scripts-server/build/hapi20';
|
|
231
|
-
|
|
232
|
-
const modulesPlugin = createGetModulesHapi20Plugin(modules);
|
|
233
|
-
|
|
234
|
-
server.register(modulesPlugin);
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
Если вы хотите использовать другой серверный фреймворк, вы можете использовать общий хелпер:
|
|
238
|
-
|
|
239
|
-
```ts
|
|
240
|
-
import { createGetModulesMethod } from '@alfalab/scripts-server';
|
|
241
|
-
|
|
242
|
-
const getModules = createGetModulesMethod(modules);
|
|
243
|
-
|
|
244
|
-
// getModules будет иметь следующую сигнатуру:
|
|
245
|
-
type ModulesMethod = {
|
|
246
|
-
method: string; // http метод, который нужно использовать для обработки запроса
|
|
247
|
-
path: string; // путь, который нужно использовать для обработки запроса
|
|
248
|
-
handler: (request: GetResourcesRequest) => Promise<GetResourcesResponse>; // обработчик запроса
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// далее в зависимости от фреймворка вы можете использовать этот метод
|
|
252
|
-
// для конфигурации вашего сервера
|
|
253
|
-
// вы можете посмотреть примеры реализации тких методов для express, hapi@16 и hapi@20.
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
### (Опционально) Разобраться с изоляцией стилей
|
|
257
|
-
|
|
258
|
-
#### Embedded модули
|
|
259
|
-
В случае с embedded модулями, стили модуля будут применены только к элементам, которые находятся внутри элемента
|
|
260
|
-
с классом `module-{имя модуля}`. Это позволяет изолировать стили модуля от стилей хост-приложения.
|
|
261
|
-
|
|
262
|
-
Вашей ответственностью будет добавить к рут-элементу модуля класс .module-nameOfModule. Вы должны сделать это в самом верхнем компоненте/элементе вашего модуля.
|
|
263
|
-
|
|
264
|
-
Вы можете переопределить префикс для css классов модуля, в `arui-scripts.config.ts`, подробнее в [конфигурации модулей](#Конфигурация-модулей).
|
|
265
|
-
|
|
266
|
-
Если ваше react-приложение использует порталы, вам так же надо не забыть добавить префикс к элементу-порталу.
|
|
267
|
-
|
|
268
|
-
#### MF модули
|
|
269
|
-
Никакого встроенного механизма изоляции стилей для MF модулей нет. Если хост-приложение и модуль используют css-modules,
|
|
270
|
-
то конфликтов возникнуть не должно. Если же это не так - вы можете попробовать решить эту проблему используя shadow-dom.
|
|
271
|
-
|
|
272
|
-
### Тестирование модулей
|
|
273
|
-
|
|
274
|
-
TODO
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
## Подключение модулей в хост-приложение
|
|
278
|
-
|
|
279
|
-
### (Опционально) Настроить общие библиотеки
|
|
280
|
-
В случае, если модуль который вы хотите подключить использует какие-то общие библиотеки с хост-приложением, вы должны
|
|
281
|
-
определить параметры для этих библиотек в `arui-scripts.config.ts`.
|
|
282
|
-
|
|
283
|
-
```ts
|
|
284
|
-
import { PackageSettings } from 'arui-scripts';
|
|
285
|
-
|
|
286
|
-
const aruiScriptsConfig: PackageSettings = {
|
|
287
|
-
embeddedModules: {
|
|
288
|
-
shared: {
|
|
289
|
-
'react': 'reactDOM',
|
|
290
|
-
'react-dom': 'reactDOM',
|
|
291
|
-
}
|
|
292
|
-
},
|
|
293
|
-
mfModules: {
|
|
294
|
-
shared: {
|
|
295
|
-
'react': {
|
|
296
|
-
eager: true,
|
|
297
|
-
singleton: true,
|
|
298
|
-
requiredVersion: '^17.0.0',
|
|
299
|
-
},
|
|
300
|
-
'react-dom': {
|
|
301
|
-
eager: true,
|
|
302
|
-
singleton: true,
|
|
303
|
-
requiredVersion: '^17.0.0',
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
},
|
|
307
|
-
}
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
### Подключить модуль на страницу
|
|
311
|
-
Клиентские и серверные модули подключаются на страницу немного по-разному.
|
|
312
|
-
|
|
313
|
-
Подключение серверных модулей будет выглядеть так:
|
|
314
|
-
|
|
315
|
-
```tsx
|
|
316
|
-
import React, { useMemo } from 'react';
|
|
317
|
-
import { createLoader, useModuleLoader, getModuleResourcesPath } from '@alfalab/scripts-modules';
|
|
318
|
-
|
|
319
|
-
// Это просто функция, которая должна обратиться к серверу модуля.
|
|
320
|
-
// Скорее всего у вас уже есть хелпер, который создает подобные функции
|
|
321
|
-
// и насыщает запрос дополнительными данными, например, токеном авторизации, traceId и т.д.
|
|
322
|
-
const customFetch = (loaderParams) => {
|
|
323
|
-
// arui-scripts не знает заранее ни дополнительных параметров (авторизация, заголовки), которые вы хотите передать в запрос,
|
|
324
|
-
// ни того, на какой адрес нужно делать запрос. Поэтому вам нужно самим реализовать эту функцию.
|
|
325
|
-
return fetch(`http://localhost:8081/${getModuleResourcesPath}`, {
|
|
326
|
-
method: 'POST',
|
|
327
|
-
body: JSON.stringify(loaderParams),
|
|
328
|
-
headers: {
|
|
329
|
-
'Content-Type': 'application/json',
|
|
330
|
-
},
|
|
331
|
-
}).then((response) => response.json());
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
export const ServerModuleMounter = () => {
|
|
335
|
-
// Загрузчик - это функция, которая прячет в себе запрос к серверу, подключение ресурсов на страницу и т.д.
|
|
336
|
-
const loader = useMemo(() => createLoader({
|
|
337
|
-
hostAppId: 'example', // id вашего хост-приложения
|
|
338
|
-
fetchFunction: customFetch,
|
|
339
|
-
// С помощью этой функции вы можете передать дополнительные параметры в запрос к серверу модуля.
|
|
340
|
-
getModuleRequestParams: async () => ({
|
|
341
|
-
paramName: 'some param that will be passed to module',
|
|
342
|
-
}),
|
|
343
|
-
}), []);
|
|
344
|
-
|
|
345
|
-
// useModuleLoader - это простой хук, который с помощью переданного загрузчика подключает модуль на страницу.
|
|
346
|
-
const {
|
|
347
|
-
loadingState, // состояние загрузки модуля. 'pending' - модуль еще не загружен, 'resolved' - модуль загружен, 'rejected' - произошла ошибка при загрузке модуля
|
|
348
|
-
targetElementRef, // ссылка на элемент, в который будет подключен модуль
|
|
349
|
-
} = useModuleLoader(
|
|
350
|
-
"ServerModuleEmbedded", // id модуля, который был указан в arui-scripts.config.ts
|
|
351
|
-
loader,
|
|
352
|
-
);
|
|
353
|
-
|
|
354
|
-
return (
|
|
355
|
-
<div>
|
|
356
|
-
{ loadingState === 'pending' && <div>Loading...</div> }
|
|
357
|
-
{ loadingState === 'rejected' && <div>Failed to load module</div> }
|
|
358
|
-
|
|
359
|
-
<div ref={ targetElementRef } />
|
|
360
|
-
</div>
|
|
361
|
-
);
|
|
362
|
-
};
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
При подключении же клиентских модулей необходимо использовать функцию `createClientLoader` вместо `createLoader`:
|
|
366
|
-
|
|
367
|
-
```tsx
|
|
368
|
-
import React from 'react';
|
|
369
|
-
import { createClientLoader, useModuleLoader } from '@alfalab/scripts-modules';
|
|
370
|
-
import { Underlay } from '@alfalab/core-components/underlay';
|
|
371
|
-
import { Spinner } from '@alfalab/core-components/spinner';
|
|
372
|
-
|
|
373
|
-
const loader = createClientLoader({
|
|
374
|
-
baseUrl: 'http://localhost:8081/', // базовый адрес приложения с подключаемым модулем
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
export const EmbeddedModuleMounter = () => {
|
|
378
|
-
const { loadingState, targetElementRef } = useModuleLoader(
|
|
379
|
-
"ClientModuleEmbedded", // id модуля, который был указан в arui-scripts.config.ts
|
|
380
|
-
loader
|
|
381
|
-
);
|
|
382
|
-
|
|
383
|
-
return (
|
|
384
|
-
<div>
|
|
385
|
-
{ loadingState === 'pending' && <Spinner size='m' /> }
|
|
386
|
-
{ loadingState === 'rejected' && <div>Failed to load module</div> }
|
|
387
|
-
|
|
388
|
-
<div ref={ targetElementRef } />
|
|
389
|
-
</div>
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
# Конфигурация модулей
|
|
397
|
-
|
|
398
|
-
## Embedded модули
|
|
399
|
-
|
|
400
|
-
`embeddedModules` - объект, управляющий конфигурацией embedded модулей.
|
|
401
|
-
|
|
402
|
-
- `embeddedModules.shared` - объект, описывающий библиотеки, которые приложение будет предоставлять модулям. Ключ объекта - название библиотеки,
|
|
403
|
-
значение - название переменной в window, в которую будет записана библиотека.
|
|
404
|
-
- `embeddedModules.exposes` - объект, описывающий embedded модули. Ключ объекта - id модуля, значение - объект с конфигурацией модуля.
|
|
405
|
-
- `embeddedModules.exposes[id].entry` - путь до точки входа модуля. Должен быть либо абсолютным, либо относительным от корня проекта.
|
|
406
|
-
- `embeddedModules.exposes[id].cssPrefix` - префикс для css классов модуля. По умолчанию все стили модуля будут префиксированы с `.module-{id модуля}`. Вы можете передать сюда
|
|
407
|
-
свой префикс, или `false` чтобы отключить префиксирование.
|
|
408
|
-
- `embeddedModules.exposes[id].embeddedConfig` - объект, описывающий какие библиотеки должны быть помечены для модуля как `external`. Ключ объекта - название библиотеки,
|
|
409
|
-
значение - название переменной в window, в которой модуль будет искать эту библиотеку.
|
|
410
|
-
|
|
411
|
-
## MF модули
|
|
412
|
-
|
|
413
|
-
`mfModules` - объект, управляющий конфигурацией Module Federation модулей.
|
|
414
|
-
|
|
415
|
-
- `mfModules.name` - опциональное имя модуля. Это имя будет использовано как название контейнера модуля. Если не указано, то
|
|
416
|
-
будет использовано имя пакета, в котором все `-` будут заменены на `_`. Должно быть уникальным в рамках хост-приложения,
|
|
417
|
-
и быть валидным именем для js переменной.
|
|
418
|
-
- `mfModules.shared` - объект, описывающий библиотеки, которые приложение будет предоставлять модулям, и библиотеки, которые модуль будет пытаться получить от хост-приложения.
|
|
419
|
-
Подробнее про варианты описания можно почитать в [документации](https://webpack.js.org/plugins/module-federation-plugin/#specify-package-versions).
|
|
420
|
-
- `exposes` - объект, описывающий какие модули должны быть предоставлены хост-приложению. Ключ объекта - название модуля,
|
|
421
|
-
значение - точка входа модуля.
|
|
422
|
-
|
|
423
|
-
# API библиотек
|