@gravity-ui/app-builder 0.31.1 → 0.31.2-beta.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/README.md +238 -12
- package/dist/commands/build/build-service/server.js +1 -0
- package/dist/commands/dev/server.js +1 -0
- package/dist/common/config.js +9 -0
- package/dist/common/models/index.d.ts +25 -7
- package/dist/common/swc/compile.d.ts +3 -3
- package/dist/common/swc/compile.js +3 -2
- package/dist/common/swc/utils.d.ts +4 -3
- package/dist/common/swc/utils.js +15 -2
- package/dist/common/swc/watch.d.ts +3 -3
- package/dist/common/swc/watch.js +3 -2
- package/dist/common/webpack/config.js +29 -16
- package/dist/create-cli.d.ts +4 -0
- package/dist/create-cli.js +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -113,6 +113,25 @@ export default config;
|
|
|
113
113
|
- `target` (`client | server`) — select compilation unit.
|
|
114
114
|
- `verbose` (`boolean`) - turn on verbose output.
|
|
115
115
|
|
|
116
|
+
#### Environment Variables
|
|
117
|
+
|
|
118
|
+
`app-builder` automatically injects environment variables during the build process that are available in your application code:
|
|
119
|
+
|
|
120
|
+
- `process.env.PUBLIC_PATH` — automatically set to the resolved public path value (including CDN URLs if configured). This allows your application code to dynamically access the correct resource URLs at runtime.
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
// In your application code, you can access:
|
|
124
|
+
const publicPath = process.env.PUBLIC_PATH; // e.g., "https://cdn.example.com/build/" or "/build/"
|
|
125
|
+
|
|
126
|
+
// Useful for dynamically loading assets or configuring Module Federation
|
|
127
|
+
const assetUrl = `${process.env.PUBLIC_PATH}images/logo.png`;
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Note**: On the server side, `process.env.PUBLIC_PATH` is only available when using SWC compiler (`compiler: 'swc'`). With TypeScript compiler, this variable is not injected.
|
|
131
|
+
|
|
132
|
+
- `process.env.NODE_ENV` — current environment (`'development'` | `'production'`)
|
|
133
|
+
- `process.env.IS_SSR` — boolean flag indicating if code is running in SSR context
|
|
134
|
+
|
|
116
135
|
### Server
|
|
117
136
|
|
|
118
137
|
`app-builder` compiles server with typescript.
|
|
@@ -238,6 +257,7 @@ With this `{rootDir}/src/ui/tsconfig.json`:
|
|
|
238
257
|
- `prefix` (`string`) — path to files inside the bucket
|
|
239
258
|
- `region` (`string`) — AWS region or any string
|
|
240
259
|
- `endpoint` (`string`) - cdn host to upload files
|
|
260
|
+
- `publicPath` (`string`) - public path to access files from the browser
|
|
241
261
|
- `compress` (`boolean`) - upload also gzip and brotli compressed versions of files
|
|
242
262
|
- `additionalPattern` (`string[]`) — patterns for uploading additional files. By default, only files generated by webpack are loaded.
|
|
243
263
|
- `sentryConfig` (`Options`) — `@sentry/webpack-plugin` [configuration options](https://www.npmjs.com/package/@sentry/webpack-plugin/v/2.7.1).
|
|
@@ -338,18 +358,158 @@ Module Federation is a Webpack 5 feature that enables micro-frontend architectur
|
|
|
338
358
|
`app-builder` uses `@module-federation/enhanced` for advanced Module Federation support.
|
|
339
359
|
|
|
340
360
|
- `moduleFederation` (`object`) — Module Federation configuration
|
|
361
|
+
|
|
341
362
|
- `name` (`string`) — unique name of the application in the Module Federation ecosystem. Required parameter.
|
|
342
363
|
- `version` (`string`) — application version. When specified, the entry file will be named `entry-{version}.js` instead of `entry.js`.
|
|
343
364
|
- `disableManifest` (`boolean`) — disable manifest file generation. When `true`, uses regular `.js` files for remote entry instead of manifest files. Default is `false`.
|
|
344
|
-
- `
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
365
|
+
- `remotes` (`string[]`) — list of remote application names that this application can load. This is a simplified alternative to `originalRemotes` that automatically generates remote URLs based on your public path configuration.
|
|
366
|
+
|
|
367
|
+
**How it works:**
|
|
368
|
+
|
|
369
|
+
- In **development mode**: Remote URLs are automatically generated using the pattern `{commonPublicPath}{remoteName}/entry.js` (or manifest files if enabled)
|
|
370
|
+
- In **production mode**: You need to ensure remote applications are deployed and accessible at the expected URLs
|
|
371
|
+
- Remote entry files are loaded at runtime to provide federated modules from other micro-frontends
|
|
372
|
+
|
|
373
|
+
**File naming patterns** (depends on configuration):
|
|
374
|
+
|
|
375
|
+
- With `disableManifest: false` (default): `mf-manifest.json` or `mf-manifest-[version].json` (with versioning)
|
|
376
|
+
- With `disableManifest: true`: `entry.js` or `entry-[version].js` (with versioning)
|
|
377
|
+
|
|
378
|
+
**Example:**
|
|
379
|
+
|
|
380
|
+
```ts
|
|
381
|
+
// Simple configuration - URLs auto-generated in development
|
|
382
|
+
remotes: ['header', 'navigation', 'footer'];
|
|
383
|
+
|
|
384
|
+
// Results in loading from (in development):
|
|
385
|
+
// - https://localhost:3000/header/mf-manifest.json
|
|
386
|
+
// - https://localhost:3000/navigation/mf-manifest.json
|
|
387
|
+
// - https://localhost:3000/footer/mf-manifest.json
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**Development vs Production:**
|
|
391
|
+
|
|
392
|
+
- **Development**: App-builder automatically starts all remote applications and generates their URLs
|
|
393
|
+
- **Production**: Remote applications must be independently deployed and accessible at the generated URLs
|
|
394
|
+
|
|
395
|
+
**Integration with other options:**
|
|
396
|
+
|
|
397
|
+
- Works with `enabledRemotes` to selectively enable remotes in development
|
|
398
|
+
- Affected by `remotesRuntimeVersioning` for versioned file names
|
|
399
|
+
- File format controlled by `disableManifest` option
|
|
400
|
+
|
|
401
|
+
For more complex scenarios requiring custom URLs or cross-environment configurations, use `originalRemotes` instead.
|
|
402
|
+
|
|
403
|
+
- `enabledRemotes` (`string[]`) — list of enabled remotes for module federation. **Development mode only**. If not specified, all remotes from the `remotes` array will be enabled by default.
|
|
404
|
+
|
|
405
|
+
**Purpose:**
|
|
406
|
+
|
|
407
|
+
- Allows selective enabling/disabling of specific remotes during development
|
|
408
|
+
- Useful for debugging, testing individual micro-frontends, or working with partial system setups
|
|
409
|
+
- Helps reduce development startup time by loading only needed remotes
|
|
410
|
+
- Can be overridden via CLI flag: `--mf-remotes=header,footer`
|
|
411
|
+
|
|
412
|
+
**Loading behavior:**
|
|
413
|
+
|
|
414
|
+
- **Enabled remotes**: Loaded from local development server (auto-started by app-builder)
|
|
415
|
+
- **Disabled remotes**: Loaded from CDN (if `publicPathPrefix` or CDN configuration is available), allowing you to use stable production versions while developing specific parts locally
|
|
416
|
+
|
|
417
|
+
**Example:**
|
|
418
|
+
|
|
419
|
+
```ts
|
|
420
|
+
// Load all available remotes (header, navigation, footer)
|
|
421
|
+
remotes: ['header', 'navigation', 'footer'];
|
|
422
|
+
|
|
423
|
+
// Enable only specific remotes in development
|
|
424
|
+
enabledRemotes: ['header', 'footer']; // navigation will be skipped
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
**CLI Override:**
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
# Override enabledRemotes from command line
|
|
431
|
+
npx app-builder dev --mf-remotes=header,navigation
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
**Note:** This option has no effect in production builds - all configured remotes will be included in the production bundle configuration.
|
|
435
|
+
|
|
436
|
+
- `originalRemotes` (`RemotesObject`) — full configuration of remote applications in Module Federation Plugin format. Use this when you need explicit control over remote URLs or for cross-environment deployments.
|
|
437
|
+
|
|
438
|
+
**When to use:**
|
|
439
|
+
|
|
440
|
+
- Custom remote URLs (different domains, ports, paths)
|
|
441
|
+
- Cross-environment loading (staging, production CDN URLs)
|
|
442
|
+
- Complex deployment scenarios
|
|
443
|
+
- When `remotes` auto-generation doesn't meet your needs
|
|
444
|
+
|
|
445
|
+
**Format:**
|
|
446
|
+
|
|
447
|
+
```ts
|
|
448
|
+
originalRemotes: {
|
|
449
|
+
remoteName: 'remoteName@remoteUrl',
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
**Examples:**
|
|
454
|
+
|
|
455
|
+
```ts
|
|
456
|
+
originalRemotes: {
|
|
457
|
+
header: 'header@https://cdn.example.com/header/entry.js',
|
|
458
|
+
footer: 'footer@https://cdn.example.com/footer/entry.js',
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**Note:** When `originalRemotes` is specified, the `remotes` option is ignored. Use either `remotes` OR `originalRemotes`, not both.
|
|
463
|
+
|
|
464
|
+
- `remotesRuntimeVersioning` (`boolean`) — enables runtime versioning for remote applications. When enabled, remote entry files include version information in their filenames, allowing for cache busting and version-specific loading.
|
|
465
|
+
|
|
466
|
+
**How it affects file names:**
|
|
467
|
+
|
|
468
|
+
- With `disableManifest: false`: `mf-manifest-[version].json` instead of `mf-manifest.json`
|
|
469
|
+
- With `disableManifest: true`: `entry-[version].js` instead of `entry.js`
|
|
470
|
+
- Version is taken from the remote application's `version` configuration
|
|
471
|
+
|
|
472
|
+
**Benefits:**
|
|
473
|
+
|
|
474
|
+
- **Cache busting**: Different versions get different URLs, preventing browser caching issues
|
|
475
|
+
- **Rollback capability**: Can load specific versions of remotes
|
|
476
|
+
- **Deployment safety**: Gradual rollouts with version-specific remote loading
|
|
477
|
+
|
|
478
|
+
**Example with versioning enabled:**
|
|
479
|
+
|
|
480
|
+
```ts
|
|
481
|
+
// Host application
|
|
482
|
+
{
|
|
483
|
+
moduleFederation: {
|
|
484
|
+
name: 'shell',
|
|
485
|
+
remotes: ['header', 'navigation'],
|
|
486
|
+
remotesRuntimeVersioning: true, // Enable versioning
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Remote application (header)
|
|
491
|
+
{
|
|
492
|
+
moduleFederation: {
|
|
493
|
+
name: 'header',
|
|
494
|
+
version: '2.1.0', // This version appears in filename
|
|
495
|
+
exposes: { './Header': './src/Header' }
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Results in loading: header/mf-manifest-2.1.0.json
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
**Runtime behavior:**
|
|
503
|
+
|
|
504
|
+
- The version is resolved at runtime from the remote's manifest or entry file
|
|
505
|
+
- Enables loading different versions of the same remote in different environments
|
|
506
|
+
- Works with both `remotes` and `originalRemotes` configurations
|
|
507
|
+
|
|
348
508
|
- `isolateStyles` (`object`) — CSS style isolation settings to prevent conflicts between micro-frontends.
|
|
349
509
|
- `getPrefix` (`(entryName: string) => string`) — function to generate CSS class prefix.
|
|
350
510
|
- `prefixSelector` (`(prefix: string, selector: string, prefixedSelector: string, filePath: string) => string`) — function to add prefix to CSS selectors.
|
|
351
511
|
- Also supports all standard options from [@module-federation/enhanced](https://module-federation.io/), except `name` and `remotes`, such as:
|
|
352
|
-
- `filename` — entry file name (default `
|
|
512
|
+
- `filename` — entry file name (default `entry.js`)
|
|
353
513
|
- `exposes` — modules that this application exports
|
|
354
514
|
- `shared` — shared dependencies between applications
|
|
355
515
|
- `runtimePlugins` — plugins for Module Federation runtime
|
|
@@ -363,7 +523,6 @@ export default defineConfig({
|
|
|
363
523
|
client: {
|
|
364
524
|
moduleFederation: {
|
|
365
525
|
name: 'shell',
|
|
366
|
-
publicPath: 'https://cdn.example.com/my-app/',
|
|
367
526
|
// Simple remotes configuration
|
|
368
527
|
remotes: ['header', 'footer', 'sidebar'],
|
|
369
528
|
shared: {
|
|
@@ -384,12 +543,11 @@ export default defineConfig({
|
|
|
384
543
|
moduleFederation: {
|
|
385
544
|
name: 'main-shell',
|
|
386
545
|
version: '2.1.0',
|
|
387
|
-
publicPath: 'https://cdn.example.com/my-app/',
|
|
388
546
|
// Detailed remotes configuration
|
|
389
547
|
originalRemotes: {
|
|
390
|
-
header: 'header@https://cdn.example.com/header/
|
|
391
|
-
footer: 'footer@https://cdn.example.com/footer/
|
|
392
|
-
userProfile: 'userProfile@https://cdn.example.com/user-profile/
|
|
548
|
+
header: 'header@https://cdn.example.com/header/entry.js',
|
|
549
|
+
footer: 'footer@https://cdn.example.com/footer/entry.js',
|
|
550
|
+
userProfile: 'userProfile@https://cdn.example.com/user-profile/entry.js',
|
|
393
551
|
},
|
|
394
552
|
remotesRuntimeVersioning: true,
|
|
395
553
|
isolateStyles: {
|
|
@@ -425,7 +583,6 @@ export default defineConfig({
|
|
|
425
583
|
client: {
|
|
426
584
|
moduleFederation: {
|
|
427
585
|
name: 'header',
|
|
428
|
-
publicPath: 'https://cdn.example.com/my-app/',
|
|
429
586
|
// Expose modules for other applications
|
|
430
587
|
exposes: {
|
|
431
588
|
'./Header': './src/components/Header',
|
|
@@ -452,7 +609,6 @@ export default defineConfig({
|
|
|
452
609
|
moduleFederation: {
|
|
453
610
|
name: 'dashboard',
|
|
454
611
|
version: '1.5.0',
|
|
455
|
-
publicPath: 'https://cdn.example.com/my-app/',
|
|
456
612
|
// Consume remote modules
|
|
457
613
|
remotes: ['charts', 'notifications'],
|
|
458
614
|
// Expose own modules
|
|
@@ -469,3 +625,73 @@ export default defineConfig({
|
|
|
469
625
|
},
|
|
470
626
|
});
|
|
471
627
|
```
|
|
628
|
+
|
|
629
|
+
**Advanced Remotes Configuration Examples:**
|
|
630
|
+
|
|
631
|
+
```ts
|
|
632
|
+
export default defineConfig({
|
|
633
|
+
client: {
|
|
634
|
+
moduleFederation: {
|
|
635
|
+
name: 'advanced-shell',
|
|
636
|
+
version: '3.0.0',
|
|
637
|
+
|
|
638
|
+
// Example 1: Simple remotes for development
|
|
639
|
+
remotes: ['header', 'sidebar', 'footer', 'analytics'],
|
|
640
|
+
|
|
641
|
+
// Example 2: Enable only specific remotes in development
|
|
642
|
+
enabledRemotes: ['header', 'sidebar'], // Only these will load in dev mode
|
|
643
|
+
|
|
644
|
+
// Example 3: Runtime versioning with manifests (production-ready)
|
|
645
|
+
remotesRuntimeVersioning: true, // Enables version-specific loading
|
|
646
|
+
disableManifest: false, // Use mf-manifest-[version].json files
|
|
647
|
+
|
|
648
|
+
shared: {
|
|
649
|
+
react: {singleton: true, requiredVersion: '^18.0.0'},
|
|
650
|
+
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
|
|
651
|
+
},
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
// Alternative: Explicit URLs for production
|
|
657
|
+
export default defineConfig({
|
|
658
|
+
client: {
|
|
659
|
+
moduleFederation: {
|
|
660
|
+
name: 'production-shell',
|
|
661
|
+
|
|
662
|
+
// Use originalRemotes for explicit control
|
|
663
|
+
originalRemotes: {
|
|
664
|
+
// Static CDN URLs
|
|
665
|
+
header: 'header@https://cdn.company.com/header/mf-manifest-2.1.0.json',
|
|
666
|
+
sidebar: 'sidebar@https://cdn.company.com/sidebar/entry-1.5.0.js',
|
|
667
|
+
|
|
668
|
+
// Dynamic remote loading
|
|
669
|
+
analytics: `promise new Promise((resolve) => {
|
|
670
|
+
const remoteUrl = process.env.NODE_ENV === 'production'
|
|
671
|
+
? 'https://analytics.cdn.com/entry.js'
|
|
672
|
+
: 'http://localhost:3003/entry.js';
|
|
673
|
+
resolve(\`analytics@\${remoteUrl}\`);
|
|
674
|
+
})`,
|
|
675
|
+
},
|
|
676
|
+
|
|
677
|
+
shared: {
|
|
678
|
+
react: {singleton: true, requiredVersion: '^18.0.0'},
|
|
679
|
+
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
|
|
680
|
+
},
|
|
681
|
+
},
|
|
682
|
+
},
|
|
683
|
+
});
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
**Development Workflow with Remotes:**
|
|
687
|
+
|
|
688
|
+
```bash
|
|
689
|
+
# Start host application with all remotes
|
|
690
|
+
npx app-builder dev
|
|
691
|
+
|
|
692
|
+
# Start host with only specific remotes (faster development)
|
|
693
|
+
npx app-builder dev --mf-remotes=header,sidebar
|
|
694
|
+
|
|
695
|
+
# The above is equivalent to setting enabledRemotes in config:
|
|
696
|
+
# enabledRemotes: ['header', 'sidebar']
|
|
697
|
+
```
|
|
@@ -20,6 +20,7 @@ compile({
|
|
|
20
20
|
projectPath: ${JSON.stringify(paths_1.default.appServer)},
|
|
21
21
|
additionalPaths: ${JSON.stringify(config.server.swcOptions?.additionalPaths)},
|
|
22
22
|
exclude: ${JSON.stringify(config.server.swcOptions?.exclude)},
|
|
23
|
+
publicPath: ${JSON.stringify(config.client.browserPublicPath)},
|
|
23
24
|
});`;
|
|
24
25
|
}
|
|
25
26
|
function createTypescriptBuildScript(config) {
|
package/dist/common/config.js
CHANGED
|
@@ -144,6 +144,9 @@ async function getProjectConfig(command, { env, storybook, ...argv }) {
|
|
|
144
144
|
...omitUndefined(server),
|
|
145
145
|
},
|
|
146
146
|
};
|
|
147
|
+
if (projectConfig.client?.moduleFederation && argv.mfRemotes) {
|
|
148
|
+
projectConfig.client.moduleFederation.enabledRemotes = argv.mfRemotes;
|
|
149
|
+
}
|
|
147
150
|
return normalizeConfig(projectConfig, command);
|
|
148
151
|
}
|
|
149
152
|
async function normalizeConfig(userConfig, mode) {
|
|
@@ -187,10 +190,14 @@ async function normalizeConfig(userConfig, mode) {
|
|
|
187
190
|
config.lib.newJsxTransform = config.lib.newJsxTransform ?? true;
|
|
188
191
|
return config;
|
|
189
192
|
}
|
|
193
|
+
// TODO(DakEnviy): Make mode type strict
|
|
190
194
|
async function normalizeClientConfig(client, mode) {
|
|
195
|
+
const cdnConfig = Array.isArray(client.cdn) ? client.cdn[0] : client.cdn;
|
|
191
196
|
let publicPath = client.publicPath || path.normalize(`${client.publicPathPrefix || ''}/build/`);
|
|
197
|
+
let browserPublicPath = (mode !== 'dev' && cdnConfig?.publicPath) || publicPath;
|
|
192
198
|
if (client.moduleFederation) {
|
|
193
199
|
publicPath = path.normalize(`${publicPath}${client.moduleFederation.name}/`);
|
|
200
|
+
browserPublicPath = path.normalize(`${browserPublicPath}${client.moduleFederation.name}/`);
|
|
194
201
|
}
|
|
195
202
|
let transformCssWithLightningCss = Boolean(client.transformCssWithLightningCss);
|
|
196
203
|
if (client.moduleFederation?.isolateStyles && transformCssWithLightningCss) {
|
|
@@ -208,6 +215,8 @@ async function normalizeClientConfig(client, mode) {
|
|
|
208
215
|
: (client.reactRefresh ?? ((options) => options)),
|
|
209
216
|
newJsxTransform: client.newJsxTransform ?? true,
|
|
210
217
|
publicPath,
|
|
218
|
+
cdnPublicPath: cdnConfig?.publicPath,
|
|
219
|
+
browserPublicPath,
|
|
211
220
|
assetsManifestFile: client.assetsManifestFile ||
|
|
212
221
|
(client.moduleFederation?.version
|
|
213
222
|
? `assets-manifest-${client.moduleFederation.version}.json`
|
|
@@ -77,22 +77,23 @@ export type ModuleFederationConfig = Omit<moduleFederationPlugin.ModuleFederatio
|
|
|
77
77
|
* @default false
|
|
78
78
|
*/
|
|
79
79
|
disableManifest?: boolean;
|
|
80
|
-
/**
|
|
81
|
-
* Base URL for loading resources of this micro-frontend
|
|
82
|
-
* Should point to a publicly accessible URL where the files will be hosted
|
|
83
|
-
* @example 'https://cdn.example.com/my-app/'
|
|
84
|
-
*/
|
|
85
|
-
publicPath: string;
|
|
86
80
|
/**
|
|
87
81
|
* List of remote application names that this application can load
|
|
88
82
|
* Simplified alternative to originalRemotes - only names are specified
|
|
89
83
|
* @example ['header', 'footer', 'navigation']
|
|
90
84
|
*/
|
|
91
85
|
remotes?: string[];
|
|
86
|
+
/**
|
|
87
|
+
* List of enabled remotes for module federation
|
|
88
|
+
* If not specified, all remotes will be enabled by default
|
|
89
|
+
* It used only for development mode
|
|
90
|
+
* @example ['header', 'navigation']
|
|
91
|
+
*/
|
|
92
|
+
enabledRemotes?: string[];
|
|
92
93
|
/**
|
|
93
94
|
* Full configuration of remote applications in Module Federation format
|
|
94
95
|
* Allows more detailed configuration of each remote application
|
|
95
|
-
* @example { header: 'header@https://header.example.com/
|
|
96
|
+
* @example { header: 'header@https://header.example.com/entry.js' }
|
|
96
97
|
*/
|
|
97
98
|
originalRemotes?: moduleFederationPlugin.ModuleFederationPluginOptions['remotes'];
|
|
98
99
|
/**
|
|
@@ -323,6 +324,7 @@ export interface CdnUploadConfig {
|
|
|
323
324
|
prefix?: string;
|
|
324
325
|
region?: string;
|
|
325
326
|
endpoint?: string;
|
|
327
|
+
publicPath?: string;
|
|
326
328
|
compress?: boolean;
|
|
327
329
|
cacheControl?: UploadOptions['cacheControl'];
|
|
328
330
|
/**
|
|
@@ -361,7 +363,22 @@ export interface ServiceConfig {
|
|
|
361
363
|
export type NormalizedClientConfig = Omit<ClientConfig, 'publicPathPrefix' | 'publicPath' | 'assetsManifestFile' | 'hiddenSourceMap' | 'svgr' | 'lazyCompilation' | 'devServer' | 'disableForkTsChecker' | 'disableReactRefresh' | 'transformCssWithLightningCss'> & {
|
|
362
364
|
bundler: Bundler;
|
|
363
365
|
javaScriptLoader: JavaScriptLoader;
|
|
366
|
+
/**
|
|
367
|
+
* Build public path
|
|
368
|
+
* (concatenated with micro-frontend name if module federation is configured).
|
|
369
|
+
*/
|
|
364
370
|
publicPath: string;
|
|
371
|
+
/**
|
|
372
|
+
* Public path for CDN,
|
|
373
|
+
* it presents even if CDN is disabled.
|
|
374
|
+
*/
|
|
375
|
+
cdnPublicPath?: string;
|
|
376
|
+
/**
|
|
377
|
+
* Final public path for browser,
|
|
378
|
+
* it is based on cdnPublicPath if CDN is enabled or publicPath otherwise
|
|
379
|
+
* (concatenated with micro-frontend name if module federation is configured).
|
|
380
|
+
*/
|
|
381
|
+
browserPublicPath: string;
|
|
365
382
|
assetsManifestFile: string;
|
|
366
383
|
hiddenSourceMap: boolean;
|
|
367
384
|
svgr: NonNullable<ClientConfig['svgr']>;
|
|
@@ -378,6 +395,7 @@ export type NormalizedClientConfig = Omit<ClientConfig, 'publicPathPrefix' | 'pu
|
|
|
378
395
|
}) => Configuration | Promise<Configuration>;
|
|
379
396
|
rspack: (config: RspackConfiguration, options: {
|
|
380
397
|
configType: `${WebpackMode}`;
|
|
398
|
+
isSsr: boolean;
|
|
381
399
|
}) => RspackConfiguration | Promise<RspackConfiguration>;
|
|
382
400
|
debugWebpack?: boolean;
|
|
383
401
|
babel: (config: Babel.TransformOptions, options: {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { Logger } from '../logger';
|
|
2
|
-
import type {
|
|
3
|
-
type SwcCompileOptions = Pick<
|
|
2
|
+
import type { GetSwcOptionsParams } from './utils';
|
|
3
|
+
type SwcCompileOptions = Pick<GetSwcOptionsParams, 'additionalPaths' | 'exclude' | 'publicPath'> & {
|
|
4
4
|
projectPath: string;
|
|
5
5
|
outputPath: string;
|
|
6
6
|
logger: Logger;
|
|
7
7
|
};
|
|
8
|
-
export declare function compile({ projectPath, outputPath, logger, additionalPaths, exclude, }: SwcCompileOptions): Promise<void>;
|
|
8
|
+
export declare function compile({ projectPath, outputPath, logger, additionalPaths, exclude, publicPath, }: SwcCompileOptions): Promise<void>;
|
|
9
9
|
export {};
|
|
@@ -5,13 +5,14 @@ const pretty_time_1 = require("../logger/pretty-time");
|
|
|
5
5
|
// @ts-ignore @swc/cli is not typed
|
|
6
6
|
const cli_1 = require("@swc/cli");
|
|
7
7
|
const utils_1 = require("./utils");
|
|
8
|
-
async function compile({ projectPath, outputPath, logger, additionalPaths, exclude, }) {
|
|
8
|
+
async function compile({ projectPath, outputPath, logger, additionalPaths, exclude, publicPath, }) {
|
|
9
9
|
const start = process.hrtime.bigint();
|
|
10
10
|
logger.message('Start compilation');
|
|
11
|
-
const { swcOptions, directoriesToCompile } = (0, utils_1.
|
|
11
|
+
const { swcOptions, directoriesToCompile } = (0, utils_1.getSwcOptions)({
|
|
12
12
|
projectPath,
|
|
13
13
|
additionalPaths,
|
|
14
14
|
exclude,
|
|
15
|
+
publicPath,
|
|
15
16
|
});
|
|
16
17
|
const cliOptions = {
|
|
17
18
|
filenames: directoriesToCompile,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export declare const EXTENSIONS_TO_COMPILE: string[];
|
|
2
|
-
export
|
|
2
|
+
export interface GetSwcOptionsParams {
|
|
3
3
|
projectPath: string;
|
|
4
4
|
filename?: string;
|
|
5
5
|
additionalPaths?: string[];
|
|
6
6
|
exclude?: string | string[];
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
publicPath: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function getSwcOptions({ projectPath, filename, additionalPaths, exclude, publicPath, }: GetSwcOptionsParams): {
|
|
9
10
|
swcOptions: import("@swc/types").Options;
|
|
10
11
|
directoriesToCompile: string[];
|
|
11
12
|
};
|
package/dist/common/swc/utils.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.EXTENSIONS_TO_COMPILE = void 0;
|
|
7
|
-
exports.
|
|
7
|
+
exports.getSwcOptions = getSwcOptions;
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const tsconfig_to_swcconfig_1 = require("tsconfig-to-swcconfig");
|
|
10
10
|
const DEFAULT_EXCLUDE = ['node_modules'];
|
|
@@ -19,13 +19,26 @@ function resolvePaths(paths, baseUrl) {
|
|
|
19
19
|
}
|
|
20
20
|
return entries;
|
|
21
21
|
}
|
|
22
|
-
function
|
|
22
|
+
function getSwcOptions({ projectPath, filename = 'tsconfig.json', additionalPaths, exclude, publicPath, }) {
|
|
23
23
|
const swcOptions = (0, tsconfig_to_swcconfig_1.convert)(filename, projectPath);
|
|
24
24
|
swcOptions.exclude = swcOptions.exclude || [];
|
|
25
25
|
swcOptions.jsc = {
|
|
26
26
|
...swcOptions.jsc,
|
|
27
27
|
// SWC requires absolute path as baseUrl
|
|
28
28
|
baseUrl: projectPath,
|
|
29
|
+
transform: {
|
|
30
|
+
...swcOptions.jsc?.transform,
|
|
31
|
+
optimizer: {
|
|
32
|
+
...swcOptions.jsc?.transform?.optimizer,
|
|
33
|
+
globals: {
|
|
34
|
+
...swcOptions.jsc?.transform?.optimizer?.globals,
|
|
35
|
+
vars: {
|
|
36
|
+
'process.env.PUBLIC_PATH': JSON.stringify(publicPath),
|
|
37
|
+
...swcOptions.jsc?.transform?.optimizer?.globals?.vars,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
29
42
|
};
|
|
30
43
|
let customExclude = [];
|
|
31
44
|
if (Array.isArray(exclude)) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { Logger } from '../logger';
|
|
2
|
-
import type {
|
|
3
|
-
type SwcWatchOptions = Pick<
|
|
2
|
+
import type { GetSwcOptionsParams } from './utils';
|
|
3
|
+
type SwcWatchOptions = Pick<GetSwcOptionsParams, 'additionalPaths' | 'exclude' | 'publicPath'> & {
|
|
4
4
|
outputPath: string;
|
|
5
5
|
logger: Logger;
|
|
6
6
|
onAfterFilesEmitted?: () => void;
|
|
7
7
|
};
|
|
8
|
-
export declare function watch(projectPath: string, { outputPath, logger, onAfterFilesEmitted, additionalPaths, exclude }: SwcWatchOptions): Promise<void>;
|
|
8
|
+
export declare function watch(projectPath: string, { outputPath, logger, onAfterFilesEmitted, additionalPaths, exclude, publicPath, }: SwcWatchOptions): Promise<void>;
|
|
9
9
|
export {};
|
package/dist/common/swc/watch.js
CHANGED
|
@@ -4,12 +4,13 @@ exports.watch = watch;
|
|
|
4
4
|
// @ts-ignore @swc/cli is not typed
|
|
5
5
|
const cli_1 = require("@swc/cli");
|
|
6
6
|
const utils_1 = require("./utils");
|
|
7
|
-
async function watch(projectPath, { outputPath, logger, onAfterFilesEmitted, additionalPaths, exclude }) {
|
|
7
|
+
async function watch(projectPath, { outputPath, logger, onAfterFilesEmitted, additionalPaths, exclude, publicPath, }) {
|
|
8
8
|
logger.message('Start compilation in watch mode');
|
|
9
|
-
const { swcOptions, directoriesToCompile } = (0, utils_1.
|
|
9
|
+
const { swcOptions, directoriesToCompile } = (0, utils_1.getSwcOptions)({
|
|
10
10
|
projectPath,
|
|
11
11
|
additionalPaths,
|
|
12
12
|
exclude,
|
|
13
|
+
publicPath,
|
|
13
14
|
});
|
|
14
15
|
const cliOptions = {
|
|
15
16
|
filenames: directoriesToCompile,
|
|
@@ -353,10 +353,6 @@ function configureResolve({ isEnvProduction, config }) {
|
|
|
353
353
|
fallback: config.fallback,
|
|
354
354
|
};
|
|
355
355
|
}
|
|
356
|
-
function isModuleFederationEntry(entryName, fileName) {
|
|
357
|
-
// Ignore bootstrap file for module federation entries
|
|
358
|
-
return entryName === fileName || `${entryName}-bootstrap` === fileName;
|
|
359
|
-
}
|
|
360
356
|
function createEntryArray(entry) {
|
|
361
357
|
if (typeof entry === 'string') {
|
|
362
358
|
return [require.resolve('./public-path'), entry];
|
|
@@ -380,7 +376,6 @@ function configureEntry({ config, entriesDirectory }) {
|
|
|
380
376
|
}), {});
|
|
381
377
|
}
|
|
382
378
|
let entryFiles = fs.readdirSync(entriesDirectory).filter((file) => /\.[jt]sx?$/.test(file));
|
|
383
|
-
let result = {};
|
|
384
379
|
if (config.moduleFederation) {
|
|
385
380
|
const { name, remotes, originalRemotes } = config.moduleFederation;
|
|
386
381
|
const entryFile = entryFiles.find((item) => path.parse(item).name === name);
|
|
@@ -394,12 +389,13 @@ function configureEntry({ config, entriesDirectory }) {
|
|
|
394
389
|
}
|
|
395
390
|
entryFiles = entryFiles.filter((file) => {
|
|
396
391
|
const fileName = path.parse(file).name;
|
|
397
|
-
return (
|
|
398
|
-
|
|
392
|
+
return (
|
|
393
|
+
// Ignore bootstrap file for module federation host
|
|
394
|
+
fileName !== `${name}-bootstrap` &&
|
|
395
|
+
remoteNames.every(
|
|
396
|
+
// Ignore bootstrap and entry files for module federation remotes
|
|
397
|
+
(remote) => remote !== fileName && `${remote}-bootstrap` !== fileName));
|
|
399
398
|
});
|
|
400
|
-
result = {
|
|
401
|
-
main: createEntryArray(path.resolve(entriesDirectory, entryFile)),
|
|
402
|
-
};
|
|
403
399
|
}
|
|
404
400
|
if (Array.isArray(config.entryFilter) && config.entryFilter.length) {
|
|
405
401
|
entryFiles = entryFiles.filter((file) => config.entryFilter?.includes(path.parse(file).name));
|
|
@@ -407,7 +403,7 @@ function configureEntry({ config, entriesDirectory }) {
|
|
|
407
403
|
if (!entryFiles.length) {
|
|
408
404
|
throw new Error('No entries were found after applying entry filter');
|
|
409
405
|
}
|
|
410
|
-
return entryFiles.reduce((acc, file) => addEntry(acc, path.resolve(entriesDirectory, file)),
|
|
406
|
+
return entryFiles.reduce((acc, file) => addEntry(acc, path.resolve(entriesDirectory, file)), {});
|
|
411
407
|
}
|
|
412
408
|
function getFileNames({ isEnvProduction, isSsr, config }) {
|
|
413
409
|
let ext = 'js';
|
|
@@ -859,6 +855,7 @@ function getDefinitions({ config, isSsr }) {
|
|
|
859
855
|
return {
|
|
860
856
|
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
|
861
857
|
'process.env.IS_SSR': JSON.stringify(isSsr),
|
|
858
|
+
'process.env.PUBLIC_PATH': JSON.stringify(config.browserPublicPath),
|
|
862
859
|
...config.definitions,
|
|
863
860
|
};
|
|
864
861
|
}
|
|
@@ -966,9 +963,12 @@ function configureCommonPlugins(options, bundlerPlugins) {
|
|
|
966
963
|
}
|
|
967
964
|
plugins.push(createMomentTimezoneDataPlugin(config.momentTz));
|
|
968
965
|
if (config.moduleFederation) {
|
|
969
|
-
const { name, version, disableManifest,
|
|
966
|
+
const { name, version, disableManifest, remotes, enabledRemotes = remotes, originalRemotes, remotesRuntimeVersioning, isolateStyles: _isolateStyles, // Omit isolateStyles from restOptions
|
|
967
|
+
runtimePlugins, ...restOptions } = config.moduleFederation;
|
|
970
968
|
let actualRemotes = originalRemotes;
|
|
971
|
-
if (remotes) {
|
|
969
|
+
if (!actualRemotes && remotes && enabledRemotes) {
|
|
970
|
+
// Remove micro-frontend name from public path
|
|
971
|
+
const commonPublicPath = config.browserPublicPath.replace(`${name}/`, '');
|
|
972
972
|
let remoteFile;
|
|
973
973
|
if (disableManifest) {
|
|
974
974
|
if (remotesRuntimeVersioning) {
|
|
@@ -984,13 +984,26 @@ function configureCommonPlugins(options, bundlerPlugins) {
|
|
|
984
984
|
else {
|
|
985
985
|
remoteFile = 'mf-manifest.json';
|
|
986
986
|
}
|
|
987
|
-
actualRemotes = remotes.reduce((acc,
|
|
987
|
+
actualRemotes = remotes.reduce((acc, remote) => {
|
|
988
|
+
let remotePath;
|
|
989
|
+
if (isEnvDevelopment) {
|
|
990
|
+
if (enabledRemotes.includes(remote)) {
|
|
991
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
992
|
+
remotePath = `${commonPublicPath}${remote}/${remoteFile.replace('[version]', version)}`;
|
|
993
|
+
}
|
|
994
|
+
else if (config.cdnPublicPath) {
|
|
995
|
+
remotePath = `${config.cdnPublicPath}${remote}/${remoteFile}`;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
if (!remotePath) {
|
|
999
|
+
remotePath = `${commonPublicPath}${remote}/${remoteFile}`;
|
|
1000
|
+
}
|
|
988
1001
|
// eslint-disable-next-line no-param-reassign
|
|
989
|
-
acc[
|
|
1002
|
+
acc[remote] = `${remote}@${remotePath}`;
|
|
990
1003
|
return acc;
|
|
991
1004
|
}, {});
|
|
992
1005
|
}
|
|
993
|
-
const actualRuntimePlugins = runtimePlugins || [];
|
|
1006
|
+
const actualRuntimePlugins = runtimePlugins.slice() || [];
|
|
994
1007
|
if (remotesRuntimeVersioning) {
|
|
995
1008
|
actualRuntimePlugins.push(require.resolve('./runtime-versioning-plugin'));
|
|
996
1009
|
}
|
package/dist/create-cli.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export declare function createCli(argv: string[]): {
|
|
|
17
17
|
"disable-react-refresh": boolean | undefined;
|
|
18
18
|
lazyCompilation: boolean | undefined;
|
|
19
19
|
"lazy-compilation": boolean | undefined;
|
|
20
|
+
"mf-remotes": string[] | undefined;
|
|
21
|
+
mfRemotes: string[] | undefined;
|
|
20
22
|
reactProfiling: boolean | undefined;
|
|
21
23
|
"react-profiling": boolean | undefined;
|
|
22
24
|
analyzeBundle: "true" | "statoscope" | "rsdoctor" | undefined;
|
|
@@ -45,6 +47,8 @@ export declare function createCli(argv: string[]): {
|
|
|
45
47
|
"disable-react-refresh": boolean | undefined;
|
|
46
48
|
lazyCompilation: boolean | undefined;
|
|
47
49
|
"lazy-compilation": boolean | undefined;
|
|
50
|
+
"mf-remotes": string[] | undefined;
|
|
51
|
+
mfRemotes: string[] | undefined;
|
|
48
52
|
reactProfiling: boolean | undefined;
|
|
49
53
|
"react-profiling": boolean | undefined;
|
|
50
54
|
analyzeBundle: "true" | "statoscope" | "rsdoctor" | undefined;
|
package/dist/create-cli.js
CHANGED
|
@@ -142,6 +142,12 @@ function createCli(argv) {
|
|
|
142
142
|
group: 'Client',
|
|
143
143
|
type: 'boolean',
|
|
144
144
|
describe: 'Display final webpack configurations for debugging purposes',
|
|
145
|
+
})
|
|
146
|
+
.option('mf-remotes', {
|
|
147
|
+
group: 'Client',
|
|
148
|
+
type: 'string',
|
|
149
|
+
describe: 'Enabled remotes for module federation (all remotes by default)',
|
|
150
|
+
array: true,
|
|
145
151
|
}),
|
|
146
152
|
handler: handlerP(getCommandHandler('dev', (args, cmd) => {
|
|
147
153
|
if ((0, models_1.isLibraryConfig)(args)) {
|
package/package.json
CHANGED