arui-scripts 14.5.0-feat-modules.2 → 14.5.0-feat-modules.4

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/docs/modules.md CHANGED
@@ -1,58 +1,29 @@
1
- # Модули приложений
2
-
3
- ## Мотивация
1
+ # Что такое модули
4
2
  Модули приложений предназначены для решения простой проблемы - переиспользование фронтового кода между приложениями.
5
3
 
6
4
  В целом, для того чтобы переиспользовать код у нас есть много разных способов, например:
7
5
  - копировать код из одного приложения в другое. Быстро, но неудобно и неэффективно.
8
6
  - выносить код в отдельный пакет и подключать его через npm. Понятный и достаточно удобный вариант,
9
- но ограничивает нас в скорости изменений. Если мы хотим заменить обновить код в пакете, процесс
10
- раскатки этого обновления на все приложения может занять много времени.
7
+ но ограничивает нас в скорости изменений. Если мы хотим заменить обновить код в пакете, процесс
8
+ раскатки этого обновления на все приложения может занять много времени.
11
9
  - Сделать так, чтобы мы могли подключать код из других приложений в свое. При этом мы получаем
12
- возможность быстро изменить общий код только в одном месте, а все приложения автоматически получат
13
- обновления.
10
+ возможность быстро изменить общий код только в одном месте, а все приложения автоматически получат
11
+ обновления.
14
12
 
15
13
  Модули позволяют реализовать именно последний вариант. Если вы знакомы с концепцией [module federation](https://webpack.js.org/concepts/module-federation/),
16
14
  то модули приложений - это его реализация в рамках arui-scripts, с дополнительным уровнем абстракции, который, в том числе,
17
15
  позволяет использовать модули без самого module-federation.
18
16
 
17
+ ## Общие принципы работы модулей
18
+ С точки зрения кода модуль представляет собой простой js-объект, который может быть _каким то образом_ подключен в другое приложение.
19
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
- - Возможность изменить режим подключения модуля без изменений на приложениях-хостах.
20
+ `arui-scripts` предоставляет решение для сборки таких модулей, а также отдельную библиотеку для упрощения их подключения в другие приложения.
50
21
 
51
22
  ## Режимы подключения модулей
52
23
 
53
- Кроме того, модули делятся еще и по способу их подключения:
54
- - `mf` модули. Это модули, которые подключаются с помощью [webpack module federation](https://webpack.js.org/concepts/module-federation/).
55
- - `embedded` модули. Такие модули подключаются просто через добавление нужных скриптов на страницу приложения.
24
+ В `arui-scripts` есть два способа сборки модулей:
25
+ - `mf` - Это модули, которые подключаются с помощью [webpack module federation](https://webpack.js.org/concepts/module-federation/).
26
+ - `embedded` - Это модули, которые подключаются просто добавлением нужных скриптов на страницу.
56
27
 
57
28
  Основная проблема, которую решает ModuleFederation - это возможность не загружать на хост-приложение код библиотек уже подключенных в него.
58
29
  Например, хост-приложение уже использует `react`, модуль так же написан на `react`. ModuleFederation дает нам легко "переиспользовать"
@@ -74,6 +45,43 @@
74
45
  - **+++** Встроенная изоляция стилей. Стили модуля не будут применены к хост-приложению, если только вы не захотите этого.
75
46
  - **---** Нет возможности использовать разные версии общих библиотек в разных модулях/хостах, если вы хотите их шарить.
76
47
 
48
+ ## Возможность управления модулями с сервера
49
+ Сами модули используются только на клиентской части приложения. Но, в некоторых случаях, может быть полезно иметь возможность
50
+ управлять тем, какой модуль должен быть подключен на странице с сервера, или же иметь модуль, который будет при загрузке
51
+ иметь доступ к данным, доступным только на сервере (аналогично тому, как мы передаем серверный стейт в приложения при SSR).
52
+
53
+ Поэтому `arui-scripts` предоставляет возможность создать специальный эндпоинт на вашем сервере, из которого вы сможете управлять
54
+ состоянием модуля.
55
+
56
+ Модули с такой возможностью мы называем _серверными модулями_ (обычные модули мы называем _клиентскими_).
57
+
58
+ ## Особые типы модулей
59
+ Несмотря на то, что сами по себе модули представляют собой простой js-объект, мы определяем один особый тип модулей - _монтируемые модули_.
60
+
61
+ ### Монтируемые модули
62
+ Монтируемые модули - это модули, основное предназначение которых - отрендерить какой-то компонент внутри хост-приложения.
63
+ Монтируемые модули могут быть как клиентскими, так и серверными.
64
+
65
+ Такие модули должны экспортировать две функции:
66
+
67
+ ```tsx
68
+ export function mount(targetNode, runParams, serverState): void {
69
+ // здесь происходит монтирование модуля в хост-приложение
70
+ // targetNode - это DOM-нода, в которую нужно отрендерить модуль
71
+ // runParams - это параметры, которые были переданы при запуске модуля
72
+ // serverState - это состояние, которое было передано с сервера
73
+
74
+ // Скорее всего это будет что-то вроде:
75
+ ReactDOM.render(<App preparedState={serverState} runParams={runParams} />, targetNode);
76
+ }
77
+
78
+ export function unmount(targetNode): void {
79
+ // здесь происходит демонтирование модуля из хост-приложения
80
+ // Скорее всего это будет что-то вроде:
81
+ ReactDOM.unmountComponentAtNode(targetNode);
82
+ }
83
+ ```
84
+
77
85
  ## Как создать модуль
78
86
 
79
87
  ### Описать модуль в настройках arui-scripts
@@ -119,59 +127,103 @@ export default aruiScriptsConfig;
119
127
  Все параметры конфигурации описаны [ниже](#Конфигурация-модулей).
120
128
 
121
129
  ### Создать модуль
122
- Модуль является простым js/ts файлом, который следует простой договоренности:
130
+ Модуль является простым js/ts файлом. Он может использовать любой код вашего проекта, и любые библиотеки из node_modules.
123
131
 
124
- `embedded` модули должны писать в window свой объект, который содержит функции для монтирования и демонтирования модуля.
132
+ В зависимости от режима подключения модуля, входная точка будет выглядеть по разному.
125
133
 
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
- };
134
+ #### Embedded модуль
135
+ Входная точка embedded модуля должна писать в глобальную переменную `window` объект с ключом `{НазваниеМодуля}`.
136
+ Все поля этого объекта по сути и будут являться модулем, ваши потребители смогут использовать их.
138
137
 
139
- const unmountModule: ModuleUnmountFunction = (targetNode) => {
140
- // здесь мы можем отключить наш модуль от targetNode
141
- // например:
142
- ReactDOM.unmountComponentAtNode(targetNode);
138
+ ```ts
139
+ // src/modules/client-module-embedded/index.ts
140
+
141
+ window.ClientModuleEmbedded = {
142
+ doSomething: () => {
143
+ console.log('Hello from embedded module!');
144
+ },
145
+ publicConstant: 3.14,
146
+ // ...
143
147
  };
148
+ ```
144
149
 
150
+ <details>
151
+ <summary>Писать в window? Вы что, с дуба рухнулись?</summary>
152
+ Да, конечно, это может создать определенные проблемы (конфликты имен модулей, определенные ограничения на используемые названия),
153
+ но по сути это единственный способ передать код модуля в хост-приложение.
145
154
 
146
- // ClientModuleEmbedded - имя модуля, которое мы указали в настройках arui-scripts
147
- (window as WindowWithMountableModule).ClientModuleEmbedded = {
148
- mount: mountModule,
149
- unmount: unmountModule,
155
+ Webpack module federation делает абсолютно то же самое, просто прячет работу с глобальными переменными за собой.
156
+ </details>
157
+
158
+ #### MF модуль
159
+ Входная точка MF модуля должна экспортировать все поля модуля через `export`.
160
+
161
+ ```ts
162
+ // src/modules/client-module-mf/index.ts
163
+
164
+ export const doSomething = () => {
165
+ console.log('Hello from mf module!');
150
166
  };
167
+
168
+ export const publicConstant = 3.14;
151
169
  ```
152
170
 
153
- Если у вас возникает вопрос "фу, разве не плохо писать в window?". Ответ: да, плохо. Но вы думаете что ModuleFederation
154
- работает как то иначе? По сути мы просто воспроизводим то же самое поведение, только в более ручном режиме.
171
+ #### Создание модулей предопределенного типа
155
172
 
156
- `mf` модули не должны ничего писать в window (на самом деле за них то же самое делает webpack). Они должны просто
157
- экспортировать функции `mount` и `unmount`:
173
+ **Монтируемый модуль, mf**
158
174
 
159
175
  ```tsx
176
+ // src/modules/client-module-mf/index.ts
177
+
178
+ import React from 'react';
179
+ import ReactDOM from 'react-dom';
160
180
  import type { ModuleMountFunction, ModuleUnmountFunction } from '@alfalab/scripts-modules';
181
+ import { ClientModuleMf } from './ClientModuleMf';
161
182
 
162
- export const mount: ModuleMountFunction = (moduleId, params, targetNode) => {
163
- // здесь мы можем отрендерить наш модуль в targetNode
164
- // например:
165
- ReactDOM.render(<App />, targetNode);
166
- };
183
+ export const mount: ModuleMountFunction<any, any> = (targetNode, runParams, serverState) => {
184
+ console.log('ClientModuleMf: mount', { runParams, serverState });
185
+ if (!targetNode) {
186
+ throw new Error(`Target node is not defined for module`);
187
+ }
167
188
 
189
+ ReactDOM.render(<ClientModuleMf />, targetNode);
190
+ };
168
191
  export const unmount: ModuleUnmountFunction = (targetNode) => {
169
- // здесь мы можем отключить наш модуль от targetNode
170
- // например:
192
+ console.log('ClientModuleMf: unmount');
193
+ if (!targetNode) {
194
+ return;
195
+ }
196
+
197
+ ReactDOM.unmountComponentAtNode(targetNode);
198
+ };
199
+ ```
200
+
201
+ **Монтируемый модуль, embedded**
202
+
203
+ ```tsx
204
+ // src/modules/client-module-embedded/index.ts
205
+ import React from 'react';
206
+ import ReactDOM from 'react-dom';
207
+ import type { ModuleMountFunction, ModuleUnmountFunction, WindowWithMountableModule } from '@alfalab/scripts-modules';
208
+ import { ClientModuleMf } from './ClientModuleMf';
209
+
210
+ const mount: ModuleMountFunction<any, any> = (targetNode, runParams, serverState) => {
211
+ console.log('ClientModuleEmbedded: mount', { runParams, serverState });
212
+ ReactDOM.render(<ClientModuleMf />, targetNode);
213
+ };
214
+ const unmount: ModuleUnmountFunction = (targetNode) => {
215
+ console.log('ClientModuleEmbedded: unmount');
216
+
171
217
  ReactDOM.unmountComponentAtNode(targetNode);
172
218
  };
219
+
220
+ (window as WindowWithMountableModule).ClientModuleEmbedded = {
221
+ mount: mountModule,
222
+ unmount: unmountModule,
223
+ };
173
224
  ```
174
225
 
226
+
175
227
  ### (Опционально) Определить серверный эндпоинт для модуля
176
228
  Если вы хотите, чтобы ваш модуль имел серверную часть, которая сможет подготовить данные для модуля, то вам необходимо
177
229
  определить серверный эндпоинт для модуля. Для этого вам нужно определить объект, описывающий ваши модули:
@@ -185,7 +237,8 @@ const modules: ModulesConfig = {
185
237
  version: '1.0.0',
186
238
  getRunParams: async (getResourcesRequest) => ({
187
239
  // getResouresRequest - это объект, который будет передан из хост-приложения
188
- // данные, которые вернет эта функция будут переданы в mount функцию модуля
240
+
241
+ // данные, которые вернет эта будут доступны при инициализации модуля
189
242
  paramFromServer: 'This can be any data from server',
190
243
  asyncData: 'It can be constructed from async data, so you may perform some service calls here',
191
244
  contextRoot: 'http://localhost:8081',
@@ -203,6 +256,8 @@ const modules: ModulesConfig = {
203
256
  };
204
257
  ```
205
258
 
259
+ Подробнее о `getResourcesRequest` и `getRunParams` рассказано в разделе [Подключение модулей](#Подключение-модулей).
260
+
206
261
  Далее, в зависимости от того, какой серверный фреймворк вы используете, вам нужно будет подключить ваши модули в
207
262
  соответствующий хендлер. Например, для express это будет выглядеть так:
208
263
 
@@ -214,7 +269,7 @@ const modulesRouter = createGetModulesExpress(modules);
214
269
  app.use(modulesRouter);
215
270
  ```
216
271
 
217
- Для hapi@16:
272
+ Для `hapi@16`:
218
273
 
219
274
  ```ts
220
275
  import { createGetModulesHapi16Plugin } from '@alfalab/scripts-server/build/hapi16';
@@ -224,7 +279,7 @@ const modulesPlugin = createGetModulesHapi16Plugin(modules);
224
279
  server.register(modulesPlugin);
225
280
  ```
226
281
 
227
- Для hapi@20:
282
+ Для `hapi@20`:
228
283
 
229
284
  ```ts
230
285
  import { createGetModulesHapi20Plugin } from '@alfalab/scripts-server/build/hapi20';
@@ -265,159 +320,175 @@ type ModulesMethod = {
265
320
 
266
321
  Если ваше react-приложение использует порталы, вам так же надо не забыть добавить префикс к элементу-порталу.
267
322
 
323
+ **Важно** - изоляция стилей работает только в одном направлении - стили модуля не будут применены к элементам
324
+ хост-приложения. Но стили хост-приложения могут быть применены к элементам модуля.
325
+
268
326
  #### MF модули
269
327
  Никакого встроенного механизма изоляции стилей для MF модулей нет. Если хост-приложение и модуль используют css-modules,
270
328
  то конфликтов возникнуть не должно. Если же это не так - вы можете попробовать решить эту проблему используя shadow-dom.
271
329
 
272
330
  ### Тестирование модулей
331
+ Поскольку в общем случае модули представляют собой простой js код - для тестирования вы можете пользоваться любыми привычными вам инструментами.
273
332
 
274
- TODO
333
+ Для тестирования модулей в cypress или playwright вы можете создать отдельный эндпоинт в вашем приложении, который будет
334
+ подключать модуль в ваше же приложение.
275
335
 
276
336
 
277
- ## Подключение модулей в хост-приложение
337
+ # Подключение модулей
278
338
 
279
- ### (Опционально) Настроить общие библиотеки
280
- В случае, если модуль который вы хотите подключить использует какие-то общие библиотеки с хост-приложением, вы должны
281
- определить параметры для этих библиотек в `arui-scripts.config.ts`.
339
+ ## Создание загрузчика
340
+ Базовый способ подключение модулей - это использование `createModuleLoader` из `@alfalab/scripts-modules`. Этот метод
341
+ вернет вам функцию, которая позволит подключить модуль в ваше приложение.
282
342
 
283
343
  ```ts
284
- import { PackageSettings } from 'arui-scripts';
344
+ import { createModuleLoader } from '@alfalab/scripts-modules';
345
+
346
+ const loader = createModuleLoader({
347
+ moduleId: 'test', // id модуля, который вы хотите подключить
348
+ // функция, которая должна вернуть описание модуля.
349
+ getModuleResources: async ({ moduleId, hostAppId, params }) => ({
350
+ scripts: ['http://localhost:8081/static/js/main.js'], // скрипты модуля
351
+ styles: ['http://localhost:8081/static/css/main.css'], // стили модуля
352
+ moduleVersion: '1.0.0', // версия модуля
353
+ appName: 'moduleSourceAppName', // имя приложения, которое является источником модуля
354
+ mountMode: 'embedded', // режим монтирования модуля
355
+ moduleRunParams: { // параметры, которые будут доступны при инициализации модуля
356
+ baseUrl: 'http://localhost:8081',
357
+ },
358
+ }),
359
+ });
360
+ ```
285
361
 
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
- }
362
+ Вам вовсе не обязательно руками описывать функцию `getModuleResources`. В зависимости от типа модуля, вы можете
363
+ использовать один из готовых хелперов:
364
+
365
+ Для клиентских модулей:
366
+ ```ts
367
+ import { createModuleLoader, createClientResourcesFetcher } from '@alfalab/scripts-modules';
368
+
369
+ const loader = createModuleLoader({
370
+ moduleId: 'test',
371
+ getModuleResources: createClientResourcesFetcher({
372
+ baseUrl: 'http://localhost:8081',
373
+ mountMode: 'mf',
374
+ }),
375
+ });
308
376
  ```
309
377
 
310
- ### Подключить модуль на страницу
311
- Клиентские и серверные модули подключаются на страницу немного по-разному.
378
+ `createClientResourcesFetcher` сам сделает запрос за манифестом приложения, и правильным образом сформирует описание модуля.
312
379
 
313
- Подключение серверных модулей будет выглядеть так:
380
+ Для серверных модулей:
381
+ ```ts
382
+ import { createModuleLoader, createServerResourcesFetcher } from '@alfalab/scripts-modules';
383
+
384
+ const loader = createModuleLoader({
385
+ moduleId: 'test',
386
+ getModuleResources: createServerResourcesFetcher({
387
+ baseUrl: 'http://localhost:8081',
388
+ headers: { 'X-Auth': 'bla-bla' } // опционально вы можете передать дополнительные заголовки для запроса
389
+ }),
390
+ });
391
+ ```
314
392
 
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
- }
393
+ `createServerResourcesFetcher` сам сделает запрос к ручке, которая отдает описание модуля.
333
394
 
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
- );
395
+ В случае же совсем кастомных требований, вы можете реализовать функцию `getModuleResources` самостоятельно.
353
396
 
354
- return (
355
- <div>
356
- { loadingState === 'pending' && <div>Loading...</div> }
357
- { loadingState === 'rejected' && <div>Failed to load module</div> }
397
+ ## Использование загрузчика
398
+ После того как вы создали `loader` - вы легко можете получить доступ к модулю:
358
399
 
359
- <div ref={ targetElementRef } />
360
- </div>
361
- );
362
- };
400
+ ```ts
401
+ const { module, unmount, moduleResources } = await loader({
402
+ getResourcesParams: {}, // параметры, которые будут переданы в getModuleResources
403
+ });
404
+
405
+ console.log(module); // модуль, который вы загрузили. Тут будут доступны всё, что было экспортировано из модуля
406
+ console.log(moduleResources); // полный ответ от getModuleResources
407
+
408
+ // вызов этой функции отмонтирует модуль из вашего приложения - удалит скрипты и стили модуля, а так же удалит
409
+ // все глобальные переменные, которые были определены в модуле.
410
+ unmount();
411
+ ```
412
+
413
+ При вызове `loader` вы можете передать параметры, которые попадут в функцию `getModuleResources`. Это может быть полезно,
414
+ если вы хотите передать какие-то параметры на сервер модуля.
415
+
416
+ `getModuleResources` будет вызвана со следующими параметрами:
417
+ ```ts
418
+ const getModuleResourcesParams = {
419
+ moduleId: 'test', // id модуля, который вы хотите подключить
420
+ hostAppId: 'hostAppId', // id вашего приложения
421
+ params: {}, // параметры, которые вы передали в loader как `getResourcesParams`
422
+ }
363
423
  ```
364
424
 
365
- При подключении же клиентских модулей необходимо использовать функцию `createClientLoader` вместо `createLoader`:
425
+ На сервер будут отправлены именно эти параметры, они будут доступны как параметр в `getRunParams` в описании вашего модуля.
426
+
427
+ При использовании клиентских модулей, вам не нужно беспокоиться о том, какие параметры вы передаете в `getModuleResources` - они
428
+ никак не используются в клиентских модулях.
429
+
430
+ ## Использования загрузчика в реакт-приложении
431
+
432
+ Для того чтобы упростить работу с загрузчиком в реакт-приложении, мы предоставляем хук `useModuleLoader`:
366
433
 
367
434
  ```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';
435
+ import { createModuleLoader, useModuleLoader } from '@alfalab/scripts-modules';
372
436
 
373
- const loader = createClientLoader({
374
- baseUrl: 'http://localhost:8081/', // базовый адрес приложения с подключаемым модулем
437
+ const loader = createModuleLoader({
438
+ moduleId: 'test',
439
+ getModuleResources: createClientResourcesFetcher({
440
+ baseUrl: 'http://localhost:8081',
441
+ mountMode: 'mf',
442
+ }),
375
443
  });
376
444
 
377
- export const EmbeddedModuleMounter = () => {
378
- const { loadingState, targetElementRef } = useModuleLoader(
379
- "ClientModuleEmbedded", // id модуля, который был указан в arui-scripts.config.ts
380
- loader
381
- );
445
+ const MyComponent = () => {
446
+ const { loadingState, module, resources } = useModuleLoader(loader); // вторым параметром можно передать параметры, которые будут переданы в getModuleResources
382
447
 
383
448
  return (
384
449
  <div>
385
- { loadingState === 'pending' && <Spinner size='m' /> }
386
- { loadingState === 'rejected' && <div>Failed to load module</div> }
387
-
388
- <div ref={ targetElementRef } />
450
+ {loadingState === 'loading' && <div>Loading...</div>}
451
+ {loadingState === 'error' && <div>Error</div>}
452
+ {loadingState === 'success' && (
453
+ <div>
454
+ <div>Module loaded</div>
455
+ <div>{module}</div> {/* модуль, который вы загрузили. Тут будет доступно всё, что было экспортировано из модуля */}
456
+ <div>{resources}</div>
457
+ </div>
458
+ )}
389
459
  </div>
390
460
  );
391
- }
392
-
461
+ };
393
462
  ```
394
463
 
464
+ ### Использование монтируемых модулей
395
465
 
396
- # Конфигурация модулей
397
-
398
- ## Embedded модули
466
+ Для работы с монтируемыми модулями так же есть готовый хук `useModuleMounter`:
399
467
 
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 модули
468
+ ```tsx
469
+ import { createModuleLoader, useModuleMounter } from '@alfalab/scripts-modules';
412
470
 
413
- `mfModules` - объект, управляющий конфигурацией Module Federation модулей.
471
+ const loader = createModuleLoader({
472
+ moduleId: 'test',
473
+ getModuleResources: createClientResourcesFetcher({
474
+ baseUrl: 'http://localhost:8081',
475
+ mountMode: 'embedded',
476
+ }),
477
+ });
414
478
 
415
- - `mfModules.name` - опциональное имя модуля. Это имя будет использовано как название контейнера модуля. Если не указано, то
416
- будет использовано имя пакета, в котором все `-` будут заменены на `_`. Должно быть уникальным в рамках хост-приложения,
417
- и быть валидным именем для js переменной.
418
- - `mfModules.shared` - объект, описывающий библиотеки, которые приложение будет предоставлять модулям, и библиотеки, которые модуль будет пытаться получить от хост-приложения.
419
- Подробнее про варианты описания можно почитать в [документации](https://webpack.js.org/plugins/module-federation-plugin/#specify-package-versions).
420
- - `exposes` - объект, описывающий какие модули должны быть предоставлены хост-приложению. Ключ объекта - название модуля,
421
- значение - точка входа модуля.
479
+ const MyComponent = () => {
480
+ const { loadingState, targetElementRef } = useModuleMounter({
481
+ loader,
482
+ loaderParams: {}, // параметры, которые будут переданы в getModuleResources, опционально
483
+ runParams: {}, // параметры, которые будут переданы в mount функцию модуля, опционально
484
+ });
422
485
 
423
- # API библиотек
486
+ return (
487
+ <div>
488
+ {loadingState === 'loading' && <div>Loading...</div>}
489
+ {loadingState === 'error' && <div>Error</div>}
490
+ <div ref={targetElementRef} /> {/* сюда будет монтироваться модуль */}
491
+ </div>
492
+ );
493
+ };
494
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arui-scripts",
3
- "version": "14.5.0-feat-modules.2",
3
+ "version": "14.5.0-feat-modules.4",
4
4
  "main": "./build/index.js",
5
5
  "typings": "./build/index.d.ts",
6
6
  "license": "MPL-2.0",