introduce FrontendFeature

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2023-12-13 16:50:09 +01:00
parent 8563ee2b8f
commit a3792432bb
16 changed files with 82 additions and 79 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-plugin-api': patch
---
Add the `FrontendFeature` type, which is the union of `BackstagePlugin` and `ExtensionOverrides`
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/frontend-app-api': patch
'@backstage/core-compat-api': patch
---
Leverage the new `FrontendFeature` type to simplify interfaces
+2 -3
View File
@@ -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<TParams extends AnyRouteRefParams>(
@@ -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);
}
+4 -7
View File
@@ -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<FrontendFeature[]>;
}): {
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;
}): {
@@ -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<string, RouteRef | SubRouteRef>();
const externalRoutesById = new Map<string, ExternalRouteRef>();
@@ -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<unknown>[];
config: Config;
}
@@ -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<unknown>[];
parameters: Array<ExtensionParameters>;
forbidden?: Set<string>;
@@ -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<FrontendFeature[]>;
}): {
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 } {
@@ -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' ||
@@ -863,6 +863,9 @@ export { FetchApi };
export { fetchApiRef };
// @public (undocumented)
export type FrontendFeature = BackstagePlugin | ExtensionOverrides;
export { githubAuthApiRef };
export { gitlabAuthApiRef };
@@ -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';
@@ -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<string>('name');
@@ -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,
@@ -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';
@@ -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;