cli: update frontend plugin template to use new extension API
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Update `create-plugin` template to use the new composability API, by switching to exporting a single routable extension component.
|
||||
@@ -20,6 +20,7 @@ import chalk from 'chalk';
|
||||
import inquirer, { Answers, Question } from 'inquirer';
|
||||
import { exec as execCb } from 'child_process';
|
||||
import { resolve as resolvePath, join as joinPath } from 'path';
|
||||
import camelCase from 'lodash/camelCase';
|
||||
import os from 'os';
|
||||
import { Command } from 'commander';
|
||||
import {
|
||||
@@ -106,14 +107,10 @@ export async function addPluginDependencyToApp(
|
||||
|
||||
export async function addPluginToApp(
|
||||
rootDir: string,
|
||||
pluginName: string,
|
||||
pluginVar: string,
|
||||
pluginPackage: string,
|
||||
) {
|
||||
const pluginNameCapitalized = pluginName
|
||||
.split('-')
|
||||
.map(name => capitalize(name))
|
||||
.join('');
|
||||
const pluginExport = `export { plugin as ${pluginNameCapitalized} } from '${pluginPackage}';`;
|
||||
const pluginExport = `export { ${pluginVar} } from '${pluginPackage}';`;
|
||||
const pluginsFilePath = 'packages/app/src/plugins.ts';
|
||||
const pluginsFile = resolvePath(rootDir, pluginsFilePath);
|
||||
|
||||
@@ -223,6 +220,7 @@ export default async (cmd: Command) => {
|
||||
const name = cmd.scope
|
||||
? `@${cmd.scope.replace(/^@/, '')}/plugin-${pluginId}`
|
||||
: `plugin-${pluginId}`;
|
||||
const pluginVar = `${camelCase(answers.id)}Plugin`;
|
||||
const npmRegistry = cmd.npmRegistry && cmd.scope ? cmd.npmRegistry : '';
|
||||
const privatePackage = cmd.private === false ? false : true;
|
||||
const isMonoRepo = await fs.pathExists(paths.resolveTargetRoot('lerna.json'));
|
||||
@@ -259,6 +257,7 @@ export default async (cmd: Command) => {
|
||||
tempDir,
|
||||
{
|
||||
...answers,
|
||||
pluginVar,
|
||||
pluginVersion,
|
||||
name,
|
||||
privatePackage,
|
||||
@@ -278,7 +277,7 @@ export default async (cmd: Command) => {
|
||||
await addPluginDependencyToApp(paths.targetRoot, name, pluginVersion);
|
||||
|
||||
Task.section('Import plugin in app');
|
||||
await addPluginToApp(paths.targetRoot, pluginId, name);
|
||||
await addPluginToApp(paths.targetRoot, pluginVar, name);
|
||||
}
|
||||
|
||||
if (ownerIds && ownerIds.length) {
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { plugin } from '../src/plugin';
|
||||
|
||||
createDevApp().registerPlugin(plugin).render();
|
||||
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { {{ pluginVar }}, ExamplePage } from '../src/plugin';
|
||||
|
||||
createDevApp()
|
||||
.registerPlugin({{ pluginVar }})
|
||||
.addPage({
|
||||
element: <ExamplePage />,
|
||||
title: 'Root Page',
|
||||
})
|
||||
.render();
|
||||
+7
-6
@@ -1,13 +1,12 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import ExampleComponent from './ExampleComponent';
|
||||
import { ExampleComponent } from './ExampleComponent';
|
||||
import { ThemeProvider } from '@material-ui/core';
|
||||
import { lightTheme } from '@backstage/theme';
|
||||
import { rest } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { msw } from '@backstage/test-utils';
|
||||
|
||||
|
||||
describe('ExampleComponent', () => {
|
||||
const server = setupServer();
|
||||
// Enable sane handlers for network requests
|
||||
@@ -15,15 +14,17 @@ describe('ExampleComponent', () => {
|
||||
|
||||
// setup mock response
|
||||
beforeEach(() => {
|
||||
server.use(rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))))
|
||||
})
|
||||
server.use(
|
||||
rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))),
|
||||
);
|
||||
});
|
||||
|
||||
it('should render', () => {
|
||||
const rendered = render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<ExampleComponent />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
expect(rendered.getByText('Welcome to {{ id }}!')).toBeInTheDocument();
|
||||
);
|
||||
expect(rendered.getByText('Welcome to {{ id }}!')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
+2
-4
@@ -9,9 +9,9 @@ import {
|
||||
HeaderLabel,
|
||||
SupportButton,
|
||||
} from '@backstage/core';
|
||||
import ExampleFetchComponent from '../ExampleFetchComponent';
|
||||
import { ExampleFetchComponent } from '../ExampleFetchComponent';
|
||||
|
||||
const ExampleComponent = () => (
|
||||
export const ExampleComponent = () => (
|
||||
<Page themeId="tool">
|
||||
<Header title="Welcome to {{ id }}!" subtitle="Optional subtitle">
|
||||
<HeaderLabel label="Owner" value="Team X" />
|
||||
@@ -36,5 +36,3 @@ const ExampleComponent = () => (
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
|
||||
export default ExampleComponent;
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from './ExampleComponent';
|
||||
export { ExampleComponent } from './ExampleComponent';
|
||||
|
||||
+8
-4
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import ExampleFetchComponent from './ExampleFetchComponent';
|
||||
import { ExampleFetchComponent } from './ExampleFetchComponent';
|
||||
import { rest } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { msw } from '@backstage/test-utils';
|
||||
@@ -9,11 +9,15 @@ describe('ExampleFetchComponent', () => {
|
||||
const server = setupServer();
|
||||
// Enable sane handlers for network requests
|
||||
msw.setupDefaultHandlers(server);
|
||||
|
||||
|
||||
// setup mock response
|
||||
beforeEach(() => {
|
||||
server.use(rest.get('https://randomuser.me/*', (_, res, ctx) => res(ctx.status(200), ctx.delay(2000), ctx.json({}))))
|
||||
})
|
||||
server.use(
|
||||
rest.get('https://randomuser.me/*', (_, res, ctx) =>
|
||||
res(ctx.status(200), ctx.delay(2000), ctx.json({})),
|
||||
),
|
||||
);
|
||||
});
|
||||
it('should render', async () => {
|
||||
const rendered = render(<ExampleFetchComponent />);
|
||||
expect(await rendered.findByTestId('progress')).toBeInTheDocument();
|
||||
|
||||
+2
-4
@@ -48,7 +48,7 @@ export const DenseTable = ({ users }: DenseTableProps) => {
|
||||
{ title: 'Nationality', field: 'nationality' },
|
||||
];
|
||||
|
||||
const data = users.map((user) => {
|
||||
const data = users.map(user => {
|
||||
return {
|
||||
avatar: (
|
||||
<img
|
||||
@@ -73,7 +73,7 @@ export const DenseTable = ({ users }: DenseTableProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const ExampleFetchComponent = () => {
|
||||
export const ExampleFetchComponent = () => {
|
||||
const { value, loading, error } = useAsync(async (): Promise<User[]> => {
|
||||
const response = await fetch('https://randomuser.me/api/?results=20');
|
||||
const data = await response.json();
|
||||
@@ -88,5 +88,3 @@ const ExampleFetchComponent = () => {
|
||||
|
||||
return <DenseTable users={value || []} />;
|
||||
};
|
||||
|
||||
export default ExampleFetchComponent;
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from './ExampleFetchComponent';
|
||||
export { ExampleFetchComponent } from './ExampleFetchComponent';
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { plugin } from './plugin';
|
||||
@@ -0,0 +1 @@
|
||||
export { {{ pluginVar }} } from './plugin';
|
||||
@@ -1,7 +1,7 @@
|
||||
import { plugin } from './plugin';
|
||||
import { {{ pluginVar }} } from './plugin';
|
||||
|
||||
describe('{{ id }}', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(plugin).toBeDefined();
|
||||
expect({{ pluginVar }}).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import { createPlugin, createRouteRef } from '@backstage/core';
|
||||
import ExampleComponent from './components/ExampleComponent';
|
||||
import { createPlugin, createRoutableExtension } from '@backstage/core';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
path: '/{{ id }}',
|
||||
title: '{{ id }}',
|
||||
});
|
||||
import { rootRouteRef } from './routes';
|
||||
|
||||
export const plugin = createPlugin({
|
||||
export const {{ pluginVar }} = createPlugin({
|
||||
id: '{{ id }}',
|
||||
register({ router }) {
|
||||
router.addRoute(rootRouteRef, ExampleComponent);
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
export const ExamplePage = {{ pluginVar }}.provide(
|
||||
createRoutableExtension({
|
||||
component: () =>
|
||||
import('./components/ExampleComponent').then(m => m.ExampleComponent),
|
||||
mountPoint: rootRouteRef,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { createRouteRef } from '@backstage/core';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
title: '{{ id }}',
|
||||
});
|
||||
Reference in New Issue
Block a user