From 5fd78ba82f534014c7440f96dac94a65e32ae8e9 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 17 Mar 2026 11:37:13 +0100 Subject: [PATCH] frontend-plugin-api, frontend-app-api: API review cleanup Remove @backstage/core-plugin-api leakage from the @backstage/frontend-app-api public API surface. Rename PluginOptions to CreateFrontendPluginOptions with a deprecated alias. Remove ResolvedExtensionInputs from the main @backstage/frontend-plugin-api entry point. Signed-off-by: Patrik Oldsberg Made-with: Cursor --- .changeset/fix-api-review-frontend-app-api.md | 5 ++ .../fix-api-review-frontend-plugin-api.md | 5 ++ packages/frontend-app-api/report.api.md | 7 +- .../src/tree/instantiateAppNodeTree.ts | 3 +- .../src/wiring/createPluginInfoAttacher.ts | 2 +- .../src/wiring/createSpecializedApp.test.tsx | 3 +- .../src/wiring/createSpecializedApp.tsx | 28 ++++---- .../src/wiring/InternalExtensionDefinition.ts | 3 +- packages/frontend-plugin-api/report.api.md | 68 ++++++++++--------- .../src/wiring/createExtension.ts | 3 +- .../src/wiring/createFrontendPlugin.ts | 26 ++++++- .../frontend-plugin-api/src/wiring/index.ts | 2 +- 12 files changed, 95 insertions(+), 60 deletions(-) create mode 100644 .changeset/fix-api-review-frontend-app-api.md create mode 100644 .changeset/fix-api-review-frontend-plugin-api.md diff --git a/.changeset/fix-api-review-frontend-app-api.md b/.changeset/fix-api-review-frontend-app-api.md new file mode 100644 index 0000000000..ff287ac2a5 --- /dev/null +++ b/.changeset/fix-api-review-frontend-app-api.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': patch +--- + +Removed `@backstage/core-plugin-api` leakage from the public API surface. All types such as `ApiHolder` and `ConfigApi` are now imported from `@backstage/frontend-plugin-api`. diff --git a/.changeset/fix-api-review-frontend-plugin-api.md b/.changeset/fix-api-review-frontend-plugin-api.md new file mode 100644 index 0000000000..10458d8ebb --- /dev/null +++ b/.changeset/fix-api-review-frontend-plugin-api.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': minor +--- + +Renamed `PluginOptions` to `CreateFrontendPluginOptions` and deprecated the old name. Removed `ResolvedExtensionInputs` from the main entry point; it is still available as an inline type in extension factory signatures. diff --git a/packages/frontend-app-api/report.api.md b/packages/frontend-app-api/report.api.md index bf69cc83e6..0a0b23203b 100644 --- a/packages/frontend-app-api/report.api.md +++ b/packages/frontend-app-api/report.api.md @@ -3,11 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { ApiHolder } from '@backstage/core-plugin-api'; -import { ApiHolder as ApiHolder_2 } from '@backstage/frontend-plugin-api'; +import { ApiHolder } from '@backstage/frontend-plugin-api'; import { AppNode } from '@backstage/frontend-plugin-api'; import { AppTree } from '@backstage/frontend-plugin-api'; -import { ConfigApi } from '@backstage/core-plugin-api'; +import { ConfigApi } from '@backstage/frontend-plugin-api'; import { ExtensionDataContainer } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDataValue } from '@backstage/frontend-plugin-api'; @@ -186,7 +185,7 @@ export type ExtensionFactoryMiddleware = ( }) => ExtensionDataContainer, context: { node: AppNode; - apis: ApiHolder_2; + apis: ApiHolder; config?: JsonObject; }, ) => Iterable>; diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts index 9e8e3cc2eb..3de09acf41 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts @@ -19,8 +19,9 @@ import { ExtensionDataContainer, ExtensionDataRef, ExtensionInput, - ResolvedExtensionInputs, } from '@backstage/frontend-plugin-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { ResolvedExtensionInputs } from '../../../frontend-plugin-api/src/wiring/createExtension'; import { ExtensionFactoryMiddleware } from '../wiring/types'; import mapValues from 'lodash/mapValues'; import { AppNode, AppNodeInstance } from '@backstage/frontend-plugin-api'; diff --git a/packages/frontend-app-api/src/wiring/createPluginInfoAttacher.ts b/packages/frontend-app-api/src/wiring/createPluginInfoAttacher.ts index bfab0ae9a3..9a88590d7c 100644 --- a/packages/frontend-app-api/src/wiring/createPluginInfoAttacher.ts +++ b/packages/frontend-app-api/src/wiring/createPluginInfoAttacher.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { ConfigApi } from '@backstage/core-plugin-api'; +import { ConfigApi } from '@backstage/frontend-plugin-api'; import { FrontendFeature, FrontendPluginInfo, diff --git a/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx b/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx index 51c31aca25..aabb725f8a 100644 --- a/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx +++ b/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx @@ -28,12 +28,13 @@ import { createExtensionInput, useRouteRef, analyticsApiRef, + configApiRef, createExtensionDataRef, + featureFlagsApiRef, } from '@backstage/frontend-plugin-api'; import { screen, render } from '@testing-library/react'; import { createSpecializedApp } from './createSpecializedApp'; import { mockApis, TestApiRegistry } from '@backstage/test-utils'; -import { configApiRef, featureFlagsApiRef } from '@backstage/core-plugin-api'; import { MemoryRouter } from 'react-router-dom'; import { ApiProvider, ConfigReader } from '@backstage/core-app-api'; import { Fragment } from 'react'; diff --git a/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx index fda00b70b1..63d9924147 100644 --- a/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx +++ b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx @@ -16,30 +16,28 @@ import { ConfigReader } from '@backstage/config'; import { + AnyApiFactory, ApiBlueprint, + ApiHolder, + AppNode, AppTree, AppTreeApi, appTreeApiRef, - RouteRef, - ExternalRouteRef, - SubRouteRef, AnyRouteRefParams, - RouteFunc, - RouteResolutionApi, - createApiFactory, - routeResolutionApiRef, - AppNode, - FrontendFeature, -} from '@backstage/frontend-plugin-api'; -import { ExtensionFactoryMiddleware } from './types'; -import { - AnyApiFactory, - ApiHolder, ConfigApi, configApiRef, + createApiFactory, + ExternalRouteRef, featureFlagsApiRef, + FrontendFeature, identityApiRef, -} from '@backstage/core-plugin-api'; + RouteFunc, + RouteRef, + RouteResolutionApi, + routeResolutionApiRef, + SubRouteRef, +} from '@backstage/frontend-plugin-api'; +import { ExtensionFactoryMiddleware } from './types'; import { ApiFactoryRegistry, ApiResolver } from '@backstage/core-app-api'; import { createExtensionDataContainer, diff --git a/packages/frontend-internal/src/wiring/InternalExtensionDefinition.ts b/packages/frontend-internal/src/wiring/InternalExtensionDefinition.ts index b7c3674f9a..d6a4eb2eab 100644 --- a/packages/frontend-internal/src/wiring/InternalExtensionDefinition.ts +++ b/packages/frontend-internal/src/wiring/InternalExtensionDefinition.ts @@ -24,8 +24,9 @@ import { ExtensionDefinitionParameters, ExtensionInput, PortableSchema, - ResolvedExtensionInputs, } from '@backstage/frontend-plugin-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { ResolvedExtensionInputs } from '../../../frontend-plugin-api/src/wiring/createExtension'; import { OpaqueType } from '@internal/opaque'; export const OpaqueExtensionDefinition = OpaqueType.create<{ diff --git a/packages/frontend-plugin-api/report.api.md b/packages/frontend-plugin-api/report.api.md index 768f26c0eb..b5a24b0eaa 100644 --- a/packages/frontend-plugin-api/report.api.md +++ b/packages/frontend-plugin-api/report.api.md @@ -729,13 +729,45 @@ export function createFrontendPlugin< [name in string]: ExternalRouteRef; } = {}, >( - options: PluginOptions, + options: CreateFrontendPluginOptions< + TId, + TRoutes, + TExternalRoutes, + TExtensions + >, ): OverridableFrontendPlugin< TRoutes, TExternalRoutes, MakeSortedExtensionsMap >; +// @public +export interface CreateFrontendPluginOptions< + TId extends string, + TRoutes extends { + [name in string]: RouteRef | SubRouteRef; + }, + TExternalRoutes extends { + [name in string]: ExternalRouteRef; + }, + TExtensions extends readonly ExtensionDefinition[], +> { + // (undocumented) + extensions?: TExtensions; + // (undocumented) + externalRoutes?: TExternalRoutes; + // (undocumented) + featureFlags?: FeatureFlagConfig[]; + icon?: IconElement; + // (undocumented) + info?: FrontendPluginInfoOptions; + // (undocumented) + pluginId: TId; + // (undocumented) + routes?: TRoutes; + title?: string; +} + // @public export function createRouteRef< TParams extends @@ -1847,8 +1879,8 @@ export type PluginHeaderActionsApi = { // @public export const pluginHeaderActionsApiRef: ApiRef_2; -// @public (undocumented) -export interface PluginOptions< +// @public @deprecated (undocumented) +export type PluginOptions< TId extends string, TRoutes extends { [name in string]: RouteRef | SubRouteRef; @@ -1857,22 +1889,7 @@ export interface PluginOptions< [name in string]: ExternalRouteRef; }, TExtensions extends readonly ExtensionDefinition[], -> { - // (undocumented) - extensions?: TExtensions; - // (undocumented) - externalRoutes?: TExternalRoutes; - // (undocumented) - featureFlags?: FeatureFlagConfig[]; - icon?: IconElement; - // (undocumented) - info?: FrontendPluginInfoOptions; - // (undocumented) - pluginId: TId; - // (undocumented) - routes?: TRoutes; - title?: string; -} +> = CreateFrontendPluginOptions; // @public export type PluginWrapperApi = { @@ -1950,19 +1967,6 @@ export const Progress: { // @public (undocumented) export type ProgressProps = {}; -// @public -export type ResolvedExtensionInputs< - TInputs extends { - [name in string]: ExtensionInput; - }, -> = { - [InputName in keyof TInputs]: false extends TInputs[InputName]['config']['singleton'] - ? Array>> - : false extends TInputs[InputName]['config']['optional'] - ? Expand> - : Expand | undefined>; -}; - // @public export type RouteFunc = ( ...input: TParams extends undefined ? readonly [] : readonly [params: TParams] diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index 329205ae76..0a1e9c91f8 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -53,7 +53,8 @@ type ResolvedExtensionInput = /** * Converts an extension input map into a matching collection of resolved inputs. - * @public + * + * @ignore */ export type ResolvedExtensionInputs< TInputs extends { diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts index 08329b6fd4..f4e9a18990 100644 --- a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts +++ b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts @@ -170,8 +170,12 @@ export interface FrontendPlugin< info(): Promise; } -/** @public */ -export interface PluginOptions< +/** + * Options for {@link createFrontendPlugin}. + * + * @public + */ +export interface CreateFrontendPluginOptions< TId extends string, TRoutes extends { [name in string]: RouteRef | SubRouteRef }, TExternalRoutes extends { [name in string]: ExternalRouteRef }, @@ -194,6 +198,17 @@ export interface PluginOptions< info?: FrontendPluginInfoOptions; } +/** + * @deprecated Use {@link CreateFrontendPluginOptions} instead. + * @public + */ +export type PluginOptions< + TId extends string, + TRoutes extends { [name in string]: RouteRef | SubRouteRef }, + TExternalRoutes extends { [name in string]: ExternalRouteRef }, + TExtensions extends readonly ExtensionDefinition[], +> = CreateFrontendPluginOptions; + /** * Creates a new plugin that can be installed in a Backstage app. * @@ -230,7 +245,12 @@ export function createFrontendPlugin< TRoutes extends { [name in string]: RouteRef | SubRouteRef } = {}, TExternalRoutes extends { [name in string]: ExternalRouteRef } = {}, >( - options: PluginOptions, + options: CreateFrontendPluginOptions< + TId, + TRoutes, + TExternalRoutes, + TExtensions + >, ): OverridableFrontendPlugin< TRoutes, TExternalRoutes, diff --git a/packages/frontend-plugin-api/src/wiring/index.ts b/packages/frontend-plugin-api/src/wiring/index.ts index d3acef46b6..f3a39cc60d 100644 --- a/packages/frontend-plugin-api/src/wiring/index.ts +++ b/packages/frontend-plugin-api/src/wiring/index.ts @@ -22,7 +22,6 @@ export { type ExtensionDefinitionParameters, type CreateExtensionOptions, type OverridableExtensionDefinition, - type ResolvedExtensionInputs, } from './createExtension'; export { createExtensionInput, @@ -36,6 +35,7 @@ export { } from './createExtensionDataRef'; export { createFrontendPlugin, + type CreateFrontendPluginOptions, type FrontendPlugin, type OverridableFrontendPlugin, type PluginOptions,