From 1f95d7903d4015e39dc3abd97b35166944ab70ac Mon Sep 17 00:00:00 2001 From: secustor Date: Wed, 3 Sep 2025 17:07:02 +0200 Subject: [PATCH 01/26] feat(devtools): add extension to allow providing tabs Signed-off-by: secustor --- package.json | 3 + packages/app-next/package.json | 1 + packages/app-next/src/App.tsx | 5 +- packages/backend/package.json | 3 + .../catalog-unprocessed-entities/package.json | 1 + .../src/alpha/devToolsRoute.ts | 32 ++++++++ .../src/alpha/index.ts | 1 + .../src/alpha/devToolsRouteBlueprint.tsx | 81 +++++++++++++++++++ .../src/alpha/devToolsRouteDataRef.ts | 37 +++++++++ plugins/devtools/src/alpha/index.ts | 4 + plugins/devtools/src/alpha/plugin.tsx | 25 ++++-- .../DefaultDevToolsPage.tsx | 16 +++- .../components/DevToolsPage/DevToolsPage.tsx | 9 ++- yarn.lock | 2 + 14 files changed, 208 insertions(+), 12 deletions(-) create mode 100644 plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts create mode 100644 plugins/devtools/src/alpha/devToolsRouteBlueprint.tsx create mode 100644 plugins/devtools/src/alpha/devToolsRouteDataRef.ts diff --git a/package.json b/package.json index d4ebc22bc1..88238a1a21 100644 --- a/package.json +++ b/package.json @@ -185,5 +185,8 @@ } }, "tsConfig": "./tsconfig.json" + }, + "volta": { + "node": "22.19.0" } } diff --git a/packages/app-next/package.json b/packages/app-next/package.json index 44602b2438..0a39471e34 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -56,6 +56,7 @@ "@backstage/plugin-catalog-import": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", "@backstage/plugin-catalog-unprocessed-entities": "workspace:^", + "@backstage/plugin-devtools": "workspace:^", "@backstage/plugin-home": "workspace:^", "@backstage/plugin-kubernetes": "workspace:^", "@backstage/plugin-kubernetes-cluster": "workspace:^", diff --git a/packages/app-next/src/App.tsx b/packages/app-next/src/App.tsx index 6c567f3fe1..92d1f06d6a 100644 --- a/packages/app-next/src/App.tsx +++ b/packages/app-next/src/App.tsx @@ -45,6 +45,8 @@ import { convertLegacyPageExtension } from '@backstage/core-compat-api'; import { convertLegacyEntityContentExtension } from '@backstage/plugin-catalog-react/alpha'; import { pluginInfoResolver } from './pluginInfoResolver'; import { appModuleNav } from './modules/appModuleNav'; +import devtoolsPlugin from '@backstage/plugin-devtools/alpha'; +import { unprocessedEntitiesDevToolsRoute } from '@backstage/plugin-catalog-unprocessed-entities/alpha'; /* @@ -113,7 +115,7 @@ const customHomePageModule = createFrontendModule({ const notFoundErrorPageModule = createFrontendModule({ pluginId: 'app', - extensions: [notFoundErrorPage], + extensions: [notFoundErrorPage, unprocessedEntitiesDevToolsRoute], }); const collectedLegacyPlugins = convertLegacyAppRoot( @@ -133,6 +135,7 @@ const app = createApp({ notFoundErrorPageModule, appModuleNav, customHomePageModule, + devtoolsPlugin, ...collectedLegacyPlugins, ], advanced: { diff --git a/packages/backend/package.json b/packages/backend/package.json index a60b6d3940..0d03b12d19 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -71,5 +71,8 @@ }, "devDependencies": { "@backstage/cli": "workspace:^" + }, + "volta": { + "node": "22.19.0" } } diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index 004eb96208..c48f290d25 100644 --- a/plugins/catalog-unprocessed-entities/package.json +++ b/plugins/catalog-unprocessed-entities/package.json @@ -56,6 +56,7 @@ "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", + "@backstage/plugin-devtools": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.60", diff --git a/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts b/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts new file mode 100644 index 0000000000..c90a6b8270 --- /dev/null +++ b/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts @@ -0,0 +1,32 @@ +/* + * 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 { DevToolsRouteBlueprint } from '@backstage/plugin-devtools/alpha'; + +/** + * DevTools route extension for catalog unprocessed entities + * This demonstrates how to create extensions at the app level using the new DevTools extension system + */ +export const unprocessedEntitiesDevToolsRoute = DevToolsRouteBlueprint.make({ + params: { + path: 'unprocessed-entities', + title: 'Unprocessed Entities', + loader: () => + import('../components/UnprocessedEntities').then(m => ({ + default: m.UnprocessedEntitiesContent, + })), + }, +}); diff --git a/plugins/catalog-unprocessed-entities/src/alpha/index.ts b/plugins/catalog-unprocessed-entities/src/alpha/index.ts index 2f137f09ee..5738bb8a59 100644 --- a/plugins/catalog-unprocessed-entities/src/alpha/index.ts +++ b/plugins/catalog-unprocessed-entities/src/alpha/index.ts @@ -15,3 +15,4 @@ */ export { default } from './plugin'; +export { unprocessedEntitiesDevToolsRoute } from './devToolsRoute'; diff --git a/plugins/devtools/src/alpha/devToolsRouteBlueprint.tsx b/plugins/devtools/src/alpha/devToolsRouteBlueprint.tsx new file mode 100644 index 0000000000..fec93854ae --- /dev/null +++ b/plugins/devtools/src/alpha/devToolsRouteBlueprint.tsx @@ -0,0 +1,81 @@ +/* + * 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 { createExtensionBlueprint } from '@backstage/frontend-plugin-api'; +import { devToolsRouteDataRef } from './devToolsRouteDataRef'; +import { lazy, Suspense, JSX } from 'react'; + +/** + * Parameters for creating a DevTools route extension + * @alpha + */ +export interface DevToolsRouteBlueprintParams { + path: string; + title: string; + loader: () => Promise<{ default: () => JSX.Element }> | (() => JSX.Element); +} + +/** + * Extension blueprint for creating DevTools routes + * + * @example + * ```tsx + * const myDevToolsRoute = DevToolsRouteBlueprint.make({ + * params: { + * path: 'my-feature', + * title: 'My Feature', + * loader: () => import('./MyContent').then(m => ({ default: m.MyContent })) + * } + * }); + * ``` + * + * @alpha + */ +export const DevToolsRouteBlueprint = createExtensionBlueprint({ + kind: 'devtools-route', + attachTo: { id: 'page:devtools', input: 'routes' }, + output: [devToolsRouteDataRef], + factory(params: DevToolsRouteBlueprintParams) { + // Handle both sync and async component loading + const Component = typeof params.loader === 'function' + ? (() => { + const result = params.loader(); + if (result instanceof Promise) { + // Create a lazy component for async loading + return lazy(() => result); + } + // Direct component function + return result; + + })() + : params.loader; + + const children = typeof Component === 'function' + ? Loading...}> + : Component; + + return [ + devToolsRouteDataRef({ + path: params.path, + title: params.title, + children, + }), + ]; + }, + dataRefs: { + route: devToolsRouteDataRef, + }, +}); \ No newline at end of file diff --git a/plugins/devtools/src/alpha/devToolsRouteDataRef.ts b/plugins/devtools/src/alpha/devToolsRouteDataRef.ts new file mode 100644 index 0000000000..f4533f13e5 --- /dev/null +++ b/plugins/devtools/src/alpha/devToolsRouteDataRef.ts @@ -0,0 +1,37 @@ +/* + * 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 { createExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { JSX } from 'react'; + +/** + * Represents a DevTools route that can be contributed by extensions + * @alpha + */ +export interface DevToolsRouteData { + path: string; + title: string; + children: JSX.Element; +} + +/** + * Extension data reference for DevTools routes + * @alpha + */ +export const devToolsRouteDataRef = createExtensionDataRef() + .with({ + id: 'devtools.route', + }); \ No newline at end of file diff --git a/plugins/devtools/src/alpha/index.ts b/plugins/devtools/src/alpha/index.ts index 2f137f09ee..d326cac1be 100644 --- a/plugins/devtools/src/alpha/index.ts +++ b/plugins/devtools/src/alpha/index.ts @@ -15,3 +15,7 @@ */ export { default } from './plugin'; +export { DevToolsRouteBlueprint } from './devToolsRouteBlueprint'; +export { devToolsRouteDataRef } from './devToolsRouteDataRef'; +export type { DevToolsRouteData } from './devToolsRouteDataRef'; +export type { DevToolsRouteBlueprintParams } from './devToolsRouteBlueprint'; diff --git a/plugins/devtools/src/alpha/plugin.tsx b/plugins/devtools/src/alpha/plugin.tsx index 0f6d9ec4a1..02dca0141a 100644 --- a/plugins/devtools/src/alpha/plugin.tsx +++ b/plugins/devtools/src/alpha/plugin.tsx @@ -21,6 +21,7 @@ import { ApiBlueprint, PageBlueprint, NavItemBlueprint, + createExtensionInput, } from '@backstage/frontend-plugin-api'; import { devToolsApiRef, DevToolsClient } from '../api'; @@ -30,6 +31,7 @@ import { } from '@backstage/core-compat-api'; import BuildIcon from '@material-ui/icons/Build'; import { rootRouteRef } from '../routes'; +import { devToolsRouteDataRef } from './devToolsRouteDataRef'; /** @alpha */ export const devToolsApi = ApiBlueprint.make({ @@ -46,14 +48,21 @@ export const devToolsApi = ApiBlueprint.make({ }); /** @alpha */ -export const devToolsPage = PageBlueprint.make({ - params: { - path: '/devtools', - routeRef: convertLegacyRouteRef(rootRouteRef), - loader: () => - import('../components/DevToolsPage').then(m => - compatWrapper(), - ), +export const devToolsPage = PageBlueprint.makeWithOverrides({ + inputs: { + routes: createExtensionInput([devToolsRouteDataRef], { + optional: true, + }), + }, + factory(originalFactory, { inputs }) { + return originalFactory({ + path: '/devtools', + routeRef: convertLegacyRouteRef(rootRouteRef), + loader: () => + import('../components/DevToolsPage').then(m => + compatWrapper( route.get(devToolsRouteDataRef))} />), + ), + }); }, }); diff --git a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx index b4e5d3db5b..6375e562f4 100644 --- a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx +++ b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx @@ -23,9 +23,14 @@ import { ConfigContent } from '../Content/ConfigContent'; import { DevToolsLayout } from '../DevToolsLayout'; import { InfoContent } from '../Content/InfoContent'; import { RequirePermission } from '@backstage/plugin-permission-react'; +import { DevToolsRouteData } from '../../alpha/devToolsRouteDataRef'; + +export interface DefaultDevToolsPageProps { + extensionRoutes?: DevToolsRouteData[]; +} /** @public */ -export const DefaultDevToolsPage = () => ( +export const DefaultDevToolsPage = ({ extensionRoutes }: DefaultDevToolsPageProps) => ( @@ -37,5 +42,14 @@ export const DefaultDevToolsPage = () => ( + {extensionRoutes?.map((route, index) => ( + + {route.children} + + ))} ); diff --git a/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx index 07a549f8eb..6c351bbf96 100644 --- a/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx +++ b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx @@ -16,9 +16,14 @@ import { useOutlet } from 'react-router-dom'; import { DefaultDevToolsPage } from '../DefaultDevToolsPage'; +import { DevToolsRouteData } from '../../alpha/devToolsRouteDataRef'; -export const DevToolsPage = () => { +export interface DevToolsPageProps { + extensionRoutes?: DevToolsRouteData[]; +} + +export const DevToolsPage = ({ extensionRoutes }: DevToolsPageProps) => { const outlet = useOutlet(); - return <>{outlet || }; + return <>{outlet || }; }; diff --git a/yarn.lock b/yarn.lock index 853749b99e..b90990c0b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4938,6 +4938,7 @@ __metadata: "@backstage/dev-utils": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" + "@backstage/plugin-devtools": "workspace:^" "@material-ui/core": "npm:^4.9.13" "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:^4.0.0-alpha.60" @@ -29922,6 +29923,7 @@ __metadata: "@backstage/plugin-catalog-import": "workspace:^" "@backstage/plugin-catalog-react": "workspace:^" "@backstage/plugin-catalog-unprocessed-entities": "workspace:^" + "@backstage/plugin-devtools": "workspace:^" "@backstage/plugin-home": "workspace:^" "@backstage/plugin-kubernetes": "workspace:^" "@backstage/plugin-kubernetes-cluster": "workspace:^" From 42d31b0a03b16e6afab54661b5285d48b78367ee Mon Sep 17 00:00:00 2001 From: secustor Date: Wed, 3 Sep 2025 17:18:16 +0200 Subject: [PATCH 02/26] feat(devtools): working example Signed-off-by: secustor --- .../src/alpha/devToolsRouteBlueprint.tsx | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/plugins/devtools/src/alpha/devToolsRouteBlueprint.tsx b/plugins/devtools/src/alpha/devToolsRouteBlueprint.tsx index fec93854ae..21f25a013a 100644 --- a/plugins/devtools/src/alpha/devToolsRouteBlueprint.tsx +++ b/plugins/devtools/src/alpha/devToolsRouteBlueprint.tsx @@ -25,12 +25,12 @@ import { lazy, Suspense, JSX } from 'react'; export interface DevToolsRouteBlueprintParams { path: string; title: string; - loader: () => Promise<{ default: () => JSX.Element }> | (() => JSX.Element); + loader: () => Promise<{ default: () => JSX.Element }>; } /** * Extension blueprint for creating DevTools routes - * + * * @example * ```tsx * const myDevToolsRoute = DevToolsRouteBlueprint.make({ @@ -41,7 +41,7 @@ export interface DevToolsRouteBlueprintParams { * } * }); * ``` - * + * * @alpha */ export const DevToolsRouteBlueprint = createExtensionBlueprint({ @@ -49,23 +49,14 @@ export const DevToolsRouteBlueprint = createExtensionBlueprint({ attachTo: { id: 'page:devtools', input: 'routes' }, output: [devToolsRouteDataRef], factory(params: DevToolsRouteBlueprintParams) { - // Handle both sync and async component loading - const Component = typeof params.loader === 'function' - ? (() => { - const result = params.loader(); - if (result instanceof Promise) { - // Create a lazy component for async loading - return lazy(() => result); - } - // Direct component function - return result; - - })() - : params.loader; + // Create a lazy component for async loading + const LazyComponent = lazy(params.loader); - const children = typeof Component === 'function' - ? Loading...}> - : Component; + const children = ( + Loading...}> + + + ); return [ devToolsRouteDataRef({ @@ -78,4 +69,4 @@ export const DevToolsRouteBlueprint = createExtensionBlueprint({ dataRefs: { route: devToolsRouteDataRef, }, -}); \ No newline at end of file +}); From 45c814961972de8c38eee31cd478da4bd3f5cd23 Mon Sep 17 00:00:00 2001 From: secustor Date: Wed, 3 Sep 2025 17:43:22 +0200 Subject: [PATCH 03/26] add prettier and remove volta references Signed-off-by: secustor --- package.json | 3 --- packages/backend/package.json | 3 --- plugins/devtools/src/alpha/devToolsRouteDataRef.ts | 6 +++--- plugins/devtools/src/alpha/plugin.tsx | 8 +++++++- .../DefaultDevToolsPage/DefaultDevToolsPage.tsx | 8 +++++--- .../devtools/src/components/DevToolsPage/DevToolsPage.tsx | 4 +++- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 88238a1a21..d4ebc22bc1 100644 --- a/package.json +++ b/package.json @@ -185,8 +185,5 @@ } }, "tsConfig": "./tsconfig.json" - }, - "volta": { - "node": "22.19.0" } } diff --git a/packages/backend/package.json b/packages/backend/package.json index 0d03b12d19..a60b6d3940 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -71,8 +71,5 @@ }, "devDependencies": { "@backstage/cli": "workspace:^" - }, - "volta": { - "node": "22.19.0" } } diff --git a/plugins/devtools/src/alpha/devToolsRouteDataRef.ts b/plugins/devtools/src/alpha/devToolsRouteDataRef.ts index f4533f13e5..5cea0931c6 100644 --- a/plugins/devtools/src/alpha/devToolsRouteDataRef.ts +++ b/plugins/devtools/src/alpha/devToolsRouteDataRef.ts @@ -31,7 +31,7 @@ export interface DevToolsRouteData { * Extension data reference for DevTools routes * @alpha */ -export const devToolsRouteDataRef = createExtensionDataRef() - .with({ +export const devToolsRouteDataRef = + createExtensionDataRef().with({ id: 'devtools.route', - }); \ No newline at end of file + }); diff --git a/plugins/devtools/src/alpha/plugin.tsx b/plugins/devtools/src/alpha/plugin.tsx index 02dca0141a..d4c4b5d755 100644 --- a/plugins/devtools/src/alpha/plugin.tsx +++ b/plugins/devtools/src/alpha/plugin.tsx @@ -60,7 +60,13 @@ export const devToolsPage = PageBlueprint.makeWithOverrides({ routeRef: convertLegacyRouteRef(rootRouteRef), loader: () => import('../components/DevToolsPage').then(m => - compatWrapper( route.get(devToolsRouteDataRef))} />), + compatWrapper( + + route.get(devToolsRouteDataRef), + )} + />, + ), ), }); }, diff --git a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx index 6375e562f4..e34c48bb0f 100644 --- a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx +++ b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx @@ -30,7 +30,9 @@ export interface DefaultDevToolsPageProps { } /** @public */ -export const DefaultDevToolsPage = ({ extensionRoutes }: DefaultDevToolsPageProps) => ( +export const DefaultDevToolsPage = ({ + extensionRoutes, +}: DefaultDevToolsPageProps) => ( @@ -43,9 +45,9 @@ export const DefaultDevToolsPage = ({ extensionRoutes }: DefaultDevToolsPageProp {extensionRoutes?.map((route, index) => ( - {route.children} diff --git a/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx index 6c351bbf96..341fa29094 100644 --- a/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx +++ b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx @@ -25,5 +25,7 @@ export interface DevToolsPageProps { export const DevToolsPage = ({ extensionRoutes }: DevToolsPageProps) => { const outlet = useOutlet(); - return <>{outlet || }; + return ( + <>{outlet || } + ); }; From f4d90863319d3a68d8fa0117eb9c66606c1a40bb Mon Sep 17 00:00:00 2001 From: secustor Date: Wed, 3 Sep 2025 17:57:03 +0200 Subject: [PATCH 04/26] some simplification Signed-off-by: secustor --- .../src/alpha/devToolsRoute.ts | 8 +++++--- .../src/alpha/devToolsRouteBlueprint.tsx | 20 ++++++++----------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts b/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts index c90a6b8270..e2479341c8 100644 --- a/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts +++ b/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { createElement } from 'react'; import { DevToolsRouteBlueprint } from '@backstage/plugin-devtools/alpha'; /** @@ -25,8 +26,9 @@ export const unprocessedEntitiesDevToolsRoute = DevToolsRouteBlueprint.make({ path: 'unprocessed-entities', title: 'Unprocessed Entities', loader: () => - import('../components/UnprocessedEntities').then(m => ({ - default: m.UnprocessedEntitiesContent, - })), + import('../components/UnprocessedEntities').then( + ({ UnprocessedEntitiesContent }) => + createElement(UnprocessedEntitiesContent), + ), }, }); diff --git a/plugins/devtools/src/alpha/devToolsRouteBlueprint.tsx b/plugins/devtools/src/alpha/devToolsRouteBlueprint.tsx index 21f25a013a..150ce43fb3 100644 --- a/plugins/devtools/src/alpha/devToolsRouteBlueprint.tsx +++ b/plugins/devtools/src/alpha/devToolsRouteBlueprint.tsx @@ -14,9 +14,12 @@ * limitations under the License. */ -import { createExtensionBlueprint } from '@backstage/frontend-plugin-api'; +import { + createExtensionBlueprint, + ExtensionBoundary, +} from '@backstage/frontend-plugin-api'; import { devToolsRouteDataRef } from './devToolsRouteDataRef'; -import { lazy, Suspense, JSX } from 'react'; +import { JSX } from 'react'; /** * Parameters for creating a DevTools route extension @@ -25,7 +28,7 @@ import { lazy, Suspense, JSX } from 'react'; export interface DevToolsRouteBlueprintParams { path: string; title: string; - loader: () => Promise<{ default: () => JSX.Element }>; + loader: () => Promise; } /** @@ -48,15 +51,8 @@ export const DevToolsRouteBlueprint = createExtensionBlueprint({ kind: 'devtools-route', attachTo: { id: 'page:devtools', input: 'routes' }, output: [devToolsRouteDataRef], - factory(params: DevToolsRouteBlueprintParams) { - // Create a lazy component for async loading - const LazyComponent = lazy(params.loader); - - const children = ( - Loading...}> - - - ); + factory(params: DevToolsRouteBlueprintParams, { node }) { + const children = ExtensionBoundary.lazy(node, params.loader); return [ devToolsRouteDataRef({ From be6cef5dab18b99fe6a9dbd140f286e4e7633598 Mon Sep 17 00:00:00 2001 From: secustor Date: Wed, 3 Sep 2025 18:21:53 +0200 Subject: [PATCH 05/26] refactor: move blueprints to `react` package Signed-off-by: secustor --- .changeset/slimy-ghosts-rescue.md | 7 + .../catalog-unprocessed-entities/package.json | 2 +- .../src/alpha/devToolsRoute.ts | 2 +- plugins/devtools-react/.eslintrc.js | 1 + plugins/devtools-react/README.md | 5 + plugins/devtools-react/catalog-info.yaml | 10 ++ plugins/devtools-react/package.json | 46 +++++++ plugins/devtools-react/report.api.md | 54 ++++++++ .../src}/devToolsRouteBlueprint.tsx | 0 .../src}/devToolsRouteDataRef.ts | 0 plugins/devtools-react/src/index.ts | 29 ++++ plugins/devtools/package.json | 1 + plugins/devtools/src/alpha/index.ts | 4 - plugins/devtools/src/alpha/plugin.tsx | 2 +- .../DefaultDevToolsPage.tsx | 2 +- .../components/DevToolsPage/DevToolsPage.tsx | 2 +- yarn.lock | 125 +++++++++++++++--- 17 files changed, 262 insertions(+), 30 deletions(-) create mode 100644 .changeset/slimy-ghosts-rescue.md create mode 100644 plugins/devtools-react/.eslintrc.js create mode 100644 plugins/devtools-react/README.md create mode 100644 plugins/devtools-react/catalog-info.yaml create mode 100644 plugins/devtools-react/package.json create mode 100644 plugins/devtools-react/report.api.md rename plugins/{devtools/src/alpha => devtools-react/src}/devToolsRouteBlueprint.tsx (100%) rename plugins/{devtools/src/alpha => devtools-react/src}/devToolsRouteDataRef.ts (100%) create mode 100644 plugins/devtools-react/src/index.ts diff --git a/.changeset/slimy-ghosts-rescue.md b/.changeset/slimy-ghosts-rescue.md new file mode 100644 index 0000000000..e519411341 --- /dev/null +++ b/.changeset/slimy-ghosts-rescue.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-catalog-unprocessed-entities': patch +'@backstage/plugin-devtools-react': patch +'@backstage/plugin-devtools': patch +--- + +Add support for adding `unprocessed-entities` and other tabs to `devtools` diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index c48f290d25..d220dd5b79 100644 --- a/plugins/catalog-unprocessed-entities/package.json +++ b/plugins/catalog-unprocessed-entities/package.json @@ -56,7 +56,7 @@ "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", - "@backstage/plugin-devtools": "workspace:^", + "@backstage/plugin-devtools-react": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "^4.0.0-alpha.60", diff --git a/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts b/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts index e2479341c8..7e4faa0ac3 100644 --- a/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts +++ b/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts @@ -15,7 +15,7 @@ */ import { createElement } from 'react'; -import { DevToolsRouteBlueprint } from '@backstage/plugin-devtools/alpha'; +import { DevToolsRouteBlueprint } from '@backstage/plugin-devtools-react'; /** * DevTools route extension for catalog unprocessed entities diff --git a/plugins/devtools-react/.eslintrc.js b/plugins/devtools-react/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/devtools-react/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/devtools-react/README.md b/plugins/devtools-react/README.md new file mode 100644 index 0000000000..a6ca2b4723 --- /dev/null +++ b/plugins/devtools-react/README.md @@ -0,0 +1,5 @@ +# @backstage/plugin-devtools-react + +Welcome to the web library package for the devtools plugin! + +_This plugin was created through the Backstage CLI_ diff --git a/plugins/devtools-react/catalog-info.yaml b/plugins/devtools-react/catalog-info.yaml new file mode 100644 index 0000000000..98b847bee3 --- /dev/null +++ b/plugins/devtools-react/catalog-info.yaml @@ -0,0 +1,10 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-devtools-react + title: '@backstage/plugin-devtools-react' + description: Web library for the devtools plugin +spec: + lifecycle: experimental + type: backstage-web-library + owner: maintainers diff --git a/plugins/devtools-react/package.json b/plugins/devtools-react/package.json new file mode 100644 index 0000000000..78f7167c04 --- /dev/null +++ b/plugins/devtools-react/package.json @@ -0,0 +1,46 @@ +{ + "name": "@backstage/plugin-devtools-react", + "version": "0.1.0", + "license": "Apache-2.0", + "private": true, + "description": "Web library for the devtools plugin", + "main": "src/index.ts", + "types": "src/index.ts", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "web-library", + "pluginId": "devtools" + }, + "sideEffects": false, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/core-plugin-api": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", + "@material-ui/core": "^4.9.13" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@testing-library/jest-dom": "^6.0.0", + "@testing-library/react": "^14.0.0", + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/devtools-react/report.api.md b/plugins/devtools-react/report.api.md new file mode 100644 index 0000000000..dccd40f75f --- /dev/null +++ b/plugins/devtools-react/report.api.md @@ -0,0 +1,54 @@ +## API Report File for "@backstage/plugin-devtools-react" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { ExtensionBlueprint } from '@backstage/frontend-plugin-api'; +import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { JSX as JSX_2 } from 'react'; + +// @alpha +export const DevToolsRouteBlueprint: ExtensionBlueprint<{ + kind: 'devtools-route'; + params: DevToolsRouteBlueprintParams; + output: ExtensionDataRef; + inputs: {}; + config: {}; + configInput: {}; + dataRefs: { + route: ConfigurableExtensionDataRef< + DevToolsRouteData, + 'devtools.route', + {} + >; + }; +}>; + +// @alpha +export interface DevToolsRouteBlueprintParams { + // (undocumented) + loader: () => Promise; + // (undocumented) + path: string; + // (undocumented) + title: string; +} + +// @alpha +export interface DevToolsRouteData { + // (undocumented) + children: JSX_2.Element; + // (undocumented) + path: string; + // (undocumented) + title: string; +} + +// @alpha +export const devToolsRouteDataRef: ConfigurableExtensionDataRef< + DevToolsRouteData, + 'devtools.route', + {} +>; +``` diff --git a/plugins/devtools/src/alpha/devToolsRouteBlueprint.tsx b/plugins/devtools-react/src/devToolsRouteBlueprint.tsx similarity index 100% rename from plugins/devtools/src/alpha/devToolsRouteBlueprint.tsx rename to plugins/devtools-react/src/devToolsRouteBlueprint.tsx diff --git a/plugins/devtools/src/alpha/devToolsRouteDataRef.ts b/plugins/devtools-react/src/devToolsRouteDataRef.ts similarity index 100% rename from plugins/devtools/src/alpha/devToolsRouteDataRef.ts rename to plugins/devtools-react/src/devToolsRouteDataRef.ts diff --git a/plugins/devtools-react/src/index.ts b/plugins/devtools-react/src/index.ts new file mode 100644 index 0000000000..4adcfb39ad --- /dev/null +++ b/plugins/devtools-react/src/index.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2025 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. + */ + +/** + * Web library for the devtools plugin. + * + * @packageDocumentation + */ +export { + type DevToolsRouteData, + devToolsRouteDataRef, +} from './devToolsRouteDataRef'; +export { + type DevToolsRouteBlueprintParams, + DevToolsRouteBlueprint, +} from './devToolsRouteBlueprint.tsx'; diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index f05ec985d2..0fe2f413c7 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -57,6 +57,7 @@ "@backstage/errors": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", "@backstage/plugin-devtools-common": "workspace:^", + "@backstage/plugin-devtools-react": "workspace:^", "@backstage/plugin-permission-react": "workspace:^", "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.9.1", diff --git a/plugins/devtools/src/alpha/index.ts b/plugins/devtools/src/alpha/index.ts index d326cac1be..2f137f09ee 100644 --- a/plugins/devtools/src/alpha/index.ts +++ b/plugins/devtools/src/alpha/index.ts @@ -15,7 +15,3 @@ */ export { default } from './plugin'; -export { DevToolsRouteBlueprint } from './devToolsRouteBlueprint'; -export { devToolsRouteDataRef } from './devToolsRouteDataRef'; -export type { DevToolsRouteData } from './devToolsRouteDataRef'; -export type { DevToolsRouteBlueprintParams } from './devToolsRouteBlueprint'; diff --git a/plugins/devtools/src/alpha/plugin.tsx b/plugins/devtools/src/alpha/plugin.tsx index d4c4b5d755..fb7c627bdc 100644 --- a/plugins/devtools/src/alpha/plugin.tsx +++ b/plugins/devtools/src/alpha/plugin.tsx @@ -31,7 +31,7 @@ import { } from '@backstage/core-compat-api'; import BuildIcon from '@material-ui/icons/Build'; import { rootRouteRef } from '../routes'; -import { devToolsRouteDataRef } from './devToolsRouteDataRef'; +import { devToolsRouteDataRef } from '@backstage/plugin-devtools-react'; /** @alpha */ export const devToolsApi = ApiBlueprint.make({ diff --git a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx index e34c48bb0f..53ba1fd0dd 100644 --- a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx +++ b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx @@ -23,7 +23,7 @@ import { ConfigContent } from '../Content/ConfigContent'; import { DevToolsLayout } from '../DevToolsLayout'; import { InfoContent } from '../Content/InfoContent'; import { RequirePermission } from '@backstage/plugin-permission-react'; -import { DevToolsRouteData } from '../../alpha/devToolsRouteDataRef'; +import { DevToolsRouteData } from '@backstage/plugin-devtools-react'; export interface DefaultDevToolsPageProps { extensionRoutes?: DevToolsRouteData[]; diff --git a/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx index 341fa29094..04db0665a3 100644 --- a/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx +++ b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx @@ -16,7 +16,7 @@ import { useOutlet } from 'react-router-dom'; import { DefaultDevToolsPage } from '../DefaultDevToolsPage'; -import { DevToolsRouteData } from '../../alpha/devToolsRouteDataRef'; +import { DevToolsRouteData } from '@backstage/plugin-devtools-react'; export interface DevToolsPageProps { extensionRoutes?: DevToolsRouteData[]; diff --git a/yarn.lock b/yarn.lock index b90990c0b6..15078530d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4938,7 +4938,7 @@ __metadata: "@backstage/dev-utils": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" - "@backstage/plugin-devtools": "workspace:^" + "@backstage/plugin-devtools-react": "workspace:^" "@material-ui/core": "npm:^4.9.13" "@material-ui/icons": "npm:^4.9.1" "@material-ui/lab": "npm:^4.0.0-alpha.60" @@ -5100,6 +5100,23 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-devtools-react@workspace:^, @backstage/plugin-devtools-react@workspace:plugins/devtools-react": + version: 0.0.0-use.local + resolution: "@backstage/plugin-devtools-react@workspace:plugins/devtools-react" + dependencies: + "@backstage/cli": "workspace:^" + "@backstage/core-plugin-api": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" + "@backstage/test-utils": "workspace:^" + "@material-ui/core": "npm:^4.9.13" + "@testing-library/jest-dom": "npm:^6.0.0" + "@testing-library/react": "npm:^14.0.0" + react: "npm:^16.13.1 || ^17.0.0 || ^18.0.0" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + languageName: unknown + linkType: soft + "@backstage/plugin-devtools@workspace:^, @backstage/plugin-devtools@workspace:plugins/devtools": version: 0.0.0-use.local resolution: "@backstage/plugin-devtools@workspace:plugins/devtools" @@ -5112,6 +5129,7 @@ __metadata: "@backstage/errors": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" "@backstage/plugin-devtools-common": "workspace:^" + "@backstage/plugin-devtools-react": "workspace:^" "@backstage/plugin-permission-react": "workspace:^" "@material-ui/core": "npm:^4.9.13" "@material-ui/icons": "npm:^4.9.1" @@ -19484,6 +19502,22 @@ __metadata: languageName: node linkType: hard +"@testing-library/dom@npm:^9.0.0": + version: 9.3.4 + resolution: "@testing-library/dom@npm:9.3.4" + dependencies: + "@babel/code-frame": "npm:^7.10.4" + "@babel/runtime": "npm:^7.12.5" + "@types/aria-query": "npm:^5.0.1" + aria-query: "npm:5.1.3" + chalk: "npm:^4.1.0" + dom-accessibility-api: "npm:^0.5.9" + lz-string: "npm:^1.5.0" + pretty-format: "npm:^27.0.2" + checksum: 10/510da752ea76f4a10a0a4e3a77917b0302cf03effe576cd3534cab7e796533ee2b0e9fb6fb11b911a1ebd7c70a0bb6f235bf4f816c9b82b95b8fe0cddfd10975 + languageName: node + linkType: hard + "@testing-library/jest-dom@npm:6.5.0": version: 6.5.0 resolution: "@testing-library/jest-dom@npm:6.5.0" @@ -19536,6 +19570,20 @@ __metadata: languageName: node linkType: hard +"@testing-library/react@npm:^14.0.0": + version: 14.3.1 + resolution: "@testing-library/react@npm:14.3.1" + dependencies: + "@babel/runtime": "npm:^7.12.5" + "@testing-library/dom": "npm:^9.0.0" + "@types/react-dom": "npm:^18.0.0" + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + checksum: 10/83359dcdf9eaf067839f34604e1a181cbc14fc09f3a07672403700fcc6a900c4b8054ad1114fc24b4b9f89d84e2a09e1b7c9afce2306b1d4b4c9e30eb1cb12de + languageName: node + linkType: hard + "@testing-library/react@npm:^16.0.0": version: 16.3.0 resolution: "@testing-library/react@npm:16.3.0" @@ -23938,6 +23986,15 @@ __metadata: languageName: node linkType: hard +"aria-query@npm:5.1.3": + version: 5.1.3 + resolution: "aria-query@npm:5.1.3" + dependencies: + deep-equal: "npm:^2.0.5" + checksum: 10/e5da608a7c4954bfece2d879342b6c218b6b207e2d9e5af270b5e38ef8418f02d122afdc948b68e32649b849a38377785252059090d66fa8081da95d1609c0d2 + languageName: node + linkType: hard + "aria-query@npm:5.3.0": version: 5.3.0 resolution: "aria-query@npm:5.3.0" @@ -23954,7 +24011,7 @@ __metadata: languageName: node linkType: hard -"array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": +"array-buffer-byte-length@npm:^1.0.0, array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2": version: 1.0.2 resolution: "array-buffer-byte-length@npm:1.0.2" dependencies: @@ -27714,6 +27771,32 @@ __metadata: languageName: node linkType: hard +"deep-equal@npm:^2.0.5": + version: 2.2.3 + resolution: "deep-equal@npm:2.2.3" + dependencies: + array-buffer-byte-length: "npm:^1.0.0" + call-bind: "npm:^1.0.5" + es-get-iterator: "npm:^1.1.3" + get-intrinsic: "npm:^1.2.2" + is-arguments: "npm:^1.1.1" + is-array-buffer: "npm:^3.0.2" + is-date-object: "npm:^1.0.5" + is-regex: "npm:^1.1.4" + is-shared-array-buffer: "npm:^1.0.2" + isarray: "npm:^2.0.5" + object-is: "npm:^1.1.5" + object-keys: "npm:^1.1.1" + object.assign: "npm:^4.1.4" + regexp.prototype.flags: "npm:^1.5.1" + side-channel: "npm:^1.0.4" + which-boxed-primitive: "npm:^1.0.2" + which-collection: "npm:^1.0.1" + which-typed-array: "npm:^1.1.13" + checksum: 10/1ce49d0b71d0f14d8ef991a742665eccd488dfc9b3cada069d4d7a86291e591c92d2589c832811dea182b4015736b210acaaebce6184be356c1060d176f5a05f + languageName: node + linkType: hard + "deep-equal@npm:~1.0.1": version: 1.0.1 resolution: "deep-equal@npm:1.0.1" @@ -28922,7 +29005,7 @@ __metadata: languageName: node linkType: hard -"es-get-iterator@npm:^1.0.2": +"es-get-iterator@npm:^1.0.2, es-get-iterator@npm:^1.1.3": version: 1.1.3 resolution: "es-get-iterator@npm:1.1.3" dependencies: @@ -31517,7 +31600,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.3.0": +"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.3.0": version: 1.3.0 resolution: "get-intrinsic@npm:1.3.0" dependencies: @@ -33450,7 +33533,7 @@ __metadata: languageName: node linkType: hard -"is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": +"is-array-buffer@npm:^3.0.2, is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": version: 3.0.5 resolution: "is-array-buffer@npm:3.0.5" dependencies: @@ -33923,7 +34006,7 @@ __metadata: languageName: node linkType: hard -"is-regex@npm:^1.2.1": +"is-regex@npm:^1.1.4, is-regex@npm:^1.2.1": version: 1.2.1 resolution: "is-regex@npm:1.2.1" dependencies: @@ -33972,7 +34055,7 @@ __metadata: languageName: node linkType: hard -"is-shared-array-buffer@npm:^1.0.4": +"is-shared-array-buffer@npm:^1.0.2, is-shared-array-buffer@npm:^1.0.4": version: 1.0.4 resolution: "is-shared-array-buffer@npm:1.0.4" dependencies: @@ -43395,6 +43478,15 @@ __metadata: languageName: node linkType: hard +"react@npm:^16.13.1 || ^17.0.0 || ^18.0.0, react@npm:^18.0.2": + version: 18.3.1 + resolution: "react@npm:18.3.1" + dependencies: + loose-envify: "npm:^1.1.0" + checksum: 10/261137d3f3993eaa2368a83110466fc0e558bc2c7f7ae7ca52d94f03aac945f45146bd85e5f481044db1758a1dbb57879e2fcdd33924e2dde1bdc550ce73f7bf + languageName: node + linkType: hard + "react@npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0": version: 19.1.1 resolution: "react@npm:19.1.1" @@ -43412,15 +43504,6 @@ __metadata: languageName: node linkType: hard -"react@npm:^18.0.2": - version: 18.3.1 - resolution: "react@npm:18.3.1" - dependencies: - loose-envify: "npm:^1.1.0" - checksum: 10/261137d3f3993eaa2368a83110466fc0e558bc2c7f7ae7ca52d94f03aac945f45146bd85e5f481044db1758a1dbb57879e2fcdd33924e2dde1bdc550ce73f7bf - languageName: node - linkType: hard - "read-cmd-shim@npm:^2.0.0": version: 2.0.0 resolution: "read-cmd-shim@npm:2.0.0" @@ -43702,7 +43785,7 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.5.3": +"regexp.prototype.flags@npm:^1.5.1, regexp.prototype.flags@npm:^1.5.3": version: 1.5.4 resolution: "regexp.prototype.flags@npm:1.5.4" dependencies: @@ -45206,7 +45289,7 @@ __metadata: languageName: node linkType: hard -"side-channel@npm:^1.0.6, side-channel@npm:^1.1.0": +"side-channel@npm:^1.0.4, side-channel@npm:^1.0.6, side-channel@npm:^1.1.0": version: 1.1.0 resolution: "side-channel@npm:1.1.0" dependencies: @@ -49373,7 +49456,7 @@ __metadata: languageName: node linkType: hard -"which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": +"which-boxed-primitive@npm:^1.0.2, which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": version: 1.1.1 resolution: "which-boxed-primitive@npm:1.1.1" dependencies: @@ -49407,7 +49490,7 @@ __metadata: languageName: node linkType: hard -"which-collection@npm:^1.0.2": +"which-collection@npm:^1.0.1, which-collection@npm:^1.0.2": version: 1.0.2 resolution: "which-collection@npm:1.0.2" dependencies: @@ -49429,7 +49512,7 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.18, which-typed-array@npm:^1.1.2": +"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.18, which-typed-array@npm:^1.1.2": version: 1.1.19 resolution: "which-typed-array@npm:1.1.19" dependencies: From e405ca9b7a34dc0eb98799374caaad693b9a4424 Mon Sep 17 00:00:00 2001 From: secustor Date: Wed, 3 Sep 2025 18:43:36 +0200 Subject: [PATCH 06/26] chore: fix api reports Signed-off-by: secustor --- plugins/devtools-backend/package.json | 3 +- plugins/devtools-common/package.json | 3 +- plugins/devtools-react/package.json | 45 ++++++++++++------- plugins/devtools-react/report.api.md | 8 ++-- .../src/devToolsRouteBlueprint.tsx | 5 +-- .../src/devToolsRouteDataRef.ts | 4 +- plugins/devtools/package.json | 3 +- plugins/devtools/report-alpha.api.md | 17 +++++-- plugins/devtools/report.api.md | 11 ++++- .../DefaultDevToolsPage.tsx | 10 +---- .../components/DevToolsPage/DevToolsPage.tsx | 3 ++ .../src/components/DevToolsPage/index.ts | 2 +- plugins/devtools/src/components/index.ts | 1 + 13 files changed, 73 insertions(+), 42 deletions(-) diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index 038fcbce82..6fafab972a 100644 --- a/plugins/devtools-backend/package.json +++ b/plugins/devtools-backend/package.json @@ -7,7 +7,8 @@ "pluginPackages": [ "@backstage/plugin-devtools", "@backstage/plugin-devtools-backend", - "@backstage/plugin-devtools-common" + "@backstage/plugin-devtools-common", + "@backstage/plugin-devtools-react" ] }, "publishConfig": { diff --git a/plugins/devtools-common/package.json b/plugins/devtools-common/package.json index a5056edf41..ec39a0809d 100644 --- a/plugins/devtools-common/package.json +++ b/plugins/devtools-common/package.json @@ -8,7 +8,8 @@ "pluginPackages": [ "@backstage/plugin-devtools", "@backstage/plugin-devtools-backend", - "@backstage/plugin-devtools-common" + "@backstage/plugin-devtools-common", + "@backstage/plugin-devtools-react" ] }, "publishConfig": { diff --git a/plugins/devtools-react/package.json b/plugins/devtools-react/package.json index 78f7167c04..4e96fdb29c 100644 --- a/plugins/devtools-react/package.json +++ b/plugins/devtools-react/package.json @@ -1,38 +1,49 @@ { "name": "@backstage/plugin-devtools-react", "version": "0.1.0", - "license": "Apache-2.0", - "private": true, "description": "Web library for the devtools plugin", - "main": "src/index.ts", - "types": "src/index.ts", + "backstage": { + "role": "web-library", + "pluginId": "devtools", + "pluginPackages": [ + "@backstage/plugin-devtools", + "@backstage/plugin-devtools-backend", + "@backstage/plugin-devtools-common", + "@backstage/plugin-devtools-react" + ] + }, "publishConfig": { "access": "public", "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, - "backstage": { - "role": "web-library", - "pluginId": "devtools" + "private": true, + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/devtools-react" }, + "license": "Apache-2.0", "sideEffects": false, + "main": "src/index.ts", + "types": "src/index.ts", + "files": [ + "dist" + ], "scripts": { - "start": "backstage-cli package start", "build": "backstage-cli package build", - "lint": "backstage-cli package lint", - "test": "backstage-cli package test", "clean": "backstage-cli package clean", + "lint": "backstage-cli package lint", "prepack": "backstage-cli package prepack", - "postpack": "backstage-cli package postpack" + "postpack": "backstage-cli package postpack", + "start": "backstage-cli package start", + "test": "backstage-cli package test" }, "dependencies": { "@backstage/core-plugin-api": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", "@material-ui/core": "^4.9.13" }, - "peerDependencies": { - "react": "^16.13.1 || ^17.0.0 || ^18.0.0" - }, "devDependencies": { "@backstage/cli": "workspace:^", "@backstage/test-utils": "workspace:^", @@ -40,7 +51,7 @@ "@testing-library/react": "^14.0.0", "react": "^16.13.1 || ^17.0.0 || ^18.0.0" }, - "files": [ - "dist" - ] + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + } } diff --git a/plugins/devtools-react/report.api.md b/plugins/devtools-react/report.api.md index dccd40f75f..a6a6b6e69b 100644 --- a/plugins/devtools-react/report.api.md +++ b/plugins/devtools-react/report.api.md @@ -8,7 +8,7 @@ import { ExtensionBlueprint } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; -// @alpha +// @public export const DevToolsRouteBlueprint: ExtensionBlueprint<{ kind: 'devtools-route'; params: DevToolsRouteBlueprintParams; @@ -25,7 +25,7 @@ export const DevToolsRouteBlueprint: ExtensionBlueprint<{ }; }>; -// @alpha +// @public export interface DevToolsRouteBlueprintParams { // (undocumented) loader: () => Promise; @@ -35,7 +35,7 @@ export interface DevToolsRouteBlueprintParams { title: string; } -// @alpha +// @public export interface DevToolsRouteData { // (undocumented) children: JSX_2.Element; @@ -45,7 +45,7 @@ export interface DevToolsRouteData { title: string; } -// @alpha +// @public export const devToolsRouteDataRef: ConfigurableExtensionDataRef< DevToolsRouteData, 'devtools.route', diff --git a/plugins/devtools-react/src/devToolsRouteBlueprint.tsx b/plugins/devtools-react/src/devToolsRouteBlueprint.tsx index 150ce43fb3..a048641418 100644 --- a/plugins/devtools-react/src/devToolsRouteBlueprint.tsx +++ b/plugins/devtools-react/src/devToolsRouteBlueprint.tsx @@ -23,7 +23,7 @@ import { JSX } from 'react'; /** * Parameters for creating a DevTools route extension - * @alpha + * @public */ export interface DevToolsRouteBlueprintParams { path: string; @@ -44,8 +44,7 @@ export interface DevToolsRouteBlueprintParams { * } * }); * ``` - * - * @alpha + * @public */ export const DevToolsRouteBlueprint = createExtensionBlueprint({ kind: 'devtools-route', diff --git a/plugins/devtools-react/src/devToolsRouteDataRef.ts b/plugins/devtools-react/src/devToolsRouteDataRef.ts index 5cea0931c6..d6c506505a 100644 --- a/plugins/devtools-react/src/devToolsRouteDataRef.ts +++ b/plugins/devtools-react/src/devToolsRouteDataRef.ts @@ -19,7 +19,7 @@ import { JSX } from 'react'; /** * Represents a DevTools route that can be contributed by extensions - * @alpha + * @public */ export interface DevToolsRouteData { path: string; @@ -29,7 +29,7 @@ export interface DevToolsRouteData { /** * Extension data reference for DevTools routes - * @alpha + * @public */ export const devToolsRouteDataRef = createExtensionDataRef().with({ diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index 0fe2f413c7..5da086b0dc 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -7,7 +7,8 @@ "pluginPackages": [ "@backstage/plugin-devtools", "@backstage/plugin-devtools-backend", - "@backstage/plugin-devtools-common" + "@backstage/plugin-devtools-common", + "@backstage/plugin-devtools-react" ] }, "publishConfig": { diff --git a/plugins/devtools/report-alpha.api.md b/plugins/devtools/report-alpha.api.md index 7696267c35..d5b87ce96b 100644 --- a/plugins/devtools/report-alpha.api.md +++ b/plugins/devtools/report-alpha.api.md @@ -6,9 +6,12 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; import { ApiFactory } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { DevToolsRouteData } from '@backstage/plugin-devtools-react'; import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; +import { ExtensionInput } from '@backstage/frontend-plugin-api'; import { IconComponent } from '@backstage/core-plugin-api'; import { JSX as JSX_2 } from 'react'; import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api'; @@ -58,8 +61,6 @@ const _default: OverridableFrontendPlugin< }; }>; 'page:devtools': ExtensionDefinition<{ - kind: 'page'; - name: undefined; config: { path: string | undefined; }; @@ -76,7 +77,17 @@ const _default: OverridableFrontendPlugin< optional: true; } >; - inputs: {}; + inputs: { + routes: ExtensionInput< + ConfigurableExtensionDataRef, + { + singleton: false; + optional: true; + } + >; + }; + kind: 'page'; + name: undefined; params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; diff --git a/plugins/devtools/report.api.md b/plugins/devtools/report.api.md index e7a42f4377..c3434c0219 100644 --- a/plugins/devtools/report.api.md +++ b/plugins/devtools/report.api.md @@ -4,6 +4,7 @@ ```ts import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { DevToolsRouteData } from '@backstage/plugin-devtools-react'; import { ElementType } from 'react'; import { JSX as JSX_2 } from 'react/jsx-runtime'; import { ReactNode } from 'react'; @@ -27,7 +28,15 @@ export type DevToolsLayoutProps = { }; // @public (undocumented) -export const DevToolsPage: () => JSX_2.Element; +export const DevToolsPage: ({ + extensionRoutes, +}: DevToolsPageProps) => JSX_2.Element; + +// @public (undocumented) +export interface DevToolsPageProps { + // (undocumented) + extensionRoutes?: DevToolsRouteData[]; +} // @public (undocumented) export const devToolsPlugin: BackstagePlugin< diff --git a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx index 53ba1fd0dd..682983f46f 100644 --- a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx +++ b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx @@ -23,16 +23,10 @@ import { ConfigContent } from '../Content/ConfigContent'; import { DevToolsLayout } from '../DevToolsLayout'; import { InfoContent } from '../Content/InfoContent'; import { RequirePermission } from '@backstage/plugin-permission-react'; -import { DevToolsRouteData } from '@backstage/plugin-devtools-react'; - -export interface DefaultDevToolsPageProps { - extensionRoutes?: DevToolsRouteData[]; -} +import { DevToolsPageProps } from '../DevToolsPage'; /** @public */ -export const DefaultDevToolsPage = ({ - extensionRoutes, -}: DefaultDevToolsPageProps) => ( +export const DefaultDevToolsPage = ({ extensionRoutes }: DevToolsPageProps) => ( diff --git a/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx index 04db0665a3..f76017b89e 100644 --- a/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx +++ b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx @@ -18,6 +18,9 @@ import { useOutlet } from 'react-router-dom'; import { DefaultDevToolsPage } from '../DefaultDevToolsPage'; import { DevToolsRouteData } from '@backstage/plugin-devtools-react'; +/** + @public + */ export interface DevToolsPageProps { extensionRoutes?: DevToolsRouteData[]; } diff --git a/plugins/devtools/src/components/DevToolsPage/index.ts b/plugins/devtools/src/components/DevToolsPage/index.ts index 88d8bc06fa..743559f282 100644 --- a/plugins/devtools/src/components/DevToolsPage/index.ts +++ b/plugins/devtools/src/components/DevToolsPage/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { DevToolsPage } from './DevToolsPage'; +export { DevToolsPage, type DevToolsPageProps } from './DevToolsPage'; diff --git a/plugins/devtools/src/components/index.ts b/plugins/devtools/src/components/index.ts index 9938866bcc..f79797d65a 100644 --- a/plugins/devtools/src/components/index.ts +++ b/plugins/devtools/src/components/index.ts @@ -16,3 +16,4 @@ export * from './Content'; export * from './DevToolsLayout'; +export { type DevToolsPageProps } from './DevToolsPage'; From 9e4729ad956b9c319aa8d06f5c9b6544b78ff374 Mon Sep 17 00:00:00 2001 From: secustor Date: Wed, 3 Sep 2025 19:07:48 +0200 Subject: [PATCH 07/26] docs: add instructions to readmes Signed-off-by: secustor --- .../catalog-unprocessed-entities/README.md | 10 +++++-- plugins/devtools/README.md | 27 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/plugins/catalog-unprocessed-entities/README.md b/plugins/catalog-unprocessed-entities/README.md index ebadbb7054..3918d97e50 100644 --- a/plugins/catalog-unprocessed-entities/README.md +++ b/plugins/catalog-unprocessed-entities/README.md @@ -52,16 +52,22 @@ Import `catalogUnprocessedEntitiesPlugin` in your `App.tsx` and add it to your a ```typescript import catalogUnprocessedEntitiesPlugin from '@backstage/plugin-catalog-unprocessed-entities'; - -// ... +import { unprocessedEntitiesDevToolsRoute } from '@backstage/plugin-catalog-unprocessed-entities/alpha'; export const app = createApp({ features: [ + appFeature, // ... catalogUnprocessedEntitiesPlugin, // ... ], }); + +// Optionally add unprocessed entities route to devtools +const appFeature = createFrontendModule({ + pluginId: 'app', + extensions: [unprocessedEntitiesDevToolsRoute], +}); ``` ## Customization diff --git a/plugins/devtools/README.md b/plugins/devtools/README.md index 3d82190c2f..1112de0d36 100644 --- a/plugins/devtools/README.md +++ b/plugins/devtools/README.md @@ -160,6 +160,33 @@ You can also add tabs to show content from other plugins that fit well with the #### Catalog Unprocessed Entities Tab +##### New Frontend System + +Create an extension and/or load a 3rd party extension to add additional tabs. + +```tsx +import { DevToolsRouteBlueprint } from '@backstage/plugin-devtools-react'; + +export const unprocessedEntitiesDevToolsRoute = DevToolsRouteBlueprint.make({ + params: { + path: 'unprocessed-entities', + title: 'Unprocessed Entities', + loader: () => + import('../components/UnprocessedEntities').then( + ({ UnprocessedEntitiesContent }) => + createElement(UnprocessedEntitiesContent), + ), + }, +}); + +const appFeature = createFrontendModule({ + pluginId: 'app', + extensions: [unprocessedEntitiesDevToolsRoute], +}); +``` + +##### Old System + Here's how to add the Catalog Unprocessed Entities tab: 1. Install and setup the [Catalog Unprocessed Entities plugin](https://github.com/backstage/backstage/tree/master/plugins/catalog-unprocessed-entities) as per its documentation From 8ff1cac1d103a2bbf136795c1bd8ded594ad6bf8 Mon Sep 17 00:00:00 2001 From: secustor Date: Wed, 3 Sep 2025 19:12:01 +0200 Subject: [PATCH 08/26] chore: remove private Signed-off-by: secustor --- plugins/devtools-react/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/devtools-react/package.json b/plugins/devtools-react/package.json index 4e96fdb29c..d6ed74c1e5 100644 --- a/plugins/devtools-react/package.json +++ b/plugins/devtools-react/package.json @@ -17,7 +17,6 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, - "private": true, "repository": { "type": "git", "url": "https://github.com/backstage/backstage", From 314f48635df1c78b5cedb2270596329801d86b0e Mon Sep 17 00:00:00 2001 From: secustor Date: Wed, 3 Sep 2025 19:19:10 +0200 Subject: [PATCH 09/26] fix: peer dependencies Signed-off-by: secustor --- plugins/devtools-react/package.json | 8 +++++--- yarn.lock | 24 +++++++++++++----------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/plugins/devtools-react/package.json b/plugins/devtools-react/package.json index d6ed74c1e5..7d0bcaf8ac 100644 --- a/plugins/devtools-react/package.json +++ b/plugins/devtools-react/package.json @@ -47,10 +47,12 @@ "@backstage/cli": "workspace:^", "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^6.0.0", - "@testing-library/react": "^14.0.0", - "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + "@testing-library/react": "^14.0.0" }, "peerDependencies": { - "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0", + "react-router-dom": "^6.3.0" } } diff --git a/yarn.lock b/yarn.lock index 15078530d8..2123b5e26b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5111,9 +5111,11 @@ __metadata: "@material-ui/core": "npm:^4.9.13" "@testing-library/jest-dom": "npm:^6.0.0" "@testing-library/react": "npm:^14.0.0" - react: "npm:^16.13.1 || ^17.0.0 || ^18.0.0" peerDependencies: - react: ^16.13.1 || ^17.0.0 || ^18.0.0 + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.3.0 languageName: unknown linkType: soft @@ -43478,15 +43480,6 @@ __metadata: languageName: node linkType: hard -"react@npm:^16.13.1 || ^17.0.0 || ^18.0.0, react@npm:^18.0.2": - version: 18.3.1 - resolution: "react@npm:18.3.1" - dependencies: - loose-envify: "npm:^1.1.0" - checksum: 10/261137d3f3993eaa2368a83110466fc0e558bc2c7f7ae7ca52d94f03aac945f45146bd85e5f481044db1758a1dbb57879e2fcdd33924e2dde1bdc550ce73f7bf - languageName: node - linkType: hard - "react@npm:^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0": version: 19.1.1 resolution: "react@npm:19.1.1" @@ -43504,6 +43497,15 @@ __metadata: languageName: node linkType: hard +"react@npm:^18.0.2": + version: 18.3.1 + resolution: "react@npm:18.3.1" + dependencies: + loose-envify: "npm:^1.1.0" + checksum: 10/261137d3f3993eaa2368a83110466fc0e558bc2c7f7ae7ca52d94f03aac945f45146bd85e5f481044db1758a1dbb57879e2fcdd33924e2dde1bdc550ce73f7bf + languageName: node + linkType: hard + "read-cmd-shim@npm:^2.0.0": version: 2.0.0 resolution: "read-cmd-shim@npm:2.0.0" From 4c688ff6426ad48a0430084f967eb90fc54c0d14 Mon Sep 17 00:00:00 2001 From: secustor Date: Tue, 9 Sep 2025 14:55:39 +0200 Subject: [PATCH 10/26] fixup peerdependencies Signed-off-by: secustor --- plugins/devtools-react/package.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/plugins/devtools-react/package.json b/plugins/devtools-react/package.json index 7d0bcaf8ac..d5c57f7657 100644 --- a/plugins/devtools-react/package.json +++ b/plugins/devtools-react/package.json @@ -47,12 +47,21 @@ "@backstage/cli": "workspace:^", "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^6.0.0", - "@testing-library/react": "^14.0.0" + "@testing-library/react": "^14.0.0", + "@types/react": "^18.0.0", + "react": "^18.0.2", + "react-dom": "^18.0.2", + "react-router-dom": "^6.3.0" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", "react": "^17.0.0 || ^18.0.0", "react-dom": "^17.0.0 || ^18.0.0", "react-router-dom": "^6.3.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } } From efee6ad09b8f1739d0f6dcf55b97de525b186067 Mon Sep 17 00:00:00 2001 From: secustor Date: Tue, 9 Sep 2025 15:09:05 +0200 Subject: [PATCH 11/26] run yarn install Signed-off-by: secustor --- yarn.lock | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/yarn.lock b/yarn.lock index 2123b5e26b..24708a5699 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5111,11 +5111,18 @@ __metadata: "@material-ui/core": "npm:^4.9.13" "@testing-library/jest-dom": "npm:^6.0.0" "@testing-library/react": "npm:^14.0.0" + "@types/react": "npm:^18.0.0" + react: "npm:^18.0.2" + react-dom: "npm:^18.0.2" + react-router-dom: "npm:^6.3.0" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 react-router-dom: ^6.3.0 + peerDependenciesMeta: + "@types/react": + optional: true languageName: unknown linkType: soft From ed6daceda80db4374bb3e277cd8df2a8ba6f415f Mon Sep 17 00:00:00 2001 From: secustor Date: Tue, 9 Sep 2025 15:23:08 +0200 Subject: [PATCH 12/26] add missing api report Signed-off-by: secustor --- .../report-alpha.api.md | 13 +++++++++++++ .../src/alpha/devToolsRoute.ts | 2 ++ 2 files changed, 15 insertions(+) diff --git a/plugins/catalog-unprocessed-entities/report-alpha.api.md b/plugins/catalog-unprocessed-entities/report-alpha.api.md index 17d28ee165..2786100e1b 100644 --- a/plugins/catalog-unprocessed-entities/report-alpha.api.md +++ b/plugins/catalog-unprocessed-entities/report-alpha.api.md @@ -6,6 +6,8 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; import { ApiFactory } from '@backstage/frontend-plugin-api'; +import { DevToolsRouteBlueprintParams } from '@backstage/plugin-devtools-react'; +import { DevToolsRouteData } from '@backstage/plugin-devtools-react'; import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; @@ -88,5 +90,16 @@ const _default: OverridableFrontendPlugin< >; export default _default; +// @alpha +export const unprocessedEntitiesDevToolsRoute: ExtensionDefinition<{ + kind: 'devtools-route'; + name: undefined; + config: {}; + configInput: {}; + output: ExtensionDataRef; + inputs: {}; + params: DevToolsRouteBlueprintParams; +}>; + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts b/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts index 7e4faa0ac3..27a3eb0d13 100644 --- a/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts +++ b/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts @@ -20,6 +20,8 @@ import { DevToolsRouteBlueprint } from '@backstage/plugin-devtools-react'; /** * DevTools route extension for catalog unprocessed entities * This demonstrates how to create extensions at the app level using the new DevTools extension system + * + * @alpha */ export const unprocessedEntitiesDevToolsRoute = DevToolsRouteBlueprint.make({ params: { From 871888a3a10f1416e115afc3a06ad9260ae048f7 Mon Sep 17 00:00:00 2001 From: Sebastian Poxhofer Date: Sat, 13 Sep 2025 08:45:02 +0200 Subject: [PATCH 13/26] Apply suggestions from code review Co-authored-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Signed-off-by: Sebastian Poxhofer --- .changeset/slimy-ghosts-rescue.md | 2 +- plugins/devtools-react/README.md | 2 +- plugins/devtools-react/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.changeset/slimy-ghosts-rescue.md b/.changeset/slimy-ghosts-rescue.md index e519411341..c732293c54 100644 --- a/.changeset/slimy-ghosts-rescue.md +++ b/.changeset/slimy-ghosts-rescue.md @@ -1,6 +1,6 @@ --- '@backstage/plugin-catalog-unprocessed-entities': patch -'@backstage/plugin-devtools-react': patch +'@backstage/plugin-devtools-react': minor '@backstage/plugin-devtools': patch --- diff --git a/plugins/devtools-react/README.md b/plugins/devtools-react/README.md index a6ca2b4723..fe08b182a7 100644 --- a/plugins/devtools-react/README.md +++ b/plugins/devtools-react/README.md @@ -1,5 +1,5 @@ # @backstage/plugin-devtools-react -Welcome to the web library package for the devtools plugin! +Welcome to the web library package for the `devtools` plugin! _This plugin was created through the Backstage CLI_ diff --git a/plugins/devtools-react/package.json b/plugins/devtools-react/package.json index d5c57f7657..bec1564d80 100644 --- a/plugins/devtools-react/package.json +++ b/plugins/devtools-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-react", - "version": "0.1.0", + "version": "0.0.0", "description": "Web library for the devtools plugin", "backstage": { "role": "web-library", From 579f2ad003bd65359449798d24d2b8229e4b2199 Mon Sep 17 00:00:00 2001 From: secustor Date: Mon, 15 Sep 2025 05:24:18 +0200 Subject: [PATCH 14/26] implement suggestions Signed-off-by: secustor --- plugins/catalog-unprocessed-entities/README.md | 12 ++++++------ plugins/devtools/README.md | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/plugins/catalog-unprocessed-entities/README.md b/plugins/catalog-unprocessed-entities/README.md index 3918d97e50..65466f978c 100644 --- a/plugins/catalog-unprocessed-entities/README.md +++ b/plugins/catalog-unprocessed-entities/README.md @@ -54,6 +54,12 @@ Import `catalogUnprocessedEntitiesPlugin` in your `App.tsx` and add it to your a import catalogUnprocessedEntitiesPlugin from '@backstage/plugin-catalog-unprocessed-entities'; import { unprocessedEntitiesDevToolsRoute } from '@backstage/plugin-catalog-unprocessed-entities/alpha'; +// Optionally add unprocessed entities route to devtools +const appFeature = createFrontendModule({ + pluginId: 'app', + extensions: [unprocessedEntitiesDevToolsRoute], +}); + export const app = createApp({ features: [ appFeature, @@ -62,12 +68,6 @@ export const app = createApp({ // ... ], }); - -// Optionally add unprocessed entities route to devtools -const appFeature = createFrontendModule({ - pluginId: 'app', - extensions: [unprocessedEntitiesDevToolsRoute], -}); ``` ## Customization diff --git a/plugins/devtools/README.md b/plugins/devtools/README.md index 1112de0d36..f7aff3bd34 100644 --- a/plugins/devtools/README.md +++ b/plugins/devtools/README.md @@ -164,6 +164,10 @@ You can also add tabs to show content from other plugins that fit well with the Create an extension and/or load a 3rd party extension to add additional tabs. +```shell +yarn --cwd plugins/ add @backstage/plugin-devtools-react +``` + ```tsx import { DevToolsRouteBlueprint } from '@backstage/plugin-devtools-react'; From eda37c161dbd3ad175da165486eaf9beb4255d4e Mon Sep 17 00:00:00 2001 From: secustor Date: Tue, 16 Sep 2025 22:22:58 +0200 Subject: [PATCH 15/26] implement suggestions Signed-off-by: secustor --- packages/app-next/src/App.tsx | 4 +- .../{devToolsRoute.ts => devToolsRoute.tsx} | 28 +++++----- .../src/alpha/index.ts | 2 +- ...print.tsx => devToolsContentBlueprint.tsx} | 53 +++++++++++++------ ...vToolsRouteDataRef.ts => extensionData.ts} | 26 ++------- plugins/devtools-react/src/index.ts | 8 +-- plugins/devtools/src/alpha/plugin.tsx | 23 ++++---- .../DefaultDevToolsPage.tsx | 16 +++--- .../components/DevToolsPage/DevToolsPage.tsx | 9 ++-- 9 files changed, 83 insertions(+), 86 deletions(-) rename plugins/catalog-unprocessed-entities/src/alpha/{devToolsRoute.ts => devToolsRoute.tsx} (50%) rename plugins/devtools-react/src/{devToolsRouteBlueprint.tsx => devToolsContentBlueprint.tsx} (56%) rename plugins/devtools-react/src/{devToolsRouteDataRef.ts => extensionData.ts} (59%) diff --git a/packages/app-next/src/App.tsx b/packages/app-next/src/App.tsx index 92d1f06d6a..af0c3a972d 100644 --- a/packages/app-next/src/App.tsx +++ b/packages/app-next/src/App.tsx @@ -46,7 +46,7 @@ import { convertLegacyEntityContentExtension } from '@backstage/plugin-catalog-r import { pluginInfoResolver } from './pluginInfoResolver'; import { appModuleNav } from './modules/appModuleNav'; import devtoolsPlugin from '@backstage/plugin-devtools/alpha'; -import { unprocessedEntitiesDevToolsRoute } from '@backstage/plugin-catalog-unprocessed-entities/alpha'; +import { unprocessedEntitiesDevToolsContent } from '@backstage/plugin-catalog-unprocessed-entities/alpha'; /* @@ -115,7 +115,7 @@ const customHomePageModule = createFrontendModule({ const notFoundErrorPageModule = createFrontendModule({ pluginId: 'app', - extensions: [notFoundErrorPage, unprocessedEntitiesDevToolsRoute], + extensions: [notFoundErrorPage, unprocessedEntitiesDevToolsContent], }); const collectedLegacyPlugins = convertLegacyAppRoot( diff --git a/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts b/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.tsx similarity index 50% rename from plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts rename to plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.tsx index 27a3eb0d13..e2c652f21a 100644 --- a/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.ts +++ b/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.tsx @@ -14,23 +14,23 @@ * limitations under the License. */ -import { createElement } from 'react'; -import { DevToolsRouteBlueprint } from '@backstage/plugin-devtools-react'; +import { DevToolsContentBlueprint } from '@backstage/plugin-devtools-react'; +import { compatWrapper } from '@backstage/core-compat-api'; /** - * DevTools route extension for catalog unprocessed entities - * This demonstrates how to create extensions at the app level using the new DevTools extension system + * DevTools content for catalog unprocessed entities. * * @alpha */ -export const unprocessedEntitiesDevToolsRoute = DevToolsRouteBlueprint.make({ - params: { - path: 'unprocessed-entities', - title: 'Unprocessed Entities', - loader: () => - import('../components/UnprocessedEntities').then( - ({ UnprocessedEntitiesContent }) => - createElement(UnprocessedEntitiesContent), - ), +export const unprocessedEntitiesDevToolsContent = DevToolsContentBlueprint.make( + { + params: { + path: 'unprocessed-entities', + title: 'Unprocessed Entities', + loader: () => + import('../components/UnprocessedEntities').then(m => + compatWrapper(), + ), + }, }, -}); +); diff --git a/plugins/catalog-unprocessed-entities/src/alpha/index.ts b/plugins/catalog-unprocessed-entities/src/alpha/index.ts index 5738bb8a59..13c527949f 100644 --- a/plugins/catalog-unprocessed-entities/src/alpha/index.ts +++ b/plugins/catalog-unprocessed-entities/src/alpha/index.ts @@ -15,4 +15,4 @@ */ export { default } from './plugin'; -export { unprocessedEntitiesDevToolsRoute } from './devToolsRoute'; +export { unprocessedEntitiesDevToolsContent } from './devToolsRoute'; diff --git a/plugins/devtools-react/src/devToolsRouteBlueprint.tsx b/plugins/devtools-react/src/devToolsContentBlueprint.tsx similarity index 56% rename from plugins/devtools-react/src/devToolsRouteBlueprint.tsx rename to plugins/devtools-react/src/devToolsContentBlueprint.tsx index a048641418..835599dff3 100644 --- a/plugins/devtools-react/src/devToolsRouteBlueprint.tsx +++ b/plugins/devtools-react/src/devToolsContentBlueprint.tsx @@ -15,11 +15,13 @@ */ import { + coreExtensionData, createExtensionBlueprint, ExtensionBoundary, + RouteRef, } from '@backstage/frontend-plugin-api'; -import { devToolsRouteDataRef } from './devToolsRouteDataRef'; import { JSX } from 'react'; +import { contentTitleDataRef } from './extensionData'; /** * Parameters for creating a DevTools route extension @@ -29,6 +31,7 @@ export interface DevToolsRouteBlueprintParams { path: string; title: string; loader: () => Promise; + routeRef?: RouteRef; } /** @@ -46,22 +49,38 @@ export interface DevToolsRouteBlueprintParams { * ``` * @public */ -export const DevToolsRouteBlueprint = createExtensionBlueprint({ - kind: 'devtools-route', - attachTo: { id: 'page:devtools', input: 'routes' }, - output: [devToolsRouteDataRef], - factory(params: DevToolsRouteBlueprintParams, { node }) { - const children = ExtensionBoundary.lazy(node, params.loader); - - return [ - devToolsRouteDataRef({ - path: params.path, - title: params.title, - children, - }), - ]; - }, +export const DevToolsContentBlueprint = createExtensionBlueprint({ + kind: 'devtools-content', + attachTo: { id: 'page:devtools', input: 'contents' }, + output: [ + coreExtensionData.reactElement, + coreExtensionData.routePath, + coreExtensionData.routeRef.optional(), + contentTitleDataRef, + ], dataRefs: { - route: devToolsRouteDataRef, + title: contentTitleDataRef, + }, + config: { + schema: { + path: z => z.string().optional(), + title: z => z.string().optional(), + }, + }, + *factory(params: DevToolsRouteBlueprintParams, { node, config }) { + const path = config.path ?? params.path; + const title = config.title ?? params.title; + + yield coreExtensionData.reactElement( + ExtensionBoundary.lazy(node, params.loader), + ); + + yield coreExtensionData.routePath(path); + + yield contentTitleDataRef(title); + + if (params.routeRef) { + yield coreExtensionData.routeRef(params.routeRef); + } }, }); diff --git a/plugins/devtools-react/src/devToolsRouteDataRef.ts b/plugins/devtools-react/src/extensionData.ts similarity index 59% rename from plugins/devtools-react/src/devToolsRouteDataRef.ts rename to plugins/devtools-react/src/extensionData.ts index d6c506505a..62821c3e12 100644 --- a/plugins/devtools-react/src/devToolsRouteDataRef.ts +++ b/plugins/devtools-react/src/extensionData.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2025 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. @@ -13,25 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import { createExtensionDataRef } from '@backstage/frontend-plugin-api'; -import { JSX } from 'react'; -/** - * Represents a DevTools route that can be contributed by extensions - * @public - */ -export interface DevToolsRouteData { - path: string; - title: string; - children: JSX.Element; -} - -/** - * Extension data reference for DevTools routes - * @public - */ -export const devToolsRouteDataRef = - createExtensionDataRef().with({ - id: 'devtools.route', - }); +/** @internal */ +export const contentTitleDataRef = createExtensionDataRef().with({ + id: 'devtools.content-title', +}); diff --git a/plugins/devtools-react/src/index.ts b/plugins/devtools-react/src/index.ts index 4adcfb39ad..18b17387c1 100644 --- a/plugins/devtools-react/src/index.ts +++ b/plugins/devtools-react/src/index.ts @@ -19,11 +19,7 @@ * * @packageDocumentation */ -export { - type DevToolsRouteData, - devToolsRouteDataRef, -} from './devToolsRouteDataRef'; export { type DevToolsRouteBlueprintParams, - DevToolsRouteBlueprint, -} from './devToolsRouteBlueprint.tsx'; + DevToolsContentBlueprint, +} from './devToolsContentBlueprint.tsx'; diff --git a/plugins/devtools/src/alpha/plugin.tsx b/plugins/devtools/src/alpha/plugin.tsx index fb7c627bdc..7c020639bf 100644 --- a/plugins/devtools/src/alpha/plugin.tsx +++ b/plugins/devtools/src/alpha/plugin.tsx @@ -22,6 +22,7 @@ import { PageBlueprint, NavItemBlueprint, createExtensionInput, + coreExtensionData, } from '@backstage/frontend-plugin-api'; import { devToolsApiRef, DevToolsClient } from '../api'; @@ -31,7 +32,6 @@ import { } from '@backstage/core-compat-api'; import BuildIcon from '@material-ui/icons/Build'; import { rootRouteRef } from '../routes'; -import { devToolsRouteDataRef } from '@backstage/plugin-devtools-react'; /** @alpha */ export const devToolsApi = ApiBlueprint.make({ @@ -50,9 +50,16 @@ export const devToolsApi = ApiBlueprint.make({ /** @alpha */ export const devToolsPage = PageBlueprint.makeWithOverrides({ inputs: { - routes: createExtensionInput([devToolsRouteDataRef], { - optional: true, - }), + contents: createExtensionInput( + [ + coreExtensionData.reactElement, + coreExtensionData.routePath, + coreExtensionData.routeRef.optional(), + ], + { + optional: true, + }, + ), }, factory(originalFactory, { inputs }) { return originalFactory({ @@ -60,13 +67,7 @@ export const devToolsPage = PageBlueprint.makeWithOverrides({ routeRef: convertLegacyRouteRef(rootRouteRef), loader: () => import('../components/DevToolsPage').then(m => - compatWrapper( - - route.get(devToolsRouteDataRef), - )} - />, - ), + compatWrapper(), ), }); }, diff --git a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx index 682983f46f..b2c1457161 100644 --- a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx +++ b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx @@ -19,14 +19,14 @@ import { devToolsInfoReadPermission, } from '@backstage/plugin-devtools-common'; -import { ConfigContent } from '../Content/ConfigContent'; +import { ConfigContent } from '../Content'; import { DevToolsLayout } from '../DevToolsLayout'; -import { InfoContent } from '../Content/InfoContent'; +import { InfoContent } from '../Content'; import { RequirePermission } from '@backstage/plugin-permission-react'; import { DevToolsPageProps } from '../DevToolsPage'; /** @public */ -export const DefaultDevToolsPage = ({ extensionRoutes }: DevToolsPageProps) => ( +export const DefaultDevToolsPage = ({ extensions }: DevToolsPageProps) => ( @@ -38,13 +38,13 @@ export const DefaultDevToolsPage = ({ extensionRoutes }: DevToolsPageProps) => ( - {extensionRoutes?.map((route, index) => ( + {extensions?.map((extension, index) => ( - {route.children} + {extension.children} ))} diff --git a/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx index f76017b89e..8f84ed4a9b 100644 --- a/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx +++ b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx @@ -16,19 +16,16 @@ import { useOutlet } from 'react-router-dom'; import { DefaultDevToolsPage } from '../DefaultDevToolsPage'; -import { DevToolsRouteData } from '@backstage/plugin-devtools-react'; /** @public */ export interface DevToolsPageProps { - extensionRoutes?: DevToolsRouteData[]; + extensions?: any[]; } -export const DevToolsPage = ({ extensionRoutes }: DevToolsPageProps) => { +export const DevToolsPage = ({ extensions }: DevToolsPageProps) => { const outlet = useOutlet(); - return ( - <>{outlet || } - ); + return <>{outlet || }; }; From 81242cb5d987e9a155fc0a0b64b87cc893e1b5f2 Mon Sep 17 00:00:00 2001 From: secustor Date: Fri, 3 Oct 2025 13:16:03 +0200 Subject: [PATCH 16/26] add missing export Signed-off-by: secustor --- .../src/devToolsContentBlueprint.tsx | 1 - plugins/devtools-react/src/extensionData.ts | 2 +- plugins/devtools-react/src/index.ts | 1 + plugins/devtools/src/alpha/plugin.tsx | 16 ++++++++++++---- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/plugins/devtools-react/src/devToolsContentBlueprint.tsx b/plugins/devtools-react/src/devToolsContentBlueprint.tsx index e1c4c4ebfb..835599dff3 100644 --- a/plugins/devtools-react/src/devToolsContentBlueprint.tsx +++ b/plugins/devtools-react/src/devToolsContentBlueprint.tsx @@ -68,7 +68,6 @@ export const DevToolsContentBlueprint = createExtensionBlueprint({ }, }, *factory(params: DevToolsRouteBlueprintParams, { node, config }) { - console.log('DevToolsContentBlueprint', params, config); const path = config.path ?? params.path; const title = config.title ?? params.title; diff --git a/plugins/devtools-react/src/extensionData.ts b/plugins/devtools-react/src/extensionData.ts index 62821c3e12..33754f6556 100644 --- a/plugins/devtools-react/src/extensionData.ts +++ b/plugins/devtools-react/src/extensionData.ts @@ -15,7 +15,7 @@ */ import { createExtensionDataRef } from '@backstage/frontend-plugin-api'; -/** @internal */ +/** @public */ export const contentTitleDataRef = createExtensionDataRef().with({ id: 'devtools.content-title', }); diff --git a/plugins/devtools-react/src/index.ts b/plugins/devtools-react/src/index.ts index 18b17387c1..1a31aa4f09 100644 --- a/plugins/devtools-react/src/index.ts +++ b/plugins/devtools-react/src/index.ts @@ -23,3 +23,4 @@ export { type DevToolsRouteBlueprintParams, DevToolsContentBlueprint, } from './devToolsContentBlueprint.tsx'; +export { contentTitleDataRef } from './extensionData'; diff --git a/plugins/devtools/src/alpha/plugin.tsx b/plugins/devtools/src/alpha/plugin.tsx index 7c020639bf..5628492b90 100644 --- a/plugins/devtools/src/alpha/plugin.tsx +++ b/plugins/devtools/src/alpha/plugin.tsx @@ -32,6 +32,7 @@ import { } from '@backstage/core-compat-api'; import BuildIcon from '@material-ui/icons/Build'; import { rootRouteRef } from '../routes'; +import { contentTitleDataRef } from '@backstage/plugin-devtools-react'; /** @alpha */ export const devToolsApi = ApiBlueprint.make({ @@ -55,6 +56,7 @@ export const devToolsPage = PageBlueprint.makeWithOverrides({ coreExtensionData.reactElement, coreExtensionData.routePath, coreExtensionData.routeRef.optional(), + contentTitleDataRef, ], { optional: true, @@ -65,10 +67,16 @@ export const devToolsPage = PageBlueprint.makeWithOverrides({ return originalFactory({ path: '/devtools', routeRef: convertLegacyRouteRef(rootRouteRef), - loader: () => - import('../components/DevToolsPage').then(m => - compatWrapper(), - ), + loader: () => { + const extensions = inputs.contents.map(content => ({ + path: content.get(coreExtensionData.routePath), + title: content.get(contentTitleDataRef), + children: content.get(coreExtensionData.reactElement), + })); + return import('../components/DevToolsPage').then(m => + compatWrapper(), + ); + }, }); }, }); From 2fe3b3b91a68eb6f2936dbead6ab2c90d4fcb181 Mon Sep 17 00:00:00 2001 From: secustor Date: Mon, 6 Oct 2025 13:06:24 +0200 Subject: [PATCH 17/26] update api reports Signed-off-by: secustor --- .../report-alpha.api.md | 27 +++++++-- plugins/devtools-react/report.api.md | 58 ++++++++++--------- plugins/devtools/report-alpha.api.md | 14 ++++- plugins/devtools/report.api.md | 7 +-- 4 files changed, 66 insertions(+), 40 deletions(-) diff --git a/plugins/catalog-unprocessed-entities/report-alpha.api.md b/plugins/catalog-unprocessed-entities/report-alpha.api.md index 2786100e1b..1f41fb4081 100644 --- a/plugins/catalog-unprocessed-entities/report-alpha.api.md +++ b/plugins/catalog-unprocessed-entities/report-alpha.api.md @@ -7,7 +7,6 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; import { ApiFactory } from '@backstage/frontend-plugin-api'; import { DevToolsRouteBlueprintParams } from '@backstage/plugin-devtools-react'; -import { DevToolsRouteData } from '@backstage/plugin-devtools-react'; import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; @@ -91,12 +90,28 @@ const _default: OverridableFrontendPlugin< export default _default; // @alpha -export const unprocessedEntitiesDevToolsRoute: ExtensionDefinition<{ - kind: 'devtools-route'; +export const unprocessedEntitiesDevToolsContent: ExtensionDefinition<{ + kind: 'devtools-content'; name: undefined; - config: {}; - configInput: {}; - output: ExtensionDataRef; + config: { + path: string | undefined; + title: string | undefined; + }; + configInput: { + title?: string | undefined; + path?: string | undefined; + }; + output: + | ExtensionDataRef + | ExtensionDataRef + | ExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ExtensionDataRef; inputs: {}; params: DevToolsRouteBlueprintParams; }>; diff --git a/plugins/devtools-react/report.api.md b/plugins/devtools-react/report.api.md index a6a6b6e69b..b1b6711bab 100644 --- a/plugins/devtools-react/report.api.md +++ b/plugins/devtools-react/report.api.md @@ -3,25 +3,46 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionBlueprint } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; +import { RouteRef } from '@backstage/frontend-plugin-api'; + +// @public (undocumented) +export const contentTitleDataRef: ConfigurableExtensionDataRef< + string, + 'devtools.content-title', + {} +>; // @public -export const DevToolsRouteBlueprint: ExtensionBlueprint<{ - kind: 'devtools-route'; +export const DevToolsContentBlueprint: ExtensionBlueprint<{ + kind: 'devtools-content'; params: DevToolsRouteBlueprintParams; - output: ExtensionDataRef; + output: + | ExtensionDataRef + | ExtensionDataRef + | ExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ExtensionDataRef; inputs: {}; - config: {}; - configInput: {}; + config: { + path: string | undefined; + title: string | undefined; + }; + configInput: { + title?: string | undefined; + path?: string | undefined; + }; dataRefs: { - route: ConfigurableExtensionDataRef< - DevToolsRouteData, - 'devtools.route', - {} - >; + title: ConfigurableExtensionDataRef; }; }>; @@ -32,23 +53,8 @@ export interface DevToolsRouteBlueprintParams { // (undocumented) path: string; // (undocumented) - title: string; -} - -// @public -export interface DevToolsRouteData { - // (undocumented) - children: JSX_2.Element; - // (undocumented) - path: string; + routeRef?: RouteRef; // (undocumented) title: string; } - -// @public -export const devToolsRouteDataRef: ConfigurableExtensionDataRef< - DevToolsRouteData, - 'devtools.route', - {} ->; ``` diff --git a/plugins/devtools/report-alpha.api.md b/plugins/devtools/report-alpha.api.md index d5b87ce96b..42d5d3a26c 100644 --- a/plugins/devtools/report-alpha.api.md +++ b/plugins/devtools/report-alpha.api.md @@ -7,7 +7,6 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; import { ApiFactory } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; -import { DevToolsRouteData } from '@backstage/plugin-devtools-react'; import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; @@ -78,8 +77,17 @@ const _default: OverridableFrontendPlugin< } >; inputs: { - routes: ExtensionInput< - ConfigurableExtensionDataRef, + contents: ExtensionInput< + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + RouteRef, + 'core.routing.ref', + { + optional: true; + } + > + | ConfigurableExtensionDataRef, { singleton: false; optional: true; diff --git a/plugins/devtools/report.api.md b/plugins/devtools/report.api.md index c3434c0219..70fbcaadb7 100644 --- a/plugins/devtools/report.api.md +++ b/plugins/devtools/report.api.md @@ -4,7 +4,6 @@ ```ts import { BackstagePlugin } from '@backstage/core-plugin-api'; -import { DevToolsRouteData } from '@backstage/plugin-devtools-react'; import { ElementType } from 'react'; import { JSX as JSX_2 } from 'react/jsx-runtime'; import { ReactNode } from 'react'; @@ -28,14 +27,12 @@ export type DevToolsLayoutProps = { }; // @public (undocumented) -export const DevToolsPage: ({ - extensionRoutes, -}: DevToolsPageProps) => JSX_2.Element; +export const DevToolsPage: ({ extensions }: DevToolsPageProps) => JSX_2.Element; // @public (undocumented) export interface DevToolsPageProps { // (undocumented) - extensionRoutes?: DevToolsRouteData[]; + extensions?: any[]; } // @public (undocumented) From 45c2618e1f8a58e6c61751fac1653f9e07df6712 Mon Sep 17 00:00:00 2001 From: secustor Date: Wed, 22 Oct 2025 22:32:29 +0200 Subject: [PATCH 18/26] implement change requests Signed-off-by: secustor --- .../catalog-unprocessed-entities/README.md | 4 +-- .../report-alpha.api.md | 6 ++--- plugins/devtools-react/report.api.md | 18 +++---------- .../src/devToolsContentBlueprint.tsx | 16 +++++------- plugins/devtools-react/src/extensionData.ts | 21 --------------- plugins/devtools-react/src/index.ts | 3 +-- plugins/devtools/README.md | 26 ++++++++++--------- plugins/devtools/report-alpha.api.md | 7 ++--- plugins/devtools/src/alpha/plugin.tsx | 5 ++-- 9 files changed, 36 insertions(+), 70 deletions(-) delete mode 100644 plugins/devtools-react/src/extensionData.ts diff --git a/plugins/catalog-unprocessed-entities/README.md b/plugins/catalog-unprocessed-entities/README.md index 65466f978c..98bdf953d2 100644 --- a/plugins/catalog-unprocessed-entities/README.md +++ b/plugins/catalog-unprocessed-entities/README.md @@ -52,12 +52,12 @@ Import `catalogUnprocessedEntitiesPlugin` in your `App.tsx` and add it to your a ```typescript import catalogUnprocessedEntitiesPlugin from '@backstage/plugin-catalog-unprocessed-entities'; -import { unprocessedEntitiesDevToolsRoute } from '@backstage/plugin-catalog-unprocessed-entities/alpha'; +import { unprocessedEntitiesDevToolsContent } from '@backstage/plugin-catalog-unprocessed-entities/alpha'; // Optionally add unprocessed entities route to devtools const appFeature = createFrontendModule({ pluginId: 'app', - extensions: [unprocessedEntitiesDevToolsRoute], + extensions: [unprocessedEntitiesDevToolsContent], }); export const app = createApp({ diff --git a/plugins/catalog-unprocessed-entities/report-alpha.api.md b/plugins/catalog-unprocessed-entities/report-alpha.api.md index 1f41fb4081..a5d73ab4fa 100644 --- a/plugins/catalog-unprocessed-entities/report-alpha.api.md +++ b/plugins/catalog-unprocessed-entities/report-alpha.api.md @@ -6,7 +6,7 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; import { ApiFactory } from '@backstage/frontend-plugin-api'; -import { DevToolsRouteBlueprintParams } from '@backstage/plugin-devtools-react'; +import { DevToolsContentBlueprintParams } from '@backstage/plugin-devtools-react'; import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; @@ -111,9 +111,9 @@ export const unprocessedEntitiesDevToolsContent: ExtensionDefinition<{ optional: true; } > - | ExtensionDataRef; + | ExtensionDataRef; inputs: {}; - params: DevToolsRouteBlueprintParams; + params: DevToolsContentBlueprintParams; }>; // (No @packageDocumentation comment for this package) diff --git a/plugins/devtools-react/report.api.md b/plugins/devtools-react/report.api.md index b1b6711bab..d1b1a42250 100644 --- a/plugins/devtools-react/report.api.md +++ b/plugins/devtools-react/report.api.md @@ -4,23 +4,15 @@ ```ts import { AnyRouteRefParams } from '@backstage/frontend-plugin-api'; -import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionBlueprint } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; -// @public (undocumented) -export const contentTitleDataRef: ConfigurableExtensionDataRef< - string, - 'devtools.content-title', - {} ->; - // @public export const DevToolsContentBlueprint: ExtensionBlueprint<{ kind: 'devtools-content'; - params: DevToolsRouteBlueprintParams; + params: DevToolsContentBlueprintParams; output: | ExtensionDataRef | ExtensionDataRef @@ -31,7 +23,7 @@ export const DevToolsContentBlueprint: ExtensionBlueprint<{ optional: true; } > - | ExtensionDataRef; + | ExtensionDataRef; inputs: {}; config: { path: string | undefined; @@ -41,13 +33,11 @@ export const DevToolsContentBlueprint: ExtensionBlueprint<{ title?: string | undefined; path?: string | undefined; }; - dataRefs: { - title: ConfigurableExtensionDataRef; - }; + dataRefs: never; }>; // @public -export interface DevToolsRouteBlueprintParams { +export interface DevToolsContentBlueprintParams { // (undocumented) loader: () => Promise; // (undocumented) diff --git a/plugins/devtools-react/src/devToolsContentBlueprint.tsx b/plugins/devtools-react/src/devToolsContentBlueprint.tsx index 835599dff3..9c11916a77 100644 --- a/plugins/devtools-react/src/devToolsContentBlueprint.tsx +++ b/plugins/devtools-react/src/devToolsContentBlueprint.tsx @@ -21,13 +21,12 @@ import { RouteRef, } from '@backstage/frontend-plugin-api'; import { JSX } from 'react'; -import { contentTitleDataRef } from './extensionData'; /** * Parameters for creating a DevTools route extension * @public */ -export interface DevToolsRouteBlueprintParams { +export interface DevToolsContentBlueprintParams { path: string; title: string; loader: () => Promise; @@ -35,11 +34,11 @@ export interface DevToolsRouteBlueprintParams { } /** - * Extension blueprint for creating DevTools routes + * Extension blueprint for creating DevTools content pages (appearing as tabs) * * @example * ```tsx - * const myDevToolsRoute = DevToolsRouteBlueprint.make({ + * const myDevToolsRoute = DevToolsContentBlueprint.make({ * params: { * path: 'my-feature', * title: 'My Feature', @@ -56,18 +55,15 @@ export const DevToolsContentBlueprint = createExtensionBlueprint({ coreExtensionData.reactElement, coreExtensionData.routePath, coreExtensionData.routeRef.optional(), - contentTitleDataRef, + coreExtensionData.title, ], - dataRefs: { - title: contentTitleDataRef, - }, config: { schema: { path: z => z.string().optional(), title: z => z.string().optional(), }, }, - *factory(params: DevToolsRouteBlueprintParams, { node, config }) { + *factory(params: DevToolsContentBlueprintParams, { node, config }) { const path = config.path ?? params.path; const title = config.title ?? params.title; @@ -77,7 +73,7 @@ export const DevToolsContentBlueprint = createExtensionBlueprint({ yield coreExtensionData.routePath(path); - yield contentTitleDataRef(title); + yield coreExtensionData.title(title); if (params.routeRef) { yield coreExtensionData.routeRef(params.routeRef); diff --git a/plugins/devtools-react/src/extensionData.ts b/plugins/devtools-react/src/extensionData.ts deleted file mode 100644 index 33754f6556..0000000000 --- a/plugins/devtools-react/src/extensionData.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2025 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 { createExtensionDataRef } from '@backstage/frontend-plugin-api'; - -/** @public */ -export const contentTitleDataRef = createExtensionDataRef().with({ - id: 'devtools.content-title', -}); diff --git a/plugins/devtools-react/src/index.ts b/plugins/devtools-react/src/index.ts index 1a31aa4f09..822b885769 100644 --- a/plugins/devtools-react/src/index.ts +++ b/plugins/devtools-react/src/index.ts @@ -20,7 +20,6 @@ * @packageDocumentation */ export { - type DevToolsRouteBlueprintParams, + type DevToolsContentBlueprintParams, DevToolsContentBlueprint, } from './devToolsContentBlueprint.tsx'; -export { contentTitleDataRef } from './extensionData'; diff --git a/plugins/devtools/README.md b/plugins/devtools/README.md index f7aff3bd34..aef25ae21c 100644 --- a/plugins/devtools/README.md +++ b/plugins/devtools/README.md @@ -169,23 +169,25 @@ yarn --cwd plugins/ add @backstage/plugin-devtools-react ``` ```tsx -import { DevToolsRouteBlueprint } from '@backstage/plugin-devtools-react'; +import { DevToolsContentBlueprint } from '@backstage/plugin-devtools-react'; -export const unprocessedEntitiesDevToolsRoute = DevToolsRouteBlueprint.make({ - params: { - path: 'unprocessed-entities', - title: 'Unprocessed Entities', - loader: () => - import('../components/UnprocessedEntities').then( - ({ UnprocessedEntitiesContent }) => - createElement(UnprocessedEntitiesContent), - ), +export const unprocessedEntitiesDevToolsContent = DevToolsContentBlueprint.make( + { + params: { + path: 'unprocessed-entities', + title: 'Unprocessed Entities', + loader: () => + import('../components/UnprocessedEntities').then( + ({ UnprocessedEntitiesContent }) => + createElement(UnprocessedEntitiesContent), + ), + }, }, -}); +); const appFeature = createFrontendModule({ pluginId: 'app', - extensions: [unprocessedEntitiesDevToolsRoute], + extensions: [unprocessedEntitiesDevToolsContent], }); ``` diff --git a/plugins/devtools/report-alpha.api.md b/plugins/devtools/report-alpha.api.md index 42d5d3a26c..07c881215c 100644 --- a/plugins/devtools/report-alpha.api.md +++ b/plugins/devtools/report-alpha.api.md @@ -78,6 +78,7 @@ const _default: OverridableFrontendPlugin< >; inputs: { contents: ExtensionInput< + | ConfigurableExtensionDataRef | ConfigurableExtensionDataRef | ConfigurableExtensionDataRef | ConfigurableExtensionDataRef< @@ -86,8 +87,7 @@ const _default: OverridableFrontendPlugin< { optional: true; } - > - | ConfigurableExtensionDataRef, + >, { singleton: false; optional: true; @@ -99,7 +99,8 @@ const _default: OverridableFrontendPlugin< params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; - loader: () => Promise; + loader: () => Promise; routeRef?: RouteRef; }; }>; diff --git a/plugins/devtools/src/alpha/plugin.tsx b/plugins/devtools/src/alpha/plugin.tsx index 5628492b90..2cb72498b4 100644 --- a/plugins/devtools/src/alpha/plugin.tsx +++ b/plugins/devtools/src/alpha/plugin.tsx @@ -32,7 +32,6 @@ import { } from '@backstage/core-compat-api'; import BuildIcon from '@material-ui/icons/Build'; import { rootRouteRef } from '../routes'; -import { contentTitleDataRef } from '@backstage/plugin-devtools-react'; /** @alpha */ export const devToolsApi = ApiBlueprint.make({ @@ -56,7 +55,7 @@ export const devToolsPage = PageBlueprint.makeWithOverrides({ coreExtensionData.reactElement, coreExtensionData.routePath, coreExtensionData.routeRef.optional(), - contentTitleDataRef, + coreExtensionData.title, ], { optional: true, @@ -70,7 +69,7 @@ export const devToolsPage = PageBlueprint.makeWithOverrides({ loader: () => { const extensions = inputs.contents.map(content => ({ path: content.get(coreExtensionData.routePath), - title: content.get(contentTitleDataRef), + title: content.get(coreExtensionData.title), children: content.get(coreExtensionData.reactElement), })); return import('../components/DevToolsPage').then(m => From 5b32df5fd8de46286f9ce6cb23e3549d40dec77e Mon Sep 17 00:00:00 2001 From: secustor Date: Wed, 22 Oct 2025 23:34:39 +0200 Subject: [PATCH 19/26] trigger release for common and backend package because of pluginPackages field Signed-off-by: secustor --- .changeset/slimy-ghosts-rescue.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.changeset/slimy-ghosts-rescue.md b/.changeset/slimy-ghosts-rescue.md index c732293c54..620214e0a6 100644 --- a/.changeset/slimy-ghosts-rescue.md +++ b/.changeset/slimy-ghosts-rescue.md @@ -2,6 +2,8 @@ '@backstage/plugin-catalog-unprocessed-entities': patch '@backstage/plugin-devtools-react': minor '@backstage/plugin-devtools': patch +'@backstage/plugin-devtools-backend': patch +'@backstage/plugin-devtools-common': patch --- Add support for adding `unprocessed-entities` and other tabs to `devtools` From 68d88173d8bf60ef19af5d5ef37a6f8fe0d151eb Mon Sep 17 00:00:00 2001 From: secustor Date: Wed, 22 Oct 2025 23:41:23 +0200 Subject: [PATCH 20/26] update example Signed-off-by: secustor --- .../src/devToolsContentBlueprint.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/plugins/devtools-react/src/devToolsContentBlueprint.tsx b/plugins/devtools-react/src/devToolsContentBlueprint.tsx index 9c11916a77..c10d8dade0 100644 --- a/plugins/devtools-react/src/devToolsContentBlueprint.tsx +++ b/plugins/devtools-react/src/devToolsContentBlueprint.tsx @@ -38,12 +38,17 @@ export interface DevToolsContentBlueprintParams { * * @example * ```tsx - * const myDevToolsRoute = DevToolsContentBlueprint.make({ - * params: { - * path: 'my-feature', - * title: 'My Feature', - * loader: () => import('./MyContent').then(m => ({ default: m.MyContent })) - * } + * const myDevToolsContent = DevToolsContentBlueprint.make({ + * { + * params: { + * path: 'my-dev-tools', + * title: 'My DevTools', + * loader: () => + * import('../components/MyDevTools').then(m => + * compatWrapper(), + * ), + * }, + * }, * }); * ``` * @public From 9abc06d33588ff737d32caa6414232559f4a3311 Mon Sep 17 00:00:00 2001 From: secustor Date: Tue, 9 Dec 2025 15:36:51 +0100 Subject: [PATCH 21/26] fix:linting issue Signed-off-by: secustor --- plugins/devtools/src/alpha/plugin.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/devtools/src/alpha/plugin.tsx b/plugins/devtools/src/alpha/plugin.tsx index 6f4bfeed89..9a1b4b0af0 100644 --- a/plugins/devtools/src/alpha/plugin.tsx +++ b/plugins/devtools/src/alpha/plugin.tsx @@ -68,9 +68,9 @@ export const devToolsPage = PageBlueprint.makeWithOverrides({ title: content.get(coreExtensionData.title), children: content.get(coreExtensionData.reactElement), })); - return import('../components/DevToolsPage').then(m => + return import('../components/DevToolsPage').then(m => ( - ); + )); }, }); }, From 623e71d1d81b11149bde6ca6befc8278eeeccb00 Mon Sep 17 00:00:00 2001 From: secustor Date: Fri, 2 Jan 2026 21:32:39 +0100 Subject: [PATCH 22/26] dedupe lockfile Signed-off-by: secustor --- yarn.lock | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/yarn.lock b/yarn.lock index c9db21a76e..582f84651a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -32221,25 +32221,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.3.0": - version: 1.3.0 - resolution: "get-intrinsic@npm:1.3.0" - dependencies: - call-bind-apply-helpers: "npm:^1.0.2" - es-define-property: "npm:^1.0.1" - es-errors: "npm:^1.3.0" - es-object-atoms: "npm:^1.1.1" - function-bind: "npm:^1.1.2" - get-proto: "npm:^1.0.1" - gopd: "npm:^1.2.0" - has-symbols: "npm:^1.1.0" - hasown: "npm:^2.0.2" - math-intrinsics: "npm:^1.1.0" - checksum: 10/6e9dd920ff054147b6f44cb98104330e87caafae051b6d37b13384a45ba15e71af33c3baeac7cb630a0aaa23142718dcf25b45cfdd86c184c5dcb4e56d953a10 - languageName: node - linkType: hard - -"get-intrinsic@npm:^1.2.2": +"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.3.0": version: 1.3.1 resolution: "get-intrinsic@npm:1.3.1" dependencies: From 5408b0dd264d2d905d0d4278bbf23af964b8de1b Mon Sep 17 00:00:00 2001 From: secustor Date: Fri, 2 Jan 2026 22:12:26 +0100 Subject: [PATCH 23/26] implement Contents interface Signed-off-by: secustor --- .changeset/slimy-ghosts-rescue.md | 2 +- .../report-alpha.api.md | 4 ++-- plugins/devtools/report-alpha.api.md | 19 +------------------ plugins/devtools/report.api.md | 15 +++++++++++++-- plugins/devtools/src/alpha/plugin.tsx | 4 ++-- .../DefaultDevToolsPage.tsx | 12 ++++++------ .../components/DevToolsPage/DevToolsPage.tsx | 16 +++++++++++++--- .../src/components/DevToolsPage/index.ts | 6 +++++- plugins/devtools/src/components/index.ts | 5 ++++- 9 files changed, 47 insertions(+), 36 deletions(-) diff --git a/.changeset/slimy-ghosts-rescue.md b/.changeset/slimy-ghosts-rescue.md index 620214e0a6..ae01e3f718 100644 --- a/.changeset/slimy-ghosts-rescue.md +++ b/.changeset/slimy-ghosts-rescue.md @@ -6,4 +6,4 @@ '@backstage/plugin-devtools-common': patch --- -Add support for adding `unprocessed-entities` and other tabs to `devtools` +Add support for adding `unprocessed-entities` and other tabs to `devtools` when using the New Frontend system diff --git a/plugins/catalog-unprocessed-entities/report-alpha.api.md b/plugins/catalog-unprocessed-entities/report-alpha.api.md index 53e721899e..d3bd6dd28f 100644 --- a/plugins/catalog-unprocessed-entities/report-alpha.api.md +++ b/plugins/catalog-unprocessed-entities/report-alpha.api.md @@ -91,7 +91,7 @@ const _default: OverridableFrontendPlugin< export default _default; // @alpha -export const unprocessedEntitiesDevToolsContent: ExtensionDefinition<{ +export const unprocessedEntitiesDevToolsContent: OverridableExtensionDefinition<{ kind: 'devtools-content'; name: undefined; config: { @@ -106,7 +106,7 @@ export const unprocessedEntitiesDevToolsContent: ExtensionDefinition<{ | ExtensionDataRef | ExtensionDataRef | ExtensionDataRef< - RouteRef, + RouteRef_2, 'core.routing.ref', { optional: true; diff --git a/plugins/devtools/report-alpha.api.md b/plugins/devtools/report-alpha.api.md index 1789cc7f34..3f78a90587 100644 --- a/plugins/devtools/report-alpha.api.md +++ b/plugins/devtools/report-alpha.api.md @@ -9,13 +9,8 @@ import { ApiFactory } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; -<<<<<<< HEAD -import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { ExtensionInput } from '@backstage/frontend-plugin-api'; -import { IconComponent } from '@backstage/core-plugin-api'; -======= import { IconComponent } from '@backstage/frontend-plugin-api'; ->>>>>>> master import { JSX as JSX_2 } from 'react'; import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api'; @@ -65,13 +60,7 @@ const _default: OverridableFrontendPlugin< routeRef: RouteRef_2; }; }>; -<<<<<<< HEAD - 'page:devtools': ExtensionDefinition<{ -======= 'page:devtools': OverridableExtensionDefinition<{ - kind: 'page'; - name: undefined; ->>>>>>> master config: { path: string | undefined; }; @@ -94,7 +83,7 @@ const _default: OverridableFrontendPlugin< | ConfigurableExtensionDataRef | ConfigurableExtensionDataRef | ConfigurableExtensionDataRef< - RouteRef, + RouteRef_2, 'core.routing.ref', { optional: true; @@ -111,14 +100,8 @@ const _default: OverridableFrontendPlugin< params: { defaultPath?: [Error: `Use the 'path' param instead`]; path: string; -<<<<<<< HEAD - loader: () => Promise; - routeRef?: RouteRef; -======= loader: () => Promise; routeRef?: RouteRef_2; ->>>>>>> master }; }>; } diff --git a/plugins/devtools/report.api.md b/plugins/devtools/report.api.md index 5f9a46016d..804e678e89 100644 --- a/plugins/devtools/report.api.md +++ b/plugins/devtools/report.api.md @@ -6,6 +6,7 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { ElementType } from 'react'; import { JSX as JSX_2 } from 'react/jsx-runtime'; +import { ReactElement } from 'react'; import { ReactNode } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; import { TabProps } from '@material-ui/core/Tab'; @@ -28,12 +29,22 @@ export type DevToolsLayoutProps = { }; // @public (undocumented) -export const DevToolsPage: ({ extensions }: DevToolsPageProps) => JSX_2.Element; +export const DevToolsPage: ({ contents }: DevToolsPageProps) => JSX_2.Element; + +// @public (undocumented) +export interface DevToolsPageContent { + // (undocumented) + children: ReactElement; + // (undocumented) + path: string; + // (undocumented) + title: string; +} // @public (undocumented) export interface DevToolsPageProps { // (undocumented) - extensions?: any[]; + contents?: DevToolsPageContent[]; } // @public (undocumented) diff --git a/plugins/devtools/src/alpha/plugin.tsx b/plugins/devtools/src/alpha/plugin.tsx index 9a1b4b0af0..edeaae5723 100644 --- a/plugins/devtools/src/alpha/plugin.tsx +++ b/plugins/devtools/src/alpha/plugin.tsx @@ -63,13 +63,13 @@ export const devToolsPage = PageBlueprint.makeWithOverrides({ path: '/devtools', routeRef: rootRouteRef, loader: () => { - const extensions = inputs.contents.map(content => ({ + const contents = inputs.contents.map(content => ({ path: content.get(coreExtensionData.routePath), title: content.get(coreExtensionData.title), children: content.get(coreExtensionData.reactElement), })); return import('../components/DevToolsPage').then(m => ( - + )); }, }); diff --git a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx index 36393db3b0..a3830ae8bd 100644 --- a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx +++ b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx @@ -28,7 +28,7 @@ import { ScheduledTasksContent } from '../Content/ScheduledTasksContent'; import { DevToolsPageProps } from '../DevToolsPage'; /** @public */ -export const DefaultDevToolsPage = ({ extensions }: DevToolsPageProps) => ( +export const DefaultDevToolsPage = ({ contents }: DevToolsPageProps) => ( @@ -45,13 +45,13 @@ export const DefaultDevToolsPage = ({ extensions }: DevToolsPageProps) => ( - {extensions?.map((extension, index) => ( + {contents?.map((content, index) => ( - {extension.children} + {content.children} ))} diff --git a/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx index 8f84ed4a9b..89305b2c1a 100644 --- a/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx +++ b/plugins/devtools/src/components/DevToolsPage/DevToolsPage.tsx @@ -16,16 +16,26 @@ import { useOutlet } from 'react-router-dom'; import { DefaultDevToolsPage } from '../DefaultDevToolsPage'; +import { ReactElement } from 'react'; /** @public */ export interface DevToolsPageProps { - extensions?: any[]; + contents?: DevToolsPageContent[]; } -export const DevToolsPage = ({ extensions }: DevToolsPageProps) => { +/** + @public + */ +export interface DevToolsPageContent { + title: string; + path: string; + children: ReactElement; +} + +export const DevToolsPage = ({ contents }: DevToolsPageProps) => { const outlet = useOutlet(); - return <>{outlet || }; + return <>{outlet || }; }; diff --git a/plugins/devtools/src/components/DevToolsPage/index.ts b/plugins/devtools/src/components/DevToolsPage/index.ts index 743559f282..a0a4028d10 100644 --- a/plugins/devtools/src/components/DevToolsPage/index.ts +++ b/plugins/devtools/src/components/DevToolsPage/index.ts @@ -14,4 +14,8 @@ * limitations under the License. */ -export { DevToolsPage, type DevToolsPageProps } from './DevToolsPage'; +export { + DevToolsPage, + type DevToolsPageProps, + type DevToolsPageContent, +} from './DevToolsPage'; diff --git a/plugins/devtools/src/components/index.ts b/plugins/devtools/src/components/index.ts index f79797d65a..9744a4b801 100644 --- a/plugins/devtools/src/components/index.ts +++ b/plugins/devtools/src/components/index.ts @@ -16,4 +16,7 @@ export * from './Content'; export * from './DevToolsLayout'; -export { type DevToolsPageProps } from './DevToolsPage'; +export { + type DevToolsPageProps, + type DevToolsPageContent, +} from './DevToolsPage'; From 8cf3f520c9f2417b131a5661781727964bd1e58c Mon Sep 17 00:00:00 2001 From: secustor Date: Fri, 2 Jan 2026 22:51:44 +0100 Subject: [PATCH 24/26] remove unnecessary path from route key Signed-off-by: secustor --- .../src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx index a3830ae8bd..efbed90737 100644 --- a/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx +++ b/plugins/devtools/src/components/DefaultDevToolsPage/DefaultDevToolsPage.tsx @@ -47,7 +47,7 @@ export const DefaultDevToolsPage = ({ contents }: DevToolsPageProps) => ( {contents?.map((content, index) => ( From 283c928e11b895bda555550223c5950142898629 Mon Sep 17 00:00:00 2001 From: secustor Date: Thu, 8 Jan 2026 23:50:46 +0100 Subject: [PATCH 25/26] remove compatWrapper Signed-off-by: secustor --- packages/app-next/src/App.tsx | 5 +---- .../src/alpha/devToolsRoute.tsx | 7 +++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/app-next/src/App.tsx b/packages/app-next/src/App.tsx index af0c3a972d..6c567f3fe1 100644 --- a/packages/app-next/src/App.tsx +++ b/packages/app-next/src/App.tsx @@ -45,8 +45,6 @@ import { convertLegacyPageExtension } from '@backstage/core-compat-api'; import { convertLegacyEntityContentExtension } from '@backstage/plugin-catalog-react/alpha'; import { pluginInfoResolver } from './pluginInfoResolver'; import { appModuleNav } from './modules/appModuleNav'; -import devtoolsPlugin from '@backstage/plugin-devtools/alpha'; -import { unprocessedEntitiesDevToolsContent } from '@backstage/plugin-catalog-unprocessed-entities/alpha'; /* @@ -115,7 +113,7 @@ const customHomePageModule = createFrontendModule({ const notFoundErrorPageModule = createFrontendModule({ pluginId: 'app', - extensions: [notFoundErrorPage, unprocessedEntitiesDevToolsContent], + extensions: [notFoundErrorPage], }); const collectedLegacyPlugins = convertLegacyAppRoot( @@ -135,7 +133,6 @@ const app = createApp({ notFoundErrorPageModule, appModuleNav, customHomePageModule, - devtoolsPlugin, ...collectedLegacyPlugins, ], advanced: { diff --git a/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.tsx b/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.tsx index e2c652f21a..505648da4b 100644 --- a/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.tsx +++ b/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.tsx @@ -15,7 +15,6 @@ */ import { DevToolsContentBlueprint } from '@backstage/plugin-devtools-react'; -import { compatWrapper } from '@backstage/core-compat-api'; /** * DevTools content for catalog unprocessed entities. @@ -28,9 +27,9 @@ export const unprocessedEntitiesDevToolsContent = DevToolsContentBlueprint.make( path: 'unprocessed-entities', title: 'Unprocessed Entities', loader: () => - import('../components/UnprocessedEntities').then(m => - compatWrapper(), - ), + import('../components/UnprocessedEntities').then(m => ( + + )), }, }, ); From 8d488c997a5383f61eede749d898b9eb87ef89db Mon Sep 17 00:00:00 2001 From: secustor Date: Fri, 9 Jan 2026 00:56:36 +0100 Subject: [PATCH 26/26] Change pluginID from `app` to `catalog-unprocessed-entities` and disable tab by default Signed-off-by: secustor --- packages/app-next/app-config.yaml | 5 +++++ packages/app-next/src/App.tsx | 9 +++++++++ plugins/catalog-unprocessed-entities/README.md | 18 +++++++++++++++--- .../{devToolsRoute.tsx => devToolsContent.tsx} | 1 + .../src/alpha/index.ts | 2 +- plugins/devtools-react/src/index.ts | 2 +- plugins/devtools/README.md | 10 +++++----- 7 files changed, 37 insertions(+), 10 deletions(-) rename plugins/catalog-unprocessed-entities/src/alpha/{devToolsRoute.tsx => devToolsContent.tsx} (98%) diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index 1121cfc0d5..8e6322e366 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -111,6 +111,11 @@ app: # - entity-content:azure-devops/pull-requests # - entity-content:azure-devops/git-tags + # Enable the catalog-unprocessed-entities tab in devtools + - devtools-content:catalog-unprocessed-entities: true + # Disable the catalog-unprocessed-entities element outside devtools + - page:catalog-unprocessed-entities: false + # scmAuthExtension: >- # createScmAuthExtension({ # id: 'apis.scmAuth.addons.ghe', diff --git a/packages/app-next/src/App.tsx b/packages/app-next/src/App.tsx index 6c567f3fe1..47f717c7ee 100644 --- a/packages/app-next/src/App.tsx +++ b/packages/app-next/src/App.tsx @@ -45,6 +45,8 @@ import { convertLegacyPageExtension } from '@backstage/core-compat-api'; import { convertLegacyEntityContentExtension } from '@backstage/plugin-catalog-react/alpha'; import { pluginInfoResolver } from './pluginInfoResolver'; import { appModuleNav } from './modules/appModuleNav'; +import devtoolsPlugin from '@backstage/plugin-devtools/alpha'; +import { unprocessedEntitiesDevToolsContent } from '@backstage/plugin-catalog-unprocessed-entities/alpha'; /* @@ -116,6 +118,11 @@ const notFoundErrorPageModule = createFrontendModule({ extensions: [notFoundErrorPage], }); +const devtoolsPluginUnprocessed = createFrontendModule({ + pluginId: 'catalog-unprocessed-entities', + extensions: [unprocessedEntitiesDevToolsContent], +}); + const collectedLegacyPlugins = convertLegacyAppRoot( } /> @@ -133,6 +140,8 @@ const app = createApp({ notFoundErrorPageModule, appModuleNav, customHomePageModule, + devtoolsPlugin, + devtoolsPluginUnprocessed, ...collectedLegacyPlugins, ], advanced: { diff --git a/plugins/catalog-unprocessed-entities/README.md b/plugins/catalog-unprocessed-entities/README.md index 98bdf953d2..b39a4a7ad4 100644 --- a/plugins/catalog-unprocessed-entities/README.md +++ b/plugins/catalog-unprocessed-entities/README.md @@ -55,21 +55,33 @@ import catalogUnprocessedEntitiesPlugin from '@backstage/plugin-catalog-unproces import { unprocessedEntitiesDevToolsContent } from '@backstage/plugin-catalog-unprocessed-entities/alpha'; // Optionally add unprocessed entities route to devtools -const appFeature = createFrontendModule({ - pluginId: 'app', +const devtoolsPluginUnprocessed = createFrontendModule({ + pluginId: 'catalog-unprocessed-entities', extensions: [unprocessedEntitiesDevToolsContent], }); export const app = createApp({ features: [ - appFeature, // ... catalogUnprocessedEntitiesPlugin, + + // Optionally add unprocessed entities route to devtools + devtoolsPluginUnprocessed, + devtoolsPlugin, // devtools plugin needs to be added too, if autodiscover is disabled // ... ], }); ``` +```yaml +app: + extensions: + # Enable the catalog-unprocessed-entities tab in devtools + - devtools-content:catalog-unprocessed-entities: true + # Disable the catalog-unprocessed-entities element outside devtools including the sidebar + - page:catalog-unprocessed-entities: false +``` + ## Customization If you want to use the provided endpoints in a different way, you can use the ApiRef doing the following: diff --git a/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.tsx b/plugins/catalog-unprocessed-entities/src/alpha/devToolsContent.tsx similarity index 98% rename from plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.tsx rename to plugins/catalog-unprocessed-entities/src/alpha/devToolsContent.tsx index 505648da4b..3dcdc0ad6b 100644 --- a/plugins/catalog-unprocessed-entities/src/alpha/devToolsRoute.tsx +++ b/plugins/catalog-unprocessed-entities/src/alpha/devToolsContent.tsx @@ -23,6 +23,7 @@ import { DevToolsContentBlueprint } from '@backstage/plugin-devtools-react'; */ export const unprocessedEntitiesDevToolsContent = DevToolsContentBlueprint.make( { + disabled: true, params: { path: 'unprocessed-entities', title: 'Unprocessed Entities', diff --git a/plugins/catalog-unprocessed-entities/src/alpha/index.ts b/plugins/catalog-unprocessed-entities/src/alpha/index.ts index 13c527949f..001fa77643 100644 --- a/plugins/catalog-unprocessed-entities/src/alpha/index.ts +++ b/plugins/catalog-unprocessed-entities/src/alpha/index.ts @@ -15,4 +15,4 @@ */ export { default } from './plugin'; -export { unprocessedEntitiesDevToolsContent } from './devToolsRoute'; +export { unprocessedEntitiesDevToolsContent } from './devToolsContent'; diff --git a/plugins/devtools-react/src/index.ts b/plugins/devtools-react/src/index.ts index 822b885769..70b4915ae8 100644 --- a/plugins/devtools-react/src/index.ts +++ b/plugins/devtools-react/src/index.ts @@ -22,4 +22,4 @@ export { type DevToolsContentBlueprintParams, DevToolsContentBlueprint, -} from './devToolsContentBlueprint.tsx'; +} from './devToolsContentBlueprint'; diff --git a/plugins/devtools/README.md b/plugins/devtools/README.md index 967f3693a6..b23c35d4dd 100644 --- a/plugins/devtools/README.md +++ b/plugins/devtools/README.md @@ -179,20 +179,20 @@ import { DevToolsContentBlueprint } from '@backstage/plugin-devtools-react'; export const unprocessedEntitiesDevToolsContent = DevToolsContentBlueprint.make( { + disabled: true, params: { path: 'unprocessed-entities', title: 'Unprocessed Entities', loader: () => - import('../components/UnprocessedEntities').then( - ({ UnprocessedEntitiesContent }) => - createElement(UnprocessedEntitiesContent), - ), + import('../components/UnprocessedEntities').then(m => ( + + )), }, }, ); const appFeature = createFrontendModule({ - pluginId: 'app', + pluginId: 'catalog-unprocessed-entities', extensions: [unprocessedEntitiesDevToolsContent], }); ```