From 0ad542cbc7e144c45feb4c2b20babebae9a5c60d Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 24 Jul 2024 14:36:22 +0200 Subject: [PATCH] feat: added input types for the config to the extensions Signed-off-by: blam --- .../src/collectLegacyRoutes.tsx | 4 +- .../extractRouteInfoFromAppNode.test.ts | 2 +- .../src/tree/createAppTree.ts | 2 +- .../src/tree/instantiateAppNodeTree.test.ts | 14 +-- .../src/tree/resolveAppTree.test.ts | 4 +- packages/frontend-plugin-api/api-report.md | 102 +++++++++++++----- .../src/apis/definitions/AppTreeApi.ts | 2 +- .../src/extensions/IconBundleBlueprint.ts | 6 ++ .../src/schema/createSchemaFromZod.test.ts | 15 ++- .../src/schema/createSchemaFromZod.ts | 2 +- .../frontend-plugin-api/src/schema/types.ts | 4 +- .../src/wiring/createExtension.test.ts | 8 +- .../src/wiring/createExtension.ts | 78 +++++++++++--- .../src/wiring/createExtensionBlueprint.ts | 38 ++++++- .../src/wiring/createExtensionOverrides.ts | 2 +- .../src/wiring/createPlugin.ts | 2 +- .../src/wiring/resolveExtensionDefinition.ts | 23 ++-- packages/frontend-test-utils/api-report.md | 6 +- .../src/app/createExtensionTester.tsx | 14 +-- plugins/catalog-react/api-report-alpha.md | 19 ++-- plugins/catalog/api-report-alpha.md | 4 +- .../alpha/createCatalogFilterExtension.tsx | 2 +- plugins/search-react/api-report-alpha.md | 2 +- plugins/search/api-report-alpha.md | 27 +++-- plugins/techdocs/api-report-alpha.md | 23 ++-- plugins/user-settings/api-report-alpha.md | 11 +- 26 files changed, 300 insertions(+), 116 deletions(-) diff --git a/packages/core-compat-api/src/collectLegacyRoutes.tsx b/packages/core-compat-api/src/collectLegacyRoutes.tsx index b9f4e5528c..3523b0a40b 100644 --- a/packages/core-compat-api/src/collectLegacyRoutes.tsx +++ b/packages/core-compat-api/src/collectLegacyRoutes.tsx @@ -101,7 +101,7 @@ function visitRouteChildren(options: { parentExtensionId: string; context: { pluginId: string; - extensions: ExtensionDefinition[]; + extensions: ExtensionDefinition[]; getUniqueName: () => string; discoverPlugin: (plugin: LegacyBackstagePlugin) => void; }; @@ -154,7 +154,7 @@ export function collectLegacyRoutes( ): BackstagePlugin[] { const pluginExtensions = new Map< LegacyBackstagePlugin, - ExtensionDefinition[] + ExtensionDefinition[] >(); const getUniqueName = (() => { diff --git a/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts index 1c332b331f..bdab2781e0 100644 --- a/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts +++ b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts @@ -70,7 +70,7 @@ function createTestExtension(options: { }); } -function routeInfoFromExtensions(extensions: ExtensionDefinition[]) { +function routeInfoFromExtensions(extensions: ExtensionDefinition[]) { const plugin = createPlugin({ id: 'test', extensions, diff --git a/packages/frontend-app-api/src/tree/createAppTree.ts b/packages/frontend-app-api/src/tree/createAppTree.ts index 12c7f7fc3a..ee9704c3c3 100644 --- a/packages/frontend-app-api/src/tree/createAppTree.ts +++ b/packages/frontend-app-api/src/tree/createAppTree.ts @@ -25,7 +25,7 @@ import { instantiateAppNodeTree } from './instantiateAppNodeTree'; /** @internal */ export interface CreateAppTreeOptions { features: FrontendFeature[]; - builtinExtensions: Extension[]; + builtinExtensions: Extension[]; config: Config; } diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts index c5c66e8319..a82c8d99ee 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -59,22 +59,22 @@ const simpleExtension = resolveExtensionDefinition( }), ); -function makeSpec( - extension: Extension, +function makeSpec( + extension: Extension, spec?: Partial, ): AppNodeSpec { return { id: extension.id, attachTo: extension.attachTo, disabled: extension.disabled, - extension, + extension: extension as Extension, source: undefined, ...spec, }; } -function makeNode( - extension: Extension, +function makeNode( + extension: Extension, spec?: Partial, ): AppNode { return { @@ -85,8 +85,8 @@ function makeNode( }; } -function makeInstanceWithId( - extension: Extension, +function makeInstanceWithId( + extension: Extension, config?: TConfig, ): AppNode { const node = makeNode(extension, { config }); diff --git a/packages/frontend-app-api/src/tree/resolveAppTree.test.ts b/packages/frontend-app-api/src/tree/resolveAppTree.test.ts index f964fceb50..d9b65555fe 100644 --- a/packages/frontend-app-api/src/tree/resolveAppTree.test.ts +++ b/packages/frontend-app-api/src/tree/resolveAppTree.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createExtension } from '@backstage/frontend-plugin-api'; +import { createExtension, Extension } from '@backstage/frontend-plugin-api'; import { resolveAppTree } from './resolveAppTree'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; @@ -26,7 +26,7 @@ const extension = resolveExtensionDefinition( output: {}, factory: () => ({}), }), -); +) as Extension; const baseSpec = { extension, diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index 45ed51b415..c4a96cba59 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -230,7 +230,7 @@ export interface AppNodeSpec { // (undocumented) readonly disabled: boolean; // (undocumented) - readonly extension: Extension; + readonly extension: Extension; // (undocumented) readonly id: string; // (undocumented) @@ -391,7 +391,7 @@ export function createApiExtension< configSchema?: PortableSchema; inputs?: TInputs; }, -): ExtensionDefinition; +): ExtensionDefinition; // @public (undocumented) export namespace createApiExtension { @@ -487,7 +487,7 @@ export function createComponentExtension< inputs: Expand>; }) => ComponentType; }; -}): ExtensionDefinition; +}): ExtensionDefinition; // @public (undocumented) export namespace createComponentExtension { @@ -512,12 +512,34 @@ export function createExtension< TOutput extends AnyExtensionDataMap, TInputs extends AnyExtensionInputMap, TConfig, + TConfigInput, TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType; }, >( - options: CreateExtensionOptions, -): ExtensionDefinition; + options: CreateExtensionOptions< + TOutput, + TInputs, + TConfig, + TConfigInput, + TConfigSchema + >, +): ExtensionDefinition< + TConfig & + (string extends keyof TConfigSchema + ? {} + : { + [key in keyof TConfigSchema]: z.infer>; + }), + TConfigInput & + (string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >) +>; // @public export function createExtensionBlueprint< @@ -545,6 +567,13 @@ export function createExtensionBlueprint< : { [key in keyof TConfigSchema]: z.infer>; }, + string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >, TDataRefs >; @@ -627,6 +656,7 @@ export interface CreateExtensionOptions< TOutput extends AnyExtensionDataMap, TInputs extends AnyExtensionInputMap, TConfig, + TConfigInput, TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType; }, @@ -641,15 +671,20 @@ export interface CreateExtensionOptions< schema: TConfigSchema; }; // @deprecated (undocumented) - configSchema?: PortableSchema; + configSchema?: PortableSchema; // (undocumented) disabled?: boolean; // (undocumented) factory(context: { node: AppNode; - config: TConfig & { - [key in keyof TConfigSchema]: z.infer>; - }; + config: TConfig & + (string extends keyof TConfigSchema + ? {} + : { + [key in keyof TConfigSchema]: z.infer< + ReturnType + >; + }); inputs: Expand>; }): Expand>; // (undocumented) @@ -702,9 +737,14 @@ export function createNavItemExtension(options: { routeRef: RouteRef; title: string; icon: IconComponent_2; -}): ExtensionDefinition<{ - title: string; -}>; +}): ExtensionDefinition< + { + title: string; + }, + { + title?: string | undefined; + } +>; // @public (undocumented) export namespace createNavItemExtension { @@ -726,7 +766,7 @@ export function createNavLogoExtension(options: { namespace?: string; logoIcon: JSX.Element; logoFull: JSX.Element; -}): ExtensionDefinition; +}): ExtensionDefinition<{}, {}>; // @public (undocumented) export namespace createNavLogoExtension { @@ -837,7 +877,7 @@ export namespace createRouterExtension { // @public @deprecated (undocumented) export function createSchemaFromZod( schemaCreator: (zImpl: typeof z) => ZodSchema, -): PortableSchema; +): PortableSchema; // @public (undocumented) export function createSignInPageExtension< @@ -881,7 +921,7 @@ export function createSubRouteRef< // @public (undocumented) export function createThemeExtension( theme: AppTheme, -): ExtensionDefinition; +): ExtensionDefinition<{}, {}>; // @public (undocumented) export namespace createThemeExtension { @@ -897,7 +937,7 @@ export namespace createThemeExtension { export function createTranslationExtension(options: { name?: string; resource: TranslationResource | TranslationMessages; -}): ExtensionDefinition; +}): ExtensionDefinition<{}, {}>; // @public (undocumented) export namespace createTranslationExtension { @@ -935,7 +975,7 @@ export { ErrorApiErrorContext }; export { errorApiRef }; // @public (undocumented) -export interface Extension { +export interface Extension { // (undocumented) $$type: '@backstage/Extension'; // (undocumented) @@ -944,7 +984,7 @@ export interface Extension { input: string; }; // (undocumented) - readonly configSchema?: PortableSchema; + readonly configSchema?: PortableSchema; // (undocumented) readonly disabled: boolean; // (undocumented) @@ -959,6 +999,9 @@ export interface ExtensionBlueprint< TConfig extends { [key in string]: unknown; }, + TConfigInput extends { + [key in string]: unknown; + }, TDataRefs extends AnyExtensionDataMap, > { // (undocumented) @@ -1014,7 +1057,15 @@ export interface ExtensionBlueprint< [key in keyof TExtensionConfigSchema]: z.infer< ReturnType >; - } & TConfig + } & TConfig, + z.input< + z.ZodObject<{ + [key in keyof TExtensionConfigSchema]: ReturnType< + TExtensionConfigSchema[key] + >; + }> + > & + TConfigInput >; } @@ -1062,7 +1113,7 @@ export type ExtensionDataValues = { }; // @public (undocumented) -export interface ExtensionDefinition { +export interface ExtensionDefinition { // (undocumented) $$type: '@backstage/ExtensionDefinition'; // (undocumented) @@ -1071,7 +1122,7 @@ export interface ExtensionDefinition { input: string; }; // (undocumented) - readonly configSchema?: PortableSchema; + readonly configSchema?: PortableSchema; // (undocumented) readonly disabled: boolean; // (undocumented) @@ -1107,7 +1158,7 @@ export interface ExtensionOverrides { // @public (undocumented) export interface ExtensionOverridesOptions { // (undocumented) - extensions: ExtensionDefinition[]; + extensions: ExtensionDefinition[]; // (undocumented) featureFlags?: FeatureFlagConfig[]; } @@ -1171,6 +1222,7 @@ export const IconBundleBlueprint: ExtensionBlueprint< >; }, {}, + {}, { icons: ConfigurableExtensionDataRef< 'core.icons', @@ -1235,7 +1287,7 @@ export interface PluginOptions< ExternalRoutes extends AnyExternalRoutes, > { // (undocumented) - extensions?: ExtensionDefinition[]; + extensions?: ExtensionDefinition[]; // (undocumented) externalRoutes?: ExternalRoutes; // (undocumented) @@ -1247,8 +1299,8 @@ export interface PluginOptions< } // @public (undocumented) -export type PortableSchema = { - parse: (input: unknown) => TOutput; +export type PortableSchema = { + parse: (input: TInput) => TOutput; schema: JsonObject; }; diff --git a/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts b/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts index 6c79a16964..4b5672f894 100644 --- a/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts +++ b/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts @@ -29,7 +29,7 @@ import { BackstagePlugin, Extension, ExtensionDataRef } from '../../wiring'; export interface AppNodeSpec { readonly id: string; readonly attachTo: { id: string; input: string }; - readonly extension: Extension; + readonly extension: Extension; readonly disabled: boolean; readonly config?: unknown; readonly source?: BackstagePlugin; diff --git a/packages/frontend-plugin-api/src/extensions/IconBundleBlueprint.ts b/packages/frontend-plugin-api/src/extensions/IconBundleBlueprint.ts index 2ad1404102..438da09b4a 100644 --- a/packages/frontend-plugin-api/src/extensions/IconBundleBlueprint.ts +++ b/packages/frontend-plugin-api/src/extensions/IconBundleBlueprint.ts @@ -28,6 +28,12 @@ export const IconBundleBlueprint = createExtensionBlueprint({ output: { icons: iconsDataRef, }, + config: { + schema: { + icons: z => z.string().default('blob'), + test: z => z.string(), + }, + }, factory: (params: { icons: { [key in string]: IconComponent } }) => params, dataRefs: { icons: iconsDataRef, diff --git a/packages/frontend-plugin-api/src/schema/createSchemaFromZod.test.ts b/packages/frontend-plugin-api/src/schema/createSchemaFromZod.test.ts index 90eba44249..477a1a58c6 100644 --- a/packages/frontend-plugin-api/src/schema/createSchemaFromZod.test.ts +++ b/packages/frontend-plugin-api/src/schema/createSchemaFromZod.test.ts @@ -25,10 +25,19 @@ describe('createSchemaFromZod', () => { }), ); - expect(() => parse({ derp: { bar: 'derp' } })).toThrow( + expect(() => { + // @ts-expect-error + return parse({ derp: { bar: 'derp' } }); + }).toThrow( `Missing required value at 'foo'; Expected number, received string at 'derp.bar'`, ); - expect(() => parse(undefined)).toThrow(`Missing required value`); - expect(() => parse('derp')).toThrow(`Expected object, received string`); + expect(() => { + // @ts-expect-error + return parse(undefined); + }).toThrow(`Missing required value`); + expect(() => { + // @ts-expect-error + return parse('derp'); + }).toThrow(`Expected object, received string`); }); }); diff --git a/packages/frontend-plugin-api/src/schema/createSchemaFromZod.ts b/packages/frontend-plugin-api/src/schema/createSchemaFromZod.ts index c69ef5dc60..9e63515ae4 100644 --- a/packages/frontend-plugin-api/src/schema/createSchemaFromZod.ts +++ b/packages/frontend-plugin-api/src/schema/createSchemaFromZod.ts @@ -25,7 +25,7 @@ import { PortableSchema } from './types'; */ export function createSchemaFromZod( schemaCreator: (zImpl: typeof z) => ZodSchema, -): PortableSchema { +): PortableSchema { const schema = schemaCreator(z); return { // TODO: Types allow z.array etc here but it will break stuff diff --git a/packages/frontend-plugin-api/src/schema/types.ts b/packages/frontend-plugin-api/src/schema/types.ts index 9636d3b896..5644068e90 100644 --- a/packages/frontend-plugin-api/src/schema/types.ts +++ b/packages/frontend-plugin-api/src/schema/types.ts @@ -17,7 +17,7 @@ import { JsonObject } from '@backstage/types'; /** @public */ -export type PortableSchema = { - parse: (input: unknown) => TOutput; +export type PortableSchema = { + parse: (input: TInput) => TOutput; schema: JsonObject; }; diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index 468da21eb7..5b737746c2 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -334,6 +334,7 @@ describe('createExtension', () => { foo: 'x', bar: 'y', baz: 'z', + // @ts-expect-error qux: 'w', }), ).toEqual({ @@ -349,8 +350,9 @@ describe('createExtension', () => { foo: 'x', bar: 'bar', }); - expect(() => extension.configSchema?.parse({})).toThrow( - "Missing required value at 'foo'", - ); + expect(() => { + // @ts-expect-error + return extension.configSchema?.parse({}); + }).toThrow("Missing required value at 'foo'"); }); }); diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index ac91557906..cc2bec91c9 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -82,6 +82,7 @@ export interface CreateExtensionOptions< TOutput extends AnyExtensionDataMap, TInputs extends AnyExtensionInputMap, TConfig, + TConfigInput, TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType }, > { kind?: string; @@ -92,33 +93,38 @@ export interface CreateExtensionOptions< inputs?: TInputs; output: TOutput; /** @deprecated - use `config.schema` instead */ - configSchema?: PortableSchema; + configSchema?: PortableSchema; config?: { schema: TConfigSchema; }; factory(context: { node: AppNode; - config: TConfig & { - [key in keyof TConfigSchema]: z.infer>; - }; + config: TConfig & + (string extends keyof TConfigSchema + ? {} + : { + [key in keyof TConfigSchema]: z.infer< + ReturnType + >; + }); inputs: Expand>; }): Expand>; } /** @public */ -export interface ExtensionDefinition { +export interface ExtensionDefinition { $$type: '@backstage/ExtensionDefinition'; readonly kind?: string; readonly namespace?: string; readonly name?: string; readonly attachTo: { id: string; input: string }; readonly disabled: boolean; - readonly configSchema?: PortableSchema; + readonly configSchema?: PortableSchema; } /** @internal */ -export interface InternalExtensionDefinition - extends ExtensionDefinition { +export interface InternalExtensionDefinition + extends ExtensionDefinition { readonly version: 'v1'; readonly inputs: AnyExtensionInputMap; readonly output: AnyExtensionDataMap; @@ -130,10 +136,13 @@ export interface InternalExtensionDefinition } /** @internal */ -export function toInternalExtensionDefinition( - overrides: ExtensionDefinition, -): InternalExtensionDefinition { - const internal = overrides as InternalExtensionDefinition; +export function toInternalExtensionDefinition( + overrides: ExtensionDefinition, +): InternalExtensionDefinition { + const internal = overrides as InternalExtensionDefinition< + TConfig, + TConfigInput + >; if (internal.$$type !== '@backstage/ExtensionDefinition') { throw new Error( `Invalid extension definition instance, bad type '${internal.$$type}'`, @@ -152,10 +161,32 @@ export function createExtension< TOutput extends AnyExtensionDataMap, TInputs extends AnyExtensionInputMap, TConfig, + TConfigInput, TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType }, >( - options: CreateExtensionOptions, -): ExtensionDefinition { + options: CreateExtensionOptions< + TOutput, + TInputs, + TConfig, + TConfigInput, + TConfigSchema + >, +): ExtensionDefinition< + TConfig & + (string extends keyof TConfigSchema + ? {} + : { + [key in keyof TConfigSchema]: z.infer>; + }), + TConfigInput & + (string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >) +> { const newConfigSchema = options.config?.schema; if (newConfigSchema && options.configSchema) { throw new Error(`Cannot provide both configSchema and config.schema`); @@ -205,5 +236,22 @@ export function createExtension< parts.push(`attachTo=${options.attachTo.id}@${options.attachTo.input}`); return `ExtensionDefinition{${parts.join(',')}}`; }, - } as InternalExtensionDefinition; + } as InternalExtensionDefinition< + TConfig & + (string extends keyof TConfigSchema + ? {} + : { + [key in keyof TConfigSchema]: z.infer< + ReturnType + >; + }), + TConfigInput & + (string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >) + >; } diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index ce3e650342..7393ca233e 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -67,6 +67,7 @@ export interface ExtensionBlueprint< TInputs extends AnyExtensionInputMap, TOutput extends AnyExtensionDataMap, TConfig extends { [key in string]: unknown }, + TConfigInput extends { [key in string]: unknown }, TDataRefs extends AnyExtensionDataMap, > { dataRefs: TDataRefs; @@ -125,7 +126,15 @@ export interface ExtensionBlueprint< [key in keyof TExtensionConfigSchema]: z.infer< ReturnType >; - } & TConfig + } & TConfig, + z.input< + z.ZodObject<{ + [key in keyof TExtensionConfigSchema]: ReturnType< + TExtensionConfigSchema[key] + >; + }> + > & + TConfigInput >; } @@ -199,7 +208,18 @@ class ExtensionBlueprintImpl< >; } & { [key in keyof TConfigSchema]: z.infer>; - } + }, + z.input< + z.ZodObject< + { + [key in keyof TExtensionConfigSchema]: ReturnType< + TExtensionConfigSchema[key] + >; + } & { + [key in keyof TConfigSchema]: ReturnType; + } + > + > > { const schema = { ...this.options.config?.schema, @@ -279,6 +299,13 @@ export function createExtensionBlueprint< string extends keyof TConfigSchema ? {} : { [key in keyof TConfigSchema]: z.infer> }, + string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >, TDataRefs > { return new ExtensionBlueprintImpl(options) as ExtensionBlueprint< @@ -290,6 +317,13 @@ export function createExtensionBlueprint< : { [key in keyof TConfigSchema]: z.infer>; }, + string extends keyof TConfigSchema + ? {} + : z.input< + z.ZodObject<{ + [key in keyof TConfigSchema]: ReturnType; + }> + >, TDataRefs >; } diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts index 1f88038944..75c43a36d2 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.ts @@ -23,7 +23,7 @@ import { ExtensionOverrides, FeatureFlagConfig } from './types'; /** @public */ export interface ExtensionOverridesOptions { - extensions: ExtensionDefinition[]; + extensions: ExtensionDefinition[]; featureFlags?: FeatureFlagConfig[]; } diff --git a/packages/frontend-plugin-api/src/wiring/createPlugin.ts b/packages/frontend-plugin-api/src/wiring/createPlugin.ts index 797c46e58e..b671d95d2f 100644 --- a/packages/frontend-plugin-api/src/wiring/createPlugin.ts +++ b/packages/frontend-plugin-api/src/wiring/createPlugin.ts @@ -34,7 +34,7 @@ export interface PluginOptions< id: string; routes?: Routes; externalRoutes?: ExternalRoutes; - extensions?: ExtensionDefinition[]; + extensions?: ExtensionDefinition[]; featureFlags?: FeatureFlagConfig[]; } diff --git a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts index 0a44de4ab4..eddd923702 100644 --- a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts +++ b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts @@ -26,16 +26,17 @@ import { import { PortableSchema } from '../schema'; /** @public */ -export interface Extension { +export interface Extension { $$type: '@backstage/Extension'; readonly id: string; readonly attachTo: { id: string; input: string }; readonly disabled: boolean; - readonly configSchema?: PortableSchema; + readonly configSchema?: PortableSchema; } /** @internal */ -export interface InternalExtension extends Extension { +export interface InternalExtension + extends Extension { readonly version: 'v1'; readonly inputs: AnyExtensionInputMap; readonly output: AnyExtensionDataMap; @@ -47,10 +48,10 @@ export interface InternalExtension extends Extension { } /** @internal */ -export function toInternalExtension( - overrides: Extension, -): InternalExtension { - const internal = overrides as InternalExtension; +export function toInternalExtension( + overrides: Extension, +): InternalExtension { + const internal = overrides as InternalExtension; if (internal.$$type !== '@backstage/Extension') { throw new Error( `Invalid extension instance, bad type '${internal.$$type}'`, @@ -65,10 +66,10 @@ export function toInternalExtension( } /** @internal */ -export function resolveExtensionDefinition( - definition: ExtensionDefinition, +export function resolveExtensionDefinition( + definition: ExtensionDefinition, context?: { namespace?: string }, -): Extension { +): Extension { const internalDefinition = toInternalExtensionDefinition(definition); const { name, kind, namespace: _, ...rest } = internalDefinition; const namespace = internalDefinition.namespace ?? context?.namespace; @@ -91,5 +92,5 @@ export function resolveExtensionDefinition( toString() { return `Extension{id=${id}}`; }, - } as Extension; + } as Extension; } diff --git a/packages/frontend-test-utils/api-report.md b/packages/frontend-test-utils/api-report.md index 9629e13bfa..3b368394ca 100644 --- a/packages/frontend-test-utils/api-report.md +++ b/packages/frontend-test-utils/api-report.md @@ -39,10 +39,10 @@ export { ErrorWithContext }; // @public (undocumented) export class ExtensionTester { // (undocumented) - add( - extension: ExtensionDefinition, + add( + extension: ExtensionDefinition, options?: { - config?: TConfig; + config?: TConfigInput; }, ): ExtensionTester; // (undocumented) diff --git a/packages/frontend-test-utils/src/app/createExtensionTester.tsx b/packages/frontend-test-utils/src/app/createExtensionTester.tsx index a98879fbba..69c4b09d06 100644 --- a/packages/frontend-test-utils/src/app/createExtensionTester.tsx +++ b/packages/frontend-test-utils/src/app/createExtensionTester.tsx @@ -88,9 +88,9 @@ const TestAppNavExtension = createExtension({ /** @public */ export class ExtensionTester { /** @internal */ - static forSubject( - subject: ExtensionDefinition, - options?: { config?: TConfig }, + static forSubject( + subject: ExtensionDefinition, + options?: { config?: TConfigInput }, ): ExtensionTester { const tester = new ExtensionTester(); const { output, factory, ...rest } = toInternalExtensionDefinition(subject); @@ -107,7 +107,7 @@ export class ExtensionTester { path: '/', }), }); - tester.add(extension, options); + tester.add(extension, options as TConfigInput & {}); return tester; } @@ -117,9 +117,9 @@ export class ExtensionTester { config?: JsonValue; }>(); - add( - extension: ExtensionDefinition, - options?: { config?: TConfig }, + add( + extension: ExtensionDefinition, + options?: { config?: TConfigInput }, ): ExtensionTester { const { name, namespace } = extension; diff --git a/plugins/catalog-react/api-report-alpha.md b/plugins/catalog-react/api-report-alpha.md index d2bfb29458..1ffa67068f 100644 --- a/plugins/catalog-react/api-report-alpha.md +++ b/plugins/catalog-react/api-report-alpha.md @@ -115,7 +115,7 @@ export function createEntityCardExtension< config: TConfig; inputs: Expand>; }) => Promise; -}): ExtensionDefinition; +}): ExtensionDefinition; // @alpha (undocumented) export function createEntityContentExtension< @@ -138,11 +138,18 @@ export function createEntityContentExtension< loader: (options: { inputs: Expand>; }) => Promise; -}): ExtensionDefinition<{ - title: string; - path: string; - filter?: string | undefined; -}>; +}): ExtensionDefinition< + { + title: string; + path: string; + filter?: string | undefined; + }, + { + filter?: string | undefined; + title?: string | undefined; + path?: string | undefined; + } +>; // @alpha export function isOwnerOf(owner: Entity, entity: Entity): boolean; diff --git a/plugins/catalog/api-report-alpha.md b/plugins/catalog/api-report-alpha.md index 102e2a7aa3..a62b047c4c 100644 --- a/plugins/catalog/api-report-alpha.md +++ b/plugins/catalog/api-report-alpha.md @@ -99,14 +99,14 @@ export const catalogTranslationRef: TranslationRef< // @alpha (undocumented) export function createCatalogFilterExtension< TInputs extends AnyExtensionInputMap, - TConfig = never, + TConfig, >(options: { namespace?: string; name?: string; inputs?: TInputs; configSchema?: PortableSchema; loader: (options: { config: TConfig }) => Promise; -}): ExtensionDefinition; +}): ExtensionDefinition; // @alpha (undocumented) const _default: BackstagePlugin< diff --git a/plugins/catalog/src/alpha/createCatalogFilterExtension.tsx b/plugins/catalog/src/alpha/createCatalogFilterExtension.tsx index 48ee647b81..d8a218f8f3 100644 --- a/plugins/catalog/src/alpha/createCatalogFilterExtension.tsx +++ b/plugins/catalog/src/alpha/createCatalogFilterExtension.tsx @@ -26,7 +26,7 @@ import { /** @alpha */ export function createCatalogFilterExtension< TInputs extends AnyExtensionInputMap, - TConfig = never, + TConfig, >(options: { namespace?: string; name?: string; diff --git a/plugins/search-react/api-report-alpha.md b/plugins/search-react/api-report-alpha.md index aea85d0f81..3da9ac23a4 100644 --- a/plugins/search-react/api-report-alpha.md +++ b/plugins/search-react/api-report-alpha.md @@ -25,7 +25,7 @@ export function createSearchResultListItemExtension< }, >( options: SearchResultItemExtensionOptions, -): ExtensionDefinition; +): ExtensionDefinition; // @alpha (undocumented) export namespace createSearchResultListItemExtension { diff --git a/plugins/search/api-report-alpha.md b/plugins/search/api-report-alpha.md index db2999ba25..e9b86f1f87 100644 --- a/plugins/search/api-report-alpha.md +++ b/plugins/search/api-report-alpha.md @@ -17,18 +17,29 @@ const _default: BackstagePlugin< export default _default; // @alpha (undocumented) -export const searchApi: ExtensionDefinition<{}>; +export const searchApi: ExtensionDefinition<{}, {}>; // @alpha (undocumented) -export const searchNavItem: ExtensionDefinition<{ - title: string; -}>; +export const searchNavItem: ExtensionDefinition< + { + title: string; + }, + { + title?: string | undefined; + } +>; // @alpha (undocumented) -export const searchPage: ExtensionDefinition<{ - path: string; - noTrack: boolean; -}>; +export const searchPage: ExtensionDefinition< + { + path: string; + noTrack: boolean; + }, + { + path: string; + noTrack: boolean; + } +>; // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/techdocs/api-report-alpha.md b/plugins/techdocs/api-report-alpha.md index cf9f5fa9a3..04a0c5c512 100644 --- a/plugins/techdocs/api-report-alpha.md +++ b/plugins/techdocs/api-report-alpha.md @@ -23,13 +23,22 @@ const _default: BackstagePlugin< export default _default; // @alpha (undocumented) -export const techDocsSearchResultListItemExtension: ExtensionDefinition<{ - lineClamp: number; - noTrack: boolean; - asListItem: boolean; - asLink: boolean; - title?: string | undefined; -}>; +export const techDocsSearchResultListItemExtension: ExtensionDefinition< + { + lineClamp: number; + noTrack: boolean; + asListItem: boolean; + asLink: boolean; + title?: string | undefined; + }, + { + lineClamp: number; + noTrack: boolean; + asListItem: boolean; + asLink: boolean; + title?: string | undefined; + } +>; // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/user-settings/api-report-alpha.md b/plugins/user-settings/api-report-alpha.md index b06fd1e583..fdb2c5f6bf 100644 --- a/plugins/user-settings/api-report-alpha.md +++ b/plugins/user-settings/api-report-alpha.md @@ -18,9 +18,14 @@ const _default: BackstagePlugin< export default _default; // @alpha (undocumented) -export const settingsNavItem: ExtensionDefinition<{ - title: string; -}>; +export const settingsNavItem: ExtensionDefinition< + { + title: string; + }, + { + title?: string | undefined; + } +>; // @alpha (undocumented) export const userSettingsTranslationRef: TranslationRef<