frontend-app-api: remove plugins from internal route objects

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2025-08-16 15:02:43 +02:00
parent a1127c8c93
commit ef5442750d
6 changed files with 14 additions and 162 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-app-api': patch
---
Internal cleanup of routing system data.
@@ -20,7 +20,6 @@ import { BackstageRouteObject } from './types';
import { fireEvent, render } from '@testing-library/react';
import { RouteTracker } from './RouteTracker';
import { Link, MemoryRouter, Route, Routes } from 'react-router-dom';
import { createPlugin } from '@backstage/core-plugin-api';
import {
createRouteRef,
AnalyticsApi,
@@ -34,16 +33,12 @@ describe('RouteTracker', () => {
const routeRef0 = createRouteRef();
const routeRef1 = createRouteRef();
const routeRef2 = createRouteRef();
const plugin0 = createPlugin({ id: 'home' });
const plugin1 = createPlugin({ id: 'plugin1' });
const plugin2 = createPlugin({ id: 'plugin2' });
const routeObjects: BackstageRouteObject[] = [
{
path: '',
element: <div>home page</div>,
routeRefs: new Set([routeRef0]),
plugins: new Set([plugin0]),
caseSensitive: false,
children: [MATCH_ALL_ROUTE],
appNode: {
@@ -57,7 +52,6 @@ describe('RouteTracker', () => {
path: '/path/:p1/:p2',
element: <Link to="/path2/hello">go</Link>,
routeRefs: new Set([routeRef1]),
plugins: new Set([plugin1]),
caseSensitive: false,
children: [MATCH_ALL_ROUTE],
appNode: {
@@ -71,7 +65,6 @@ describe('RouteTracker', () => {
path: '/path2/:param',
element: <div>hi there</div>,
routeRefs: new Set([routeRef2]),
plugins: new Set([plugin2]),
caseSensitive: false,
children: [MATCH_ALL_ROUTE],
appNode: {
@@ -15,7 +15,6 @@
*/
import { createElement } from 'react';
import { BackstagePlugin } from '@backstage/core-plugin-api';
import { extractRouteInfoFromAppNode } from './extractRouteInfoFromAppNode';
import {
AnyRouteRefParams,
@@ -128,7 +127,6 @@ function routeObj(
refs: RouteRef[],
children: any[] = [],
type: 'mounted' | 'gathered' = 'mounted',
backstagePlugin?: BackstagePlugin,
appNode?: AppNode,
) {
return {
@@ -143,11 +141,9 @@ function routeObj(
caseSensitive: false,
element: 'match-all',
routeRefs: new Set(),
plugins: new Set(),
},
...children,
],
plugins: backstagePlugin ? new Set([backstagePlugin]) : new Set(),
};
}
@@ -203,14 +199,7 @@ describe('discovery', () => {
[ref5, ref1],
]);
expect(info.routeObjects).toEqual([
routeObj(
'nothing',
[],
undefined,
undefined,
undefined,
expect.any(Object),
),
routeObj('nothing', [], undefined, undefined, expect.any(Object)),
routeObj(
'foo',
[ref1],
@@ -218,41 +207,16 @@ describe('discovery', () => {
routeObj(
'bar/:id',
[ref2],
[
routeObj(
'baz',
[ref3],
undefined,
undefined,
expect.any(Object),
expect.any(Object),
),
],
[routeObj('baz', [ref3], undefined, undefined, expect.any(Object))],
undefined,
expect.any(Object),
expect.any(Object),
),
routeObj(
'blop',
[ref5],
undefined,
undefined,
expect.any(Object),
expect.any(Object),
),
routeObj('blop', [ref5], undefined, undefined, expect.any(Object)),
],
undefined,
expect.any(Object),
expect.any(Object),
),
routeObj(
'divsoup',
[ref4],
undefined,
undefined,
expect.any(Object),
expect.any(Object),
),
routeObj('divsoup', [ref4], undefined, undefined, expect.any(Object)),
]);
});
@@ -413,30 +377,13 @@ describe('discovery', () => {
[ref5, ref3],
]);
expect(info.routeObjects).toEqual([
routeObj(
'foo',
[ref1, ref2],
[],
'mounted',
expect.any(Object),
expect.any(Object),
),
routeObj('foo', [ref1, ref2], [], 'mounted', expect.any(Object)),
routeObj(
'bar',
[ref3],
[
routeObj(
'',
[ref4, ref5],
[],
'mounted',
expect.any(Object),
expect.any(Object),
),
],
[routeObj('', [ref4, ref5], [], 'mounted', expect.any(Object))],
'mounted',
expect.any(Object),
expect.any(Object),
),
]);
});
@@ -510,22 +457,18 @@ describe('discovery', () => {
undefined,
undefined,
expect.any(Object),
expect.any(Object),
),
],
undefined,
expect.any(Object),
expect.any(Object),
),
],
'mounted',
expect.any(Object),
expect.any(Object),
),
],
undefined,
expect.any(Object),
expect.any(Object),
),
]);
});
@@ -584,14 +527,7 @@ describe('discovery', () => {
'r',
[],
[
routeObj(
'x',
[ref1],
[],
'mounted',
expect.any(Object),
expect.any(Object),
),
routeObj('x', [ref1], [], 'mounted', expect.any(Object)),
routeObj(
'y',
[],
@@ -606,7 +542,6 @@ describe('discovery', () => {
undefined,
'mounted',
expect.any(Object),
expect.any(Object),
),
routeObj(
'b',
@@ -614,21 +549,17 @@ describe('discovery', () => {
undefined,
'mounted',
expect.any(Object),
expect.any(Object),
),
],
'mounted',
expect.any(Object),
expect.any(Object),
),
],
'mounted',
undefined,
expect.any(Object),
),
],
undefined,
undefined,
expect.any(Object),
),
]);
@@ -669,22 +600,8 @@ describe('discovery', () => {
[r3, undefined],
]);
expect(info.routeObjects).toEqual([
routeObj(
'foo',
[r1],
undefined,
undefined,
expect.any(Object),
expect.any(Object),
),
routeObj(
'bar',
[r3],
undefined,
undefined,
expect.any(Object),
expect.any(Object),
),
routeObj('foo', [r1], undefined, undefined, expect.any(Object)),
routeObj('bar', [r3], undefined, undefined, expect.any(Object)),
]);
});
@@ -17,7 +17,6 @@
import { RouteRef, coreExtensionData } from '@backstage/frontend-plugin-api';
import { BackstageRouteObject } from './types';
import { AppNode } from '@backstage/frontend-plugin-api';
import { toLegacyPlugin } from './toLegacyPlugin';
import {
createExactRouteAliasResolver,
RouteAliasResolver,
@@ -32,7 +31,6 @@ export const MATCH_ALL_ROUTE: BackstageRouteObject = {
path: '*',
element: 'match-all', // These elements aren't used, so we add in a bit of debug information
routeRefs: new Set(),
plugins: new Set(),
};
// Joins a list of paths together, avoiding trailing and duplicate slashes
@@ -100,7 +98,6 @@ export function extractRouteInfoFromAppNode(
routeRefs: new Set<RouteRef>(),
caseSensitive: false,
children: [MATCH_ALL_ROUTE],
plugins: new Set(),
appNode: current,
};
parentChildren.push(currentObj);
@@ -141,9 +138,6 @@ export function extractRouteInfoFromAppNode(
routeParents.set(routeRef, newParentRef);
currentObj?.routeRefs.add(routeRef);
if (current.spec.plugin) {
currentObj?.plugins.add(toLegacyPlugin(current.spec.plugin));
}
}
for (const children of current.edges.attachments.values()) {
@@ -1,55 +0,0 @@
/*
* 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 { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { BackstagePlugin as LegacyBackstagePlugin } from '@backstage/core-plugin-api';
import { getOrCreateGlobalSingleton } from '@backstage/version-bridge';
// Make sure that we only convert each new plugin instance to its legacy equivalent once
const legacyPluginStore = getOrCreateGlobalSingleton(
'legacy-plugin-compatibility-store',
() => new WeakMap<FrontendPlugin, LegacyBackstagePlugin>(),
);
export function toLegacyPlugin(plugin: FrontendPlugin): LegacyBackstagePlugin {
let legacy = legacyPluginStore.get(plugin);
if (legacy) {
return legacy;
}
const errorMsg = 'Not implemented in legacy plugin compatibility layer';
const notImplemented = () => {
throw new Error(errorMsg);
};
legacy = {
getId(): string {
return plugin.id;
},
get routes() {
return {};
},
get externalRoutes() {
return {};
},
getApis: notImplemented,
getFeatureFlags: notImplemented,
provide: notImplemented,
};
legacyPluginStore.set(plugin, legacy);
return legacy;
}
@@ -20,7 +20,6 @@ import {
RouteRef,
SubRouteRef,
} from '@backstage/frontend-plugin-api';
import { BackstagePlugin as LegacyBackstagePlugin } from '@backstage/core-plugin-api';
/** @internal */
export type AnyRouteRef = RouteRef | SubRouteRef | ExternalRouteRef;
@@ -35,6 +34,5 @@ export interface BackstageRouteObject {
element: React.ReactNode;
path: string;
routeRefs: Set<RouteRef>;
plugins: Set<LegacyBackstagePlugin>;
appNode?: AppNode;
}