chore: removals

Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
blam
2024-09-18 16:26:46 +02:00
parent 3f12044826
commit 4a5ba19344
66 changed files with 158 additions and 1319 deletions
@@ -15,7 +15,7 @@
*/
import { createApiRef } from '@backstage/core-plugin-api';
import { BackstagePlugin, Extension, ExtensionDataRef } from '../../wiring';
import { FrontendPlugin, Extension, ExtensionDataRef } from '../../wiring';
/**
* The specification for this {@link AppNode} in the {@link AppTree}.
@@ -32,7 +32,7 @@ export interface AppNodeSpec {
readonly extension: Extension<unknown, unknown>;
readonly disabled: boolean;
readonly config?: unknown;
readonly source?: BackstagePlugin;
readonly source?: FrontendPlugin;
}
/**
@@ -48,7 +48,6 @@ describe('ApiBlueprint', () => {
"inputs": {},
"kind": "api",
"name": "test",
"namespace": undefined,
"output": [
[Function],
],
@@ -123,7 +122,6 @@ describe('ApiBlueprint', () => {
},
"kind": "api",
"name": "test",
"namespace": undefined,
"output": [
[Function],
],
@@ -38,7 +38,6 @@ describe('AppRootElementBlueprint', () => {
"inputs": {},
"kind": "app-root-element",
"name": undefined,
"namespace": undefined,
"output": [
[Function],
],
@@ -46,7 +46,6 @@ describe('AppRootWrapperBlueprint', () => {
"inputs": {},
"kind": "app-root-wrapper",
"name": undefined,
"namespace": undefined,
"output": [
[Function],
],
@@ -56,7 +56,6 @@ describe('NavItemBlueprint', () => {
"inputs": {},
"kind": "nav-item",
"name": undefined,
"namespace": undefined,
"output": [
[Function],
],
@@ -41,7 +41,6 @@ describe('NavLogoBlueprint', () => {
"inputs": {},
"kind": "nav-logo",
"name": undefined,
"namespace": undefined,
"output": [
[Function],
],
@@ -66,7 +66,6 @@ describe('PageBlueprint', () => {
"inputs": {},
"kind": "page",
"name": "test-page",
"namespace": undefined,
"output": [
[Function],
[Function],
@@ -46,7 +46,6 @@ describe('RouterBlueprint', () => {
"inputs": {},
"kind": "app-router-component",
"name": undefined,
"namespace": undefined,
"output": [
[Function],
],
@@ -42,7 +42,6 @@ describe('SignInPageBlueprint', () => {
"inputs": {},
"kind": "sign-in-page",
"name": undefined,
"namespace": undefined,
"output": [
[Function],
],
@@ -42,7 +42,6 @@ describe('ThemeBlueprint', () => {
"inputs": {},
"kind": "theme",
"name": "light",
"namespace": undefined,
"output": [
[Function],
],
@@ -57,7 +57,6 @@ describe('TranslationBlueprint', () => {
"inputs": {},
"kind": "translation",
"name": "blob",
"namespace": undefined,
"output": [
[Function],
],
@@ -15,11 +15,11 @@
*/
import React, { Component, ComponentType, PropsWithChildren } from 'react';
import { BackstagePlugin } from '../wiring';
import { FrontendPlugin } from '../wiring';
import { CoreErrorBoundaryFallbackProps } from '../types';
type ErrorBoundaryProps = PropsWithChildren<{
plugin?: BackstagePlugin;
plugin?: FrontendPlugin;
Fallback: ComponentType<CoreErrorBoundaryFallbackProps>;
}>;
type ErrorBoundaryState = { error?: Error };
+2 -2
View File
@@ -15,7 +15,7 @@
*/
import { ReactNode } from 'react';
import { BackstagePlugin } from './wiring';
import { FrontendPlugin } from './wiring';
// TODO(Rugvip): This might be a quite useful utility type, maybe add to @backstage/types?
/**
@@ -34,7 +34,7 @@ export type CoreNotFoundErrorPageProps = {
/** @public */
export type CoreErrorBoundaryFallbackProps = {
plugin?: BackstagePlugin;
plugin?: FrontendPlugin;
error: Error;
resetError: () => void;
};
@@ -718,7 +718,6 @@ describe('createExtension', () => {
it('should complain when overriding with incompatible output', () => {
const testExtension = createExtension({
namespace: 'test',
attachTo: { id: 'root', input: 'blob' },
output: [stringDataRef],
factory() {
@@ -115,7 +115,6 @@ export type VerifyExtensionFactoryOutput<
/** @public */
export type CreateExtensionOptions<
TKind extends string | undefined,
TNamespace extends string | undefined,
TName extends string | undefined,
UOutput extends AnyExtensionDataRef,
TInputs extends {
@@ -128,7 +127,6 @@ export type CreateExtensionOptions<
UFactoryOutput extends ExtensionDataValue<any, any>,
> = {
kind?: TKind;
namespace?: TNamespace;
name?: TName;
attachTo: { id: string; input: string };
disabled?: boolean;
@@ -150,7 +148,6 @@ export type CreateExtensionOptions<
/** @public */
export type ExtensionDefinitionParameters = {
kind?: string;
namespace?: string;
name?: string;
configInput?: { [K in string]: any };
config?: { [K in string]: any };
@@ -233,7 +230,6 @@ export type ExtensionDefinition<
>,
): ExtensionDefinition<{
kind: T['kind'];
namespace: T['namespace'];
name: T['name'];
output: AnyExtensionDataRef extends UNewOutput ? T['output'] : UNewOutput;
inputs: T['inputs'] & TExtraInputs;
@@ -265,12 +261,10 @@ export function createExtension<
TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType },
UFactoryOutput extends ExtensionDataValue<any, any>,
const TKind extends string | undefined = undefined,
const TNamespace extends string | undefined = undefined,
const TName extends string | undefined = undefined,
>(
options: CreateExtensionOptions<
TKind,
undefined,
TName,
UOutput,
TInputs,
@@ -294,97 +288,6 @@ export function createExtension<
inputs: TInputs;
params: never;
kind: string | undefined extends TKind ? undefined : TKind;
namespace: string | undefined extends TNamespace ? undefined : TNamespace;
name: string | undefined extends TName ? undefined : TName;
}>;
/**
* @public
* @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release.
*/
export function createExtension<
UOutput extends AnyExtensionDataRef,
TInputs extends {
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{ optional: boolean; singleton: boolean }
>;
},
TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType },
UFactoryOutput extends ExtensionDataValue<any, any>,
const TKind extends string | undefined = undefined,
const TNamespace extends string | undefined = undefined,
const TName extends string | undefined = undefined,
>(
options: CreateExtensionOptions<
TKind,
TNamespace,
TName,
UOutput,
TInputs,
TConfigSchema,
UFactoryOutput
>,
): ExtensionDefinition<{
config: string extends keyof TConfigSchema
? {}
: {
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
};
configInput: string extends keyof TConfigSchema
? {}
: z.input<
z.ZodObject<{
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
}>
>;
output: UOutput;
inputs: TInputs;
params: never;
kind: string | undefined extends TKind ? undefined : TKind;
namespace: string | undefined extends TNamespace ? undefined : TNamespace;
name: string | undefined extends TName ? undefined : TName;
}>;
export function createExtension<
UOutput extends AnyExtensionDataRef,
TInputs extends {
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{ optional: boolean; singleton: boolean }
>;
},
TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType },
UFactoryOutput extends ExtensionDataValue<any, any>,
const TKind extends string | undefined = undefined,
const TNamespace extends string | undefined = undefined,
const TName extends string | undefined = undefined,
>(
options: CreateExtensionOptions<
TKind,
TNamespace,
TName,
UOutput,
TInputs,
TConfigSchema,
UFactoryOutput
>,
): ExtensionDefinition<{
config: string extends keyof TConfigSchema
? {}
: {
[key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
};
configInput: string extends keyof TConfigSchema
? {}
: z.input<
z.ZodObject<{
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
}>
>;
output: UOutput;
inputs: TInputs;
params: object;
kind: string | undefined extends TKind ? undefined : TKind;
namespace: string | undefined extends TNamespace ? undefined : TNamespace;
name: string | undefined extends TName ? undefined : TName;
}> {
const schemaDeclaration = options.config?.schema;
@@ -417,11 +320,9 @@ export function createExtension<
output: UOutput;
inputs: TInputs;
kind: string | undefined extends TKind ? undefined : TKind;
namespace: string | undefined extends TNamespace ? undefined : TNamespace;
name: string | undefined extends TName ? undefined : TName;
},
kind: options.kind,
namespace: options.namespace,
name: options.name,
attachTo: options.attachTo,
disabled: options.disabled ?? false,
@@ -434,9 +335,6 @@ export function createExtension<
if (options.kind) {
parts.push(`kind=${options.kind}`);
}
if (options.namespace) {
parts.push(`namespace=${options.namespace}`);
}
if (options.name) {
parts.push(`name=${options.name}`);
}
@@ -467,7 +365,6 @@ export function createExtension<
return createExtension({
kind: options.kind,
namespace: options.namespace,
name: options.name,
attachTo: overrideOptions.attachTo ?? options.attachTo,
disabled: overrideOptions.disabled ?? options.disabled,
@@ -43,7 +43,6 @@ import {
*/
export type CreateExtensionBlueprintOptions<
TKind extends string,
TNamespace extends string | undefined,
TName extends string | undefined,
TParams,
UOutput extends AnyExtensionDataRef,
@@ -58,7 +57,6 @@ export type CreateExtensionBlueprintOptions<
TDataRefs extends { [name in string]: AnyExtensionDataRef },
> = {
kind: TKind;
namespace?: TNamespace;
attachTo: { id: string; input: string };
disabled?: boolean;
inputs?: TInputs;
@@ -85,7 +83,6 @@ export type CreateExtensionBlueprintOptions<
/** @public */
export type ExtensionBlueprintParameters = {
kind: string;
namespace?: string;
name?: string;
params?: object;
configInput?: { [K in string]: any };
@@ -108,18 +105,13 @@ export interface ExtensionBlueprint<
> {
dataRefs: T['dataRefs'];
make<
TNewNamespace extends string | undefined,
TNewName extends string | undefined,
>(args: {
namespace?: undefined;
make<TNewName extends string | undefined>(args: {
name?: TNewName;
attachTo?: { id: string; input: string };
disabled?: boolean;
params: T['params'];
}): ExtensionDefinition<{
kind: T['kind'];
namespace: undefined;
name: string | undefined extends TNewName ? T['name'] : TNewName;
config: T['config'];
configInput: T['configInput'];
@@ -127,27 +119,6 @@ export interface ExtensionBlueprint<
inputs: T['inputs'];
params: T['params'];
}>;
/** @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. */
make<
TNewNamespace extends string | undefined,
TNewName extends string | undefined,
>(args: {
namespace?: TNewNamespace;
name?: TNewName;
attachTo?: { id: string; input: string };
disabled?: boolean;
params: T['params'];
}): ExtensionDefinition<{
kind: T['kind'];
namespace: string | undefined extends TNewNamespace
? T['namespace']
: TNewNamespace;
name: string | undefined extends TNewName ? T['name'] : TNewName;
config: T['config'];
configInput: T['configInput'];
output: T['output'];
inputs: T['inputs'];
}>;
/**
* Creates a new extension from the blueprint.
@@ -156,7 +127,6 @@ export interface ExtensionBlueprint<
* optionally call the original factory with the same params.
*/
makeWithOverrides<
TNewNamespace extends string | undefined,
TNewName extends string | undefined,
TExtensionConfigSchema extends {
[key in string]: (zImpl: typeof z) => z.ZodType;
@@ -170,7 +140,6 @@ export interface ExtensionBlueprint<
>;
},
>(args: {
namespace?: undefined;
name?: TNewName;
attachTo?: { id: string; input: string };
disabled?: boolean;
@@ -232,93 +201,9 @@ export interface ExtensionBlueprint<
output: AnyExtensionDataRef extends UNewOutput ? T['output'] : UNewOutput;
inputs: T['inputs'] & TExtraInputs;
kind: T['kind'];
namespace: undefined;
name: string | undefined extends TNewName ? T['name'] : TNewName;
params: T['params'];
}>;
/** @deprecated namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release. */
makeWithOverrides<
TNewNamespace extends string | undefined,
TNewName extends string | undefined,
TExtensionConfigSchema extends {
[key in string]: (zImpl: typeof z) => z.ZodType;
},
UFactoryOutput extends ExtensionDataValue<any, any>,
UNewOutput extends AnyExtensionDataRef,
TExtraInputs extends {
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{ optional: boolean; singleton: boolean }
>;
},
>(args: {
namespace: TNewNamespace;
name?: TNewName;
attachTo?: { id: string; input: string };
disabled?: boolean;
inputs?: TExtraInputs & {
[KName in keyof T['inputs']]?: `Error: Input '${KName &
string}' is already defined in parent definition`;
};
output?: Array<UNewOutput>;
config?: {
schema: TExtensionConfigSchema & {
[KName in keyof T['config']]?: `Error: Config key '${KName &
string}' is already defined in parent schema`;
};
};
factory(
originalFactory: (
params: T['params'],
context?: {
config?: T['config'];
inputs?: ResolveInputValueOverrides<NonNullable<T['inputs']>>;
},
) => ExtensionDataContainer<NonNullable<T['output']>>,
context: {
node: AppNode;
apis: ApiHolder;
config: T['config'] & {
[key in keyof TExtensionConfigSchema]: z.infer<
ReturnType<TExtensionConfigSchema[key]>
>;
};
inputs: Expand<ResolvedExtensionInputs<T['inputs'] & TExtraInputs>>;
},
): Iterable<UFactoryOutput> &
VerifyExtensionFactoryOutput<
AnyExtensionDataRef extends UNewOutput
? NonNullable<T['output']>
: UNewOutput,
UFactoryOutput
>;
}): ExtensionDefinition<{
config: (string extends keyof TExtensionConfigSchema
? {}
: {
[key in keyof TExtensionConfigSchema]: z.infer<
ReturnType<TExtensionConfigSchema[key]>
>;
}) &
T['config'];
configInput: (string extends keyof TExtensionConfigSchema
? {}
: z.input<
z.ZodObject<{
[key in keyof TExtensionConfigSchema]: ReturnType<
TExtensionConfigSchema[key]
>;
}>
>) &
T['configInput'];
output: AnyExtensionDataRef extends UNewOutput ? T['output'] : UNewOutput;
inputs: T['inputs'] & TExtraInputs;
kind: T['kind'];
namespace: string | undefined extends TNewNamespace
? T['namespace']
: TNewNamespace;
name: string | undefined extends TNewName ? T['name'] : TNewName;
}>;
}
/**
@@ -339,13 +224,11 @@ export function createExtensionBlueprint<
TConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType },
UFactoryOutput extends ExtensionDataValue<any, any>,
TKind extends string,
TNamespace extends undefined = undefined,
TName extends string | undefined = undefined,
TDataRefs extends { [name in string]: AnyExtensionDataRef } = never,
>(
options: CreateExtensionBlueprintOptions<
TKind,
undefined,
TName,
TParams,
UOutput,
@@ -356,57 +239,6 @@ export function createExtensionBlueprint<
>,
): ExtensionBlueprint<{
kind: TKind;
namespace: undefined;
name: TName;
params: TParams;
output: UOutput;
inputs: string extends keyof TInputs ? {} : TInputs;
config: string extends keyof TConfigSchema
? {}
: { [key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>> };
configInput: string extends keyof TConfigSchema
? {}
: z.input<
z.ZodObject<{
[key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;
}>
>;
dataRefs: TDataRefs;
}>;
/**
* @public
* @deprecated the namespace is no longer required, you can safely remove this option and it will default to the `pluginId`. It will be removed in a future release.
*/
export function createExtensionBlueprint<
TParams extends object,
UOutput extends AnyExtensionDataRef,
TInputs extends {
[inputName in string]: ExtensionInput<
AnyExtensionDataRef,
{ optional: boolean; singleton: boolean }
>;
},
TConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType },
UFactoryOutput extends ExtensionDataValue<any, any>,
TKind extends string,
TNamespace extends string | undefined = undefined,
TName extends string | undefined = undefined,
TDataRefs extends { [name in string]: AnyExtensionDataRef } = never,
>(
options: CreateExtensionBlueprintOptions<
TKind,
TNamespace,
TName,
TParams,
UOutput,
TInputs,
TConfigSchema,
UFactoryOutput,
TDataRefs
>,
): ExtensionBlueprint<{
kind: TKind;
namespace: TNamespace;
name: TName;
params: TParams;
output: UOutput;
@@ -435,13 +267,11 @@ export function createExtensionBlueprint<
TConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType },
UFactoryOutput extends ExtensionDataValue<any, any>,
TKind extends string,
TNamespace extends string | undefined = undefined,
TName extends string | undefined = undefined,
TDataRefs extends { [name in string]: AnyExtensionDataRef } = never,
>(
options: CreateExtensionBlueprintOptions<
TKind,
TNamespace,
TName,
TParams,
UOutput,
@@ -452,7 +282,6 @@ export function createExtensionBlueprint<
>,
): ExtensionBlueprint<{
kind: TKind;
namespace: TNamespace;
name: TName;
params: TParams;
output: UOutput;
@@ -474,7 +303,6 @@ export function createExtensionBlueprint<
make(args) {
return createExtension({
kind: options.kind,
namespace: args.namespace ?? options.namespace,
name: args.name ?? options.name,
attachTo: args.attachTo ?? options.attachTo,
disabled: args.disabled ?? options.disabled,
@@ -491,7 +319,6 @@ export function createExtensionBlueprint<
makeWithOverrides(args) {
return createExtension({
kind: options.kind,
namespace: args.namespace ?? options.namespace,
name: args.name ?? options.name,
attachTo: args.attachTo ?? options.attachTo,
disabled: args.disabled ?? options.disabled,
@@ -539,7 +366,6 @@ export function createExtensionBlueprint<
},
} as ExtensionBlueprint<{
kind: TKind;
namespace: TNamespace;
name: TName;
params: TParams;
output: UOutput;
@@ -1,136 +0,0 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createExtension } from './createExtension';
import {
createExtensionOverrides,
toInternalExtensionOverrides,
} from './createExtensionOverrides';
describe('createExtensionOverrides', () => {
it('should create overrides without extensions', () => {
expect(createExtensionOverrides({ extensions: [] })).toMatchInlineSnapshot(`
{
"$$type": "@backstage/ExtensionOverrides",
"extensions": [],
"featureFlags": [],
"toString": [Function],
"version": "v1",
}
`);
});
it('should create overrides with extensions', () => {
expect(
createExtensionOverrides({
extensions: [
createExtension({
name: 'a',
attachTo: { id: 'app', input: 'apis' },
output: [],
factory: () => [],
}),
createExtension({
namespace: 'b',
attachTo: { id: 'app', input: 'apis' },
output: [],
factory: () => [],
}),
createExtension({
kind: 'k',
namespace: 'c',
name: 'n',
attachTo: { id: 'app', input: 'apis' },
output: [],
factory: () => [],
}),
],
}),
).toMatchInlineSnapshot(`
{
"$$type": "@backstage/ExtensionOverrides",
"extensions": [
{
"$$type": "@backstage/Extension",
"T": undefined,
"attachTo": {
"id": "app",
"input": "apis",
},
"configSchema": undefined,
"disabled": false,
"factory": [Function],
"id": "a",
"inputs": {},
"output": [],
"toString": [Function],
"version": "v2",
},
{
"$$type": "@backstage/Extension",
"T": undefined,
"attachTo": {
"id": "app",
"input": "apis",
},
"configSchema": undefined,
"disabled": false,
"factory": [Function],
"id": "b",
"inputs": {},
"output": [],
"toString": [Function],
"version": "v2",
},
{
"$$type": "@backstage/Extension",
"T": undefined,
"attachTo": {
"id": "app",
"input": "apis",
},
"configSchema": undefined,
"disabled": false,
"factory": [Function],
"id": "k:c/n",
"inputs": {},
"output": [],
"toString": [Function],
"version": "v2",
},
],
"featureFlags": [],
"toString": [Function],
"version": "v1",
}
`);
});
it('should convert to internal overrides', () => {
const overrides = createExtensionOverrides({
extensions: [
createExtension({
namespace: 'a',
attachTo: { id: 'app', input: 'apis' },
output: [],
factory: () => [],
}),
],
});
const internal = toInternalExtensionOverrides(overrides);
expect(internal).toBe(overrides);
});
});
@@ -1,80 +0,0 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ExtensionDefinition } from './createExtension';
import {
Extension,
resolveExtensionDefinition,
} from './resolveExtensionDefinition';
import { ExtensionOverrides, FeatureFlagConfig } from './types';
/**
* @deprecated Use {@link createFrontendModule} instead.
* @public
*/
export interface ExtensionOverridesOptions {
extensions: ExtensionDefinition[];
featureFlags?: FeatureFlagConfig[];
}
/** @internal */
export interface InternalExtensionOverrides extends ExtensionOverrides {
readonly version: 'v1';
readonly extensions: Extension<unknown>[];
readonly featureFlags: FeatureFlagConfig[];
}
/**
* @deprecated Use {@link createFrontendModule} instead.
* @public
*/
export function createExtensionOverrides(
options: ExtensionOverridesOptions,
): ExtensionOverrides {
const extensions = options.extensions.map(def =>
resolveExtensionDefinition(def),
);
const featureFlags = options.featureFlags ?? [];
return {
$$type: '@backstage/ExtensionOverrides',
version: 'v1',
extensions,
featureFlags,
toString() {
const ex = extensions.map(String).join(',');
const ff = featureFlags.map(f => f.name).join(',');
return `ExtensionOverrides{extensions=[${ex}],featureFlags=[${ff}]}`;
},
} as InternalExtensionOverrides;
}
/** @internal */
export function toInternalExtensionOverrides(
overrides: ExtensionOverrides,
): InternalExtensionOverrides {
const internal = overrides as InternalExtensionOverrides;
if (internal.$$type !== '@backstage/ExtensionOverrides') {
throw new Error(
`Invalid extension overrides instance, bad type '${internal.$$type}'`,
);
}
if (internal.version !== 'v1') {
throw new Error(
`Invalid extension overrides instance, bad version '${internal.version}'`,
);
}
return internal;
}
@@ -15,7 +15,8 @@
*/
import React from 'react';
import { createApp } from '@backstage/frontend-app-api';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { createApp } from '../../../frontend-defaults/src/createApp';
import { screen } from '@testing-library/react';
import { FrontendPlugin, createFrontendPlugin } from './createFrontendPlugin';
import { JsonObject } from '@backstage/types';
@@ -170,7 +171,9 @@ describe('createFrontendPlugin', () => {
}
`);
// @ts-expect-error
expect(plugin.getExtension('nonexistent')).toBeUndefined();
expect(() => plugin.getExtension('nonexistent')).toThrow(
/Attempted to get non-existent extension/,
);
await renderWithEffects(
createTestAppRoot({
@@ -14,7 +14,10 @@
* limitations under the License.
*/
import { OpaqueExtensionDefinition } from '@internal/frontend';
import {
OpaqueExtensionDefinition,
OpaqueFrontendPlugin,
} from '@internal/frontend';
import { ExtensionDefinition } from './createExtension';
import {
Extension,
@@ -27,7 +30,9 @@ import { AnyExternalRoutes, AnyRoutes, FeatureFlagConfig } from './types';
export interface FrontendPlugin<
TRoutes extends AnyRoutes = AnyRoutes,
TExternalRoutes extends AnyExternalRoutes = AnyExternalRoutes,
TExtensionMap extends { [id in string]: ExtensionDefinition } = {},
TExtensionMap extends { [id in string]: ExtensionDefinition } = {
[id in string]: ExtensionDefinition;
},
> {
readonly $$type: '@backstage/FrontendPlugin';
readonly id: string;
@@ -39,15 +44,6 @@ export interface FrontendPlugin<
}): FrontendPlugin<TRoutes, TExternalRoutes, TExtensionMap>;
}
/**
* @public
* @deprecated Use {@link FrontendPlugin} instead.
*/
export type BackstagePlugin<
TRoutes extends AnyRoutes = AnyRoutes,
TExternalRoutes extends AnyExternalRoutes = AnyExternalRoutes,
TExtensionMap extends { [id in string]: ExtensionDefinition } = {},
> = FrontendPlugin<TRoutes, TExternalRoutes, TExtensionMap>;
/** @public */
export interface PluginOptions<
TId extends string,
@@ -62,16 +58,6 @@ export interface PluginOptions<
featureFlags?: FeatureFlagConfig[];
}
/** @public */
export interface InternalFrontendPlugin<
TRoutes extends AnyRoutes = AnyRoutes,
TExternalRoutes extends AnyExternalRoutes = AnyExternalRoutes,
> extends FrontendPlugin<TRoutes, TExternalRoutes> {
readonly version: 'v1';
readonly extensions: Extension<unknown>[];
readonly featureFlags: FeatureFlagConfig[];
}
/** @public */
export function createFrontendPlugin<
TId extends string,
@@ -121,16 +107,20 @@ export function createFrontendPlugin<
);
}
return {
$$type: '@backstage/FrontendPlugin',
version: 'v1',
return OpaqueFrontendPlugin.createInstance('v1', {
id: options.id,
routes: options.routes ?? ({} as TRoutes),
externalRoutes: options.externalRoutes ?? ({} as TExternalRoutes),
featureFlags: options.featureFlags ?? [],
extensions,
extensions: extensions,
getExtension(id) {
return extensionDefinitionsById.get(id);
const ext = extensionDefinitionsById.get(id);
if (!ext) {
throw new Error(
`Attempted to get non-existent extension '${id}' from plugin '${options.id}'`,
);
}
return ext;
},
toString() {
return `Plugin{id=${options.id}}`;
@@ -152,45 +142,5 @@ export function createFrontendPlugin<
extensions: [...nonOverriddenExtensions, ...overrides.extensions],
});
},
} as InternalFrontendPlugin<TRoutes, TExternalRoutes>;
});
}
/** @internal */
export function isInternalFrontendPlugin(opaque: {
$$type: string;
}): opaque is InternalFrontendPlugin {
if (
opaque.$$type === '@backstage/FrontendPlugin' ||
opaque.$$type === '@backstage/BackstagePlugin'
) {
// Make sure we throw if invalid
toInternalFrontendPlugin(opaque as FrontendPlugin);
return true;
}
return false;
}
/** @internal */
export function toInternalFrontendPlugin(
plugin: FrontendPlugin,
): InternalFrontendPlugin {
const internal = plugin as InternalFrontendPlugin;
if (
internal.$$type !== '@backstage/FrontendPlugin' &&
internal.$$type !== '@backstage/BackstagePlugin'
) {
throw new Error(`Invalid plugin instance, bad type '${internal.$$type}'`);
}
if (internal.version !== 'v1') {
throw new Error(
`Invalid plugin instance, bad version '${internal.version}'`,
);
}
return internal;
}
/**
* @public
* @deprecated Use {@link createFrontendPlugin} instead.
*/
export const createPlugin = createFrontendPlugin;
@@ -37,10 +37,8 @@ export {
type ConfigurableExtensionDataRef,
} from './createExtensionDataRef';
export {
createPlugin,
createFrontendPlugin,
type FrontendPlugin,
type BackstagePlugin,
type PluginOptions,
} from './createFrontendPlugin';
export {
@@ -48,10 +46,6 @@ export {
type FrontendModule,
type CreateFrontendModuleOptions,
} from './createFrontendModule';
export {
createExtensionOverrides,
type ExtensionOverridesOptions,
} from './createExtensionOverrides';
export { type Extension } from './resolveExtensionDefinition';
export {
type AnyRoutes,
@@ -108,103 +108,42 @@ describe('ResolveExtensionId', () => {
it('should resolve extension IDs correctly', () => {
type NamedExtension<
TKind extends string | undefined,
TNamespace extends string | undefined,
TName extends string | undefined,
> = ExtensionDefinition<{
kind: TKind;
namespace: TNamespace;
name: TName;
output: any;
}>;
const id1: 'k:ns' = {} as ResolveExtensionId<
NamedExtension<'k', 'ns', undefined>,
undefined
NamedExtension<'k', undefined>,
'ns'
>;
const id2: 'k:n' = {} as ResolveExtensionId<
NamedExtension<'k', undefined, 'n'>,
undefined
const id2: 'ns/n' = {} as ResolveExtensionId<
NamedExtension<undefined, 'n'>,
'ns'
>;
const id3: 'ns/n' = {} as ResolveExtensionId<
NamedExtension<undefined, 'ns', 'n'>,
undefined
const id3: 'ns' = {} as ResolveExtensionId<
NamedExtension<undefined, undefined>,
'ns'
>;
const id4: never = {} as ResolveExtensionId<
NamedExtension<'k', undefined, undefined>,
undefined
>;
const id5: 'ns' = {} as ResolveExtensionId<
NamedExtension<undefined, 'ns', undefined>,
undefined
>;
const id6: 'n' = {} as ResolveExtensionId<
NamedExtension<undefined, undefined, 'n'>,
undefined
>;
const id7: 'k:ns/n' = {} as ResolveExtensionId<
NamedExtension<'k', 'ns', 'n'>,
undefined
>;
const id8: 'k:ns2' = {} as ResolveExtensionId<
NamedExtension<'k', 'ns', undefined>,
'ns2'
>;
const id9: 'k:ns2/n' = {} as ResolveExtensionId<
NamedExtension<'k', undefined, 'n'>,
'ns2'
>;
const ida: 'ns2/n' = {} as ResolveExtensionId<
NamedExtension<undefined, 'ns', 'n'>,
'ns2'
>;
const idb: 'k:ns2' = {} as ResolveExtensionId<
NamedExtension<'k', undefined, undefined>,
'ns2'
>;
const idc: 'ns2' = {} as ResolveExtensionId<
NamedExtension<undefined, 'ns', undefined>,
'ns2'
>;
const idd: 'ns2/n' = {} as ResolveExtensionId<
NamedExtension<undefined, undefined, 'n'>,
'ns2'
>;
const ide: 'k:ns2/n' = {} as ResolveExtensionId<
NamedExtension<'k', 'ns', 'n'>,
'ns2'
const id4: 'k:ns/n' = {} as ResolveExtensionId<
NamedExtension<'k', 'n'>,
'ns'
>;
const invalid1: never = {} as ResolveExtensionId<
NamedExtension<string | undefined, 'ns', 'n'>,
undefined
>;
const invalid2: never = {} as ResolveExtensionId<
NamedExtension<'k', string | undefined, 'n'>,
undefined
>;
const invalid3: never = {} as ResolveExtensionId<
NamedExtension<'k', 'ns', string | undefined>,
undefined
NamedExtension<'k', string | undefined>,
'ns'
>;
expect([
id1,
id2,
id3,
id4,
id5,
id6,
id7,
id8,
id9,
ida,
idb,
idc,
idd,
ide,
invalid1,
invalid2,
invalid3,
]).toBeDefined();
const invalid2: never = {} as ResolveExtensionId<
NamedExtension<'k', string>,
'ns'
>;
expect([id1, id2, id3, id4, invalid1, invalid2]).toBeDefined();
});
});
@@ -111,22 +111,15 @@ export function toInternalExtension<TConfig, TConfigInput>(
/** @ignore */
export type ResolveExtensionId<
TExtension extends ExtensionDefinition,
TDefaultNamespace extends string | undefined,
TNamespace extends string,
> = TExtension extends ExtensionDefinition<{
kind: infer IKind extends string | undefined;
namespace: infer INamespace extends string | undefined;
name: infer IName extends string | undefined;
}>
? [string | undefined] extends [IKind | INamespace | IName]
? [string] extends [IKind | IName]
? never
: (
(
undefined extends TDefaultNamespace ? INamespace : TDefaultNamespace
) extends infer ISelectedNamespace extends string
? undefined extends IName
? ISelectedNamespace
: `${ISelectedNamespace}/${IName}`
: IName
undefined extends IName ? TNamespace : `${TNamespace}/${IName}`
) extends infer INamePart extends string
? IKind extends string
? `${IKind}:${INamePart}`