diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index 560a547af5..9ebc1a0185 100644 --- a/packages/core-compat-api/package.json +++ b/packages/core-compat-api/package.json @@ -34,7 +34,8 @@ "@backstage/core-plugin-api": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", "@backstage/version-bridge": "workspace:^", - "@types/react": "^16.13.1 || ^17.0.0" + "@types/react": "^16.13.1 || ^17.0.0", + "lodash": "^4.17.21" }, "devDependencies": { "@backstage-community/plugin-puppetdb": "^0.1.18", diff --git a/packages/core-compat-api/src/convertLegacyPlugin.test.tsx b/packages/core-compat-api/src/convertLegacyPlugin.test.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/core-compat-api/src/convertLegacyPlugin.tsx b/packages/core-compat-api/src/convertLegacyPlugin.tsx new file mode 100644 index 0000000000..052888d53c --- /dev/null +++ b/packages/core-compat-api/src/convertLegacyPlugin.tsx @@ -0,0 +1,85 @@ +/* + * Copyright 2024 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 { + BackstagePlugin as LegacyBackstagePlugin, + getComponentData, + RouteRef as LegacyRouteRef, +} from '@backstage/core-plugin-api'; +import { + ExtensionDefinition, + BackstagePlugin as NewBackstagePlugin, + createPageExtension, + createPlugin, +} from '@backstage/frontend-plugin-api'; +import kebabCase from 'lodash/kebabCase'; +import { + convertLegacyRouteRef, + convertLegacyRouteRefs, +} from './convertLegacyRouteRef'; +import { ComponentType } from 'react'; +import React from 'react'; +import { compatWrapper } from './compatWrapper'; + +/** @internal */ +export function convertLegacyExtension( + LegacyExtension: ComponentType<{}>, + legacyPlugin: LegacyBackstagePlugin, +): ExtensionDefinition { + const element = ; + + const plugin = getComponentData( + element, + 'core.plugin', + ); + if (legacyPlugin !== plugin) { + throw new Error( + `The extension does not belong to the same plugin, got ${plugin?.getId()}`, + ); + } + + const name = getComponentData(element, 'core.extensionName'); + if (!name) { + throw new Error('Extension has no name'); + } + + const mountPoint = getComponentData( + element, + 'core.mountPoint', + ); + + if (name.endsWith('Page')) { + return createPageExtension({ + defaultPath: kebabCase(name.slice(0, -'Page'.length)), + routeRef: mountPoint && convertLegacyRouteRef(mountPoint), + loader: () => Promise.resolve(compatWrapper(element)), + }); + } +} + +/** @public */ +export function convertLegacyPlugin( + legacyPlugin: LegacyBackstagePlugin, + options: { extensions: ExtensionDefinition[] }, +): NewBackstagePlugin { + return createPlugin({ + id: legacyPlugin.getId(), + featureFlags: [...legacyPlugin.getFeatureFlags()], + routes: convertLegacyRouteRefs(legacyPlugin.routes ?? {}), + externalRoutes: convertLegacyRouteRefs(legacyPlugin.externalRoutes ?? {}), + extensions: options.extensions, + }); +} diff --git a/yarn.lock b/yarn.lock index 301d7de22f..82b65687e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4202,6 +4202,7 @@ __metadata: "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^15.0.0 "@types/react": ^16.13.1 || ^17.0.0 + lodash: ^4.17.21 peerDependencies: react: ^16.13.1 || ^17.0.0 || ^18.0.0 react-router-dom: 6.0.0-beta.0 || ^6.3.0