diff --git a/.changeset/whole-glasses-visit.md b/.changeset/whole-glasses-visit.md new file mode 100644 index 0000000000..e318ce5956 --- /dev/null +++ b/.changeset/whole-glasses-visit.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +Added inline documentation for `createExtension`, `createExtensionBlueprint`, `createFrontendPlugin`, and `createFrontendModule`. diff --git a/packages/frontend-plugin-api/report.api.md b/packages/frontend-plugin-api/report.api.md index 5cc679815d..78c2ba361f 100644 --- a/packages/frontend-plugin-api/report.api.md +++ b/packages/frontend-plugin-api/report.api.md @@ -393,7 +393,7 @@ export { createApiFactory }; export { createApiRef }; -// @public (undocumented) +// @public export function createExtension< UOutput extends ExtensionDataRef, TInputs extends { @@ -688,7 +688,7 @@ export interface CreateFrontendFeatureLoaderOptions { >; } -// @public (undocumented) +// @public export function createFrontendModule< TId extends string, TExtensions extends readonly ExtensionDefinition[] = [], @@ -707,7 +707,7 @@ export interface CreateFrontendModuleOptions< pluginId: TPluginId; } -// @public (undocumented) +// @public export function createFrontendPlugin< TId extends string, TRoutes extends { diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index de97f8084a..0c0b75a572 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -27,7 +27,12 @@ import { z } from 'zod'; import { createSchemaFromZod } from '../schema/createSchemaFromZod'; import { OpaqueExtensionDefinition } from '@internal/frontend'; import { ExtensionDataContainer } from './types'; -import { ExtensionBlueprintDefineParams } from './createExtensionBlueprint'; +import { + ExtensionBlueprint, + ExtensionBlueprintDefineParams, +} from './createExtensionBlueprint'; +import { FrontendPlugin } from './createFrontendPlugin'; +import { FrontendModule } from './createFrontendModule'; /** * This symbol is used to pass parameter overrides from the extension override to the blueprint factory @@ -281,7 +286,41 @@ export type ExtensionDefinition< }>; }; -/** @public */ +/** + * Creates a new extension definition for installation in a Backstage app. + * + * @remarks + * + * This is a low-level function for creation of extensions with arbitrary inputs + * and outputs and is typically only intended to be used for advanced overrides + * or framework-level extensions. For most extension creation needs, it is + * recommended to use existing {@link ExtensionBlueprint}s instead. You can find + * blueprints both in the `@backstage/frontend-plugin-api` package as well as + * other plugin libraries. There is also a list of + * {@link https://backstage.io/docs/frontend-system/building-plugins/common-extension-blueprints | commonly used blueprints} + * in the frontend system documentation. + * + * Extension definitions that are created with this function can be installed in + * a Backstage app via a {@link FrontendPlugin} or {@link FrontendModule}. + * + * For more details on how extensions work, see the + * {@link https://backstage.io/docs/frontend-system/architecture/extensions | documentation for extensions}. + * + * @example + * + * ```ts + * const myExtension = createExtension({ + * name: 'example', + * attachTo: { id: 'app', input: 'root' }, + * output: [coreExtensionData.reactElement], + * factory() { + * return [coreExtensionData.reactElement(

Hello, world!

)]; + * }, + * }); + * ``` + * + * @public + */ export function createExtension< UOutput extends ExtensionDataRef, TInputs extends { diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index da7e6a8f02..1a24eed712 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -34,6 +34,7 @@ import { resolveInputOverrides, } from './resolveInputOverrides'; import { ExtensionDataContainer } from './types'; +import { PageBlueprint } from '../blueprints/PageBlueprint'; /** * A function used to define a parameter mapping function in order to facilitate @@ -403,9 +404,52 @@ function unwrapParams( } /** - * A simpler replacement for wrapping up `createExtension` inside a kind or type. This allows for a cleaner API for creating - * types and instances of those types. + * Creates a new extension blueprint that encapsulates the creation of + * extensions of particular kinds. * + * @remarks + * + * For details on how blueprints work, see the + * {@link https://backstage.io/docs/frontend-system/architecture/extension-blueprints | documentation for extension blueprints} + * in the frontend system documentation. + * + * Extension blueprints make it much easier for users to create new extensions + * for your plugin. Rather than letting them use {@link createExtension} + * directly, you can define a set of parameters and default factory for your + * blueprint, removing a lot of the boilerplate and complexity that is otherwise + * needed to create an extension. + * + * Each blueprint has its own `kind` that helps identify and group the + * extensions that have been created with it. For example the + * {@link PageBlueprint} has the kind `'page'`, and extensions created with it + * will be given the ID `'page:[/]'`. Blueprints should always + * be exported as `Blueprint`. + * + * When creating a blueprint the type of the parameters are inferred from the + * `factory` function that you provide. The exception to that is when you need + * your blueprint to include inferred type parameters, in which case you need to + * use the `defineParams` option. See the documentation for the `defineParams` + * option for more details on how that works. + * + * @example + * ```tsx + * // In your plugin library + * export const GreetingBlueprint = createExtensionBlueprint({ + * kind: 'greeting', + * attachTo: { id: 'example', input: 'greetings' }, + * output: [coreExtensionData.reactElement], + * factory(params: { greeting: string }) { + * return [coreExtensionData.reactElement(

{params.greeting}

)]; + * }, + * }); + * + * // Someone using your blueprint in their plugin + * const exampleGreeting = GreetingBlueprint.make({ + * params: { + * greeting: 'Hello, world!', + * }, + * }); + * ``` * @public */ export function createExtensionBlueprint< diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendModule.ts b/packages/frontend-plugin-api/src/wiring/createFrontendModule.ts index bece1539fa..7921f98635 100644 --- a/packages/frontend-plugin-api/src/wiring/createFrontendModule.ts +++ b/packages/frontend-plugin-api/src/wiring/createFrontendModule.ts @@ -45,7 +45,45 @@ export interface InternalFrontendModule extends FrontendModule { readonly featureFlags: FeatureFlagConfig[]; } -/** @public */ +/** + * Creates a new module that can be installed in a Backstage app. + * + * @remarks + * + * Modules are used to add or override extensions for an existing plugin. If a + * module provides an extension with the same ID as one provided by the plugin, + * the extension provided by the module will always take precedence. + * + * Every module is created for a specific plugin by providing the + * unique ID of the plugin that the module should be installed for. If that + * plugin is not present in the app, the module will be ignored and have no + * effect. + * + * For more information on how modules work, see the + * {@link https://backstage.io/docs/frontend-system/architecture/extension-overrides#creating-a-frontend-module | documentation for modules} + * in the frontend system documentation. + * + * It is recommended to name the module variable of the form `Module`. + * + * @example + * + * ```tsx + * import { createFrontendModule } from '@backstage/frontend-plugin-api'; + * + * export const exampleModuleCustomPage = createFrontendModule({ + * pluginId: 'example', + * extensions: [ + * // Overrides the default page for the 'example' plugin + * PageBlueprint.make({ + * path: '/example', + * loader: () => import('./CustomPage').then(m => ), + * }), + * ], + * }); + * ``` + * + * @public + */ export function createFrontendModule< TId extends string, TExtensions extends readonly ExtensionDefinition[] = [], diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts index f0d923d3f9..f545195e81 100644 --- a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts +++ b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts @@ -135,7 +135,36 @@ export interface PluginOptions< info?: FrontendPluginInfoOptions; } -/** @public */ +/** + * Creates a new plugin that can be installed in a Backstage app. + * + * @remarks + * + * Every plugin is created with a unique ID and a set of extensions + * that are installed as part of the plugin. + * + * For more information on how plugins work, see the + * {@link https://backstage.io/docs/frontend-system/building-plugins/index | documentation for plugins} + * in the frontend system documentation. + * + * @example + * + * ```tsx + * import { createFrontendPlugin } from '@backstage/frontend-plugin-api'; + * + * export const examplePlugin = createFrontendPlugin({ + * pluginId: 'example', + * extensions: [ + * PageBlueprint.make({ + * path: '/example', + * loader: () => import('./ExamplePage').then(m => ), + * }), + * ], + * }); + * ``` + * + * @public + */ export function createFrontendPlugin< TId extends string, TRoutes extends { [name in string]: RouteRef | SubRouteRef } = {},