core-compat-api: add convertLegacyApp

Co-authored-by: Camila Belo <camilaibs@gmail.com>
Co-authored-by: Vincenzo Scamporlino <vincenzos@spotify.com>
Co-authored-by: Philipp Hugenroth <philipph@spotify.com>
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-10-18 15:54:38 +02:00
parent d267d2d92b
commit 5409341b53
2 changed files with 139 additions and 0 deletions
@@ -0,0 +1,138 @@
/*
* Copyright 2023 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 React, {
Children,
Fragment,
ReactElement,
ReactNode,
isValidElement,
} from 'react';
import {
BackstagePlugin,
ExtensionOverrides,
coreExtensionData,
createExtension,
createExtensionInput,
createExtensionOverrides,
} from '@backstage/frontend-plugin-api';
import { getComponentData } from '@backstage/core-plugin-api';
import { collectLegacyRoutes } from './collectLegacyRoutes';
function selectChildren(
rootNode: ReactNode,
selector?: (element: ReactElement<{ children?: ReactNode }>) => boolean,
strictError?: string,
): Array<ReactElement<{ children?: ReactNode }>> {
return Children.toArray(rootNode).flatMap(node => {
if (!isValidElement<{ children?: ReactNode }>(node)) {
return [];
}
if (node.type === Fragment) {
return selectChildren(node.props.children, selector, strictError);
}
if (selector === undefined || selector(node)) {
return [node];
}
if (strictError) {
throw new Error(strictError);
}
return selectChildren(node.props.children, selector, strictError);
});
}
export function convertLegacyApp(
rootElement: React.JSX.Element,
): (ExtensionOverrides | BackstagePlugin)[] {
const appRouterEls = selectChildren(
rootElement,
el => getComponentData(el, 'core.type') === 'AppRouter',
);
if (appRouterEls.length !== 1) {
throw new Error(
"Failed to convert legacy app, AppRouter element could not been found. Make sure it's at the top level of the App element tree",
);
}
const rootEls = selectChildren(
appRouterEls[0].props.children,
el =>
Boolean(el.props.children) &&
selectChildren(
el.props.children,
innerEl => getComponentData(innerEl, 'core.type') === 'FlatRoutes',
).length === 1,
);
if (rootEls.length !== 1) {
throw new Error(
"Failed to convert legacy app, Root element containing FlatRoutes could not been found. Make sure it's within the AppRouter element of the App element tree",
);
}
const [rootEl] = rootEls;
const routesEls = selectChildren(
rootEls[0].props.children,
el => getComponentData(el, 'core.type') === 'FlatRoutes',
);
if (routesEls.length !== 1) {
throw new Error(
'Unexpectedly failed to find FlatRoutes in app element tree',
);
}
const [routesEl] = routesEls;
const CoreLayoutOverride = createExtension({
id: 'core.layout',
attachTo: { id: 'core', input: 'root' },
inputs: {
content: createExtensionInput(
{
element: coreExtensionData.reactElement,
},
{ singleton: true },
),
},
output: {
element: coreExtensionData.reactElement,
},
factory({ bind, inputs }) {
// Clone the root element, this replaces the FlatRoutes declared in the app with out content input
bind({
element: React.cloneElement(rootEl, undefined, inputs.content.element),
});
},
});
const CoreNavOverride = createExtension({
id: 'core.nav',
attachTo: { id: 'core.layout', input: 'nav' },
output: {},
factory() {},
disabled: true,
});
const collectedRoutes = collectLegacyRoutes(routesEl);
return [
...collectedRoutes,
createExtensionOverrides({
extensions: [CoreLayoutOverride, CoreNavOverride],
}),
];
}
+1
View File
@@ -14,3 +14,4 @@
* limitations under the License.
*/
export { collectLegacyRoutes } from './collectLegacyRoutes';
export { convertLegacyApp } from './convertLegacyApp';