diff --git a/packages/theme/src/unified/UnifiedTheme.tsx b/packages/theme/src/unified/UnifiedTheme.tsx index bb31d88016..28da80424b 100644 --- a/packages/theme/src/unified/UnifiedTheme.tsx +++ b/packages/theme/src/unified/UnifiedTheme.tsx @@ -14,7 +14,6 @@ * limitations under the License. */ -import { createTheme as createV4Theme } from '@material-ui/core/styles'; import type { Theme as Mui4Theme, ThemeOptions as ThemeOptionsV4, @@ -32,6 +31,7 @@ import { PageTheme } from '../base/types'; import { defaultComponentThemes } from '../v5'; import { createBaseThemeOptions } from '../base/createBaseThemeOptions'; import { UnifiedTheme } from './types'; +import { maybeLoadMui4Styles } from '../v4/load'; export class UnifiedThemeHolder implements UnifiedTheme { #themes = new Map(); @@ -74,9 +74,14 @@ export function createUnifiedTheme(options: UnifiedThemeOptions): UnifiedTheme { const themeOptions = createBaseThemeOptions(options); const components = { ...defaultComponentThemes, ...options.components }; const v5Theme = createV5Theme({ ...themeOptions, components }); - const v4Overrides = transformV5ComponentThemesToV4(v5Theme, components); - const v4Theme = { ...createV4Theme(themeOptions), ...v4Overrides }; + const mui4Styles = maybeLoadMui4Styles(); + if (!mui4Styles) { + return new UnifiedThemeHolder(undefined, v5Theme); + } + + const v4Overrides = transformV5ComponentThemesToV4(v5Theme, components); + const v4Theme = { ...mui4Styles.createTheme(themeOptions), ...v4Overrides }; return new UnifiedThemeHolder(v4Theme, v5Theme); } @@ -89,8 +94,13 @@ export function createUnifiedTheme(options: UnifiedThemeOptions): UnifiedTheme { export function createUnifiedThemeFromV4( options: ThemeOptionsV4, ): UnifiedTheme { - const v4Theme = createV4Theme(options); const v5Theme = adaptV4Theme(options as any); + const mui4Styles = maybeLoadMui4Styles(); + if (!mui4Styles) { + return new UnifiedThemeHolder(undefined, v5Theme); + } + + const v4Theme = mui4Styles.createTheme(options); return new UnifiedThemeHolder(v4Theme, v5Theme); } diff --git a/packages/theme/src/unified/UnifiedThemeProvider.tsx b/packages/theme/src/unified/UnifiedThemeProvider.tsx index 60f28bc353..d28dfab292 100644 --- a/packages/theme/src/unified/UnifiedThemeProvider.tsx +++ b/packages/theme/src/unified/UnifiedThemeProvider.tsx @@ -15,10 +15,6 @@ */ import React, { ReactNode } from 'react'; -import { - StylesProvider as Mui4StylesProvider, - ThemeProvider as Mui4Provider, -} from '@material-ui/core/styles'; import type { Theme as Mui4Theme } from '@material-ui/core/styles'; import { StyledEngineProvider, @@ -29,9 +25,9 @@ import { StylesProvider as Mui5StylesProvider, createGenerateClassName, } from '@mui/styles'; -import Mui4CssBaseline from '@material-ui/core/CssBaseline'; import Mui5CssBaseline from '@mui/material/CssBaseline'; import { UnifiedTheme } from './types'; +import { maybeLoadMui4CssBaseline, maybeLoadMui4Styles } from '../v4/load'; const generateV4ClassName = createGenerateClassName({ seed: 'm4', @@ -69,7 +65,11 @@ export function UnifiedThemeProvider( if (v5Theme) { cssBaseline = ; } else if (v4Theme) { - cssBaseline = ; + const CssBaseline = maybeLoadMui4CssBaseline(); + if (!CssBaseline) { + throw new Error('Failed to load MUI 4 CssBaseline component'); + } + cssBaseline = ; } } @@ -81,10 +81,15 @@ export function UnifiedThemeProvider( ); if (v4Theme) { + const styles = maybeLoadMui4Styles(); + if (!styles) { + throw new Error('Failed to load MUI 4 styles package'); + } + const { StylesProvider, ThemeProvider } = styles; result = ( - - {result} - + + {result} + ); } diff --git a/packages/theme/src/v4/load.ts b/packages/theme/src/v4/load.ts new file mode 100644 index 0000000000..899aa8af93 --- /dev/null +++ b/packages/theme/src/v4/load.ts @@ -0,0 +1,74 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Loads MUI v4 styles if it is available. + * + * This should always be used to access MUI v4 at runtime in this + * package, to ensure that it's not brought into the bundle unnecessarily. + * + * @internal + */ +export function maybeLoadMui4Styles(): + | typeof import('@material-ui/core/styles') + | undefined { + if ( + __webpack_modules__[ + require.resolveWeak('@material-ui/core/styles') as number + ] + ) { + return __webpack_modules__[ + require.resolveWeak('@material-ui/core/styles') as number + ] as typeof import('@material-ui/core/styles'); + } + if (__webpack_modules__[require.resolveWeak('@material-ui/core') as number]) { + return __webpack_modules__[ + require.resolveWeak('@material-ui/core') as number + ] as typeof import('@material-ui/core'); + } + + return undefined; +} + +/** + * Loads the MUI v4 CssBaseline component if it is available. + * + * This should always be used to access MUI v4 at runtime in this + * package, to ensure that it's not brought into the bundle unnecessarily. + * + * @internal + */ +export function maybeLoadMui4CssBaseline(): + | typeof import('@material-ui/core/CssBaseline')['default'] + | undefined { + if ( + __webpack_modules__[ + require.resolveWeak('@material-ui/core/CssBaseline') as number + ] + ) { + const m = __webpack_modules__[ + require.resolveWeak('@material-ui/core/CssBaseline') as number + ] as typeof import('@material-ui/core/CssBaseline'); + return m.default; + } + if (__webpack_modules__[require.resolveWeak('@material-ui/core') as number]) { + const m = __webpack_modules__[ + require.resolveWeak('@material-ui/core') as number + ] as typeof import('@material-ui/core'); + return m.CssBaseline; + } + return undefined; +}