diff --git a/.changeset/cyan-kiwis-suffer.md b/.changeset/cyan-kiwis-suffer.md
new file mode 100644
index 0000000000..b59b0e9a32
--- /dev/null
+++ b/.changeset/cyan-kiwis-suffer.md
@@ -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.
diff --git a/docs/architecture.drawio b/docs/architecture.drawio
new file mode 100644
index 0000000000..dbe2a5f6fa
--- /dev/null
+++ b/docs/architecture.drawio
@@ -0,0 +1 @@
+7VpLc6M4EP41PiaFBLbh6LxmLlubSqZ2Z+amBQVUBYgScmzPrx9hJIMQGJzEhtTOxUaNnp++bnU3mtm3yfYLQ1n0Fw1wPINWsJ3ZdzMIAfQ88VdIdqXEs2EpCBkJZKVK8Ex+YSm0pHRNApxrFTmlMSeZLvRpmmKfazLEGN3o1V5orI+aoRAbgmcfxab0XxLwqJS6cFnJv2ISRmpksJALTpCqLFeSRyigm5rIvp/Zt4xSXj4l21scF+ApXMp2Dx1vDxNjOOVDGvwk7Nv6n4erTfLNgzcr9/HvH5sru+zlFcVrueBVlsn58p0CId+QJEapKN3I+phxvO2cCDgsT/AC0wRzthNVZANXAiIZARayvKnwPdSJathCRwqR3NPw0HW1bPEgV34CCs4gFBhdpwEu+rEEEJuIcPycIb94uxHkF7KIJ2LcOzAUp+4t6QQP6uA5JnZt0J0NOduEzsAtFMBlnXhIxUX/qerWqXxyFv2EsuFFCQVNvbrfcpzmhKb5aMRyjsPo6CgCZ1xqKSbVMHyia46f8MtEEQSuDqEzNoLgsyPYRkLQpsrnw9CA8IHRVKhyIKSP8Tok6buPy7cgBVtsnHVRG2eSqwOOC1ELfqZz05ucZh6HDyygjh8cFz8wPdPWB+C0COga+I3voPRosM5AAMamoNUN4UQRbPjJYGQOgoWBoAGcOGhXRRgvSn6M8pz4OlYByqM9rhVwODBC+iGwiWHpmvl4wJZzxEJ8rMOOgKUG9LwFaCVjOEacvOqLaENfjvBIiVjeYZu9xjZbjf0rlykbwVrCQPWjGtqNhuWyjYb7vT+s8h10WL6fDmKf2e67VLN94UdRuIZzVb7b1t/e7VRpS/h31Yl4rjcTxapVUVCNzs63oXQblW9LnW/eqXRTNs7R+3EvzD7zRJy6R9F/IF42VgKmV1uciCxF8dSh7D8ZLxx2ms7F6baw1aQdt2iFLXuWQ1DGIxpSsXv3lbTTwvYZ2MnYSm9MW3lg1k63ne81lmA+zFoKwqBdrVpWVMiPzLcxzsI6bVq2Vl88lBP4UMsNB+Sga2px+A5j1d3HooBiEqaFNglGYiYEhaUhPopX8kVCgmCvBKKPrOg52YbFZ6/r8ksTLP+LbhXr9x/CgH0t8L1JKfcjKX+JSfb1zFbtqunvt+SMnBaqw7MZtXnr+TDpoLORj3THTumaIdPkIWy4Ke7IcTs0w4xVRibrmzSV2DWV+LLwfb7MEVhMTYvbHeVpYzgxNbZND1mo8QPyOZW9TxDDiemybWbRDeSGRhkqlBgSZczOHAsobvQGA+VZMFqizr2eL4ED3fJ3sdTY8eY8SiMfA5r0OXMiRV3w+rCs7ttj0jdm/ZafI+tnW7MRydtkWTNqGErXnuzhR8WxDTdq2RPHNr0u7wJxrG1eGjIU508cW23GaHEs+N9uVNellJ1uWj5+Y2bFtRt1U7ZUuOq+sX3/Gw==
\ No newline at end of file
diff --git a/docs/plugins/plugin-development.md b/docs/plugins/plugin-development.md
index 109f6ed6ba..bfeed58c8f 100644
--- a/docs/plugins/plugin-development.md
+++ b/docs/plugins/plugin-development.md
@@ -23,35 +23,60 @@ browser APIs or by depending on external modules to do the work.
### Routing
-Each plugin is responsible for registering its components to corresponding
-routes in the app.
+Each plugin can export routable extensions, which are then imported into the app
+and mounted at a path.
-The app will call the `createPlugin` method on each plugin, passing in a
-`router` object with a set of methods on it.
+First you will need a `RouteRef` instance to serve as the mount point of your
+extensions. This can be used within your own plugin to create a link to the
+extension page using `useRouteRef`, as well as for other plugins to link to your
+extension.
-```jsx
+It is best to place these in a separate top-level `src/routes.ts` file, in order
+to avoid import cycles, for example like this:
+
+```tsx
+/* src/routes.ts */
+import { createRouteRef } from '@backstage/core';
+
+// Note: This route ref is for internal use only, don't export it from the plugin
+export const rootRouteRef = createRouteRef({
+ title: 'Example Page',
+});
+```
+
+Now that we have a `RouteRef`, we import it into `src/plugin.ts`, create our
+plugin instance with `createPlugin`, as well as create and wrap our routable
+extension using `createRoutableExtension` from `@backstage/core`:
+
+```tsx
+/* src/plugin.ts */
import { createPlugin, createRouteRef } from '@backstage/core';
import ExampleComponent from './components/ExampleComponent';
-export const rootRouteRef = createRouteRef({
- path: '/new-plugin',
- title: 'New plugin',
-});
-
-export const plugin = createPlugin({
- id: 'new-plugin',
- register({ router }) {
- router.addRoute(rootRouteRef, ExampleComponent);
+// Create a plugin instance and export this from your plugin package
+export const examplePlugin = createPlugin({
+ id: 'example',
+ routes: {
+ root: rootRouteRef, // This is where the route ref should be exported for usage in the app
},
});
+
+// This creates a routable extension, which are typically full pages of content.
+// Each extension should also be exported from your plugin package.
+export const ExamplePage = examplePlugin.provide(
+ createRoutableExtension({
+ // The component needs to be lazy-loaded. It's what will actually be rendered in the end.
+ component: () =>
+ import('./components/ExampleComponent').then(m => m.ExampleComponent),
+ // This binds the extension to this route ref, which allows for routing within and across plugin extensions
+ mountPoint: rootRouteRef,
+ }),
+);
```
-#### `router` API
+This extension can then be imported and used in the app as follow, typically
+placed within the top-level ``:
-```typescript
-addRoute(
- target: RouteRef,
- Component: ComponentType,
- options?: RouteOptions,
-): void;
+```tsx
+} />
```
diff --git a/docs/plugins/structure-of-a-plugin.md b/docs/plugins/structure-of-a-plugin.md
index 3f631a9a46..2aacd02cf2 100644
--- a/docs/plugins/structure-of-a-plugin.md
+++ b/docs/plugins/structure-of-a-plugin.md
@@ -28,6 +28,7 @@ new-plugin/
index.ts
plugin.test.ts
plugin.ts
+ routes.ts
jest.config.js
jest.setup.ts
package.json
@@ -56,26 +57,30 @@ package.json to declare the plugin dependencies, metadata and scripts.
In the `src` folder we get to the interesting bits. Check out the `plugin.ts`:
```jsx
-import { createPlugin, createRouteRef } from '@backstage/core';
-import ExampleComponent from './components/ExampleComponent';
+import { createPlugin, createRoutableExtension } from '@backstage/core';
-export const rootRouteRef = createRouteRef({
- path: '/new-plugin',
- title: 'New plugin',
-});
+import { rootRouteRef } from './routes';
-export const plugin = createPlugin({
- id: 'new-plugin',
- register({ router }) {
- router.addRoute(rootRouteRef, ExampleComponent);
+export const examplePlugin = createPlugin({
+ id: 'example',
+ routes: {
+ root: rootRouteRef,
},
});
+
+export const ExamplePage = examplePlugin.provide(
+ createRoutableExtension({
+ component: () =>
+ import('./components/ExampleComponent').then(m => m.ExampleComponent),
+ mountPoint: rootRouteRef,
+ }),
+);
```
-This is where the plugin is created and where it hooks into the app by declaring
-what component should be shown on what URL. See reference docs for
-[createPlugin](../reference/createPlugin.md) or
-[router](../reference/createPlugin-router.md).
+This is where the plugin is created and where it creates and exports extensions
+that can be imported and used the app. See reference docs for
+[createPlugin](../reference/createPlugin.md) or introduction to the new
+[Composability System](./composability.md).
## Components
@@ -91,12 +96,15 @@ You may tweak these components, rename them and/or replace them completely.
## Connecting the plugin to the Backstage app
-There are two things needed for a Backstage app to start making use of a plugin.
+There are three things needed for a Backstage app to start making use of a
+plugin.
1. Add plugin as dependency in `app/package.json`
2. `import` plugin in `app/src/plugins.ts`
+3. Import and use one or more plugin extensions, for example in
+ `app/src/App.tsx`.
-Luckily these two steps happen automatically when you create a plugin with the
+Luckily these three steps happen automatically when you create a plugin with the
Backstage CLI.
## Talking to the outside world
diff --git a/docs/reference/createPlugin-router.md b/docs/reference/createPlugin-router.md
deleted file mode 100644
index 0ef5bdbd0f..0000000000
--- a/docs/reference/createPlugin-router.md
+++ /dev/null
@@ -1,31 +0,0 @@
----
-id: createPlugin-router
-title: createPlugin - router
-description: Documentation on createPlugin - router
----
-
-The router that is passed to the `register` function makes it possible for
-plugins to hook into routing of the Backstage app and provide the end users with
-new views to navigate to. This is done by utilising the following methods on the
-`router`:
-
-```typescript
-addRoute(
- target: RouteRef,
- Component: ComponentType,
- options?: RouteOptions,
-): void;
-```
-
-## RouteRef
-
-`addRoute` method is using mutable RouteRefs, which can be created as following:
-
-```ts
-import { createRouteRef } from '@backstage/core';
-
-const myPluginRouteRef = createRouteRef({
- path: '/my-plugin',
- title: 'My Plugin',
-});
-```
diff --git a/docs/reference/createPlugin.md b/docs/reference/createPlugin.md
index 4a58a5ecdc..e3df66fa12 100644
--- a/docs/reference/createPlugin.md
+++ b/docs/reference/createPlugin.md
@@ -17,32 +17,24 @@ type PluginConfig = {
};
type PluginHooks = {
- router: RouterHooks;
+ featureFlags: FeatureFlagsHooks;
};
```
-- [Read more about the router here](createPlugin-router.md)
- [Read more about feature flags here](createPlugin-feature-flags.md)
## Example Uses
### Creating a basic plugin
-Showcasing adding a route and a feature flag.
+Showcasing adding a feature flag.
```jsx
-import { createPlugin, createRouteRef } from '@backstage/core';
-import ExampleComponent from './components/ExampleComponent';
-
-export const rootRouteRef = createRouteRef({
- path: '/new-plugin',
- title: 'New Plugin',
-});
+import { createPlugin } from '@backstage/core';
export default createPlugin({
id: 'new-plugin',
register({ router, featureFlags }) {
- router.addRoute(rootRouteRef, ExampleComponent);
featureFlags.register('enable-example-component');
},
});
diff --git a/packages/cli/src/commands/create-plugin/createPlugin.ts b/packages/cli/src/commands/create-plugin/createPlugin.ts
index a2005c5580..affbf64922 100644
--- a/packages/cli/src/commands/create-plugin/createPlugin.ts
+++ b/packages/cli/src/commands/create-plugin/createPlugin.ts
@@ -20,6 +20,8 @@ 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 upperFirst from 'lodash/upperFirst';
import os from 'os';
import { Command } from 'commander';
import {
@@ -104,16 +106,12 @@ export async function addPluginDependencyToApp(
});
}
-export async function addPluginToApp(
+export async function addPluginImportToApp(
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);
@@ -126,6 +124,46 @@ export async function addPluginToApp(
});
}
+export async function addPluginExtensionToApp(
+ pluginId: string,
+ extensionName: string,
+ pluginPackage: string,
+) {
+ const pluginsFilePath = paths.resolveTargetRoot('packages/app/src/App.tsx');
+ if (!(await fs.pathExists(pluginsFilePath))) {
+ return;
+ }
+
+ await Task.forItem('processing', pluginsFilePath, async () => {
+ const content = await fs.readFile(pluginsFilePath, 'utf8');
+ const revLines = content.split('\n').reverse();
+
+ const lastImportIndex = revLines.findIndex(line =>
+ line.match(/ from ("|').*("|')/),
+ );
+ const lastRouteIndex = revLines.findIndex(line =>
+ line.match(/<\/FlatRoutes/),
+ );
+
+ if (lastImportIndex !== -1 && lastRouteIndex !== -1) {
+ revLines.splice(
+ lastImportIndex,
+ 0,
+ `import { ${extensionName} } from '${pluginPackage}';`,
+ );
+ const [indentation] = revLines[lastRouteIndex + 1].match(/^\s*/) ?? [];
+ revLines.splice(
+ lastRouteIndex + 1,
+ 0,
+ `${indentation}}/>`,
+ );
+
+ const newContent = revLines.reverse().join('\n');
+ await fs.writeFile(pluginsFilePath, newContent, 'utf8');
+ }
+ });
+}
+
async function cleanUp(tempDir: string) {
await Task.forItem('remove', 'temporary directory', async () => {
await fs.remove(tempDir);
@@ -223,6 +261,8 @@ export default async (cmd: Command) => {
const name = cmd.scope
? `@${cmd.scope.replace(/^@/, '')}/plugin-${pluginId}`
: `plugin-${pluginId}`;
+ const pluginVar = `${camelCase(answers.id)}Plugin`;
+ const extensionName = `${upperFirst(camelCase(answers.id))}Page`;
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,7 +299,9 @@ export default async (cmd: Command) => {
tempDir,
{
...answers,
+ pluginVar,
pluginVersion,
+ extensionName,
name,
privatePackage,
npmRegistry,
@@ -278,7 +320,8 @@ export default async (cmd: Command) => {
await addPluginDependencyToApp(paths.targetRoot, name, pluginVersion);
Task.section('Import plugin in app');
- await addPluginToApp(paths.targetRoot, pluginId, name);
+ await addPluginImportToApp(paths.targetRoot, pluginVar, name);
+ await addPluginExtensionToApp(pluginId, extensionName, name);
}
if (ownerIds && ownerIds.length) {
diff --git a/packages/cli/src/commands/plugin/diff.ts b/packages/cli/src/commands/plugin/diff.ts
index 416d67869b..fb96250567 100644
--- a/packages/cli/src/commands/plugin/diff.ts
+++ b/packages/cli/src/commands/plugin/diff.ts
@@ -39,6 +39,11 @@ const fileHandlers = [
patterns: ['package.json'],
handler: handlers.packageJson,
},
+ {
+ // Not all plugins have routes
+ patterns: ['src/routes.ts'],
+ handler: handlers.skip,
+ },
{
// make sure files in 1st level of src/ and dev/ exist
patterns: ['.eslintrc.js', /^(src|dev)\/[^/]+$/],
diff --git a/packages/cli/templates/default-plugin/dev/index.tsx b/packages/cli/templates/default-plugin/dev/index.tsx
deleted file mode 100644
index 6fce113093..0000000000
--- a/packages/cli/templates/default-plugin/dev/index.tsx
+++ /dev/null
@@ -1,4 +0,0 @@
-import { createDevApp } from '@backstage/dev-utils';
-import { plugin } from '../src/plugin';
-
-createDevApp().registerPlugin(plugin).render();
diff --git a/packages/cli/templates/default-plugin/dev/index.tsx.hbs b/packages/cli/templates/default-plugin/dev/index.tsx.hbs
new file mode 100644
index 0000000000..ade00a1613
--- /dev/null
+++ b/packages/cli/templates/default-plugin/dev/index.tsx.hbs
@@ -0,0 +1,11 @@
+import React from 'react';
+import { createDevApp } from '@backstage/dev-utils';
+import { {{ pluginVar }}, {{ extensionName }} } from '../src/plugin';
+
+createDevApp()
+ .registerPlugin({{ pluginVar }})
+ .addPage({
+ element: <{{ extensionName }} />,
+ title: 'Root Page',
+ })
+ .render();
diff --git a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs
index e805900f36..e6eab17493 100644
--- a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs
+++ b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.test.tsx.hbs
@@ -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(
,
- );
- expect(rendered.getByText('Welcome to {{ id }}!')).toBeInTheDocument();
+ );
+ expect(rendered.getByText('Welcome to {{ id }}!')).toBeInTheDocument();
});
});
diff --git a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs
index 5f90f2de1e..dcecebdf46 100644
--- a/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs
+++ b/packages/cli/templates/default-plugin/src/components/ExampleComponent/ExampleComponent.tsx.hbs
@@ -9,9 +9,9 @@ import {
HeaderLabel,
SupportButton,
} from '@backstage/core';
-import ExampleFetchComponent from '../ExampleFetchComponent';
+import { ExampleFetchComponent } from '../ExampleFetchComponent';
-const ExampleComponent = () => (
+export const ExampleComponent = () => (
@@ -36,5 +36,3 @@ const ExampleComponent = () => (
);
-
-export default ExampleComponent;
diff --git a/packages/cli/templates/default-plugin/src/components/ExampleComponent/index.ts b/packages/cli/templates/default-plugin/src/components/ExampleComponent/index.ts
index 520a3bf553..8b8437521b 100644
--- a/packages/cli/templates/default-plugin/src/components/ExampleComponent/index.ts
+++ b/packages/cli/templates/default-plugin/src/components/ExampleComponent/index.ts
@@ -1 +1 @@
-export { default } from './ExampleComponent';
+export { ExampleComponent } from './ExampleComponent';
diff --git a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs
index 81e1b4be09..fa1e289d8e 100644
--- a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs
+++ b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx.hbs
@@ -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();
expect(await rendered.findByTestId('progress')).toBeInTheDocument();
diff --git a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs
index 8cc5ed2ab7..20dd6d1f57 100644
--- a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs
+++ b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx.hbs
@@ -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: (
{
);
};
-const ExampleFetchComponent = () => {
+export const ExampleFetchComponent = () => {
const { value, loading, error } = useAsync(async (): Promise => {
const response = await fetch('https://randomuser.me/api/?results=20');
const data = await response.json();
@@ -88,5 +88,3 @@ const ExampleFetchComponent = () => {
return ;
};
-
-export default ExampleFetchComponent;
diff --git a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/index.ts b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/index.ts
index 3e53453948..41a43e84f1 100644
--- a/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/index.ts
+++ b/packages/cli/templates/default-plugin/src/components/ExampleFetchComponent/index.ts
@@ -1 +1 @@
-export { default } from './ExampleFetchComponent';
+export { ExampleFetchComponent } from './ExampleFetchComponent';
diff --git a/packages/cli/templates/default-plugin/src/index.ts b/packages/cli/templates/default-plugin/src/index.ts
deleted file mode 100644
index 99edba26c3..0000000000
--- a/packages/cli/templates/default-plugin/src/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { plugin } from './plugin';
diff --git a/packages/cli/templates/default-plugin/src/index.ts.hbs b/packages/cli/templates/default-plugin/src/index.ts.hbs
new file mode 100644
index 0000000000..be4881efaf
--- /dev/null
+++ b/packages/cli/templates/default-plugin/src/index.ts.hbs
@@ -0,0 +1 @@
+export { {{ pluginVar }}, {{ extensionName }} } from './plugin';
diff --git a/packages/cli/templates/default-plugin/src/plugin.test.ts.hbs b/packages/cli/templates/default-plugin/src/plugin.test.ts.hbs
index c2be0a5301..9d44a9c497 100644
--- a/packages/cli/templates/default-plugin/src/plugin.test.ts.hbs
+++ b/packages/cli/templates/default-plugin/src/plugin.test.ts.hbs
@@ -1,7 +1,7 @@
-import { plugin } from './plugin';
+import { {{ pluginVar }} } from './plugin';
describe('{{ id }}', () => {
it('should export plugin', () => {
- expect(plugin).toBeDefined();
+ expect({{ pluginVar }}).toBeDefined();
});
});
diff --git a/packages/cli/templates/default-plugin/src/plugin.ts.hbs b/packages/cli/templates/default-plugin/src/plugin.ts.hbs
index 1bf4d07cdb..0ae5356fd1 100644
--- a/packages/cli/templates/default-plugin/src/plugin.ts.hbs
+++ b/packages/cli/templates/default-plugin/src/plugin.ts.hbs
@@ -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 {{ extensionName }} = {{ pluginVar }}.provide(
+ createRoutableExtension({
+ component: () =>
+ import('./components/ExampleComponent').then(m => m.ExampleComponent),
+ mountPoint: rootRouteRef,
+ }),
+);
diff --git a/packages/cli/templates/default-plugin/src/routes.ts.hbs b/packages/cli/templates/default-plugin/src/routes.ts.hbs
new file mode 100644
index 0000000000..b2afba074d
--- /dev/null
+++ b/packages/cli/templates/default-plugin/src/routes.ts.hbs
@@ -0,0 +1,5 @@
+import { createRouteRef } from '@backstage/core';
+
+export const rootRouteRef = createRouteRef({
+ title: '{{ id }}',
+});