core-compat-api: collection of fixes for convertLegacyApp

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2025-02-28 17:12:45 +01:00
parent 5ab5db283f
commit 18faf657c9
5 changed files with 49 additions and 19 deletions
+10
View File
@@ -0,0 +1,10 @@
---
'@backstage/core-compat-api': patch
---
The `convertLegacyApp` has received the following changes:
- `null` routes will now be ignored.
- Converted routes no longer need to belong to a plugin, falling back to a `converted-orphan-routes` plugin instead.
- The generate layout override extension is now properly attached to the `app/root` extension.
- Converted root elements are now automatically wrapped with `compatWrapper`.
@@ -48,8 +48,10 @@ describe('collectLegacyRoutes', () => {
<FlatRoutes>
<Route path="/score-board" element={<ScoreBoardPage />} />
<Route path="/stackstorm" element={<StackstormPage />} />
<Route path="/other" element={<div />} />
<Route path="/puppetdb" element={<PuppetDbPage />} />
<Route path="/puppetdb" element={<PuppetDbPage />} />
<Route path="/other" element={<div />} />
</FlatRoutes>,
);
@@ -96,6 +98,23 @@ describe('collectLegacyRoutes', () => {
},
],
},
{
id: 'converted-orphan-routes',
extensions: [
{
id: 'page:converted-orphan-routes',
attachTo: { id: 'app/routes', input: 'routes' },
disabled: false,
defaultConfig: {},
},
{
id: 'page:converted-orphan-routes/2',
attachTo: { id: 'app/routes', input: 'routes' },
disabled: false,
defaultConfig: {},
},
],
},
{
id: 'puppetDb',
extensions: [
@@ -378,8 +397,6 @@ describe('collectLegacyRoutes', () => {
<Route element={<Navigate to="/somewhere" />} />
</FlatRoutes>,
),
).toThrow(
/Route with path undefined has en element that can not be converted/,
);
).toThrow(/Route element inside FlatRoutes had no path prop value given/);
});
});
@@ -18,6 +18,7 @@ import {
AnyRouteRefParams,
BackstagePlugin as LegacyBackstagePlugin,
RouteRef,
createPlugin,
getComponentData,
} from '@backstage/core-plugin-api';
import {
@@ -167,6 +168,9 @@ export function collectLegacyRoutes(
return () => String(currentIndex++);
})();
// Placeholder plugin for any routes that don't belong to a plugin
const orphanRoutesPlugin = createPlugin({ id: 'converted-orphan-routes' });
const getPluginExtensions = (plugin: LegacyBackstagePlugin) => {
let extensions = pluginExtensions.get(plugin);
if (!extensions) {
@@ -179,6 +183,9 @@ export function collectLegacyRoutes(
React.Children.forEach(
flatRoutesElement.props.children,
(route: ReactNode) => {
if (route === null) {
return;
}
// TODO(freben): Handle feature flag and permissions framework wrapper elements
if (!React.isValidElement(route)) {
throw new Error(
@@ -192,20 +199,13 @@ export function collectLegacyRoutes(
}
const routeElement = route.props.element;
const path: string | undefined = route.props.path;
const plugin = getComponentData<LegacyBackstagePlugin>(
routeElement,
'core.plugin',
);
const plugin =
getComponentData<LegacyBackstagePlugin>(routeElement, 'core.plugin') ??
orphanRoutesPlugin;
const routeRef = getComponentData<RouteRef>(
routeElement,
'core.mountPoint',
);
if (!plugin) {
throw new Error(
// TODO(vinzscam): add See <link-to-app-migration-docs> for more info
`Route with path ${path} has en element that can not be converted as it does not belong to a plugin. Make sure that the top-level React element of the element prop is an extension from a Backstage plugin, or remove the Route completely.`,
);
}
if (path === undefined) {
throw new Error(
`Route element inside FlatRoutes had no path prop value given`,
@@ -114,7 +114,7 @@ describe('convertLegacyApp', () => {
extensions: [
{
id: 'app/layout',
attachTo: { id: 'app', input: 'root' },
attachTo: { id: 'app/root', input: 'children' },
disabled: false,
},
{
@@ -32,6 +32,7 @@ import {
} from '@backstage/frontend-plugin-api';
import { getComponentData } from '@backstage/core-plugin-api';
import { collectLegacyRoutes } from './collectLegacyRoutes';
import { compatWrapper } from './compatWrapper';
function selectChildren(
rootNode: ReactNode,
@@ -106,7 +107,7 @@ export function convertLegacyApp(
const CoreLayoutOverride = createExtension({
name: 'layout',
attachTo: { id: 'app', input: 'root' },
attachTo: { id: 'app/root', input: 'children' },
inputs: {
content: createExtensionInput([coreExtensionData.reactElement], {
singleton: true,
@@ -117,10 +118,12 @@ export function convertLegacyApp(
// Clone the root element, this replaces the FlatRoutes declared in the app with out content input
return [
coreExtensionData.reactElement(
React.cloneElement(
rootEl,
undefined,
inputs.content.get(coreExtensionData.reactElement),
compatWrapper(
React.cloneElement(
rootEl,
undefined,
inputs.content.get(coreExtensionData.reactElement),
),
),
),
];