From a3ef72c05582f70d70ea1f4bffe0150856669e01 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Fri, 19 Jan 2024 11:35:06 +0100 Subject: [PATCH 01/11] docs: frontend plugin migration Signed-off-by: Vincenzo Scamporlino --- .../building-plugins/migrating.md | 125 ++++++++++++++++++ microsite/sidebars.json | 3 +- 2 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 docs/frontend-system/building-plugins/migrating.md diff --git a/docs/frontend-system/building-plugins/migrating.md b/docs/frontend-system/building-plugins/migrating.md new file mode 100644 index 0000000000..3131f51a43 --- /dev/null +++ b/docs/frontend-system/building-plugins/migrating.md @@ -0,0 +1,125 @@ +--- +id: migrating +title: Migrating an existing Frontend Plugin to the New Frontend System +sidebar_label: Migration Guide +# prettier-ignore +description: How to migrate an existing frontend plugin to the new frontend system +--- + +This guide allows you to migrate a frontend plugin and its own components, routes, apis to the new frontend system. + +The main concept is that routes, components, apis are now extensions. You can use the appropriate extension creators to migrate all of them to extensions. + +## Pages + +Pages that were previously created using the `createRoutableExtension` extension function can be migrated to the new Frontend System using the `createPageExtension` extension creator, exported by `@backstage/frontend-plugin-api`. + +For example, given the following page: + +```ts +export const HomepageRoot = homePlugin.provide( + createRoutableExtension({ + name: 'HomepageRoot', + component: () => import('./components').then(m => m.HomepageRoot), + mountPoint: rootRouteRef, + }), +); +``` + +it can be migrated as the following: + +```tsx +const homePage = createPageExtension({ + defaultPath: '/home', + // you can reuse the existing routeRef + // by wrapping into the convertLegacyRouteRef. + routeRef: convertLegacyRouteRef(rootRouteRef), + // these inputs usually match the props required by the component. + inputs: { + props: createExtensionInput( + { + children: coreExtensionData.reactElement.optional(), + title: titleExtensionDataRef.optional(), + }, + + { + singleton: true, + optional: true, + }, + ), + }, + loader: ({ inputs }) => + import('./components/').then(m => + // The compatWrapper utility allows you to use the existing + // legacy frontend utilities used internally by the components. + compatWrapper( + , + ), + ), +}); +``` + +## Components + +TODO + +## APIs + +Let's imagine we have the following API: + +```ts +const myApi = createApiFactory(myApiRef, new SampleMyApi()); +``` + +you can transform the API above to an extension using the `createApiExtension` creator: + +```ts +export const myApi = createApiExtension({ + factory: createApiFactory(myApiRef, new SampleMyApi()), +}); +``` + +## Plugin + +In the legacy frontend system a plugin was defined in its own `plugin.ts` file as following: + +```ts title="my-plugin/src/plugin.ts" + import { createPlugin } from '@backstage/core-plugin-api'; + + export const myPlugin = createPlugin({ + id: 'my-plugin', + apis: [myApi], + routes: { + ... + }, + externalRoutes: { + ... + }, + }); +``` + +In order to migrate the actual definition of the plugin you need to recreate the plugin using the new `createPlugin` utility exported by `@backstage/frontend-plugin-api`. +The new `createPlugin` function doesn't accept apis anymore as apis are now extensions. + +```ts title="my-plugin/src/index.ts" + import { createPlugin } from '@backstage/frontend-plugin-api'; + + export default createPlugin({ + id: 'my-plugin', + // bind all the extensions to the plugin + /* highlight-next-line */ + extensions: [homePage, myApi], + routes: { + ... + }, + externalRoutes: { + ... + }, + }); +``` + +The code above binds all the extensions to the plugin. _Important_: Make sure to export the plugin as default export of your package in `src/index.ts`. diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 146d8db059..f9c658feee 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -414,7 +414,8 @@ "frontend-system/building-plugins/index", "frontend-system/building-plugins/testing", "frontend-system/building-plugins/extension-types", - "frontend-system/building-plugins/built-in-data-refs" + "frontend-system/building-plugins/built-in-data-refs", + "frontend-system/building-plugins/migrating" ] }, { From 07f922905ed32e1729fe5a746f00af0afa8cfc47 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Fri, 19 Jan 2024 12:56:54 +0100 Subject: [PATCH 02/11] docs: move over utility api migration to frontend plugin Signed-off-by: Vincenzo Scamporlino --- .../building-plugins/migrating.md | 94 +++++++++++-- .../utility-apis/05-migrating.md | 126 ------------------ microsite/sidebars.json | 3 +- 3 files changed, 85 insertions(+), 138 deletions(-) delete mode 100644 docs/frontend-system/utility-apis/05-migrating.md diff --git a/docs/frontend-system/building-plugins/migrating.md b/docs/frontend-system/building-plugins/migrating.md index 3131f51a43..2fb64e0eab 100644 --- a/docs/frontend-system/building-plugins/migrating.md +++ b/docs/frontend-system/building-plugins/migrating.md @@ -69,20 +69,94 @@ TODO ## APIs -Let's imagine we have the following API: +There are a few things to keep in mind in regards to utility APIs. -```ts -const myApi = createApiFactory(myApiRef, new SampleMyApi()); -``` +### React package interface and ref changes -you can transform the API above to an extension using the `createApiExtension` creator: +Let's begin with [your `-react` package](../../architecture-decisions/adr011-plugin-package-structure.md). The act of exporting TypeScript interfaces and API refs have not changed from the old system. You can typically keep those as-is. For illustrative purposes, this is an example of an interface and its API ref: -```ts -export const myApi = createApiExtension({ - factory: createApiFactory(myApiRef, new SampleMyApi()), +```tsx title="in @internal/plugin-example-react" +import { createApiRef } from '@backstage/frontend-plugin-api'; + +/** + * Performs some work. + * @public + */ +export interface WorkApi { + doWork(): Promise; +} + +/** + * The work interface for the Example plugin. + * @public + */ +export const workApiRef = createApiRef({ + id: 'plugin.example.work', }); ``` +In this example, the plugin ID already follows the [Frontend System Naming Patterns](../architecture/naming-patterns). If it doesn't, you may want to consider renaming that ID at this point. Don't worry, this won't hurt consumers in the old frontend system since the ID is mostly used for debugging purposes there. In the new system, it's much more important and appears in app-config files and similar. + +Note at the top of the file that it uses the updated import from `@backstage/frontend-plugin-api` that we migrated in the previous section, instead of the old `@backstage/core-plugin-api`. + +### Plugin package changes + +Now let's turn to the main plugin package where the plugin itself is exported. You will probably already have a `createPlugin` call in here. Before we changed the `core-plugin-api` imports it'll have looked somewhat similar to the following: + +```tsx title="in @internal/plugin-example, NOTE THIS IS LEGACY CODE" +import { + storageApiRef, + createPlugin, + createApiFactory, +} from '@backstage/core-plugin-api'; +import { workApiRef } from '@internal/plugin-example-react'; +import { WorkImpl } from './WorkImpl'; + +const exampleWorkApi = createApiFactory({ + api: workApiRef, + deps: { storageApi: storageApiRef }, + factory: ({ storageApi }) => new WorkImpl({ storageApi }), +}); + +/** @public */ +export const catalogPlugin = createPlugin({ + id: 'example', + apis: [exampleWorkApi], +}); +``` + +The major changes we'll make are + +- Optionally change the old imports to the new package as per the top section of this guide +- Wrap the existing API factory in a `createApiExtension` +- Change to the new version of `createPlugin` which exports this extension +- Change the plugin export to be the default instead + +The end result, after simplifying imports and cleaning up a bit, might look like this: + +```tsx title="in @internal/plugin-example" +import { + storageApiRef, + createPlugin, + createApiFactory, + createApiExtension, +} from '@backstage/frontend-plugin-api'; +import { workApiRef } from '@internal/plugin-example-react'; +import { WorkImpl } from './WorkImpl'; + +const exampleWorkApi = createApiExtension({ + factory: createApiFactory({ + api: workApiRef, + deps: { storageApi: storageApiRef }, + factory: ({ storageApi }) => new WorkImpl({ storageApi }), + }), +}); +``` + +### Further work + +Since utility APIs are now complete extensions, you may want to take a bigger look at how they used to be used, and what the new frontend system offers. You may for example consider [adding configurability or inputs](../utility-apis/02-creating.md) to your API, if that makes sense for your current application. + ## Plugin In the legacy frontend system a plugin was defined in its own `plugin.ts` file as following: @@ -92,7 +166,7 @@ In the legacy frontend system a plugin was defined in its own `plugin.ts` file a export const myPlugin = createPlugin({ id: 'my-plugin', - apis: [myApi], + apis: [exampleWorkApi], routes: { ... }, @@ -112,7 +186,7 @@ The new `createPlugin` function doesn't accept apis anymore as apis are now exte id: 'my-plugin', // bind all the extensions to the plugin /* highlight-next-line */ - extensions: [homePage, myApi], + extensions: [homePage, exampleWorkApi], routes: { ... }, diff --git a/docs/frontend-system/utility-apis/05-migrating.md b/docs/frontend-system/utility-apis/05-migrating.md deleted file mode 100644 index a81dc796ac..0000000000 --- a/docs/frontend-system/utility-apis/05-migrating.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -id: migrating -title: Migrating Utility APIs from the old frontend system -sidebar_label: Migrating -# prettier-ignore -description: Migrating Utility APIs from the old frontend system ---- - -> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.** - -If you are migrating your plugins or app over from the old frontend system, there are a few things to keep in mind in regards to utility APIs. - -## Overview - -- Migrate your repo overall to the latest release of Backstage -- Follow the plugin migration guide -- Optionally change your package dependencies and code from `core-*-api` to `frontend-*-api` -- Keep the TypeScript interface and API ref exported as they were, except possibly reconsidering the choice of ID of the latter -- Wrap the old API factory call in an extension using `createApiExtension` -- Make sure that this extension is referenced by your migrated plugin - -## Prerequisites - -This guide assumes that you first [upgrade your repo](../../getting-started/keeping-backstage-updated.md) to the latest release of Backstage. This ensures that you do not have to fight several types of incompatibilities and updates at the same time. - -## Dependency changes - -In this article we will discuss some old interfaces that you used to import from the `@backstage/core-plugin-api` package. Those are now generally lifted over to `@backstage/frontend-plugin-api`, next to the new interfaces that are specific to the new frontend system. If you want to, you can already update your `package.json` and code imports to only use the new plugin API, but for the time being you don't have to. The old core exports will continue to work for the foreseeable future. - -To at least get access to the new interfaces, you'll need to run the following command. Note that it's just an example! It refers to `plugins/example`, which you'll have to change to the actual folder name that your package to migrate is in. - -```bash title="from your repo root" -yarn --cwd plugins/example add @backstage/frontend-plugin-api -``` - -## React package interface and ref changes - -Let's begin with [your `-react` package](../../architecture-decisions/adr011-plugin-package-structure.md). The act of exporting TypeScript interfaces and API refs have not changed from the old system. You can typically keep those as-is. For illustrative purposes, this is an example of an interface and its API ref: - -```tsx title="in @internal/plugin-example-react" -import { createApiRef } from '@backstage/frontend-plugin-api'; - -/** - * Performs some work. - * @public - */ -export interface WorkApi { - doWork(): Promise; -} - -/** - * The work interface for the Example plugin. - * @public - */ -export const workApiRef = createApiRef({ - id: 'plugin.example.work', -}); -``` - -In this example, the plugin ID already follows the common naming convention. If it doesn't, you may want to consider renaming that ID at this point. Don't worry, this won't hurt consumers in the old frontend system since the ID is mostly used for debugging purposes there. In the new system, it's much more important and appears in app-config files and similar. - -Note at the top of the file that it uses the updated import from `@backstage/frontend-plugin-api` that we migrated in the previous section, instead of the old `@backstage/core-plugin-api`. - -## Plugin package changes - -Now let's turn to the main plugin package where the plugin itself is exported. You will probably already have a `createPlugin` call in here. Before we changed the `core-plugin-api` imports it'll have looked somewhat similar to the following: - -```tsx title="in @internal/plugin-example, NOTE THIS IS LEGACY CODE" -import { - storageApiRef, - createPlugin, - createApiFactory, -} from '@backstage/core-plugin-api'; -import { workApiRef } from '@internal/plugin-example-react'; -import { WorkImpl } from './WorkImpl'; - -const exampleWorkApi = createApiFactory({ - api: workApiRef, - deps: { storageApi: storageApiRef }, - factory: ({ storageApi }) => new WorkImpl({ storageApi }), -}); - -/** @public */ -export const catalogPlugin = createPlugin({ - id: 'example', - apis: [exampleWorkApi], -}); -``` - -The major changes we'll make are - -- Optionally change the old imports to the new package as per the top section of this guide -- Wrap the existing API factory in a `createApiExtension` -- Change to the new version of `createPlugin` which exports this extension -- Change the plugin export to be the default instead - -The end result, after simplifying imports and cleaning up a bit, might look like this: - -```tsx title="in @internal/plugin-example" -import { - storageApiRef, - createPlugin, - createApiFactory, - createApiExtension, -} from '@backstage/frontend-plugin-api'; -import { workApiRef } from '@internal/plugin-example-react'; -import { WorkImpl } from './WorkImpl'; - -const exampleWorkApi = createApiExtension({ - factory: createApiFactory({ - api: workApiRef, - deps: { storageApi: storageApiRef }, - factory: ({ storageApi }) => new WorkImpl({ storageApi }), - }), -}); - -/** @public */ -export default createPlugin({ - id: 'example', - extensions: [exampleWorkApi], -}); -``` - -## Further work - -Since utility APIs are now complete extensions, you may want to take a bigger look at how they used to be used, and what the new frontend system offers. You may for example consider [adding configurability or inputs](./02-creating.md) to your API, if that makes sense for your current application. diff --git a/microsite/sidebars.json b/microsite/sidebars.json index f9c658feee..46c94b3f18 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -433,8 +433,7 @@ "frontend-system/utility-apis/index", "frontend-system/utility-apis/creating", "frontend-system/utility-apis/consuming", - "frontend-system/utility-apis/configuring", - "frontend-system/utility-apis/migrating" + "frontend-system/utility-apis/configuring" ] } ], From c49f807ac6bb343ca9417f98e521dccd934a4e0c Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Mon, 22 Jan 2024 14:39:15 +0100 Subject: [PATCH 03/11] docs: simplify examples Signed-off-by: Vincenzo Scamporlino --- .../building-plugins/migrating.md | 39 +++++++------------ 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/docs/frontend-system/building-plugins/migrating.md b/docs/frontend-system/building-plugins/migrating.md index 2fb64e0eab..e894e6b03d 100644 --- a/docs/frontend-system/building-plugins/migrating.md +++ b/docs/frontend-system/building-plugins/migrating.md @@ -1,6 +1,6 @@ --- id: migrating -title: Migrating an existing Frontend Plugin to the New Frontend System +title: Migrating Plugins sidebar_label: Migration Guide # prettier-ignore description: How to migrate an existing frontend plugin to the new frontend system @@ -17,10 +17,10 @@ Pages that were previously created using the `createRoutableExtension` extension For example, given the following page: ```ts -export const HomepageRoot = homePlugin.provide( +export const FooPage = fooPlugin.provide( createRoutableExtension({ - name: 'HomepageRoot', - component: () => import('./components').then(m => m.HomepageRoot), + name: 'FooPage', + component: () => import('./components').then(m => m.FooPage), mountPoint: rootRouteRef, }), ); @@ -29,36 +29,23 @@ export const HomepageRoot = homePlugin.provide( it can be migrated as the following: ```tsx -const homePage = createPageExtension({ - defaultPath: '/home', +import { createPageExtension } from '@backstage/frontend-plugin-api'; +import { + compatWrapper, + convertLegacyRouteRef, +} from '@backstage/core-compat-api'; + +const fooPage = createPageExtension({ + defaultPath: '/foo', // you can reuse the existing routeRef // by wrapping into the convertLegacyRouteRef. routeRef: convertLegacyRouteRef(rootRouteRef), // these inputs usually match the props required by the component. - inputs: { - props: createExtensionInput( - { - children: coreExtensionData.reactElement.optional(), - title: titleExtensionDataRef.optional(), - }, - - { - singleton: true, - optional: true, - }, - ), - }, loader: ({ inputs }) => import('./components/').then(m => // The compatWrapper utility allows you to use the existing // legacy frontend utilities used internally by the components. - compatWrapper( - , - ), + compatWrapper(), ), }); ``` From a7ecd1aa7b3105c70aef06e1b2dce2f11c8314d1 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Tue, 23 Jan 2024 07:28:50 +0100 Subject: [PATCH 04/11] docs: components Signed-off-by: Vincenzo Scamporlino --- docs/frontend-system/building-plugins/migrating.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/building-plugins/migrating.md b/docs/frontend-system/building-plugins/migrating.md index e894e6b03d..aafa7b8986 100644 --- a/docs/frontend-system/building-plugins/migrating.md +++ b/docs/frontend-system/building-plugins/migrating.md @@ -52,7 +52,7 @@ const fooPage = createPageExtension({ ## Components -TODO +The equivalent utility to replace components created with `createComponentExtension` is `createExtension` from `@backstage/frontend-plugin-api`. However, we recommend searching for an appropriate extension first, considering using `createExtension` as the last option for more complex scenarios. ## APIs From 969515dbabc1e962ab71b32c6e74754a7d511cfe Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Tue, 23 Jan 2024 09:43:05 +0100 Subject: [PATCH 05/11] docs: highlight convertLegacyRouteRefs Signed-off-by: Vincenzo Scamporlino --- docs/frontend-system/building-plugins/migrating.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/frontend-system/building-plugins/migrating.md b/docs/frontend-system/building-plugins/migrating.md index aafa7b8986..1cb1325421 100644 --- a/docs/frontend-system/building-plugins/migrating.md +++ b/docs/frontend-system/building-plugins/migrating.md @@ -174,12 +174,15 @@ The new `createPlugin` function doesn't accept apis anymore as apis are now exte // bind all the extensions to the plugin /* highlight-next-line */ extensions: [homePage, exampleWorkApi], - routes: { + // convert old route refs to the new system + /* highlight-next-line */ + routes: convertLegacyRouteRefs({ ... - }, - externalRoutes: { + }), + /* highlight-next-line */ + externalRoutes: convertLegacyRouteRefs({ ... - }, + }), }); ``` From 0f800e60780f200c0ec570723fc1dd1452753a0e Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 14 Feb 2024 09:49:41 +0100 Subject: [PATCH 06/11] docs: suggest alpha export Signed-off-by: Vincenzo Scamporlino --- .../building-plugins/migrating.md | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/frontend-system/building-plugins/migrating.md b/docs/frontend-system/building-plugins/migrating.md index 1cb1325421..d1f92ef454 100644 --- a/docs/frontend-system/building-plugins/migrating.md +++ b/docs/frontend-system/building-plugins/migrating.md @@ -166,7 +166,7 @@ In the legacy frontend system a plugin was defined in its own `plugin.ts` file a In order to migrate the actual definition of the plugin you need to recreate the plugin using the new `createPlugin` utility exported by `@backstage/frontend-plugin-api`. The new `createPlugin` function doesn't accept apis anymore as apis are now extensions. -```ts title="my-plugin/src/index.ts" +```ts title="my-plugin/src/alpha.ts" import { createPlugin } from '@backstage/frontend-plugin-api'; export default createPlugin({ @@ -186,4 +186,25 @@ The new `createPlugin` function doesn't accept apis anymore as apis are now exte }); ``` -The code above binds all the extensions to the plugin. _Important_: Make sure to export the plugin as default export of your package in `src/index.ts`. +The code above binds all the extensions to the plugin. _Important_: Make sure to export the plugin as default export of your package as a separate entrypoint, preferably `/alpha`, as suggested by the code snippet above. Make sure `src/alpha.ts` is exported in your `package.json`: + +```ts title="my-plugin/package.json" + "exports": { + ".": "./src/index.ts", + /* highlight-add-next-line */ + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + /* highlight-add-start */ + "alpha": [ + "src/alpha.ts" + ], + /* highlight-add-end */ + "package.json": [ + "package.json" + ] + } + }, +``` From d0cad4716954178806616185ac0be430d6686e01 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 14 Feb 2024 09:52:09 +0100 Subject: [PATCH 07/11] docs: remove plugin id suggestion Signed-off-by: Vincenzo Scamporlino --- docs/frontend-system/building-plugins/migrating.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/frontend-system/building-plugins/migrating.md b/docs/frontend-system/building-plugins/migrating.md index d1f92ef454..504276379b 100644 --- a/docs/frontend-system/building-plugins/migrating.md +++ b/docs/frontend-system/building-plugins/migrating.md @@ -82,8 +82,6 @@ export const workApiRef = createApiRef({ }); ``` -In this example, the plugin ID already follows the [Frontend System Naming Patterns](../architecture/naming-patterns). If it doesn't, you may want to consider renaming that ID at this point. Don't worry, this won't hurt consumers in the old frontend system since the ID is mostly used for debugging purposes there. In the new system, it's much more important and appears in app-config files and similar. - Note at the top of the file that it uses the updated import from `@backstage/frontend-plugin-api` that we migrated in the previous section, instead of the old `@backstage/core-plugin-api`. ### Plugin package changes From 63c246ab62deb2af797f45057ccf09bf21776983 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 14 Feb 2024 12:04:49 +0100 Subject: [PATCH 08/11] docs: migrate plugin as first step Signed-off-by: Vincenzo Scamporlino --- .../building-plugins/migrating.md | 173 ++++++++++-------- 1 file changed, 95 insertions(+), 78 deletions(-) diff --git a/docs/frontend-system/building-plugins/migrating.md b/docs/frontend-system/building-plugins/migrating.md index 504276379b..6f51357c76 100644 --- a/docs/frontend-system/building-plugins/migrating.md +++ b/docs/frontend-system/building-plugins/migrating.md @@ -10,7 +10,72 @@ This guide allows you to migrate a frontend plugin and its own components, route The main concept is that routes, components, apis are now extensions. You can use the appropriate extension creators to migrate all of them to extensions. -## Pages +## Migrating the plugin + +In the legacy frontend system a plugin was defined in its own `plugin.ts` file as following: + +```ts title="my-plugin/src/plugin.ts" + import { createPlugin } from '@backstage/core-plugin-api'; + + export const myPlugin = createPlugin({ + id: 'my-plugin', + apis: [], + routes: { + ... + }, + externalRoutes: { + ... + }, + }); +``` + +In order to migrate the actual definition of the plugin you need to recreate the plugin using the new `createPlugin` utility exported by `@backstage/frontend-plugin-api`. +The new `createPlugin` function doesn't accept apis anymore as apis are now extensions. + +```ts title="my-plugin/src/alpha.ts" + import { createPlugin } from '@backstage/frontend-plugin-api'; + + export default createPlugin({ + id: 'my-plugin', + // bind all the extensions to the plugin + /* highlight-next-line */ + extensions: [], + // convert old route refs to the new system + /* highlight-next-line */ + routes: convertLegacyRouteRefs({ + ... + }), + /* highlight-next-line */ + externalRoutes: convertLegacyRouteRefs({ + ... + }), + }); +``` + +The code above binds all the extensions to the plugin. _Important_: Make sure to export the plugin as default export of your package as a separate entrypoint, preferably `/alpha`, as suggested by the code snippet above. Make sure `src/alpha.ts` is exported in your `package.json`: + +```ts title="my-plugin/package.json" + "exports": { + ".": "./src/index.ts", + /* highlight-add-next-line */ + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + /* highlight-add-start */ + "alpha": [ + "src/alpha.ts" + ], + /* highlight-add-end */ + "package.json": [ + "package.json" + ] + } + }, +``` + +## Migrating Pages Pages that were previously created using the `createRoutableExtension` extension function can be migrated to the new Frontend System using the `createPageExtension` extension creator, exported by `@backstage/frontend-plugin-api`. @@ -50,11 +115,27 @@ const fooPage = createPageExtension({ }); ``` -## Components +then add the `fooPage` extension to the plugin: + +```ts title="my-plugin/src/alpha.ts" + import { createPlugin } from '@backstage/frontend-plugin-api'; + + export default createPlugin({ + id: 'my-plugin', + // bind all the extensions to the plugin + /* highlight-remove-next-line */ + extensions: [], + /* highlight-add-next-line */ + extensions: [fooPage], + ... + }); +``` + +## Migrating Components The equivalent utility to replace components created with `createComponentExtension` is `createExtension` from `@backstage/frontend-plugin-api`. However, we recommend searching for an appropriate extension first, considering using `createExtension` as the last option for more complex scenarios. -## APIs +## Migrating APIs There are a few things to keep in mind in regards to utility APIs. @@ -84,16 +165,10 @@ export const workApiRef = createApiRef({ Note at the top of the file that it uses the updated import from `@backstage/frontend-plugin-api` that we migrated in the previous section, instead of the old `@backstage/core-plugin-api`. -### Plugin package changes - -Now let's turn to the main plugin package where the plugin itself is exported. You will probably already have a `createPlugin` call in here. Before we changed the `core-plugin-api` imports it'll have looked somewhat similar to the following: +Now let's migrate the implementation of the api. Before we changed the `core-plugin-api` imports the api would have looked somewhat similar to the following: ```tsx title="in @internal/plugin-example, NOTE THIS IS LEGACY CODE" -import { - storageApiRef, - createPlugin, - createApiFactory, -} from '@backstage/core-plugin-api'; +import { storageApiRef, createApiFactory } from '@backstage/core-plugin-api'; import { workApiRef } from '@internal/plugin-example-react'; import { WorkImpl } from './WorkImpl'; @@ -102,27 +177,18 @@ const exampleWorkApi = createApiFactory({ deps: { storageApi: storageApiRef }, factory: ({ storageApi }) => new WorkImpl({ storageApi }), }); - -/** @public */ -export const catalogPlugin = createPlugin({ - id: 'example', - apis: [exampleWorkApi], -}); ``` The major changes we'll make are -- Optionally change the old imports to the new package as per the top section of this guide +- Change the old `@backstage/core-plugin-api` imports to the new `@backstage/frontend-plugin-api` package as per the top section of this guide - Wrap the existing API factory in a `createApiExtension` -- Change to the new version of `createPlugin` which exports this extension -- Change the plugin export to be the default instead The end result, after simplifying imports and cleaning up a bit, might look like this: ```tsx title="in @internal/plugin-example" import { storageApiRef, - createPlugin, createApiFactory, createApiExtension, } from '@backstage/frontend-plugin-api'; @@ -138,31 +204,7 @@ const exampleWorkApi = createApiExtension({ }); ``` -### Further work - -Since utility APIs are now complete extensions, you may want to take a bigger look at how they used to be used, and what the new frontend system offers. You may for example consider [adding configurability or inputs](../utility-apis/02-creating.md) to your API, if that makes sense for your current application. - -## Plugin - -In the legacy frontend system a plugin was defined in its own `plugin.ts` file as following: - -```ts title="my-plugin/src/plugin.ts" - import { createPlugin } from '@backstage/core-plugin-api'; - - export const myPlugin = createPlugin({ - id: 'my-plugin', - apis: [exampleWorkApi], - routes: { - ... - }, - externalRoutes: { - ... - }, - }); -``` - -In order to migrate the actual definition of the plugin you need to recreate the plugin using the new `createPlugin` utility exported by `@backstage/frontend-plugin-api`. -The new `createPlugin` function doesn't accept apis anymore as apis are now extensions. +Finally, let's add the `exampleWorkApi` extension to the plugin: ```ts title="my-plugin/src/alpha.ts" import { createPlugin } from '@backstage/frontend-plugin-api'; @@ -170,39 +212,14 @@ The new `createPlugin` function doesn't accept apis anymore as apis are now exte export default createPlugin({ id: 'my-plugin', // bind all the extensions to the plugin - /* highlight-next-line */ - extensions: [homePage, exampleWorkApi], - // convert old route refs to the new system - /* highlight-next-line */ - routes: convertLegacyRouteRefs({ - ... - }), - /* highlight-next-line */ - externalRoutes: convertLegacyRouteRefs({ - ... - }), + /* highlight-remove-next-line */ + extensions: [fooPage], + /* highlight-add-next-line */ + extensions: [exampleWorkApi, fooPage], + ... }); ``` -The code above binds all the extensions to the plugin. _Important_: Make sure to export the plugin as default export of your package as a separate entrypoint, preferably `/alpha`, as suggested by the code snippet above. Make sure `src/alpha.ts` is exported in your `package.json`: +### Further work -```ts title="my-plugin/package.json" - "exports": { - ".": "./src/index.ts", - /* highlight-add-next-line */ - "./alpha": "./src/alpha.ts", - "./package.json": "./package.json" - }, - "typesVersions": { - "*": { - /* highlight-add-start */ - "alpha": [ - "src/alpha.ts" - ], - /* highlight-add-end */ - "package.json": [ - "package.json" - ] - } - }, -``` +Since utility APIs are now complete extensions, you may want to take a bigger look at how they used to be used, and what the new frontend system offers. You may for example consider [adding configurability or inputs](../utility-apis/02-creating.md) to your API, if that makes sense for your current application. From 5f7f4ec8904bd275c2313cd9e02fb86c92746c29 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 14 Feb 2024 12:19:42 +0100 Subject: [PATCH 09/11] docs: remove createExtension suggestion Signed-off-by: Vincenzo Scamporlino --- docs/frontend-system/building-plugins/migrating.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/building-plugins/migrating.md b/docs/frontend-system/building-plugins/migrating.md index 6f51357c76..16f465ece7 100644 --- a/docs/frontend-system/building-plugins/migrating.md +++ b/docs/frontend-system/building-plugins/migrating.md @@ -133,7 +133,7 @@ then add the `fooPage` extension to the plugin: ## Migrating Components -The equivalent utility to replace components created with `createComponentExtension` is `createExtension` from `@backstage/frontend-plugin-api`. However, we recommend searching for an appropriate extension first, considering using `createExtension` as the last option for more complex scenarios. +The equivalent utility to replace components created with `createComponentExtension` is `createExtension` from `@backstage/frontend-plugin-api`. However, we recommend searching for a more appropriate extension creator first. ## Migrating APIs From d99351873b8cb13c6fe8b275edb30fa523debfa2 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Thu, 15 Feb 2024 22:15:25 +0100 Subject: [PATCH 10/11] docs: missing leading number Signed-off-by: Vincenzo Scamporlino --- .../building-plugins/{migrating.md => 05-migrating.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/frontend-system/building-plugins/{migrating.md => 05-migrating.md} (100%) diff --git a/docs/frontend-system/building-plugins/migrating.md b/docs/frontend-system/building-plugins/05-migrating.md similarity index 100% rename from docs/frontend-system/building-plugins/migrating.md rename to docs/frontend-system/building-plugins/05-migrating.md From d185dcd12e2b7dfb2a372a363c3e8695a9a944bd Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Thu, 15 Feb 2024 22:15:47 +0100 Subject: [PATCH 11/11] docs: fix migrating apis links Signed-off-by: Vincenzo Scamporlino --- docs/frontend-system/utility-apis/01-index.md | 2 +- docs/frontend-system/utility-apis/02-creating.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/frontend-system/utility-apis/01-index.md b/docs/frontend-system/utility-apis/01-index.md index 2d0f0a9698..37120a5152 100644 --- a/docs/frontend-system/utility-apis/01-index.md +++ b/docs/frontend-system/utility-apis/01-index.md @@ -40,4 +40,4 @@ These cases are all described in [the main article](./04-configuring.md). ## Migrating from the old frontend system -If you want to learn how to migrate your own utility APIs from the old frontend system to the new one, that's described in [a dedicated migration guide](./05-migrating.md). +If you want to learn how to migrate your own utility APIs from the old frontend system to the new one, that's described in the [Migrating APIs guide](../building-plugins/05-migrating.md#migrating-apis). diff --git a/docs/frontend-system/utility-apis/02-creating.md b/docs/frontend-system/utility-apis/02-creating.md index 8935b31b75..fcb7ff1a65 100644 --- a/docs/frontend-system/utility-apis/02-creating.md +++ b/docs/frontend-system/utility-apis/02-creating.md @@ -8,7 +8,7 @@ description: Creating new utility APIs in your plugins and app > **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.** -This section describes how to make a Utility API from scratch, or to add configurability and inputs to an existing one. If you are instead interested in migrating an existing Utility API from the old frontend system, check out [the migrating section](./05-migrating.md). +This section describes how to make a Utility API from scratch, or to add configurability and inputs to an existing one. If you are instead interested in migrating an existing Utility API from the old frontend system, check out the [Migrating APIs section](../building-plugins/05-migrating.md#migrating-apis). ## Creating the Utility API contract