frontend-plugin-api: add OverridableFrontendPlugin type

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2025-08-15 20:27:17 +02:00
parent a1127c8c93
commit 12b6db78eb
23 changed files with 103 additions and 67 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': minor
---
**BREAKING**: Added a new `OverridableFrontendPlugin` type that is used as the return value of `createFrontendPlugin`. This type includes the `withOverrides` and `.getExtension` methods that are helpful when creating plugin overrides, while the base `FrontendPlugin` type no longer includes these methods. This is a breaking change for the `AppTreeApi` and some other places where the `FrontendPlugin` type is still used, but also fixes some cases where the extra plugin methods were causing issues.
@@ -6,13 +6,13 @@
import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { JSX as JSX_3 } from 'react/jsx-runtime';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/frontend-plugin-api';
// @public (undocumented)
const examplePlugin: FrontendPlugin<
const examplePlugin: OverridableFrontendPlugin<
{},
{},
{
@@ -17,13 +17,13 @@
import {
Extension,
FeatureFlagConfig,
FrontendPlugin,
OverridableFrontendPlugin,
} from '@backstage/frontend-plugin-api';
import { JsonObject } from '@backstage/types';
import { OpaqueType } from '@internal/opaque';
export const OpaqueFrontendPlugin = OpaqueType.create<{
public: FrontendPlugin;
public: OverridableFrontendPlugin;
versions: {
readonly version: 'v1';
readonly extensions: Extension<unknown>[];
+32 -17
View File
@@ -719,7 +719,7 @@ export function createFrontendPlugin<
TExtensions extends readonly ExtensionDefinition[] = [],
>(
options: PluginOptions<TId, TRoutes, TExternalRoutes, TExtensions>,
): FrontendPlugin<
): OverridableFrontendPlugin<
TRoutes,
TExternalRoutes,
MakeSortedExtensionsMap<TExtensions[number], TId>
@@ -880,7 +880,7 @@ export interface ExtensionBlueprint<
// (undocumented)
make<
TName extends string | undefined,
TParamsInput extends AnyParamsInput_2<NonNullable<T['params']>>,
TParamsInput extends AnyParamsInput<NonNullable<T['params']>>,
>(args: {
name?: TName;
attachTo?: ExtensionAttachToSpec;
@@ -932,7 +932,7 @@ export interface ExtensionBlueprint<
};
factory(
originalFactory: <
TParamsInput extends AnyParamsInput_2<NonNullable<T['params']>>,
TParamsInput extends AnyParamsInput<NonNullable<T['params']>>,
>(
params: TParamsInput extends ExtensionBlueprintDefineParams
? TParamsInput
@@ -1122,7 +1122,7 @@ export type ExtensionDefinition<
}
>;
},
TParamsInput extends AnyParamsInput<NonNullable<T['params']>>,
TParamsInput extends AnyParamsInput_2<NonNullable<T['params']>>,
>(
args: Expand<
{
@@ -1141,7 +1141,7 @@ export type ExtensionDefinition<
};
factory?(
originalFactory: <
TFactoryParamsReturn extends AnyParamsInput<
TFactoryParamsReturn extends AnyParamsInput_2<
NonNullable<T['params']>
>,
>(
@@ -1328,28 +1328,16 @@ export interface FrontendPlugin<
} = {
[name in string]: ExternalRouteRef;
},
TExtensionMap extends {
[id in string]: ExtensionDefinition;
} = {
[id in string]: ExtensionDefinition;
},
> {
// (undocumented)
readonly $$type: '@backstage/FrontendPlugin';
// (undocumented)
readonly externalRoutes: TExternalRoutes;
// (undocumented)
getExtension<TId extends keyof TExtensionMap>(id: TId): TExtensionMap[TId];
// (undocumented)
readonly id: string;
info(): Promise<FrontendPluginInfo>;
// (undocumented)
readonly routes: TRoutes;
// (undocumented)
withOverrides(options: {
extensions: Array<ExtensionDefinition>;
info?: FrontendPluginInfoOptions;
}): FrontendPlugin<TRoutes, TExternalRoutes, TExtensionMap>;
}
// @public
@@ -1530,6 +1518,33 @@ export { oneloginAuthApiRef };
export { OpenIdConnectApi };
// @public
export interface OverridableFrontendPlugin<
TRoutes extends {
[name in string]: RouteRef | SubRouteRef;
} = {
[name in string]: RouteRef | SubRouteRef;
},
TExternalRoutes extends {
[name in string]: ExternalRouteRef;
} = {
[name in string]: ExternalRouteRef;
},
TExtensionMap extends {
[id in string]: ExtensionDefinition;
} = {
[id in string]: ExtensionDefinition;
},
> extends FrontendPlugin<TRoutes, TExternalRoutes> {
// (undocumented)
getExtension<TId extends keyof TExtensionMap>(id: TId): TExtensionMap[TId];
// (undocumented)
withOverrides(options: {
extensions: Array<ExtensionDefinition>;
info?: FrontendPluginInfoOptions;
}): OverridableFrontendPlugin<TRoutes, TExternalRoutes, TExtensionMap>;
}
// @public
export const PageBlueprint: ExtensionBlueprint<{
kind: 'page';
@@ -88,8 +88,12 @@ export type FrontendPluginInfoOptions = {
manifest?: () => Promise<JsonObject>;
};
/** @public */
export interface FrontendPlugin<
/**
* A variant of the {@link FrontendPlugin} interface that can also be used to install overrides for the plugin.
*
* @public
*/
export interface OverridableFrontendPlugin<
TRoutes extends { [name in string]: RouteRef | SubRouteRef } = {
[name in string]: RouteRef | SubRouteRef;
},
@@ -99,6 +103,26 @@ export interface FrontendPlugin<
TExtensionMap extends { [id in string]: ExtensionDefinition } = {
[id in string]: ExtensionDefinition;
},
> extends FrontendPlugin<TRoutes, TExternalRoutes> {
getExtension<TId extends keyof TExtensionMap>(id: TId): TExtensionMap[TId];
withOverrides(options: {
extensions: Array<ExtensionDefinition>;
/**
* Overrides the original info loaders of the plugin one by one.
*/
info?: FrontendPluginInfoOptions;
}): OverridableFrontendPlugin<TRoutes, TExternalRoutes, TExtensionMap>;
}
/** @public */
export interface FrontendPlugin<
TRoutes extends { [name in string]: RouteRef | SubRouteRef } = {
[name in string]: RouteRef | SubRouteRef;
},
TExternalRoutes extends { [name in string]: ExternalRouteRef } = {
[name in string]: ExternalRouteRef;
},
> {
readonly $$type: '@backstage/FrontendPlugin';
readonly id: string;
@@ -109,15 +133,6 @@ export interface FrontendPlugin<
* Loads the plugin info.
*/
info(): Promise<FrontendPluginInfo>;
getExtension<TId extends keyof TExtensionMap>(id: TId): TExtensionMap[TId];
withOverrides(options: {
extensions: Array<ExtensionDefinition>;
/**
* Overrides the original info loaders of the plugin one by one.
*/
info?: FrontendPluginInfoOptions;
}): FrontendPlugin<TRoutes, TExternalRoutes, TExtensionMap>;
}
/** @public */
@@ -172,7 +187,7 @@ export function createFrontendPlugin<
TExtensions extends readonly ExtensionDefinition[] = [],
>(
options: PluginOptions<TId, TRoutes, TExternalRoutes, TExtensions>,
): FrontendPlugin<
): OverridableFrontendPlugin<
TRoutes,
TExternalRoutes,
MakeSortedExtensionsMap<TExtensions[number], TId>
@@ -39,6 +39,7 @@ export {
export {
createFrontendPlugin,
type FrontendPlugin,
type OverridableFrontendPlugin,
type PluginOptions,
type FrontendPluginInfo,
type FrontendPluginInfoOptions,
+2 -2
View File
@@ -15,9 +15,9 @@ import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { ExternalRouteRef } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/frontend-plugin-api';
import { TranslationRef } from '@backstage/frontend-plugin-api';
@@ -55,7 +55,7 @@ export const apiDocsTranslationRef: TranslationRef<
>;
// @public (undocumented)
const _default: FrontendPlugin<
const _default: OverridableFrontendPlugin<
{
root: RouteRef<undefined>;
},
+2 -2
View File
@@ -6,13 +6,13 @@
import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/frontend-plugin-api';
// @public (undocumented)
const visualizerPlugin: FrontendPlugin<
const visualizerPlugin: OverridableFrontendPlugin<
{},
{},
{
+2 -2
View File
@@ -14,11 +14,11 @@ import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { IconComponent as IconComponent_2 } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { NavContentComponent } from '@backstage/frontend-plugin-api';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { ReactNode } from 'react';
import { RouteRef } from '@backstage/frontend-plugin-api';
import { SignInPageProps } from '@backstage/core-plugin-api';
@@ -27,7 +27,7 @@ import { TranslationMessages } from '@backstage/frontend-plugin-api';
import { TranslationResource } from '@backstage/frontend-plugin-api';
// @public (undocumented)
const appPlugin: FrontendPlugin<
const appPlugin: OverridableFrontendPlugin<
{},
{},
{
+2 -2
View File
@@ -12,8 +12,8 @@ import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { ExternalRouteRef } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/frontend-plugin-api';
import { TranslationRef } from '@backstage/frontend-plugin-api';
@@ -46,7 +46,7 @@ export const catalogGraphTranslationRef: TranslationRef<
>;
// @public (undocumented)
const _default: FrontendPlugin<
const _default: OverridableFrontendPlugin<
{
catalogGraph: RouteRef<undefined>;
},
+2 -2
View File
@@ -9,8 +9,8 @@ import { ApiFactory } from '@backstage/core-plugin-api';
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/frontend-plugin-api';
import { TranslationRef } from '@backstage/core-plugin-api/alpha';
@@ -88,7 +88,7 @@ export const catalogImportTranslationRef: TranslationRef<
>;
// @alpha (undocumented)
const _default: FrontendPlugin<
const _default: OverridableFrontendPlugin<
{
importPage: RouteRef<undefined>;
},
@@ -9,13 +9,13 @@ import { ApiFactory } from '@backstage/frontend-plugin-api';
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/frontend-plugin-api';
// @alpha (undocumented)
const _default: FrontendPlugin<
const _default: OverridableFrontendPlugin<
{
root: RouteRef<undefined>;
},
+2 -2
View File
@@ -18,10 +18,10 @@ import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { ExternalRouteRef } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { IconLinkVerticalProps } from '@backstage/core-components';
import { JSX as JSX_2 } from 'react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/frontend-plugin-api';
import { SearchResultItemExtensionComponent } from '@backstage/plugin-search-react/alpha';
import { SearchResultItemExtensionPredicate } from '@backstage/plugin-search-react/alpha';
@@ -114,7 +114,7 @@ export const catalogTranslationRef: TranslationRef<
>;
// @alpha (undocumented)
const _default: FrontendPlugin<
const _default: OverridableFrontendPlugin<
{
catalogIndex: RouteRef<undefined>;
catalogEntity: RouteRef<{
+2 -2
View File
@@ -9,13 +9,13 @@ import { ApiFactory } from '@backstage/frontend-plugin-api';
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/frontend-plugin-api';
// @alpha (undocumented)
const _default: FrontendPlugin<
const _default: OverridableFrontendPlugin<
{
root: RouteRef<undefined>;
},
+2 -2
View File
@@ -11,13 +11,13 @@ import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/frontend-plugin-api';
import { TranslationRef } from '@backstage/frontend-plugin-api';
// @alpha (undocumented)
const _default: FrontendPlugin<
const _default: OverridableFrontendPlugin<
{
root: RouteRef<undefined>;
},
+2 -2
View File
@@ -12,13 +12,13 @@ import { EntityPredicate } from '@backstage/plugin-catalog-react/alpha';
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/frontend-plugin-api';
import { TranslationRef } from '@backstage/core-plugin-api/alpha';
// @public (undocumented)
const _default: FrontendPlugin<
const _default: OverridableFrontendPlugin<
{
kubernetes: RouteRef<undefined>;
},
+2 -2
View File
@@ -9,12 +9,12 @@ import { ApiFactory } from '@backstage/frontend-plugin-api';
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/frontend-plugin-api';
// @alpha (undocumented)
const _default: FrontendPlugin<
const _default: OverridableFrontendPlugin<
{
root: RouteRef<undefined>;
},
+2 -2
View File
@@ -10,12 +10,12 @@ import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { ExternalRouteRef } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { TranslationRef } from '@backstage/frontend-plugin-api';
// @alpha (undocumented)
const _default: FrontendPlugin<
const _default: OverridableFrontendPlugin<
{},
{
catalogIndex: ExternalRouteRef<undefined>;
+2 -2
View File
@@ -21,11 +21,11 @@ import { FormField } from '@backstage/plugin-scaffolder-react/alpha';
import { formFieldsApiRef } from '@backstage/plugin-scaffolder-react/alpha';
import type { FormProps as FormProps_2 } from '@rjsf/core';
import { FormProps as FormProps_3 } from '@backstage/plugin-scaffolder-react';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { IconLinkVerticalProps } from '@backstage/core-components';
import { JSX as JSX_2 } from 'react';
import { LayoutOptions } from '@backstage/plugin-scaffolder-react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { PathParams } from '@backstage/core-plugin-api';
import { ReviewStepProps } from '@backstage/plugin-scaffolder-react';
import { RouteRef } from '@backstage/frontend-plugin-api';
@@ -37,7 +37,7 @@ import { TemplateGroupFilter } from '@backstage/plugin-scaffolder-react';
import { TranslationRef } from '@backstage/core-plugin-api/alpha';
// @alpha (undocumented)
const _default: FrontendPlugin<
const _default: OverridableFrontendPlugin<
{
root: RouteRef<undefined>;
selectedTemplate: SubRouteRef<
+2 -2
View File
@@ -11,9 +11,9 @@ import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/frontend-plugin-api';
import { SearchFilterExtensionComponent } from '@backstage/plugin-search-react/alpha';
import { SearchResultItemExtensionComponent } from '@backstage/plugin-search-react/alpha';
@@ -21,7 +21,7 @@ import { SearchResultItemExtensionPredicate } from '@backstage/plugin-search-rea
import { TranslationRef } from '@backstage/core-plugin-api/alpha';
// @alpha (undocumented)
const _default: FrontendPlugin<
const _default: OverridableFrontendPlugin<
{
root: RouteRef<undefined>;
},
+2 -2
View File
@@ -8,11 +8,11 @@ import { ApiFactory } from '@backstage/frontend-plugin-api';
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
// @alpha (undocumented)
const _default: FrontendPlugin<
const _default: OverridableFrontendPlugin<
{},
{},
{
+2 -2
View File
@@ -14,10 +14,10 @@ import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { IconLinkVerticalProps } from '@backstage/core-components';
import { JSX as JSX_2 } from 'react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/frontend-plugin-api';
import { SearchResultItemExtensionComponent } from '@backstage/plugin-search-react/alpha';
import { SearchResultItemExtensionPredicate } from '@backstage/plugin-search-react/alpha';
@@ -25,7 +25,7 @@ import { SearchResultListItemBlueprintParams } from '@backstage/plugin-search-re
import { TechDocsAddonOptions } from '@backstage/plugin-techdocs-react';
// @alpha (undocumented)
const _default: FrontendPlugin<
const _default: OverridableFrontendPlugin<
{
root: RouteRef<undefined>;
docRoot: RouteRef<{
+2 -2
View File
@@ -8,14 +8,14 @@ import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { RouteRef } from '@backstage/frontend-plugin-api';
import { TranslationRef } from '@backstage/core-plugin-api/alpha';
// @alpha (undocumented)
const _default: FrontendPlugin<
const _default: OverridableFrontendPlugin<
{
root: RouteRef<undefined>;
},