-
Notifications
You must be signed in to change notification settings - Fork 747
Ensure that @patternfly/patternfly CSS files are excluded when building Console plugins #17029
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,9 +25,9 @@ import { ValidationResult } from '../validation/ValidationResult'; | |
| import type { DynamicModulePackageSpecs } from './DynamicModuleImportPlugin'; | ||
| import { DynamicModuleImportPlugin, resolveDynamicModuleMaps } from './DynamicModuleImportPlugin'; | ||
|
|
||
| const loadPluginPackageJSON = () => readPkg.sync({ normalize: false }) as ConsolePluginPackageJSON; | ||
|
|
||
| // Resolve from cwd, not this file's real path, so symlinked SDK installations work | ||
| // Resolve from process.cwd(), not this file's real path, so symlinked SDK installations work. | ||
| // It should not be necessary to customize the process.cwd() resolution base path since module | ||
| // bundlers typically use a hoisted top-level node_modules hierarchy. | ||
|
Comment on lines
+28
to
+30
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Resolve plugin dependency packages from Lines 28-30 keep vendor package lookup rooted at This can use a different installed package version than the plugin uses. It causes incorrect shared-module validation and incorrect Module Federation version metadata in multi-plugin builds. Pass Proposed direction-const getVendorPackageVersion = (moduleName: string) => {
+const getVendorPackageVersion = (moduleName: string, baseDir = process.cwd()) => {
try {
- return loadVendorPackageJSON(moduleName).version;
+ return loadVendorPackageJSON(moduleName, baseDir).version;
} catch (e) {
return undefined;
}
};Propagate Also applies to: 364-385 🤖 Prompt for AI Agents |
||
| const loadVendorPackageJSON = (moduleName: string) => | ||
| // eslint-disable-next-line @typescript-eslint/no-require-imports | ||
| require( | ||
|
|
@@ -55,8 +55,12 @@ const hasPackageDependency = (pkg: readPkg.PackageJson, depName: string) => | |
| const getPluginSDKPackagePeerDependencies = () => | ||
| loadVendorPackageJSON('@openshift-console/dynamic-plugin-sdk').peerDependencies; | ||
|
|
||
| const getPatternFlyStyles = (baseDir: string) => | ||
| glob.sync(`${baseDir}/node_modules/@patternfly/react-styles/**/*.css`); | ||
| const patternFlyStylePackages = ['@patternfly/patternfly', '@patternfly/react-styles']; | ||
|
|
||
| const getPatternFlyCSSFiles = (baseDir: string): string[] => | ||
| patternFlyStylePackages | ||
| .map((moduleName) => glob.sync(`${baseDir}/node_modules/${moduleName}/**/*.css`)) | ||
| .flat(); | ||
|
|
||
| /** | ||
| * Get webpack shared module configuration to use by Console plugins. | ||
|
|
@@ -140,7 +144,7 @@ export const validateConsoleExtensionsFileSchema = ( | |
| return new SchemaValidator(description).validate(schema, extensions); | ||
| }; | ||
|
|
||
| const getCompileTimeSharedModuleWarnings = (pkg: ConsolePluginPackageJSON): string[] => { | ||
| const getCompileTimeModuleWarnings = (pkg: ConsolePluginPackageJSON): string[] => { | ||
| const warnings: string[] = []; | ||
|
|
||
| sharedPluginModules.forEach((moduleName) => { | ||
|
|
@@ -155,6 +159,14 @@ const getCompileTimeSharedModuleWarnings = (pkg: ConsolePluginPackageJSON): stri | |
| } | ||
| }); | ||
|
|
||
| patternFlyStylePackages.forEach((moduleName) => { | ||
| if (hasPackageDependency(pkg, moduleName)) { | ||
| warnings.push( | ||
| `[WARNING] Detected direct dependency on ${moduleName}, its modules are ignored to avoid breaking Console provided PatternFly CSS`, | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
|
Comment on lines
+162
to
+169
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
file='frontend/packages/console-dynamic-plugin-sdk/src/webpack/ConsoleRemotePlugin.ts'
printf '%s\n' '--- candidate structure ---'
ast-grep outline "$file"
printf '%s\n' '--- dependency helpers and PatternFly references ---'
rg -n -C 5 'getPackageDependencies|hasPackageDependency|patternFlyStylePackages|optionalDependencies|dependencies|devDependencies' "$file"
printf '%s\n' '--- relevant implementation ranges ---'
sed -n '1,90p' "$file"
sed -n '135,180p' "$file"
sed -n '500,545p' "$file"
printf '%s\n' '--- repository-wide helper definition and call sites ---'
rg -n -C 4 'function getPackageDependencies|const getPackageDependencies|getPackageDependencies\(|function hasPackageDependency|const hasPackageDependency|hasPackageDependency\(' frontend/packages/console-dynamic-plugin-sdkRepository: openshift/console Length of output: 21230 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- package type and call context ---'
rg -n -C 6 'export (type|interface) ConsolePluginPackageJSON|ConsolePluginPackageJSON|optionalDependencies' \
frontend/packages/console-dynamic-plugin-sdk/src frontend/packages/console-dynamic-plugin-sdk/test frontend/packages/console-dynamic-plugin-sdk/tests 2>/dev/null || true
printf '%s\n' '--- PatternFly CSS discovery and warning tests ---'
rg -n -C 8 'PatternFly|patternFly|compile-time|compileTime|Detected direct dependency|optionalDependencies' \
frontend/packages/console-dynamic-plugin-sdk --glob '*.{ts,tsx,js,json}'
printf '%s\n' '--- deterministic dependency-shape probe ---'
python3 - <<'PY'
# Read-only equivalent of getPackageDependencies and hasPackageDependency.
def get_package_dependencies(pkg):
merged = {}
merged.update(pkg.get("devDependencies") or {})
merged.update(pkg.get("dependencies") or {})
return merged
def has_package_dependency(pkg, name):
return name in get_package_dependencies(pkg)
cases = [
("dependencies", {"dependencies": {"`@patternfly/patternfly`": "^5.0.0"}}),
("optionalDependencies", {"optionalDependencies": {"`@patternfly/patternfly`": "^5.0.0"}}),
("both absent", {}),
]
for label, pkg in cases:
print(f"{label}: {has_package_dependency(pkg, '`@patternfly/patternfly`')}")
PYRepository: openshift/console Length of output: 50375 Include optional dependencies in the PatternFly warning.
🤖 Prompt for AI Agents |
||
| return warnings; | ||
| }; | ||
|
|
||
|
|
@@ -215,6 +227,15 @@ export const dynamicModuleImportTransformFilter = (moduleRequest: string) => { | |
| }; | ||
|
|
||
| export type ConsoleRemotePluginOptions = Partial<{ | ||
| /** | ||
| * Base directory for resolving relative paths when processing plugin assets. | ||
| * | ||
| * Must be an absolute path. | ||
| * | ||
| * If not specified, `process.cwd()` will be used as the base directory. | ||
| */ | ||
| baseDir: string; | ||
|
|
||
| /** | ||
| * Console dynamic plugin metadata. | ||
| * | ||
|
|
@@ -316,7 +337,7 @@ export type ConsoleRemotePluginOptions = Partial<{ | |
| * | ||
| * If not specified, the list will contain a single entry: | ||
| * ```ts | ||
| * path.resolve(process.cwd(), 'node_modules') | ||
| * path.resolve(baseDir, 'node_modules') | ||
| * ``` | ||
| */ | ||
| modulePaths: string[]; | ||
|
|
@@ -352,25 +373,28 @@ export type ConsoleRemotePluginOptions = Partial<{ | |
| /** | ||
| * Generates Console dynamic plugin remote container and related assets. | ||
| * | ||
| * Refer to `console-dynamic-plugin-sdk/src/shared-modules.ts` for details on Console provided | ||
| * shared modules and their configuration. | ||
| * | ||
| * @see {@link sharedPluginModules} | ||
| * @see {@link getSharedModuleMetadata} | ||
| * Refer to {@link sharedPluginModules} for details on Console provided shared modules and their configuration. | ||
| */ | ||
| export class ConsoleRemotePlugin implements WebpackPluginInstance { | ||
| private readonly adaptedOptions: Required<ConsoleRemotePluginOptions>; | ||
|
|
||
| private readonly baseDir = process.cwd(); | ||
|
|
||
| private readonly pkg = loadPluginPackageJSON(); | ||
| private readonly pkg: ConsolePluginPackageJSON; | ||
|
|
||
| private readonly dynamicModuleMaps: Record<string, DynamicModuleMap>; | ||
|
|
||
| constructor(options: ConsoleRemotePluginOptions = {}) { | ||
| const baseDir = options.baseDir ?? process.cwd(); | ||
|
|
||
| if (!path.isAbsolute(baseDir)) { | ||
| throw new Error(`baseDir must be an absolute path: ${baseDir}`); | ||
| } | ||
|
Comment on lines
+374
to
+378
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win Canonicalize and reject traversal segments in An absolute value such as As per path instructions, “Path traversal: canonicalize paths, reject ../.” 🤖 Prompt for AI AgentsSource: Path instructions |
||
|
|
||
| this.pkg = readPkg.sync({ cwd: baseDir, normalize: false }); | ||
|
|
||
| this.adaptedOptions = { | ||
| baseDir, | ||
| pluginMetadata: options.pluginMetadata ?? this.pkg.consolePlugin, | ||
| extensions: options.extensions ?? parseJSONC(path.resolve(this.baseDir, extensionsFile)), | ||
| extensions: options.extensions ?? parseJSONC(path.resolve(baseDir, extensionsFile)), | ||
| validateExtensionSchema: options.validateExtensionSchema ?? true, | ||
| validateExtensionIntegrity: options.validateExtensionIntegrity ?? true, | ||
| validateSharedModules: options.validateSharedModules ?? true, | ||
|
|
@@ -401,7 +425,7 @@ export class ConsoleRemotePlugin implements WebpackPluginInstance { | |
| } | ||
|
|
||
| const resolvedModulePaths = this.adaptedOptions.sharedDynamicModuleSettings.modulePaths ?? [ | ||
| path.resolve(process.cwd(), 'node_modules'), | ||
| path.resolve(baseDir, 'node_modules'), | ||
| ]; | ||
|
|
||
| this.dynamicModuleMaps = resolveDynamicModuleMaps( | ||
|
|
@@ -412,8 +436,13 @@ export class ConsoleRemotePlugin implements WebpackPluginInstance { | |
| } | ||
|
|
||
| apply(compiler: Compiler) { | ||
| const { pluginMetadata, extensions, validateExtensionIntegrity, sharedDynamicModuleSettings } = | ||
| this.adaptedOptions; | ||
| const { | ||
| baseDir, | ||
| pluginMetadata, | ||
| extensions, | ||
| validateExtensionIntegrity, | ||
| sharedDynamicModuleSettings, | ||
| } = this.adaptedOptions; | ||
|
|
||
| const { | ||
| name, | ||
|
|
@@ -441,8 +470,9 @@ export class ConsoleRemotePlugin implements WebpackPluginInstance { | |
| compiler.options.resolve = compiler.options.resolve ?? {}; | ||
| compiler.options.resolve.alias = compiler.options.resolve.alias ?? {}; | ||
|
|
||
| // Prevent PatternFly styles from being included in the compilation | ||
| getPatternFlyStyles(this.baseDir).forEach((cssFile) => { | ||
| // Prevent PatternFly CSS files from being included in the webpack compilation. | ||
| // Console is responsible for loading all supported PatternFly CSS at runtime. | ||
| getPatternFlyCSSFiles(baseDir).forEach((cssFile) => { | ||
| if (Array.isArray(compiler.options.resolve.alias)) { | ||
| compiler.options.resolve.alias.push({ name: cssFile, alias: false }); | ||
| } else { | ||
|
|
@@ -487,7 +517,7 @@ export class ConsoleRemotePlugin implements WebpackPluginInstance { | |
| compilation, | ||
| extensions, | ||
| exposedModules ?? {}, | ||
| path.dirname(path.resolve(this.baseDir, extensionsFile)), | ||
| path.dirname(path.resolve(baseDir, extensionsFile)), | ||
| ); | ||
|
|
||
| if (result.hasErrors()) { | ||
|
|
@@ -498,7 +528,7 @@ export class ConsoleRemotePlugin implements WebpackPluginInstance { | |
| } | ||
| } | ||
|
|
||
| getCompileTimeSharedModuleWarnings(this.pkg).forEach((message) => { | ||
| getCompileTimeModuleWarnings(this.pkg).forEach((message) => { | ||
| compilation.warnings.push(new compiler.webpack.WebpackError(message)); | ||
| }); | ||
| }); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong release, should be 5.1.0-prerelease.1