1- import * as fs from 'fs' ;
21import * as path from 'path' ;
32import type {
43 EncodedExtension ,
@@ -18,16 +17,13 @@ import {
1817 getSharedModuleMetadata ,
1918} from '../shared-modules/shared-modules-meta' ;
2019import type { DynamicModuleMap } from '../utils/dynamic-module-parser' ;
21- import { getDynamicModuleMap } from '../utils/dynamic-module-parser' ;
2220import { parseJSONC } from '../utils/jsonc' ;
2321import { loadSchema } from '../utils/schema' ;
2422import { ExtensionValidator } from '../validation/ExtensionValidator' ;
2523import { SchemaValidator } from '../validation/SchemaValidator' ;
2624import { ValidationResult } from '../validation/ValidationResult' ;
27- import type { DynamicModuleImportLoaderOptions } from './loaders/dynamic-module-import-loader' ;
28-
29- const dynamicModuleImportLoader =
30- '@openshift-console/dynamic-plugin-sdk-webpack/lib/webpack/loaders/dynamic-module-import-loader' ;
25+ import type { DynamicModulePackageSpecs } from './DynamicModuleImportPlugin' ;
26+ import { DynamicModuleImportPlugin , resolveDynamicModuleMaps } from './DynamicModuleImportPlugin' ;
3127
3228const loadPluginPackageJSON = ( ) => readPkg . sync ( { normalize : false } ) as ConsolePluginPackageJSON ;
3329
@@ -130,7 +126,7 @@ const getDeprecatedSharedModuleWarnings = (pkg: ConsolePluginPackageJSON): strin
130126
131127 if ( deprecated && pluginDeps [ moduleName ] ) {
132128 warnings . push (
133- `shared modules: [DEPRECATION ALERT] ' ${ moduleName } ' is deprecated. ${ deprecated } ` ,
129+ `[DEPRECATION WARNING] Console provided shared module ${ moduleName } has been deprecated: ${ deprecated } ` ,
134130 ) ;
135131 }
136132 } ) ;
@@ -169,7 +165,7 @@ const validateConsoleProvidedSharedModules = (pkg: ConsolePluginPackageJSON) =>
169165/**
170166 * PatternFly packages that support dynamic modules to be used with webpack module federation.
171167 *
172- * Console provided {@link sharedPluginModules} should NOT be listed here.
168+ * Console provided {@link sharedPluginModules} should _NOT_ be listed here.
173169 */
174170const dynamicModulePatternFlyPackages = [
175171 '@patternfly/react-charts' ,
@@ -180,13 +176,16 @@ const dynamicModulePatternFlyPackages = [
180176 '@patternfly/react-templates' ,
181177] ;
182178
183- type DynamicModulePackageSpec = Partial < {
184- /** @default 'dist/esm/index.js' */
185- indexModule : string ;
179+ export const dynamicModulePackageSpecs = dynamicModulePatternFlyPackages . reduce <
180+ DynamicModulePackageSpecs
181+ > ( ( acc , moduleName ) => ( { ... acc , [ moduleName ] : { } } ) , { } ) ;
186182
187- /** @default 'module' */
188- resolutionField : string ;
189- } > ;
183+ export const dynamicModuleImportTransformFilter = ( moduleRequest : string ) => {
184+ const isCode = / \. ( j s x ? | t s x ? ) $ / . test ( moduleRequest ) ;
185+ const isVendor = moduleRequest . includes ( '/node_modules/' ) ;
186+
187+ return isCode && ( ! isVendor || moduleRequest . includes ( '/node_modules/@openshift-console/' ) ) ;
188+ } ;
190189
191190export type ConsoleRemotePluginOptions = Partial < {
192191 /**
@@ -250,9 +249,9 @@ export type ConsoleRemotePluginOptions = Partial<{
250249 /**
251250 * Some vendor packages may support dynamic modules to be used with webpack module federation.
252251 *
253- * If a module request matches the `transformImports` filter, that module will have its imports
254- * transformed so that any _index_ imports for given vendor packages become imports for specific
255- * dynamic modules of these vendor packages.
252+ * If a module request matches the `moduleFilter`, code of that module will be modified so that
253+ * any _index_ imports for given vendor packages become imports for specific dynamic modules of
254+ * these vendor packages.
256255 *
257256 * For example, the following import:
258257 * ```ts
@@ -268,11 +267,23 @@ export type ConsoleRemotePluginOptions = Partial<{
268267 * Each dynamic module (such as `@patternfly/react-core/dist/dynamic/components/Alert`) will
269268 * be treated as a separate shared module at runtime. This approach allows for more efficient
270269 * federation of vendor package code, as opposed to sharing the whole vendor package index
271- * (such as `@patternfly/react-core`) that pulls in all of its code.
270+ * (such as `@patternfly/react-core`) that would cause all of its code to be pulled into the
271+ * Console compilation and inflate the vendor bundle size.
272272 */
273273 sharedDynamicModuleSettings : Partial < {
274274 /**
275- * Paths to `node_modules` directories to search when parsing dynamic modules.
275+ * Packages that support dynamic modules for use with webpack module federation.
276+ *
277+ * Each vendor package listed here should include a `dist/dynamic` directory containing
278+ * `package.json` files representing parts of that package to be shared separately between
279+ * the Console application and its plugins at runtime.
280+ *
281+ * If not specified, use a default list of PatternFly packages that support dynamic modules.
282+ */
283+ packageSpecs : DynamicModulePackageSpecs ;
284+
285+ /**
286+ * Paths to `node_modules` directories to search when resolving dynamic modules.
276287 *
277288 * Paths listed here _must_ be absolute.
278289 *
@@ -284,33 +295,22 @@ export type ConsoleRemotePluginOptions = Partial<{
284295 modulePaths : string [ ] ;
285296
286297 /**
287- * Attempt to parse dynamic modules for these packages.
288- *
289- * Each package listed here should include a `dist/dynamic` directory containing `package.json`
290- * files that refer to specific modules of that package.
291- *
292- * If not specified, use packages listed in {@link dynamicModulePatternFlyPackages} with default
293- * settings.
294- */
295- packageSpecs : Record < string , DynamicModulePackageSpec > ;
296-
297- /**
298- * Import transformations will be applied to modules that match this filter.
298+ * Modules that match this filter will have their imports transformed.
299299 *
300300 * If not specified, the following conditions must be all true for a module to be matched:
301- * - request ends with one of `.js`, `.jsx`, `.ts`, `.tsx`
301+ * - request ends with `.js`, `.jsx`, `.ts` or `.tsx`
302302 * - request does not contain `node_modules` path elements (i.e. not a vendor module request),
303303 * _except_ for `@openshift-console/*` packages
304304 */
305- transformImports : ( moduleRequest : string ) => boolean ;
305+ moduleFilter : ( moduleRequest : string ) => boolean ;
306306 } > ;
307307} > ;
308308
309309/**
310310 * Generates Console dynamic plugin remote container and related assets.
311311 *
312- * Refer to `frontend/packages/ console-dynamic-plugin-sdk/src/shared-modules.ts` for details on
313- * Console application vs. dynamic plugins shared module configuration.
312+ * Refer to `console-dynamic-plugin-sdk/src/shared-modules.ts` for details on Console provided
313+ * shared modules and their configuration.
314314 *
315315 * @see {@link sharedPluginModules }
316316 * @see {@link getSharedModuleMetadata }
@@ -342,6 +342,8 @@ export class ConsoleRemotePlugin implements WebpackPluginInstance {
342342 validateConsoleProvidedSharedModules ( this . pkg ) . report ( ) ;
343343 }
344344
345+ validateConsoleBuildMetadata ( this . adaptedOptions . pluginMetadata ) . report ( ) ;
346+
345347 const overlapDependencyNames = _ . intersection (
346348 Object . keys ( this . adaptedOptions . pluginMetadata . dependencies ?? { } ) ,
347349 Object . keys ( this . adaptedOptions . pluginMetadata . optionalDependencies ?? { } ) ,
@@ -359,24 +361,10 @@ export class ConsoleRemotePlugin implements WebpackPluginInstance {
359361 path . resolve ( process . cwd ( ) , 'node_modules' ) ,
360362 ] ;
361363
362- const sharedDynamicModulePackageSpecs =
363- this . adaptedOptions . sharedDynamicModuleSettings . packageSpecs ??
364- dynamicModulePatternFlyPackages . reduce < Record < string , DynamicModulePackageSpec > > (
365- ( acc , moduleName ) => ( { ...acc , [ moduleName ] : { } } ) ,
366- { } ,
367- ) ;
368-
369- this . sharedDynamicModuleMaps = Object . entries ( sharedDynamicModulePackageSpecs ) . reduce <
370- Record < string , DynamicModuleMap >
371- > ( ( acc , [ pkgName , { indexModule = 'dist/esm/index.js' , resolutionField = 'module' } ] ) => {
372- const basePath = resolvedModulePaths
373- . map ( ( p ) => path . resolve ( p , pkgName ) )
374- . find ( ( p ) => fs . existsSync ( p ) && fs . statSync ( p ) . isDirectory ( ) ) ;
375-
376- return basePath
377- ? { ...acc , [ pkgName ] : getDynamicModuleMap ( basePath , indexModule , resolutionField ) }
378- : acc ;
379- } , { } ) ;
364+ this . sharedDynamicModuleMaps = resolveDynamicModuleMaps (
365+ this . adaptedOptions . sharedDynamicModuleSettings . packageSpecs ?? dynamicModulePackageSpecs ,
366+ resolvedModulePaths ,
367+ ) ;
380368 }
381369
382370 apply ( compiler : Compiler ) {
@@ -401,6 +389,8 @@ export class ConsoleRemotePlugin implements WebpackPluginInstance {
401389
402390 const logger = compiler . getInfrastructureLogger ( ConsoleRemotePlugin . name ) ;
403391
392+ // Dynamic plugin assets should be loaded from /api/plugins/<plugin-name> endpoint.
393+ // Console Bridge server will fetch the asset from the appropriate plugin web server.
404394 const publicPath = `/api/plugins/${ name } /` ;
405395
406396 if ( compiler . options . output . publicPath !== undefined ) {
@@ -454,10 +444,14 @@ export class ConsoleRemotePlugin implements WebpackPluginInstance {
454444 : 'plugin-entry.js' ,
455445 } ) . apply ( compiler ) ;
456446
457- validateConsoleBuildMetadata ( pluginMetadata ) . report ( ) ;
447+ new DynamicModuleImportPlugin ( {
448+ dynamicModuleMaps : this . sharedDynamicModuleMaps ,
449+ moduleFilter : sharedDynamicModuleSettings . moduleFilter ?? dynamicModuleImportTransformFilter ,
450+ } ) . apply ( compiler ) ;
458451
459- if ( validateExtensionIntegrity ) {
460- compiler . hooks . emit . tap ( ConsoleRemotePlugin . name , ( compilation ) => {
452+ // Post-build validations performed before emitting assets
453+ compiler . hooks . emit . tap ( ConsoleRemotePlugin . name , ( compilation ) => {
454+ if ( validateExtensionIntegrity ) {
461455 const result = new ExtensionValidator ( 'Console plugin extensions' ) . validate (
462456 compilation ,
463457 extensions ,
@@ -471,49 +465,11 @@ export class ConsoleRemotePlugin implements WebpackPluginInstance {
471465 error . file = extensionsFile ;
472466 compilation . errors . push ( error ) ;
473467 }
474- } ) ;
475- }
476-
477- const transformImports =
478- sharedDynamicModuleSettings . transformImports ??
479- ( ( moduleRequest ) => {
480- const isCode = / \. ( j s x ? | t s x ? ) $ / . test ( moduleRequest ) ;
481- const isVendor = moduleRequest . includes ( '/node_modules/' ) ;
482-
483- return isCode && ( ! isVendor || moduleRequest . includes ( '/node_modules/@openshift-console/' ) ) ;
484- } ) ;
468+ }
485469
486- compiler . hooks . thisCompilation . tap ( ConsoleRemotePlugin . name , ( compilation ) => {
487470 getDeprecatedSharedModuleWarnings ( this . pkg ) . forEach ( ( message ) => {
488471 compilation . warnings . push ( new compiler . webpack . WebpackError ( message ) ) ;
489472 } ) ;
490-
491- const modifiedModules : string [ ] = [ ] ;
492-
493- compiler . webpack . NormalModule . getCompilationHooks ( compilation ) . beforeLoaders . tap (
494- ConsoleRemotePlugin . name ,
495- ( loaders , normalModule ) => {
496- const { userRequest } = normalModule ;
497-
498- const moduleRequest = userRequest . substring (
499- userRequest . lastIndexOf ( '!' ) === - 1 ? 0 : userRequest . lastIndexOf ( '!' ) + 1 ,
500- ) ;
501-
502- if ( ! modifiedModules . includes ( moduleRequest ) && transformImports ( moduleRequest ) ) {
503- const loaderOptions : DynamicModuleImportLoaderOptions = {
504- dynamicModuleMaps : this . sharedDynamicModuleMaps ,
505- resourceMetadata : { jsx : / \. ( j s x | t s x ) $ / . test ( moduleRequest ) } ,
506- } ;
507-
508- normalModule . loaders . push ( {
509- loader : dynamicModuleImportLoader ,
510- options : loaderOptions ,
511- } as any ) ;
512-
513- modifiedModules . push ( moduleRequest ) ;
514- }
515- } ,
516- ) ;
517473 } ) ;
518474 }
519475}
0 commit comments