Add @backstage/frontend-dev-utils package

Adds a new `@backstage/frontend-dev-utils` package for the new frontend
system. It provides a minimal `createDevApp` helper for wiring up a
development app from a `dev/` entry point.

The app-visualizer plugin is updated to use this new package as the
initial testing ground.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
This commit is contained in:
Patrik Oldsberg
2026-03-16 11:31:49 +01:00
parent 3775ef5b51
commit c25532a7a1
14 changed files with 315 additions and 9 deletions
@@ -0,0 +1,74 @@
/*
* Copyright 2026 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 {
PageBlueprint,
createFrontendPlugin,
} from '@backstage/frontend-plugin-api';
import { within, waitFor } from '@testing-library/react';
import { mockApis } from '@backstage/test-utils';
import { createDevApp } from './createDevApp';
import { default as appPlugin } from '@backstage/plugin-app';
describe('createDevApp', () => {
afterEach(() => {
document.getElementById('root')?.remove();
});
it('should render a dev app with a plugin', async () => {
const root = document.createElement('div');
root.id = 'root';
document.body.appendChild(root);
const testPlugin = createFrontendPlugin({
pluginId: 'test',
extensions: [
PageBlueprint.make({
params: {
path: '/',
loader: async () => <div>Test Plugin Page</div>,
},
}),
],
});
createDevApp({
features: [
appPlugin.withOverrides({
extensions: [
appPlugin
.getExtension('sign-in-page:app')
.override({ disabled: true }),
],
}),
testPlugin,
],
createAppOptions: {
advanced: {
configLoader: async () => ({ config: mockApis.config() }),
},
},
});
const body = within(document.body);
await waitFor(
() => {
expect(body.getByText('Test Plugin Page')).toBeDefined();
},
{ timeout: 10000 },
);
}, 15000);
});
@@ -0,0 +1,64 @@
/*
* Copyright 2026 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 {
FrontendFeature,
FrontendFeatureLoader,
} from '@backstage/frontend-plugin-api';
import { createApp, CreateAppOptions } from '@backstage/frontend-defaults';
import ReactDOM from 'react-dom/client';
/**
* Options for {@link createDevApp}.
*
* @public
*/
export interface CreateDevAppOptions {
/**
* The list of features to load in the dev app.
*/
features: (FrontendFeature | FrontendFeatureLoader)[];
/**
* Additional options to pass through to `createApp`.
*/
createAppOptions?: Omit<CreateAppOptions, 'features'>;
}
/**
* Creates and renders a minimal development app for the new frontend system.
*
* @example
* ```tsx
* // dev/index.ts
* import { createDevApp } from '@backstage/frontend-dev-utils';
* import myPlugin from '../src';
*
* createDevApp({ features: [myPlugin] });
* ```
*
* @public
*/
export function createDevApp(options: CreateDevAppOptions): void {
const app = createApp({
...options.createAppOptions,
features: options.features,
});
ReactDOM.createRoot(document.getElementById('root')!).render(
app.createRoot(),
);
}
+23
View File
@@ -0,0 +1,23 @@
/*
* Copyright 2026 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.
*/
/**
* Utilities for developing Backstage frontend plugins using the new frontend system.
*
* @packageDocumentation
*/
export { createDevApp, type CreateDevAppOptions } from './createDevApp';