@gravity-ui/app-builder 0.29.3 → 0.30.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 +138 -0
- package/dist/cli.js +4 -4
- package/dist/commands/build/build-service/server.js +1 -1
- package/dist/commands/dev/client.js +1 -1
- package/dist/commands/dev/index.js +4 -3
- package/dist/commands/dev/server.js +1 -1
- package/dist/common/config.js +23 -2
- package/dist/common/models/index.d.ts +66 -1
- package/dist/common/s3-upload/create-plugin.js +29 -1
- package/dist/common/utils.d.ts +3 -1
- package/dist/common/utils.js +9 -4
- package/dist/common/webpack/config.d.ts +0 -2
- package/dist/common/webpack/config.js +108 -25
- package/dist/common/webpack/public-path.js +1 -0
- package/dist/common/webpack/runtime-versioning-plugin.d.ts +5 -0
- package/dist/common/webpack/runtime-versioning-plugin.js +22 -0
- package/dist/common/webpack/storybook.js +0 -2
- package/dist/common/webpack/worker/public-path.worker.js +2 -0
- package/dist/index.d.ts +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -330,3 +330,141 @@ worker.onmessage = ({data: {result}}) => {
|
|
|
330
330
|
|
|
331
331
|
worker.postMessage({a: 1, b: 2});
|
|
332
332
|
```
|
|
333
|
+
|
|
334
|
+
##### Module Federation
|
|
335
|
+
|
|
336
|
+
Module Federation is a Webpack 5 feature that enables micro-frontend architecture, where JavaScript applications can dynamically load code from each other at runtime.
|
|
337
|
+
|
|
338
|
+
`app-builder` uses `@module-federation/enhanced` for advanced Module Federation support.
|
|
339
|
+
|
|
340
|
+
- `moduleFederation` (`object`) — Module Federation configuration
|
|
341
|
+
- `name` (`string`) — unique name of the application in the Module Federation ecosystem. Required parameter.
|
|
342
|
+
- `version` (`string`) — application version. When specified, the entry file will be named `entry-{version}.js` instead of `entry.js`.
|
|
343
|
+
- `publicPath` (`string`) — base URL for loading resources of this micro-frontend. Required parameter.
|
|
344
|
+
- `remotes` (`string[]`) — list of remote application names that this application can load. Simplified alternative to `originalRemotes`.
|
|
345
|
+
- `originalRemotes` (`RemotesObject`) — full configuration of remote applications in Module Federation Plugin format.
|
|
346
|
+
- `remotesRuntimeVersioning` (`boolean`) — enables runtime versioning for remote applications.
|
|
347
|
+
- `isolateStyles` (`object`) — CSS style isolation settings to prevent conflicts between micro-frontends.
|
|
348
|
+
- `getPrefix` (`(entryName: string) => string`) — function to generate CSS class prefix.
|
|
349
|
+
- `prefixSelector` (`(prefix: string, selector: string, prefixedSelector: string, filePath: string) => string`) — function to add prefix to CSS selectors.
|
|
350
|
+
- Also supports all standard options from [@module-federation/enhanced](https://module-federation.io/), except `name` and `remotes`, such as:
|
|
351
|
+
- `filename` — entry file name (default `remoteEntry.js`)
|
|
352
|
+
- `exposes` — modules that this application exports
|
|
353
|
+
- `shared` — shared dependencies between applications
|
|
354
|
+
- `runtimePlugins` — plugins for Module Federation runtime
|
|
355
|
+
|
|
356
|
+
**Host Application Configuration Example:**
|
|
357
|
+
|
|
358
|
+
Host applications consume remote modules from other micro-frontends:
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
export default defineConfig({
|
|
362
|
+
client: {
|
|
363
|
+
moduleFederation: {
|
|
364
|
+
name: 'shell',
|
|
365
|
+
publicPath: 'https://cdn.example.com/my-app/',
|
|
366
|
+
// Simple remotes configuration
|
|
367
|
+
remotes: ['header', 'footer', 'sidebar'],
|
|
368
|
+
shared: {
|
|
369
|
+
react: {singleton: true, requiredVersion: '^18.0.0'},
|
|
370
|
+
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
|
|
371
|
+
lodash: {singleton: true},
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
**Advanced Host Configuration:**
|
|
379
|
+
|
|
380
|
+
```ts
|
|
381
|
+
export default defineConfig({
|
|
382
|
+
client: {
|
|
383
|
+
moduleFederation: {
|
|
384
|
+
name: 'main-shell',
|
|
385
|
+
version: '2.1.0',
|
|
386
|
+
publicPath: 'https://cdn.example.com/my-app/',
|
|
387
|
+
// Detailed remotes configuration
|
|
388
|
+
originalRemotes: {
|
|
389
|
+
header: 'header@https://cdn.example.com/header/remoteEntry.js',
|
|
390
|
+
footer: 'footer@https://cdn.example.com/footer/remoteEntry.js',
|
|
391
|
+
userProfile: 'userProfile@https://cdn.example.com/user-profile/remoteEntry.js',
|
|
392
|
+
},
|
|
393
|
+
remotesRuntimeVersioning: true,
|
|
394
|
+
isolateStyles: {
|
|
395
|
+
getPrefix: (entryName) => `.app-${entryName}`,
|
|
396
|
+
prefixSelector: (prefix, selector, prefixedSelector, filePath) => {
|
|
397
|
+
if (
|
|
398
|
+
[prefix, ':root', 'html', 'body', '.g-root', '.remote-app'].some((item) =>
|
|
399
|
+
selector.startsWith(item),
|
|
400
|
+
) ||
|
|
401
|
+
filePath.includes('@gravity-ui/chartkit')
|
|
402
|
+
) {
|
|
403
|
+
return selector;
|
|
404
|
+
}
|
|
405
|
+
return prefixedSelector;
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
shared: {
|
|
409
|
+
react: {singleton: true, requiredVersion: '^18.0.0'},
|
|
410
|
+
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
|
|
411
|
+
lodash: {singleton: true},
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
});
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**Remote Application Configuration Example:**
|
|
419
|
+
|
|
420
|
+
Remote applications expose their modules for consumption by host applications:
|
|
421
|
+
|
|
422
|
+
```ts
|
|
423
|
+
export default defineConfig({
|
|
424
|
+
client: {
|
|
425
|
+
moduleFederation: {
|
|
426
|
+
name: 'header',
|
|
427
|
+
publicPath: 'https://cdn.example.com/my-app/',
|
|
428
|
+
// Expose modules for other applications
|
|
429
|
+
exposes: {
|
|
430
|
+
'./Header': './src/components/Header',
|
|
431
|
+
'./Navigation': './src/components/Navigation',
|
|
432
|
+
'./UserMenu': './src/components/UserMenu',
|
|
433
|
+
},
|
|
434
|
+
shared: {
|
|
435
|
+
react: {singleton: true, requiredVersion: '^18.0.0'},
|
|
436
|
+
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
|
|
437
|
+
lodash: {singleton: true},
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
**Bidirectional Configuration Example:**
|
|
445
|
+
|
|
446
|
+
Applications can be both host and remote simultaneously:
|
|
447
|
+
|
|
448
|
+
```ts
|
|
449
|
+
export default defineConfig({
|
|
450
|
+
client: {
|
|
451
|
+
moduleFederation: {
|
|
452
|
+
name: 'dashboard',
|
|
453
|
+
version: '1.5.0',
|
|
454
|
+
publicPath: 'https://cdn.example.com/my-app/',
|
|
455
|
+
// Consume remote modules
|
|
456
|
+
remotes: ['charts', 'notifications'],
|
|
457
|
+
// Expose own modules
|
|
458
|
+
exposes: {
|
|
459
|
+
'./DashboardLayout': './src/layouts/DashboardLayout',
|
|
460
|
+
'./DataTable': './src/components/DataTable',
|
|
461
|
+
},
|
|
462
|
+
shared: {
|
|
463
|
+
react: {singleton: true, requiredVersion: '^18.0.0'},
|
|
464
|
+
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
|
|
465
|
+
lodash: {singleton: true},
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
});
|
|
470
|
+
```
|
package/dist/cli.js
CHANGED
|
@@ -15,17 +15,17 @@ const { version } = process;
|
|
|
15
15
|
if (!semver_1.default.satisfies(version, `>=${MIN_NODE_VERSION}`, {
|
|
16
16
|
includePrerelease: true,
|
|
17
17
|
})) {
|
|
18
|
-
logger_1.default.panic((0, common_tags_1.stripIndent)
|
|
18
|
+
logger_1.default.panic((0, common_tags_1.stripIndent) `
|
|
19
19
|
App-builder requires Node.js ${MIN_NODE_VERSION} or higher (you have ${version}).
|
|
20
20
|
Upgrade Node to the latest stable release.
|
|
21
|
-
`)
|
|
21
|
+
`);
|
|
22
22
|
}
|
|
23
23
|
if (semver_1.default.prerelease(version)) {
|
|
24
|
-
logger_1.default.warning((0, common_tags_1.stripIndent)
|
|
24
|
+
logger_1.default.warning((0, common_tags_1.stripIndent) `
|
|
25
25
|
You are currently using a prerelease version of Node (${version}), which is not supported.
|
|
26
26
|
You can use this for testing, but we do not recommend it in production.
|
|
27
27
|
Before reporting any bugs, please test with a supported version of Node (>=${MIN_NODE_VERSION}).
|
|
28
|
-
`)
|
|
28
|
+
`);
|
|
29
29
|
}
|
|
30
30
|
process.on('unhandledRejection', (reason) => {
|
|
31
31
|
// This will exit the process in newer Node anyway so lets be consistent
|
|
@@ -38,7 +38,7 @@ const logger = new Logger('server', ${config.verbose});
|
|
|
38
38
|
compile(ts, {logger, projectPath: ${JSON.stringify(paths_1.default.appServer)}});`;
|
|
39
39
|
}
|
|
40
40
|
function buildServer(config) {
|
|
41
|
-
(0, utils_1.createRunFolder)();
|
|
41
|
+
(0, utils_1.createRunFolder)(config);
|
|
42
42
|
return new Promise((resolve, reject) => {
|
|
43
43
|
const build = new controllable_script_1.ControllableScript(config.server.compiler === 'swc'
|
|
44
44
|
? createSWCBuildScript(config)
|
|
@@ -152,7 +152,7 @@ async function buildDevServer(config) {
|
|
|
152
152
|
};
|
|
153
153
|
const listenOn = options.port || options.ipc;
|
|
154
154
|
if (!listenOn) {
|
|
155
|
-
options.ipc = path.resolve(
|
|
155
|
+
options.ipc = path.resolve((0, utils_1.getAppRunPath)(config), 'client.sock');
|
|
156
156
|
}
|
|
157
157
|
const proxy = options.proxy || [];
|
|
158
158
|
if (config.client.lazyCompilation && bundler !== 'rspack') {
|
|
@@ -39,13 +39,14 @@ async function default_1(config) {
|
|
|
39
39
|
process.env.NODE_ENV = 'development';
|
|
40
40
|
const shouldCompileClient = (0, utils_1.shouldCompileTarget)(config.target, 'client');
|
|
41
41
|
const shouldCompileServer = (0, utils_1.shouldCompileTarget)(config.target, 'server');
|
|
42
|
+
const appRunPath = (0, utils_1.getAppRunPath)(config);
|
|
42
43
|
if (shouldCompileClient && shouldCompileServer) {
|
|
43
44
|
try {
|
|
44
|
-
fs.accessSync(
|
|
45
|
-
rimraf_1.rimraf.sync(
|
|
45
|
+
fs.accessSync(appRunPath, fs.constants.W_OK | fs.constants.X_OK); // eslint-disable-line no-bitwise
|
|
46
|
+
rimraf_1.rimraf.sync(appRunPath);
|
|
46
47
|
}
|
|
47
48
|
catch (error) {
|
|
48
|
-
logger_1.default.warning(`Failed to remove appRun path [${
|
|
49
|
+
logger_1.default.warning(`Failed to remove appRun path [${appRunPath}]: ${error}`);
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
let clientCompiled = !shouldCompileClient;
|
|
@@ -79,7 +79,7 @@ watch(
|
|
|
79
79
|
async function watchServerCompilation(config) {
|
|
80
80
|
const serverPath = path.resolve(paths_1.default.appDist, 'server');
|
|
81
81
|
rimraf_1.rimraf.sync(serverPath);
|
|
82
|
-
(0, utils_1.createRunFolder)();
|
|
82
|
+
(0, utils_1.createRunFolder)(config);
|
|
83
83
|
const build = new controllable_script_1.ControllableScript(config.server.compiler === 'swc'
|
|
84
84
|
? createSWCBuildScript(config)
|
|
85
85
|
: createTypescriptBuildScript(config), null);
|
package/dist/common/config.js
CHANGED
|
@@ -22,14 +22,19 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
25
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
29
|
exports.getProjectConfig = getProjectConfig;
|
|
27
30
|
exports.normalizeConfig = normalizeConfig;
|
|
28
31
|
const path = __importStar(require("node:path"));
|
|
29
32
|
const cosmiconfig_1 = require("cosmiconfig");
|
|
30
33
|
const cosmiconfig_typescript_loader_1 = require("cosmiconfig-typescript-loader");
|
|
34
|
+
const common_tags_1 = require("common-tags");
|
|
31
35
|
const models_1 = require("./models");
|
|
32
36
|
const utils_1 = require("./utils");
|
|
37
|
+
const logger_1 = __importDefault(require("./logger"));
|
|
33
38
|
function splitPaths(paths) {
|
|
34
39
|
return (Array.isArray(paths) ? paths : [paths]).flatMap((p) => p.split(','));
|
|
35
40
|
}
|
|
@@ -183,6 +188,18 @@ async function normalizeConfig(userConfig, mode) {
|
|
|
183
188
|
return config;
|
|
184
189
|
}
|
|
185
190
|
async function normalizeClientConfig(client, mode) {
|
|
191
|
+
let publicPath = client.publicPath || path.normalize(`${client.publicPathPrefix || ''}/build/`);
|
|
192
|
+
if (client.moduleFederation) {
|
|
193
|
+
publicPath = path.normalize(`${publicPath}${client.moduleFederation.name}/`);
|
|
194
|
+
}
|
|
195
|
+
let transformCssWithLightningCss = Boolean(client.transformCssWithLightningCss);
|
|
196
|
+
if (client.moduleFederation?.isolateStyles && transformCssWithLightningCss) {
|
|
197
|
+
transformCssWithLightningCss = false;
|
|
198
|
+
logger_1.default.warning((0, common_tags_1.stripIndent) `
|
|
199
|
+
transformCssWithLightningCss option is disabled because moduleFederation.isolateStyles is enabled.
|
|
200
|
+
postcss loader will be used instead.
|
|
201
|
+
`);
|
|
202
|
+
}
|
|
186
203
|
const normalizedConfig = {
|
|
187
204
|
...client,
|
|
188
205
|
forkTsChecker: client.disableForkTsChecker ? false : client.forkTsChecker,
|
|
@@ -190,14 +207,18 @@ async function normalizeClientConfig(client, mode) {
|
|
|
190
207
|
? false
|
|
191
208
|
: (client.reactRefresh ?? ((options) => options)),
|
|
192
209
|
newJsxTransform: client.newJsxTransform ?? true,
|
|
193
|
-
publicPath
|
|
194
|
-
assetsManifestFile: client.assetsManifestFile ||
|
|
210
|
+
publicPath,
|
|
211
|
+
assetsManifestFile: client.assetsManifestFile ||
|
|
212
|
+
(client.moduleFederation?.version
|
|
213
|
+
? `assets-manifest-${client.moduleFederation.version}.json`
|
|
214
|
+
: 'assets-manifest.json'),
|
|
195
215
|
modules: client.modules && remapPaths(client.modules),
|
|
196
216
|
includes: client.includes && remapPaths(client.includes),
|
|
197
217
|
images: client.images && remapPaths(client.images),
|
|
198
218
|
hiddenSourceMap: client.hiddenSourceMap ?? true,
|
|
199
219
|
svgr: client.svgr ?? {},
|
|
200
220
|
entryFilter: client.entryFilter && splitPaths(client.entryFilter),
|
|
221
|
+
transformCssWithLightningCss,
|
|
201
222
|
webpack: typeof client.webpack === 'function' ? client.webpack : (config) => config,
|
|
202
223
|
rspack: typeof client.rspack === 'function' ? client.rspack : (config) => config,
|
|
203
224
|
babel: typeof client.babel === 'function' ? client.babel : (config) => config,
|
|
@@ -16,6 +16,7 @@ import type { WebpackMode } from '../webpack/config';
|
|
|
16
16
|
import type { UploadOptions } from '../s3-upload/upload';
|
|
17
17
|
import type { TerserOptions } from 'terser-webpack-plugin';
|
|
18
18
|
import type { ReactRefreshPluginOptions } from '@pmmmwh/react-refresh-webpack-plugin/types/lib/types';
|
|
19
|
+
import type { moduleFederationPlugin } from '@module-federation/enhanced';
|
|
19
20
|
type Bundler = 'webpack' | 'rspack';
|
|
20
21
|
type JavaScriptLoader = 'babel' | 'swc';
|
|
21
22
|
type ServerCompiler = 'typescript' | 'swc';
|
|
@@ -59,6 +60,64 @@ interface LazyCompilationConfig {
|
|
|
59
60
|
*/
|
|
60
61
|
entries?: boolean;
|
|
61
62
|
}
|
|
63
|
+
export type ModuleFederationConfig = Omit<moduleFederationPlugin.ModuleFederationPluginOptions, 'name' | 'remotes'> & {
|
|
64
|
+
/**
|
|
65
|
+
* Unique name of the application in the Module Federation ecosystem
|
|
66
|
+
* Used as an identifier for this micro-frontend
|
|
67
|
+
*/
|
|
68
|
+
name: string;
|
|
69
|
+
/**
|
|
70
|
+
* Application version, appended to the entry file name
|
|
71
|
+
* When specified, the file will be named `entry-{version}.js`
|
|
72
|
+
* @default undefined (file will be named `entry.js`)
|
|
73
|
+
*/
|
|
74
|
+
version?: string;
|
|
75
|
+
/**
|
|
76
|
+
* Base URL for loading resources of this micro-frontend
|
|
77
|
+
* Should point to a publicly accessible URL where the files will be hosted
|
|
78
|
+
* @example 'https://cdn.example.com/my-app/'
|
|
79
|
+
*/
|
|
80
|
+
publicPath: string;
|
|
81
|
+
/**
|
|
82
|
+
* List of remote application names that this application can load
|
|
83
|
+
* Simplified alternative to originalRemotes - only names are specified
|
|
84
|
+
* @example ['header', 'footer', 'navigation']
|
|
85
|
+
*/
|
|
86
|
+
remotes?: string[];
|
|
87
|
+
/**
|
|
88
|
+
* Full configuration of remote applications in Module Federation format
|
|
89
|
+
* Allows more detailed configuration of each remote application
|
|
90
|
+
* @example { header: 'header@https://header.example.com/remoteEntry.js' }
|
|
91
|
+
*/
|
|
92
|
+
originalRemotes?: moduleFederationPlugin.ModuleFederationPluginOptions['remotes'];
|
|
93
|
+
/**
|
|
94
|
+
* Enables runtime versioning for remote applications
|
|
95
|
+
* When enabled, remote applications will be loaded with version in the filename
|
|
96
|
+
* @default false
|
|
97
|
+
*/
|
|
98
|
+
remotesRuntimeVersioning?: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* CSS style isolation settings to prevent conflicts
|
|
101
|
+
* between styles of different micro-frontends
|
|
102
|
+
*/
|
|
103
|
+
isolateStyles?: {
|
|
104
|
+
/**
|
|
105
|
+
* Function to generate CSS class prefix
|
|
106
|
+
* @param entryName - Application entry name
|
|
107
|
+
* @returns Prefix string for CSS classes
|
|
108
|
+
*/
|
|
109
|
+
getPrefix: (entryName: string) => string;
|
|
110
|
+
/**
|
|
111
|
+
* Function to add prefix to CSS selectors
|
|
112
|
+
* @param prefix - Prefix to add
|
|
113
|
+
* @param selector - Original CSS selector
|
|
114
|
+
* @param prefixedSelector - Selector with added prefix
|
|
115
|
+
* @param filePath - Path to the styles file
|
|
116
|
+
* @returns Modified CSS selector
|
|
117
|
+
*/
|
|
118
|
+
prefixSelector: (prefix: string, selector: string, prefixedSelector: string, filePath: string) => string;
|
|
119
|
+
};
|
|
120
|
+
};
|
|
62
121
|
export interface ClientConfig {
|
|
63
122
|
modules?: string[];
|
|
64
123
|
/**
|
|
@@ -248,6 +307,11 @@ export interface ClientConfig {
|
|
|
248
307
|
};
|
|
249
308
|
bundler?: Bundler;
|
|
250
309
|
javaScriptLoader?: JavaScriptLoader;
|
|
310
|
+
/**
|
|
311
|
+
* Module Federation configuration for building micro-frontends
|
|
312
|
+
* @see https://module-federation.io/
|
|
313
|
+
*/
|
|
314
|
+
moduleFederation?: ModuleFederationConfig;
|
|
251
315
|
}
|
|
252
316
|
export interface CdnUploadConfig {
|
|
253
317
|
bucket: string;
|
|
@@ -281,7 +345,7 @@ export interface ServiceConfig {
|
|
|
281
345
|
verbose?: boolean;
|
|
282
346
|
configPath?: string;
|
|
283
347
|
}
|
|
284
|
-
export type NormalizedClientConfig = Omit<ClientConfig, 'publicPathPrefix' | 'publicPath' | 'assetsManifestFile' | 'hiddenSourceMap' | 'svgr' | 'lazyCompilation' | 'devServer' | 'disableForkTsChecker' | 'disableReactRefresh'> & {
|
|
348
|
+
export type NormalizedClientConfig = Omit<ClientConfig, 'publicPathPrefix' | 'publicPath' | 'assetsManifestFile' | 'hiddenSourceMap' | 'svgr' | 'lazyCompilation' | 'devServer' | 'disableForkTsChecker' | 'disableReactRefresh' | 'transformCssWithLightningCss'> & {
|
|
285
349
|
bundler: Bundler;
|
|
286
350
|
javaScriptLoader: JavaScriptLoader;
|
|
287
351
|
publicPath: string;
|
|
@@ -294,6 +358,7 @@ export type NormalizedClientConfig = Omit<ClientConfig, 'publicPathPrefix' | 'pu
|
|
|
294
358
|
server?: ServerConfiguration;
|
|
295
359
|
};
|
|
296
360
|
verbose?: boolean;
|
|
361
|
+
transformCssWithLightningCss: boolean;
|
|
297
362
|
webpack: (config: Configuration, options: {
|
|
298
363
|
configType: `${WebpackMode}`;
|
|
299
364
|
isSsr: boolean;
|
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
26
|
exports.createS3UploadPlugins = createS3UploadPlugins;
|
|
27
|
+
const path = __importStar(require("node:path"));
|
|
4
28
|
const webpack_plugin_1 = require("./webpack-plugin");
|
|
5
29
|
function createS3UploadPlugins(config, logger) {
|
|
6
30
|
const plugins = [];
|
|
@@ -26,6 +50,10 @@ function createS3UploadPlugins(config, logger) {
|
|
|
26
50
|
secretAccessKey,
|
|
27
51
|
};
|
|
28
52
|
}
|
|
53
|
+
let targetPath = cdn.prefix;
|
|
54
|
+
if (config.moduleFederation && targetPath !== undefined) {
|
|
55
|
+
targetPath = path.join(targetPath, config.moduleFederation.name);
|
|
56
|
+
}
|
|
29
57
|
plugins.push(new webpack_plugin_1.S3UploadPlugin({
|
|
30
58
|
exclude: config.hiddenSourceMap ? /\.map$/ : undefined,
|
|
31
59
|
compress: cdn.compress,
|
|
@@ -36,7 +64,7 @@ function createS3UploadPlugins(config, logger) {
|
|
|
36
64
|
},
|
|
37
65
|
s3UploadOptions: {
|
|
38
66
|
bucket: cdn.bucket,
|
|
39
|
-
targetPath
|
|
67
|
+
targetPath,
|
|
40
68
|
cacheControl: cdn.cacheControl,
|
|
41
69
|
},
|
|
42
70
|
additionalPattern: cdn.additionalPattern,
|
package/dist/common/utils.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import type { NormalizedServiceConfig } from './models';
|
|
2
|
+
export declare function getAppRunPath(config: NormalizedServiceConfig): string;
|
|
3
|
+
export declare function createRunFolder(config: NormalizedServiceConfig): void;
|
|
2
4
|
export declare function shouldCompileTarget(target: 'client' | 'server' | undefined, targetName: string): boolean;
|
|
3
5
|
export declare function getCacheDir(): Promise<string>;
|
|
4
6
|
export declare function getPort({ port }: {
|
package/dist/common/utils.js
CHANGED
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getAppRunPath = getAppRunPath;
|
|
6
7
|
exports.createRunFolder = createRunFolder;
|
|
7
8
|
exports.shouldCompileTarget = shouldCompileTarget;
|
|
8
9
|
exports.getCacheDir = getCacheDir;
|
|
@@ -10,11 +11,15 @@ exports.getPort = getPort;
|
|
|
10
11
|
exports.deferredPromise = deferredPromise;
|
|
11
12
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
12
13
|
const node_os_1 = __importDefault(require("node:os"));
|
|
14
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
13
15
|
const paths_1 = __importDefault(require("./paths"));
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
function getAppRunPath(config) {
|
|
17
|
+
return node_path_1.default.resolve(paths_1.default.appRun, config.client.moduleFederation?.name || '');
|
|
18
|
+
}
|
|
19
|
+
function createRunFolder(config) {
|
|
20
|
+
const appRunPath = getAppRunPath(config);
|
|
21
|
+
if (!node_fs_1.default.existsSync(appRunPath)) {
|
|
22
|
+
node_fs_1.default.mkdirSync(appRunPath, { recursive: true });
|
|
18
23
|
}
|
|
19
24
|
}
|
|
20
25
|
function shouldCompileTarget(target, targetName) {
|
|
@@ -9,8 +9,6 @@ export interface HelperOptions {
|
|
|
9
9
|
isEnvProduction: boolean;
|
|
10
10
|
configType: `${WebpackMode}`;
|
|
11
11
|
buildDirectory: string;
|
|
12
|
-
assetsManifestFile: string;
|
|
13
|
-
entry?: string | string[] | Record<string, string | string[]>;
|
|
14
12
|
entriesDirectory: string;
|
|
15
13
|
isSsr: boolean;
|
|
16
14
|
configPath?: string;
|
|
@@ -62,15 +62,17 @@ const fontSizeLimit = 8192;
|
|
|
62
62
|
function getHelperOptions({ webpackMode, config, logger, isSsr = false, configPath, }) {
|
|
63
63
|
const isEnvDevelopment = webpackMode === "development" /* WebpackMode.Dev */;
|
|
64
64
|
const isEnvProduction = webpackMode === "production" /* WebpackMode.Prod */;
|
|
65
|
+
let buildDirectory = config.outputPath || (isSsr ? paths_1.default.appSsrBuild : paths_1.default.appBuild);
|
|
66
|
+
if (config.moduleFederation) {
|
|
67
|
+
buildDirectory = path.resolve(buildDirectory, config.moduleFederation.name);
|
|
68
|
+
}
|
|
65
69
|
return {
|
|
66
70
|
config,
|
|
67
71
|
logger,
|
|
68
72
|
isEnvDevelopment,
|
|
69
73
|
isEnvProduction,
|
|
70
74
|
configType: webpackMode,
|
|
71
|
-
buildDirectory
|
|
72
|
-
assetsManifestFile: config.assetsManifestFile,
|
|
73
|
-
entry: config.entry,
|
|
75
|
+
buildDirectory,
|
|
74
76
|
entriesDirectory: isSsr ? paths_1.default.appSsrEntry : paths_1.default.appEntry,
|
|
75
77
|
isSsr,
|
|
76
78
|
configPath,
|
|
@@ -91,13 +93,21 @@ function configureExternals({ config, isSsr }) {
|
|
|
91
93
|
}
|
|
92
94
|
function configureWebpackCache(options) {
|
|
93
95
|
const { config } = options;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
let cache = config.cache;
|
|
97
|
+
if (typeof cache === 'object' && cache.type === 'filesystem') {
|
|
98
|
+
cache = {
|
|
99
|
+
...cache,
|
|
97
100
|
buildDependencies: getCacheBuildDependencies(options),
|
|
98
101
|
};
|
|
102
|
+
if (config.moduleFederation) {
|
|
103
|
+
cache = {
|
|
104
|
+
name: config.moduleFederation.name,
|
|
105
|
+
version: config.moduleFederation.version,
|
|
106
|
+
...cache,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
99
109
|
}
|
|
100
|
-
return
|
|
110
|
+
return cache;
|
|
101
111
|
}
|
|
102
112
|
async function webpackConfigFactory(options) {
|
|
103
113
|
const { config } = options;
|
|
@@ -340,6 +350,10 @@ function configureResolve({ isEnvProduction, config }) {
|
|
|
340
350
|
fallback: config.fallback,
|
|
341
351
|
};
|
|
342
352
|
}
|
|
353
|
+
function isModuleFederationEntry(entryName, fileName) {
|
|
354
|
+
// Ignore bootstrap file for module federation entries
|
|
355
|
+
return entryName === fileName || `${entryName}-bootstrap` === fileName;
|
|
356
|
+
}
|
|
343
357
|
function createEntryArray(entry) {
|
|
344
358
|
if (typeof entry === 'string') {
|
|
345
359
|
return [require.resolve('./public-path'), entry];
|
|
@@ -352,24 +366,44 @@ function addEntry(entry, file) {
|
|
|
352
366
|
[path.parse(file).name]: createEntryArray(file),
|
|
353
367
|
};
|
|
354
368
|
}
|
|
355
|
-
function configureEntry({ config,
|
|
356
|
-
if (typeof entry === 'string' || Array.isArray(entry)) {
|
|
357
|
-
return createEntryArray(entry);
|
|
369
|
+
function configureEntry({ config, entriesDirectory }) {
|
|
370
|
+
if (typeof config.entry === 'string' || Array.isArray(config.entry)) {
|
|
371
|
+
return createEntryArray(config.entry);
|
|
358
372
|
}
|
|
359
|
-
if (typeof entry === 'object') {
|
|
360
|
-
return Object.entries(entry).reduce((acc, [key, value]) => ({
|
|
373
|
+
if (typeof config.entry === 'object') {
|
|
374
|
+
return Object.entries(config.entry).reduce((acc, [key, value]) => ({
|
|
361
375
|
...acc,
|
|
362
376
|
[key]: createEntryArray(value),
|
|
363
377
|
}), {});
|
|
364
378
|
}
|
|
365
|
-
let
|
|
379
|
+
let entryFiles = fs.readdirSync(entriesDirectory).filter((file) => /\.[jt]sx?$/.test(file));
|
|
380
|
+
let result = {};
|
|
381
|
+
if (config.moduleFederation) {
|
|
382
|
+
const { name, remotes } = config.moduleFederation;
|
|
383
|
+
const entryFile = entryFiles.find((item) => path.parse(item).name === name);
|
|
384
|
+
if (!entryFile) {
|
|
385
|
+
throw new Error(`Entry "${name}" not found`);
|
|
386
|
+
}
|
|
387
|
+
// If remotes are not defined, it means that we are a remote
|
|
388
|
+
if (!remotes) {
|
|
389
|
+
return path.resolve(entriesDirectory, entryFile);
|
|
390
|
+
}
|
|
391
|
+
entryFiles = entryFiles.filter((file) => {
|
|
392
|
+
const fileName = path.parse(file).name;
|
|
393
|
+
return (!isModuleFederationEntry(name, fileName) &&
|
|
394
|
+
remotes.every((remote) => !isModuleFederationEntry(remote, fileName)));
|
|
395
|
+
});
|
|
396
|
+
result = {
|
|
397
|
+
main: createEntryArray(path.resolve(entriesDirectory, entryFile)),
|
|
398
|
+
};
|
|
399
|
+
}
|
|
366
400
|
if (Array.isArray(config.entryFilter) && config.entryFilter.length) {
|
|
367
|
-
|
|
401
|
+
entryFiles = entryFiles.filter((file) => config.entryFilter?.includes(path.parse(file).name));
|
|
368
402
|
}
|
|
369
|
-
if (!
|
|
403
|
+
if (!entryFiles.length) {
|
|
370
404
|
throw new Error('No entries were found after applying entry filter');
|
|
371
405
|
}
|
|
372
|
-
return
|
|
406
|
+
return entryFiles.reduce((acc, file) => addEntry(acc, path.resolve(entriesDirectory, file)), result);
|
|
373
407
|
}
|
|
374
408
|
function getFileNames({ isEnvProduction, isSsr, config }) {
|
|
375
409
|
let ext = 'js';
|
|
@@ -384,18 +418,25 @@ function getFileNames({ isEnvProduction, isSsr, config }) {
|
|
|
384
418
|
};
|
|
385
419
|
}
|
|
386
420
|
function configureOutput(options) {
|
|
387
|
-
let ssrOptions;
|
|
421
|
+
let ssrOptions, moduleFederationOptions;
|
|
388
422
|
if (options.isSsr) {
|
|
389
423
|
ssrOptions = {
|
|
390
424
|
library: { type: options.config.ssr?.moduleType === 'esm' ? 'module' : 'commonjs2' },
|
|
391
425
|
chunkFormat: false,
|
|
392
426
|
};
|
|
393
427
|
}
|
|
428
|
+
if (options.config.moduleFederation) {
|
|
429
|
+
moduleFederationOptions = {
|
|
430
|
+
publicPath: 'auto',
|
|
431
|
+
uniqueName: options.config.moduleFederation.name,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
394
434
|
return {
|
|
395
435
|
...getFileNames(options),
|
|
396
436
|
path: options.buildDirectory,
|
|
397
437
|
pathinfo: options.isEnvDevelopment,
|
|
398
438
|
...ssrOptions,
|
|
439
|
+
...moduleFederationOptions,
|
|
399
440
|
};
|
|
400
441
|
}
|
|
401
442
|
async function createJavaScriptLoader({ isEnvProduction, isEnvDevelopment, configType, config, isSsr, }) {
|
|
@@ -594,15 +635,27 @@ function getCssLoaders({ isEnvDevelopment, isEnvProduction, config, isSsr }, add
|
|
|
594
635
|
const isRspack = config.bundler === 'rspack';
|
|
595
636
|
const loaders = [];
|
|
596
637
|
if (!config.transformCssWithLightningCss) {
|
|
638
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
639
|
+
const postcssPlugins = [
|
|
640
|
+
[require.resolve('postcss-preset-env'), { enableClientSidePolyfills: false }],
|
|
641
|
+
];
|
|
642
|
+
if (config.moduleFederation?.isolateStyles) {
|
|
643
|
+
const { name, isolateStyles } = config.moduleFederation;
|
|
644
|
+
postcssPlugins.push([
|
|
645
|
+
require.resolve('postcss-prefix-selector'),
|
|
646
|
+
{
|
|
647
|
+
prefix: isolateStyles.getPrefix(name),
|
|
648
|
+
transform: isolateStyles.prefixSelector,
|
|
649
|
+
},
|
|
650
|
+
]);
|
|
651
|
+
}
|
|
597
652
|
loaders.push({
|
|
598
653
|
loader: require.resolve('postcss-loader'),
|
|
599
654
|
options: {
|
|
600
655
|
sourceMap: !config.disableSourceMapGeneration,
|
|
601
656
|
postcssOptions: {
|
|
602
657
|
config: false,
|
|
603
|
-
plugins:
|
|
604
|
-
[require.resolve('postcss-preset-env'), { enableClientSidePolyfills: false }],
|
|
605
|
-
],
|
|
658
|
+
plugins: postcssPlugins,
|
|
606
659
|
},
|
|
607
660
|
},
|
|
608
661
|
});
|
|
@@ -908,6 +961,32 @@ function configureCommonPlugins(options, bundlerPlugins) {
|
|
|
908
961
|
}));
|
|
909
962
|
}
|
|
910
963
|
plugins.push(createMomentTimezoneDataPlugin(config.momentTz));
|
|
964
|
+
if (config.moduleFederation) {
|
|
965
|
+
const { name, version, publicPath, remotes, originalRemotes, remotesRuntimeVersioning, runtimePlugins, ...restOptions } = config.moduleFederation;
|
|
966
|
+
let actualRemotes = originalRemotes;
|
|
967
|
+
if (remotes) {
|
|
968
|
+
actualRemotes = remotes.reduce((acc, remoteName) => {
|
|
969
|
+
const remoteFilename = remotesRuntimeVersioning
|
|
970
|
+
? 'entry-[version].js'
|
|
971
|
+
: 'entry.js';
|
|
972
|
+
// eslint-disable-next-line no-param-reassign
|
|
973
|
+
acc[remoteName] =
|
|
974
|
+
`${remoteName}@${publicPath}${remoteName}/${remoteFilename}`;
|
|
975
|
+
return acc;
|
|
976
|
+
}, {});
|
|
977
|
+
}
|
|
978
|
+
const actualRuntimePlugins = runtimePlugins || [];
|
|
979
|
+
if (remotesRuntimeVersioning) {
|
|
980
|
+
actualRuntimePlugins.push(require.resolve('./runtime-versioning-plugin'));
|
|
981
|
+
}
|
|
982
|
+
plugins.push(new bundlerPlugins.ModuleFederationPlugin({
|
|
983
|
+
name,
|
|
984
|
+
filename: version ? `entry-${version}.js` : 'entry.js',
|
|
985
|
+
remotes: actualRemotes,
|
|
986
|
+
runtimePlugins: actualRuntimePlugins,
|
|
987
|
+
...restOptions,
|
|
988
|
+
}));
|
|
989
|
+
}
|
|
911
990
|
}
|
|
912
991
|
if (isEnvProduction) {
|
|
913
992
|
if (config.analyzeBundle === 'true') {
|
|
@@ -955,18 +1034,20 @@ function configureWebpackPlugins(options) {
|
|
|
955
1034
|
TsCheckerPlugin: fork_ts_checker_webpack_plugin_1.default,
|
|
956
1035
|
CSSExtractPlugin: mini_css_extract_plugin_1.default,
|
|
957
1036
|
RSDoctorPlugin: require('@rsdoctor/webpack-plugin').RsdoctorWebpackPlugin,
|
|
1037
|
+
ModuleFederationPlugin: require('@module-federation/enhanced/webpack')
|
|
1038
|
+
.ModuleFederationPlugin,
|
|
958
1039
|
};
|
|
959
1040
|
const webpackPlugins = [
|
|
960
1041
|
...configureCommonPlugins(options, plugins),
|
|
961
1042
|
new webpack_assets_manifest_1.default(isEnvProduction
|
|
962
1043
|
? {
|
|
963
1044
|
entrypoints: true,
|
|
964
|
-
output:
|
|
1045
|
+
output: config.assetsManifestFile,
|
|
965
1046
|
}
|
|
966
1047
|
: {
|
|
967
1048
|
entrypoints: true,
|
|
968
1049
|
writeToDisk: true,
|
|
969
|
-
output: path.resolve(options.buildDirectory,
|
|
1050
|
+
output: path.resolve(options.buildDirectory, config.assetsManifestFile),
|
|
970
1051
|
}),
|
|
971
1052
|
...(process.env.WEBPACK_PROFILE === 'true' ? [new webpack.debug.ProfilingPlugin()] : []),
|
|
972
1053
|
];
|
|
@@ -991,13 +1072,15 @@ function configureRspackPlugins(options) {
|
|
|
991
1072
|
TsCheckerPlugin: ts_checker_rspack_plugin_1.TsCheckerRspackPlugin,
|
|
992
1073
|
CSSExtractPlugin: core_1.rspack.CssExtractRspackPlugin,
|
|
993
1074
|
RSDoctorPlugin: require('@rsdoctor/rspack-plugin').RsdoctorRspackPlugin,
|
|
1075
|
+
ModuleFederationPlugin: require('@module-federation/enhanced/rspack')
|
|
1076
|
+
.ModuleFederationPlugin,
|
|
994
1077
|
};
|
|
995
1078
|
const rspackPlugins = [
|
|
996
1079
|
...configureCommonPlugins(options, plugins),
|
|
997
1080
|
new rspack_manifest_plugin_1.RspackManifestPlugin({
|
|
998
1081
|
fileName: isEnvProduction
|
|
999
|
-
?
|
|
1000
|
-
: path.resolve(options.buildDirectory,
|
|
1082
|
+
? config.assetsManifestFile
|
|
1083
|
+
: path.resolve(options.buildDirectory, config.assetsManifestFile),
|
|
1001
1084
|
writeToFileEmit: true,
|
|
1002
1085
|
useLegacyEmit: true,
|
|
1003
1086
|
publicPath: '',
|
|
@@ -1183,7 +1266,7 @@ function configureRspackOptimization(helperOptions) {
|
|
|
1183
1266
|
}
|
|
1184
1267
|
const optimization = {
|
|
1185
1268
|
splitChunks: getOptimizationSplitChunks(helperOptions),
|
|
1186
|
-
runtimeChunk: 'single',
|
|
1269
|
+
runtimeChunk: helperOptions.config.moduleFederation ? false : 'single',
|
|
1187
1270
|
minimizer: [new core_1.rspack.SwcJsMinimizerRspackPlugin(swcMinifyOptions), cssMinimizer],
|
|
1188
1271
|
};
|
|
1189
1272
|
return optimization;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* global window */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const RuntimeVersioningPlugin = () => {
|
|
5
|
+
return {
|
|
6
|
+
name: 'runtime-versioning-plugin',
|
|
7
|
+
beforeRequest: (args) => {
|
|
8
|
+
if (typeof window.__REMOTE_VERSIONS__ !== 'object') {
|
|
9
|
+
return args;
|
|
10
|
+
}
|
|
11
|
+
args.options.remotes.forEach((remote) => {
|
|
12
|
+
const remoteVersion = window.__REMOTE_VERSIONS__[remote.name];
|
|
13
|
+
if (remoteVersion) {
|
|
14
|
+
// eslint-disable-next-line no-param-reassign
|
|
15
|
+
remote.entry = remote.entry.replace('[version]', remoteVersion);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
return args;
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
exports.default = RuntimeVersioningPlugin;
|
|
@@ -113,8 +113,6 @@ async function configureWebpackConfigForStorybook(mode, userConfig = {}, storybo
|
|
|
113
113
|
config: config.client,
|
|
114
114
|
configType: mode,
|
|
115
115
|
buildDirectory: config.client.outputPath || paths_1.default.appBuild,
|
|
116
|
-
assetsManifestFile: config.client.assetsManifestFile,
|
|
117
|
-
entry: config.client.entry,
|
|
118
116
|
entriesDirectory: paths_1.default.appEntry,
|
|
119
117
|
isSsr: false,
|
|
120
118
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export * from './common/s3-upload';
|
|
|
3
3
|
export { createTransformPathsToLocalModules } from './common/typescript/transformers';
|
|
4
4
|
export { defineConfig } from './common/models';
|
|
5
5
|
export { babelPreset } from './common/babel';
|
|
6
|
-
export type { ProjectConfig, ServiceConfig, LibraryConfig, ProjectFileConfig } from './common/models';
|
|
6
|
+
export type { ProjectConfig, ServiceConfig, LibraryConfig, ModuleFederationConfig, ProjectFileConfig, } from './common/models';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gravity-ui/app-builder",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.1",
|
|
4
4
|
"description": "Develop and build your React client-server projects, powered by typescript and webpack",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"@babel/preset-react": "^7.26.0",
|
|
69
69
|
"@babel/preset-typescript": "^7.26.0",
|
|
70
70
|
"@babel/runtime": "^7.26.0",
|
|
71
|
+
"@module-federation/enhanced": "^0.18.0",
|
|
71
72
|
"@okikio/sharedworker": "^1.0.7",
|
|
72
73
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
|
|
73
74
|
"@rsdoctor/rspack-plugin": "^1.0.2",
|
|
@@ -117,6 +118,7 @@
|
|
|
117
118
|
"pino-pretty": "^11.2.0",
|
|
118
119
|
"postcss": "^8.4.47",
|
|
119
120
|
"postcss-loader": "^8.1.1",
|
|
121
|
+
"postcss-prefix-selector": "^2.1.1",
|
|
120
122
|
"postcss-preset-env": "^9.1.3",
|
|
121
123
|
"react": "^18.3.1",
|
|
122
124
|
"react-dom": "^18.3.1",
|