diff --git a/workspaces/homepage/.changeset/home-plugin-overrides.md b/workspaces/homepage/.changeset/home-plugin-overrides.md new file mode 100644 index 00000000000..7dcca99a79c --- /dev/null +++ b/workspaces/homepage/.changeset/home-plugin-overrides.md @@ -0,0 +1,5 @@ +--- +'@red-hat-developer-hub/backstage-plugin-homepage': minor +--- + +Export `homePagePlugin` (upstream `@backstage/plugin-home` with homepage layout and widgets via `withOverrides`) as the alpha default so Module Federation / dynamic NFS installs register `page:home` without a separate `@backstage/plugin-home` dependency. Keep `homePageModule` for hosts that already register `homePlugin`. diff --git a/workspaces/homepage/packages/app/knip-report.md b/workspaces/homepage/packages/app/knip-report.md index 4bb9eaf1b71..045762352c4 100644 --- a/workspaces/homepage/packages/app/knip-report.md +++ b/workspaces/homepage/packages/app/knip-report.md @@ -1,6 +1,6 @@ # Knip report -## Unused dependencies (17) +## Unused dependencies (16) | Name | Location | Severity | | :----------------------------------------------- | :---------------- | :------- | @@ -18,7 +18,6 @@ | @backstage/plugin-techdocs | package.json:43:6 | error | | @backstage/plugin-catalog | package.json:33:6 | error | | @backstage/plugin-signals | package.json:42:6 | error | -| @backstage/plugin-home | package.json:36:6 | error | | @backstage/plugin-org | package.json:39:6 | error | | react-router | package.json:53:6 | error | diff --git a/workspaces/homepage/packages/app/package.json b/workspaces/homepage/packages/app/package.json index 69dc1e28a56..d73fa8b3c0f 100644 --- a/workspaces/homepage/packages/app/package.json +++ b/workspaces/homepage/packages/app/package.json @@ -33,7 +33,6 @@ "@backstage/plugin-catalog": "^2.0.6", "@backstage/plugin-catalog-graph": "^0.6.5", "@backstage/plugin-catalog-import": "^0.13.14", - "@backstage/plugin-home": "^0.9.7", "@backstage/plugin-kubernetes": "^0.12.20", "@backstage/plugin-notifications": "^0.5.18", "@backstage/plugin-org": "^0.7.5", diff --git a/workspaces/homepage/packages/app/src/App.tsx b/workspaces/homepage/packages/app/src/App.tsx index 74a7e74af6b..0255ec242d0 100644 --- a/workspaces/homepage/packages/app/src/App.tsx +++ b/workspaces/homepage/packages/app/src/App.tsx @@ -18,7 +18,7 @@ import { createApp } from '@backstage/frontend-defaults'; import { navModule } from './modules/nav'; import { signInModule } from './modules/signIn'; import { - homePageModule, + homePagePlugin, homepageTranslationsModule, } from '@red-hat-developer-hub/backstage-plugin-homepage/alpha'; import { rhdhThemeModule } from '@red-hat-developer-hub/backstage-plugin-theme/alpha'; @@ -28,7 +28,7 @@ export default createApp({ rhdhThemeModule, navModule, signInModule, - homePageModule, + homePagePlugin, homepageTranslationsModule, ], }); diff --git a/workspaces/homepage/plugins/homepage/README.md b/workspaces/homepage/plugins/homepage/README.md index ca45cdfcc01..5ff7562edf7 100644 --- a/workspaces/homepage/plugins/homepage/README.md +++ b/workspaces/homepage/plugins/homepage/README.md @@ -12,19 +12,22 @@ If you're using Backstage's new frontend system, add the plugin to your app: // packages/app/src/App.tsx import { createApp } from '@backstage/frontend-defaults'; import { - homePageDevModule, + homePagePlugin, homepageTranslationsModule, } from '@red-hat-developer-hub/backstage-plugin-homepage/alpha'; export default createApp({ features: [ // ... other plugins (nav, signIn, etc.) - homePageDevModule, + // Embeds @backstage/plugin-home (page:home) + homepage widgets/layout + homePagePlugin, homepageTranslationsModule, ], }); ``` +Do **not** also register `@backstage/plugin-home` (or discover it via `app.packages`) when using `homePagePlugin` — that would load the `home` plugin twice. If your app already loads upstream `homePlugin`, use `homePageModule` instead of `homePagePlugin`. + The plugin will automatically provide: - A homepage at `/home` (or the path configured via `page:home`) @@ -68,18 +71,19 @@ app: # ... ``` -### Modules +### Modules / plugins -The following modules are available from the alpha export: +The following features are available from the alpha export: -| Module | Description | -| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -| `homePageDevModule` | Home page layout and widgets (Onboarding, Entity, Templates, Quick Access, Search, Recently Visited, Top Visited, etc.) | -| `homepageTranslationsModule` | i18n translations (en, de, es, fr, it, ja) | +| Export | Type | Description | +| ---------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------ | +| `homePagePlugin` (default) | `FrontendPlugin` | Upstream `@backstage/plugin-home` with custom layout and widgets (`withOverrides`). Use this for dynamic installs. | +| `homePageModule` | `FrontendModule` | Same extensions as a module — only if the host already registers `homePlugin`. | +| `homepageTranslationsModule` | `FrontendModule` | i18n translations (en, de, es, fr, it, ja) | ### Extensions -The `homePageDevModule` extends the `home` plugin (`@backstage/plugin-home`) with: +`homePagePlugin` / `homePageModule` extend the `home` plugin (`@backstage/plugin-home`) with: - `home-page-layout:home/dynamic-homepage-layout` – Custom layout with config-driven widget arrangement and priority - `home-page-widget:home/rhdh-onboarding-section` – Onboarding section diff --git a/workspaces/homepage/plugins/homepage/dev/index.tsx b/workspaces/homepage/plugins/homepage/dev/index.tsx index d3d0b48617b..0b8f9a55032 100644 --- a/workspaces/homepage/plugins/homepage/dev/index.tsx +++ b/workspaces/homepage/plugins/homepage/dev/index.tsx @@ -46,7 +46,7 @@ import { starredEntitiesApiRef, } from '@backstage/plugin-catalog-react'; import { - homePageModule, + homePagePlugin, homepageTranslationsModule, } from '@red-hat-developer-hub/backstage-plugin-homepage/alpha'; import { rhdhThemeModule } from '@red-hat-developer-hub/backstage-plugin-theme/alpha'; @@ -153,7 +153,7 @@ const app = createApp({ devNavModule, catalogPlugin, searchPlugin, - homePageModule, + homePagePlugin, homepageTranslationsModule, homepageApiMocksModule, catalogDevModule, diff --git a/workspaces/homepage/plugins/homepage/report-alpha.api.md b/workspaces/homepage/plugins/homepage/report-alpha.api.md index 8c2156fb36f..a4e76497f20 100644 --- a/workspaces/homepage/plugins/homepage/report-alpha.api.md +++ b/workspaces/homepage/plugins/homepage/report-alpha.api.md @@ -3,14 +3,192 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { AnyApiFactory } from '@backstage/frontend-plugin-api'; +import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; +import { ApiFactory } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; +import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionInput } from '@backstage/frontend-plugin-api'; import { FrontendModule } from '@backstage/frontend-plugin-api'; +import { HomePageLayoutProps } from '@backstage/plugin-home-react/alpha'; +import { HomePageWidgetBlueprintParams } from '@backstage/plugin-home-react/alpha'; +import { HomePageWidgetData } from '@backstage/plugin-home-react/alpha'; +import { IconElement } from '@backstage/frontend-plugin-api'; +import { JSX as JSX_2 } from 'react'; +import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api'; +import { RouteRef } from '@backstage/frontend-plugin-api'; import { TranslationRef } from '@backstage/frontend-plugin-api'; import { TranslationResource } from '@backstage/frontend-plugin-api'; // @alpha -const homePageModule: FrontendModule; -export default homePageModule; -export { homePageModule }; +export const homePageModule: FrontendModule; + +// @alpha +const homePagePlugin: OverridableFrontendPlugin< + { + root: RouteRef; + }, + {}, + { + 'api:home/visits': OverridableExtensionDefinition<{ + kind: 'api'; + name: 'visits'; + config: {}; + configInput: {}; + output: ExtensionDataRef; + inputs: {}; + params: < + TApi, + TImpl extends TApi, + TDeps extends { [name in string]: unknown }, + >( + params: ApiFactory, + ) => ExtensionBlueprintParams; + }>; + 'app-root-element:home/visit-listener': OverridableExtensionDefinition<{ + kind: 'app-root-element'; + name: 'visit-listener'; + config: {}; + configInput: {}; + output: ExtensionDataRef; + inputs: {}; + params: { + element: JSX.Element; + }; + }>; + 'home-page-widget:home/random-joke': OverridableExtensionDefinition<{ + kind: 'home-page-widget'; + name: 'random-joke'; + config: {}; + configInput: {}; + output: ExtensionDataRef; + inputs: {}; + params: HomePageWidgetBlueprintParams; + }>; + 'home-page-widget:home/starred-entities': OverridableExtensionDefinition<{ + kind: 'home-page-widget'; + name: 'starred-entities'; + config: {}; + configInput: {}; + output: ExtensionDataRef; + inputs: {}; + params: HomePageWidgetBlueprintParams; + }>; + 'home-page-widget:home/toolkit': OverridableExtensionDefinition<{ + kind: 'home-page-widget'; + name: 'toolkit'; + config: {}; + configInput: {}; + output: ExtensionDataRef; + inputs: {}; + params: HomePageWidgetBlueprintParams; + }>; + 'page:home': OverridableExtensionDefinition<{ + config: { + path: string | undefined; + title: string | undefined; + }; + configInput: { + path?: string | undefined; + title?: string | undefined; + }; + output: + | ExtensionDataRef + | ExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ExtensionDataRef + | ExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ExtensionDataRef< + IconElement, + 'core.icon', + { + optional: true; + } + >; + inputs: { + pages: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + string, + 'core.title', + { + optional: true; + } + > + | ConfigurableExtensionDataRef< + IconElement, + 'core.icon', + { + optional: true; + } + >, + { + singleton: false; + optional: false; + internal: false; + } + >; + widgets: ExtensionInput< + ConfigurableExtensionDataRef< + HomePageWidgetData, + 'home.widget.data', + {} + >, + { + singleton: false; + optional: false; + internal: false; + } + >; + layout: ExtensionInput< + ConfigurableExtensionDataRef< + (props: HomePageLayoutProps) => JSX_2.Element, + 'home.layout.component', + {} + >, + { + singleton: true; + optional: true; + internal: true; + } + >; + }; + kind: 'page'; + name: undefined; + params: { + path: string; + title?: string; + icon?: IconElement; + loader?: () => Promise; + routeRef?: RouteRef; + noHeader?: boolean; + }; + }>; + } +>; +export default homePagePlugin; +export { homePagePlugin }; // @public export const homepageTranslationRef: TranslationRef< diff --git a/workspaces/homepage/plugins/homepage/src/alpha/alpha.test.ts b/workspaces/homepage/plugins/homepage/src/alpha/alpha.test.ts index 555977613d2..e5d41d4289f 100644 --- a/workspaces/homepage/plugins/homepage/src/alpha/alpha.test.ts +++ b/workspaces/homepage/plugins/homepage/src/alpha/alpha.test.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -import { homepageTranslationsModule, homePageModule } from '.'; +import homepageAlpha, { + homepageTranslationsModule, + homePageModule, + homePagePlugin, +} from '.'; import { homepageTranslationRef, homepageTranslations } from '../translations'; import { homePageLayoutExtension } from './extensions/homePageLayoutExtension'; import { @@ -34,7 +38,14 @@ import { quickAccessApi } from './extensions/apis'; describe('Dynamic Home Page plugin Alpha (NFS)', () => { describe('Modules', () => { - it('should export homePageDevModule with correct structure', () => { + it('should export homePagePlugin with correct structure', () => { + expect(homePagePlugin).toBeDefined(); + expect(homePagePlugin.$$type).toBe('@backstage/FrontendPlugin'); + expect(homePagePlugin.id).toBe('home'); + expect(homepageAlpha).toBe(homePagePlugin); + }); + + it('should export homePageModule with correct structure', () => { expect(homePageModule).toBeDefined(); expect(homePageModule.$$type).toBe('@backstage/FrontendModule'); expect(homePageModule.pluginId).toBe('home'); diff --git a/workspaces/homepage/plugins/homepage/src/alpha/index.ts b/workspaces/homepage/plugins/homepage/src/alpha/index.ts index ce707c341a4..85287866a2f 100644 --- a/workspaces/homepage/plugins/homepage/src/alpha/index.ts +++ b/workspaces/homepage/plugins/homepage/src/alpha/index.ts @@ -16,6 +16,7 @@ import { TranslationBlueprint } from '@backstage/plugin-app-react'; import { createFrontendModule } from '@backstage/frontend-plugin-api'; +import homePlugin from '@backstage/plugin-home/alpha'; import { catalogStarredWidget, disableRandomJoke, @@ -34,33 +35,58 @@ import { homepageTranslations } from '../translations'; import { homePageLayoutExtension } from './extensions/homePageLayoutExtension'; import { defaultWidgetsApi, quickAccessApi } from './extensions/apis'; +/** + * Homepage extensions layered onto `@backstage/plugin-home`. + * + * Shared by {@link homePagePlugin} (preferred for dynamic installs) + * and {@link homePageModule} (for apps that already register `homePlugin`). + */ +const homePageExtensions = [ + homePageLayoutExtension, + onboardingSectionWidget, + entitySectionWidget, + templateSectionWidget, + defaultWidgetsApi, + quickAccessApi, + quickAccessCardWidget, + featuredDocsCardWidget, + searchBarWidget, + TopVisitedWidget, + RecentlyVisitedWidget, + catalogStarredWidget, + disableToolkit, + disableRandomJoke, +]; + +/** + * Upstream home plugin with custom layout and widgets applied. + * + * Use this as the single NFS feature when the host app does not already + * register `@backstage/plugin-home` (e.g. dynamic plugin installs). + * The Module Federation alpha default export is this plugin so + * `dynamicFrontendFeaturesLoader` registers `page:home`. + * + * @alpha + */ +export const homePagePlugin = homePlugin.withOverrides({ + extensions: homePageExtensions, +}); + /** * Frontend module for the Dynamic Home Page plugin (New Frontend System). * - * Extends the `home` plugin with a custom layout and RHDH widgets: Onboarding, + * Extends the `home` plugin with a custom layout and widgets: Onboarding, * Entity Catalog, Templates, Quick Access, Search, Recently Visited, Top Visited, etc. - * Add to your app's `createApp({ features: [..., homePageDevModule] })`. + * + * Prefer {@link homePagePlugin} when the host does not already load + * `@backstage/plugin-home`. Keep using this module only if `homePlugin` is + * already in `createApp({ features })` or discovered via `app.packages`. * * @alpha */ export const homePageModule = createFrontendModule({ pluginId: 'home', // upstream home! - extensions: [ - homePageLayoutExtension, - onboardingSectionWidget, - entitySectionWidget, - templateSectionWidget, - defaultWidgetsApi, - quickAccessApi, - quickAccessCardWidget, - featuredDocsCardWidget, - searchBarWidget, - TopVisitedWidget, - RecentlyVisitedWidget, - catalogStarredWidget, - disableToolkit, - disableRandomJoke, - ], + extensions: homePageExtensions, }); /** @@ -88,7 +114,9 @@ export { homepageTranslationRef, homepageTranslations } from '../translations'; /** * Default export required for Module Federation to emit the `alpha` NFS expose. + * Must be a FrontendPlugin so that the Backstage instance registers `page:home` without a + * separate `@backstage/plugin-home` install. * * @alpha */ -export default homePageModule; +export default homePagePlugin; diff --git a/workspaces/homepage/yarn.lock b/workspaces/homepage/yarn.lock index b809426efeb..f411f5f6226 100644 --- a/workspaces/homepage/yarn.lock +++ b/workspaces/homepage/yarn.lock @@ -14661,7 +14661,6 @@ __metadata: "@backstage/plugin-catalog": "npm:^2.0.6" "@backstage/plugin-catalog-graph": "npm:^0.6.5" "@backstage/plugin-catalog-import": "npm:^0.13.14" - "@backstage/plugin-home": "npm:^0.9.7" "@backstage/plugin-kubernetes": "npm:^0.12.20" "@backstage/plugin-notifications": "npm:^0.5.18" "@backstage/plugin-org": "npm:^0.7.5"