From a3792432bb9e2a66616819d738daaad9f6d444b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 13 Dec 2023 16:50:09 +0100 Subject: [PATCH] introduce FrontendFeature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/big-ads-travel.md | 5 ++++ .changeset/brown-emus-explode.md | 6 +++++ packages/core-compat-api/api-report.md | 5 ++-- .../core-compat-api/src/convertLegacyApp.ts | 5 ++-- packages/frontend-app-api/api-report.md | 11 +++----- .../src/routing/collectRouteIds.ts | 7 ++--- .../src/tree/createAppTree.ts | 8 ++---- .../src/tree/resolveAppNodeSpecs.ts | 3 ++- .../frontend-app-api/src/wiring/createApp.tsx | 15 +++++------ .../frontend-app-api/src/wiring/discovery.ts | 13 +++------ packages/frontend-plugin-api/api-report.md | 3 +++ .../src/wiring/createExtensionOverrides.ts | 7 +---- .../src/wiring/createPlugin.test.ts | 3 ++- .../src/wiring/createPlugin.ts | 25 +++++------------ .../frontend-plugin-api/src/wiring/index.ts | 18 ++++++------- .../frontend-plugin-api/src/wiring/types.ts | 27 +++++++++++++++++++ 16 files changed, 82 insertions(+), 79 deletions(-) create mode 100644 .changeset/big-ads-travel.md create mode 100644 .changeset/brown-emus-explode.md diff --git a/.changeset/big-ads-travel.md b/.changeset/big-ads-travel.md new file mode 100644 index 0000000000..e58feda5a0 --- /dev/null +++ b/.changeset/big-ads-travel.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +Add the `FrontendFeature` type, which is the union of `BackstagePlugin` and `ExtensionOverrides` diff --git a/.changeset/brown-emus-explode.md b/.changeset/brown-emus-explode.md new file mode 100644 index 0000000000..255849da46 --- /dev/null +++ b/.changeset/brown-emus-explode.md @@ -0,0 +1,6 @@ +--- +'@backstage/frontend-app-api': patch +'@backstage/core-compat-api': patch +--- + +Leverage the new `FrontendFeature` type to simplify interfaces diff --git a/packages/core-compat-api/api-report.md b/packages/core-compat-api/api-report.md index a764d8c091..494747be60 100644 --- a/packages/core-compat-api/api-report.md +++ b/packages/core-compat-api/api-report.md @@ -4,10 +4,9 @@ ```ts import { AnyRouteRefParams } from '@backstage/core-plugin-api'; -import { BackstagePlugin } from '@backstage/frontend-plugin-api'; -import { ExtensionOverrides } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { ExternalRouteRef as ExternalRouteRef_2 } from '@backstage/frontend-plugin-api'; +import { FrontendFeature } from '@backstage/frontend-plugin-api'; import { default as React_2 } from 'react'; import { ReactNode } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; @@ -21,7 +20,7 @@ export function compatWrapper(element: ReactNode): React_2.JSX.Element; // @public (undocumented) export function convertLegacyApp( rootElement: React_2.JSX.Element, -): (ExtensionOverrides | BackstagePlugin)[]; +): FrontendFeature[]; // @public export function convertLegacyRouteRef( diff --git a/packages/core-compat-api/src/convertLegacyApp.ts b/packages/core-compat-api/src/convertLegacyApp.ts index 8055a7b72e..593a159875 100644 --- a/packages/core-compat-api/src/convertLegacyApp.ts +++ b/packages/core-compat-api/src/convertLegacyApp.ts @@ -22,8 +22,7 @@ import React, { isValidElement, } from 'react'; import { - BackstagePlugin, - ExtensionOverrides, + FrontendFeature, coreExtensionData, createExtension, createExtensionInput, @@ -61,7 +60,7 @@ function selectChildren( /** @public */ export function convertLegacyApp( rootElement: React.JSX.Element, -): (ExtensionOverrides | BackstagePlugin)[] { +): FrontendFeature[] { if (getComponentData(rootElement, 'core.type') === 'FlatRoutes') { return collectLegacyRoutes(rootElement); } diff --git a/packages/frontend-app-api/api-report.md b/packages/frontend-app-api/api-report.md index a453463645..a4654e9f8d 100644 --- a/packages/frontend-app-api/api-report.md +++ b/packages/frontend-app-api/api-report.md @@ -3,26 +3,23 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackstagePlugin } from '@backstage/frontend-plugin-api'; import { Config } from '@backstage/config'; import { ConfigApi } from '@backstage/core-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; -import { ExtensionOverrides } from '@backstage/frontend-plugin-api'; import { ExternalRouteRef } from '@backstage/frontend-plugin-api'; +import { FrontendFeature } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; import { SubRouteRef } from '@backstage/frontend-plugin-api'; // @public (undocumented) export function createApp(options?: { - features?: (BackstagePlugin | ExtensionOverrides)[]; + features?: FrontendFeature[]; configLoader?: () => Promise<{ config: ConfigApi; }>; bindRoutes?(context: { bind: CreateAppRouteBinder }): void; - featureLoader?: (ctx: { - config: ConfigApi; - }) => Promise<(BackstagePlugin | ExtensionOverrides)[]>; + featureLoader?: (ctx: { config: ConfigApi }) => Promise; }): { createRoot(): JSX_2.Element; }; @@ -45,7 +42,7 @@ export function createExtensionTree(options: { config: Config }): ExtensionTree; // @public export function createSpecializedApp(options?: { - features?: (BackstagePlugin | ExtensionOverrides)[]; + features?: FrontendFeature[]; config?: ConfigApi; bindRoutes?(context: { bind: CreateAppRouteBinder }): void; }): { diff --git a/packages/frontend-app-api/src/routing/collectRouteIds.ts b/packages/frontend-app-api/src/routing/collectRouteIds.ts index 7e2a4cad2e..1d6aa14e5d 100644 --- a/packages/frontend-app-api/src/routing/collectRouteIds.ts +++ b/packages/frontend-app-api/src/routing/collectRouteIds.ts @@ -15,11 +15,10 @@ */ import { - BackstagePlugin, - ExtensionOverrides, RouteRef, SubRouteRef, ExternalRouteRef, + FrontendFeature, } from '@backstage/frontend-plugin-api'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toInternalRouteRef } from '../../../frontend-plugin-api/src/routing/RouteRef'; @@ -33,9 +32,7 @@ export interface RouteRefsById { } /** @internal */ -export function collectRouteIds( - features: (BackstagePlugin | ExtensionOverrides)[], -): RouteRefsById { +export function collectRouteIds(features: FrontendFeature[]): RouteRefsById { const routesById = new Map(); const externalRoutesById = new Map(); diff --git a/packages/frontend-app-api/src/tree/createAppTree.ts b/packages/frontend-app-api/src/tree/createAppTree.ts index 950669febd..0f8ce09138 100644 --- a/packages/frontend-app-api/src/tree/createAppTree.ts +++ b/packages/frontend-app-api/src/tree/createAppTree.ts @@ -14,11 +14,7 @@ * limitations under the License. */ -import { - BackstagePlugin, - Extension, - ExtensionOverrides, -} from '@backstage/frontend-plugin-api'; +import { Extension, FrontendFeature } from '@backstage/frontend-plugin-api'; import { readAppExtensionsConfig } from './readAppExtensionsConfig'; import { resolveAppTree } from './resolveAppTree'; import { resolveAppNodeSpecs } from './resolveAppNodeSpecs'; @@ -28,7 +24,7 @@ import { instantiateAppNodeTree } from './instantiateAppNodeTree'; /** @internal */ export interface CreateAppTreeOptions { - features: (BackstagePlugin | ExtensionOverrides)[]; + features: FrontendFeature[]; builtinExtensions: Extension[]; config: Config; } diff --git a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts index 870e1f69f8..8fd9d44638 100644 --- a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts +++ b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts @@ -18,6 +18,7 @@ import { BackstagePlugin, Extension, ExtensionOverrides, + FrontendFeature, } from '@backstage/frontend-plugin-api'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toInternalExtensionOverrides } from '../../../frontend-plugin-api/src/wiring/createExtensionOverrides'; @@ -30,7 +31,7 @@ import { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/res /** @internal */ export function resolveAppNodeSpecs(options: { - features: (BackstagePlugin | ExtensionOverrides)[]; + features: FrontendFeature[]; builtinExtensions: Extension[]; parameters: Array; forbidden?: Set; diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index 67b8494953..0f8e3de953 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -19,7 +19,6 @@ import { ConfigReader, Config } from '@backstage/config'; import { AppTree, appTreeApiRef, - BackstagePlugin, ComponentRef, componentsApiRef, coreExtensionData, @@ -29,7 +28,7 @@ import { createThemeExtension, createTranslationExtension, ExtensionDataRef, - ExtensionOverrides, + FrontendFeature, RouteRef, useRouteRef, } from '@backstage/frontend-plugin-api'; @@ -217,8 +216,8 @@ export function createExtensionTree(options: { } function deduplicateFeatures( - allFeatures: (BackstagePlugin | ExtensionOverrides)[], -): (BackstagePlugin | ExtensionOverrides)[] { + allFeatures: FrontendFeature[], +): FrontendFeature[] { // Start by removing duplicates by reference const features = Array.from(new Set(allFeatures)); @@ -241,12 +240,10 @@ function deduplicateFeatures( /** @public */ export function createApp(options?: { - features?: (BackstagePlugin | ExtensionOverrides)[]; + features?: FrontendFeature[]; configLoader?: () => Promise<{ config: ConfigApi }>; bindRoutes?(context: { bind: CreateAppRouteBinder }): void; - featureLoader?: (ctx: { - config: ConfigApi; - }) => Promise<(BackstagePlugin | ExtensionOverrides)[]>; + featureLoader?: (ctx: { config: ConfigApi }) => Promise; }): { createRoot(): JSX.Element; } { @@ -291,7 +288,7 @@ export function createApp(options?: { * @public */ export function createSpecializedApp(options?: { - features?: (BackstagePlugin | ExtensionOverrides)[]; + features?: FrontendFeature[]; config?: ConfigApi; bindRoutes?(context: { bind: CreateAppRouteBinder }): void; }): { createRoot(): JSX.Element } { diff --git a/packages/frontend-app-api/src/wiring/discovery.ts b/packages/frontend-app-api/src/wiring/discovery.ts index 1181992d53..4e50290350 100644 --- a/packages/frontend-app-api/src/wiring/discovery.ts +++ b/packages/frontend-app-api/src/wiring/discovery.ts @@ -15,10 +15,7 @@ */ import { Config, ConfigReader } from '@backstage/config'; -import { - BackstagePlugin, - ExtensionOverrides, -} from '@backstage/frontend-plugin-api'; +import { FrontendFeature } from '@backstage/frontend-plugin-api'; interface DiscoveryGlobal { modules: Array<{ name: string; export?: string; default: unknown }>; @@ -58,9 +55,7 @@ function readPackageDetectionConfig(config: Config) { /** * @public */ -export function getAvailableFeatures( - config: Config, -): (BackstagePlugin | ExtensionOverrides)[] { +export function getAvailableFeatures(config: Config): FrontendFeature[] { const discovered = ( window as { '__@backstage/discovered__'?: DiscoveryGlobal } )['__@backstage/discovered__']; @@ -86,9 +81,7 @@ export function getAvailableFeatures( ); } -function isBackstageFeature( - obj: unknown, -): obj is BackstagePlugin | ExtensionOverrides { +function isBackstageFeature(obj: unknown): obj is FrontendFeature { if (obj !== null && typeof obj === 'object' && '$$type' in obj) { return ( obj.$$type === '@backstage/BackstagePlugin' || diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index 7c3ff5af34..b67c4e75eb 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -863,6 +863,9 @@ export { FetchApi }; export { fetchApiRef }; +// @public (undocumented) +export type FrontendFeature = BackstagePlugin | ExtensionOverrides; + export { githubAuthApiRef }; export { gitlabAuthApiRef }; diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts index a60e655ff9..3c14d285b3 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts @@ -19,7 +19,7 @@ import { Extension, resolveExtensionDefinition, } from './resolveExtensionDefinition'; -import { FeatureFlagConfig } from './types'; +import { ExtensionOverrides, FeatureFlagConfig } from './types'; /** @public */ export interface ExtensionOverridesOptions { @@ -27,11 +27,6 @@ export interface ExtensionOverridesOptions { featureFlags?: FeatureFlagConfig[]; } -/** @public */ -export interface ExtensionOverrides { - readonly $$type: '@backstage/ExtensionOverrides'; -} - /** @internal */ export interface InternalExtensionOverrides extends ExtensionOverrides { readonly version: 'v1'; diff --git a/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts b/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts index d533048d2b..c7f31eb60d 100644 --- a/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts @@ -18,13 +18,14 @@ import React from 'react'; import { createApp } from '@backstage/frontend-app-api'; import { screen } from '@testing-library/react'; import { createSchemaFromZod } from '../schema/createSchemaFromZod'; -import { createPlugin, BackstagePlugin } from './createPlugin'; +import { createPlugin } from './createPlugin'; import { JsonObject } from '@backstage/types'; import { createExtension } from './createExtension'; import { createExtensionDataRef } from './createExtensionDataRef'; import { coreExtensionData } from './coreExtensionData'; import { MockConfigApi, renderWithEffects } from '@backstage/test-utils'; import { createExtensionInput } from './createExtensionInput'; +import { BackstagePlugin } from './types'; const nameExtensionDataRef = createExtensionDataRef('name'); diff --git a/packages/frontend-plugin-api/src/wiring/createPlugin.ts b/packages/frontend-plugin-api/src/wiring/createPlugin.ts index 06c216a37f..67907e188e 100644 --- a/packages/frontend-plugin-api/src/wiring/createPlugin.ts +++ b/packages/frontend-plugin-api/src/wiring/createPlugin.ts @@ -15,18 +15,16 @@ */ import { ExtensionDefinition } from './createExtension'; -import { ExternalRouteRef, RouteRef } from '../routing'; -import { FeatureFlagConfig } from './types'; import { Extension, resolveExtensionDefinition, } from './resolveExtensionDefinition'; - -/** @public */ -export type AnyRoutes = { [name in string]: RouteRef }; - -/** @public */ -export type AnyExternalRoutes = { [name in string]: ExternalRouteRef }; +import { + AnyExternalRoutes, + AnyRoutes, + BackstagePlugin, + FeatureFlagConfig, +} from './types'; /** @public */ export interface PluginOptions< @@ -40,17 +38,6 @@ export interface PluginOptions< featureFlags?: FeatureFlagConfig[]; } -/** @public */ -export interface BackstagePlugin< - Routes extends AnyRoutes = AnyRoutes, - ExternalRoutes extends AnyExternalRoutes = AnyExternalRoutes, -> { - readonly $$type: '@backstage/BackstagePlugin'; - readonly id: string; - readonly routes: Routes; - readonly externalRoutes: ExternalRoutes; -} - /** @public */ export interface InternalBackstagePlugin< Routes extends AnyRoutes = AnyRoutes, diff --git a/packages/frontend-plugin-api/src/wiring/index.ts b/packages/frontend-plugin-api/src/wiring/index.ts index 2a426c145a..61f821c7ce 100644 --- a/packages/frontend-plugin-api/src/wiring/index.ts +++ b/packages/frontend-plugin-api/src/wiring/index.ts @@ -34,17 +34,17 @@ export { type ExtensionDataRef, type ConfigurableExtensionDataRef, } from './createExtensionDataRef'; -export { - createPlugin, - type BackstagePlugin, - type PluginOptions, - type AnyRoutes, - type AnyExternalRoutes, -} from './createPlugin'; +export { createPlugin, type PluginOptions } from './createPlugin'; export { createExtensionOverrides, - type ExtensionOverrides, type ExtensionOverridesOptions, } from './createExtensionOverrides'; export { type Extension } from './resolveExtensionDefinition'; -export type { FeatureFlagConfig } from './types'; +export { + type AnyRoutes, + type AnyExternalRoutes, + type BackstagePlugin, + type ExtensionOverrides, + type FeatureFlagConfig, + type FrontendFeature, +} from './types'; diff --git a/packages/frontend-plugin-api/src/wiring/types.ts b/packages/frontend-plugin-api/src/wiring/types.ts index ab9f07b35e..c2a6b09ba7 100644 --- a/packages/frontend-plugin-api/src/wiring/types.ts +++ b/packages/frontend-plugin-api/src/wiring/types.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { ExternalRouteRef, RouteRef } from '../routing'; + /** * Feature flag configuration. * @@ -23,3 +25,28 @@ export type FeatureFlagConfig = { /** Feature flag name */ name: string; }; + +/** @public */ +export type AnyRoutes = { [name in string]: RouteRef }; + +/** @public */ +export type AnyExternalRoutes = { [name in string]: ExternalRouteRef }; + +/** @public */ +export interface BackstagePlugin< + Routes extends AnyRoutes = AnyRoutes, + ExternalRoutes extends AnyExternalRoutes = AnyExternalRoutes, +> { + readonly $$type: '@backstage/BackstagePlugin'; + readonly id: string; + readonly routes: Routes; + readonly externalRoutes: ExternalRoutes; +} + +/** @public */ +export interface ExtensionOverrides { + readonly $$type: '@backstage/ExtensionOverrides'; +} + +/** @public */ +export type FrontendFeature = BackstagePlugin | ExtensionOverrides;