Merge pull request #33426 from backstage/rugvip/rename-create-app-templates

create-app: rename template directories
This commit is contained in:
Patrik Oldsberg
2026-04-09 20:25:58 +02:00
committed by GitHub
77 changed files with 323 additions and 293 deletions
+1 -1
View File
@@ -7024,7 +7024,7 @@ The `.fromConfig` of the `DefaultCatalogCollator` also now takes a `tokenManager
To apply this change to an existing plugin, it is important that all plugins that you are using have already been migrated. In this case the most crucial piece is that no entity page cards of contents may require the `entity` prop, and they must instead consume the entity from context using `useEntity`.
Since this change is large with a lot of repeated changes, we'll describe a couple of common cases rather than the entire change. If your entity pages are unchanged from the `create-app` template, you can also just bring in the latest version directly from the [template itself](https://github.com/backstage/backstage/blob/master/packages/create-app/templates/default-app/packages/app/src/components/catalog/EntityPage.tsx).
Since this change is large with a lot of repeated changes, we'll describe a couple of common cases rather than the entire change. If your entity pages are unchanged from the `create-app` template, you can also just bring in the latest version directly from the [template itself](https://github.com/backstage/backstage/blob/master/packages/create-app/templates/legacy-app/packages/app/src/components/catalog/EntityPage.tsx).
The first step of the change is to change the `packages/app/src/components/catalog/EntityPage.tsx` export to `entityPage` rather than `EntityPage`. This will require an update to `App.tsx`, which is the only change we need to do outside of `EntityPage.tsx`:
+3 -3
View File
@@ -69,7 +69,7 @@ describe('command entrypoint', () => {
expect(tryInitGitRepositoryMock).toHaveBeenCalled();
expect(templatingMock).toHaveBeenCalled();
expect(templatingMock.mock.lastCall?.[0]).toEqual(
findOwnPaths(__dirname).resolve('templates/next-app'),
findOwnPaths(__dirname).resolve('templates/default-app'),
);
expect(templatingMock.mock.lastCall?.[1]).toContain(
path.join(tmpdir(), 'MyApp'),
@@ -85,7 +85,7 @@ describe('command entrypoint', () => {
expect(tryInitGitRepositoryMock).toHaveBeenCalled();
expect(templatingMock).toHaveBeenCalled();
expect(templatingMock.mock.lastCall?.[0]).toEqual(
findOwnPaths(__dirname).resolve('templates/next-app'),
findOwnPaths(__dirname).resolve('templates/default-app'),
);
expect(templatingMock.mock.lastCall?.[1]).toEqual('myDirectory');
expect(buildAppMock).toHaveBeenCalled();
@@ -98,7 +98,7 @@ describe('command entrypoint', () => {
expect(tryInitGitRepositoryMock).toHaveBeenCalled();
expect(templatingMock).toHaveBeenCalled();
expect(templatingMock.mock.lastCall?.[0]).toEqual(
findOwnPaths(__dirname).resolve('templates/default-app'),
findOwnPaths(__dirname).resolve('templates/legacy-app'),
);
expect(templatingMock.mock.lastCall?.[1]).toContain(
path.join(tmpdir(), 'MyApp'),
+2 -2
View File
@@ -67,8 +67,8 @@ export default async (opts: OptionValues): Promise<void> => {
/* eslint-disable-next-line no-restricted-syntax */
const ownPaths = findOwnPaths(__dirname);
const builtInTemplate = opts.legacy
? ownPaths.resolve('templates/default-app')
: ownPaths.resolve('templates/next-app');
? ownPaths.resolve('templates/legacy-app')
: ownPaths.resolve('templates/default-app');
// Use `--template-path` argument as template when specified. Otherwise, use the default template.
const templateDir = opts.templatePath
+33 -27
View File
@@ -49,42 +49,36 @@ jest.mock('child_process');
jest.mock('./versions', () => ({
packageVersions: {
root: '1.2.3',
'@backstage/cli': '1.0.0',
'@backstage/cli-defaults': '1.0.0',
'@backstage/app-defaults': '1.0.0',
'@backstage/backend-defaults': '1.0.0',
'@backstage/backend-tasks': '1.0.0',
'@backstage/catalog-model': '1.0.0',
'@backstage/catalog-client': '1.0.0',
'@backstage/catalog-model': '1.0.0',
'@backstage/cli': '1.0.0',
'@backstage/cli-defaults': '1.0.0',
'@backstage/config': '1.0.0',
'@backstage/plugin-app-backend': '1.0.0',
'@backstage/plugin-auth-backend': '1.0.0',
'@backstage/plugin-auth-node': '1.0.0',
'@backstage/plugin-auth-backend-module-github-provider': '1.0.0',
'@backstage/plugin-auth-backend-module-guest-provider': '1.0.0',
'@backstage/plugin-catalog-backend': '1.0.0',
'@backstage/plugin-catalog-backend-module-logs': '1.0.0',
'@backstage/plugin-catalog-backend-module-scaffolder-entity-model': '1.0.0',
'@backstage/plugin-permission-common': '1.0.0',
'@backstage/plugin-permission-node': '1.0.0',
'@backstage/plugin-permission-backend': '1.0.0',
'@backstage/plugin-permission-backend-module-allow-all-policy': '1.0.0',
'@backstage/plugin-proxy-backend': '1.0.0',
'@backstage/plugin-scaffolder-backend': '1.0.0',
'@backstage/plugin-search-backend': '1.0.0',
'@backstage/plugin-search-backend-module-catalog': '1.0.0',
'@backstage/plugin-search-backend-module-pg': '1.0.0',
'@backstage/plugin-search-backend-module-techdocs': '1.0.0',
'@backstage/plugin-search-backend-node': '1.0.0',
'@backstage/plugin-techdocs-backend': '1.0.0',
'@backstage/app-defaults': '1.0.0',
'@backstage/core-app-api': '1.0.0',
'@backstage/core-compat-api': '1.0.0',
'@backstage/core-components': '1.0.0',
'@backstage/core-plugin-api': '1.0.0',
'@backstage/e2e-test-utils': '1.0.0',
'@backstage/frontend-defaults': '1.0.0',
'@backstage/frontend-plugin-api': '1.0.0',
'@backstage/frontend-test-utils': '1.0.0',
'@backstage/integration-react': '1.0.0',
'@backstage/plugin-api-docs': '1.0.0',
'@backstage/plugin-app-backend': '1.0.0',
'@backstage/plugin-app-react': '1.0.0',
'@backstage/plugin-app-visualizer': '1.0.0',
'@backstage/plugin-auth': '1.0.0',
'@backstage/plugin-auth-backend': '1.0.0',
'@backstage/plugin-auth-backend-module-github-provider': '1.0.0',
'@backstage/plugin-auth-backend-module-guest-provider': '1.0.0',
'@backstage/plugin-auth-node': '1.0.0',
'@backstage/plugin-catalog': '1.0.0',
'@backstage/plugin-catalog-backend': '1.0.0',
'@backstage/plugin-catalog-backend-module-logs': '1.0.0',
'@backstage/plugin-catalog-backend-module-scaffolder-entity-model': '1.0.0',
'@backstage/plugin-catalog-common': '1.0.0',
'@backstage/plugin-catalog-graph': '1.0.0',
'@backstage/plugin-catalog-import': '1.0.0',
@@ -95,20 +89,32 @@ jest.mock('./versions', () => ({
'@backstage/plugin-notifications': '1.0.0',
'@backstage/plugin-notifications-backend': '1.0.0',
'@backstage/plugin-org': '1.0.0',
'@backstage/plugin-permission-backend': '1.0.0',
'@backstage/plugin-permission-backend-module-allow-all-policy': '1.0.0',
'@backstage/plugin-permission-common': '1.0.0',
'@backstage/plugin-permission-node': '1.0.0',
'@backstage/plugin-permission-react': '1.0.0',
'@backstage/plugin-proxy-backend': '1.0.0',
'@backstage/plugin-scaffolder': '1.0.0',
'@backstage/plugin-scaffolder-backend': '1.0.0',
'@backstage/plugin-scaffolder-backend-module-github': '1.0.0',
'@backstage/plugin-scaffolder-backend-module-notifications': '1.0.0',
'@backstage/plugin-permission-react': '1.0.0',
'@backstage/plugin-search': '1.0.0',
'@backstage/plugin-search-backend': '1.0.0',
'@backstage/plugin-search-backend-module-catalog': '1.0.0',
'@backstage/plugin-search-backend-module-pg': '1.0.0',
'@backstage/plugin-search-backend-module-techdocs': '1.0.0',
'@backstage/plugin-search-backend-node': '1.0.0',
'@backstage/plugin-search-react': '1.0.0',
'@backstage/plugin-signals': '1.0.0',
'@backstage/plugin-signals-backend': '1.0.0',
'@backstage/plugin-techdocs': '1.0.0',
'@backstage/plugin-techdocs-react': '1.0.0',
'@backstage/plugin-techdocs-backend': '1.0.0',
'@backstage/plugin-techdocs-module-addons-contrib': '1.0.0',
'@backstage/plugin-techdocs-react': '1.0.0',
'@backstage/plugin-user-settings': '1.0.0',
'@backstage/theme': '1.0.0',
'@backstage/test-utils': '1.0.0',
'@backstage/theme': '1.0.0',
'@backstage/ui': '1.0.0',
},
}));
@@ -2,6 +2,21 @@ app:
title: Scaffolded Backstage App
baseUrl: http://localhost:3000
# Enable all packages by default, this will discover packages from packages/app/package.json
packages: all
extensions:
# Disable the nav items that we're manually rendering in packages/app/src/modules/nav/Sidebar.tsx
- nav-item:search: false
- nav-item:user-settings: false
- nav-item:catalog: false
- nav-item:scaffolder: false
# Configure the catalog index page to be the root page, this is normally mounted on /catalog
- page:catalog:
config:
path: /
organization:
name: My Company
@@ -27,20 +27,7 @@
"new": {
"globals": {
"license": "UNLICENSED"
},
"templates": [
"@backstage/cli-module-new/templates/legacy-frontend-plugin",
"@backstage/cli-module-new/templates/backend-plugin",
"@backstage/cli-module-new/templates/backend-plugin-module",
"@backstage/cli-module-new/templates/plugin-web-library",
"@backstage/cli-module-new/templates/plugin-node-library",
"@backstage/cli-module-new/templates/plugin-common-library",
"@backstage/cli-module-new/templates/web-library",
"@backstage/cli-module-new/templates/node-library",
"@backstage/cli-module-new/templates/cli-module",
"@backstage/cli-module-new/templates/catalog-provider-module",
"@backstage/cli-module-new/templates/scaffolder-backend-module"
]
}
}
}
},
@@ -23,5 +23,11 @@ test('App should render the welcome page', async ({ page }) => {
await expect(enterButton).toBeVisible();
await enterButton.click();
await expect(page.getByText('My Company Catalog')).toBeVisible();
const nav = page.getByRole('navigation');
await expect(
nav.getByRole('link', { name: 'Catalog', exact: true }),
).toBeVisible();
await expect(
page.getByRole('link', { name: 'APIs', exact: true }),
).toBeVisible();
});
@@ -14,33 +14,29 @@
"lint": "backstage-cli package lint"
},
"dependencies": {
"@backstage/app-defaults": "^{{version '@backstage/app-defaults'}}",
"@backstage/catalog-model": "^{{version '@backstage/catalog-model'}}",
"@backstage/cli": "^{{version '@backstage/cli'}}",
"@backstage/core-app-api": "^{{version '@backstage/core-app-api'}}",
"@backstage/core-components": "^{{version '@backstage/core-components'}}",
"@backstage/core-plugin-api": "^{{version '@backstage/core-plugin-api'}}",
"@backstage/integration-react": "^{{version '@backstage/integration-react'}}",
"@backstage/plugin-api-docs": "^{{version '@backstage/plugin-api-docs'}}",
"@backstage/plugin-catalog": "^{{version '@backstage/plugin-catalog'}}",
"@backstage/plugin-catalog-common": "^{{version '@backstage/plugin-catalog-common'}}",
"@backstage/plugin-catalog-graph": "^{{version '@backstage/plugin-catalog-graph'}}",
"@backstage/plugin-catalog-import": "^{{version '@backstage/plugin-catalog-import'}}",
"@backstage/plugin-catalog-react": "^{{version '@backstage/plugin-catalog-react'}}",
"@backstage/plugin-kubernetes": "^{{version '@backstage/plugin-kubernetes'}}",
"@backstage/cli": "^{{ version '@backstage/cli'}}",
"@backstage/core-components": "^{{ version '@backstage/core-components'}}",
"@backstage/core-plugin-api": "^{{ version '@backstage/core-plugin-api'}}",
"@backstage/frontend-defaults": "^{{ version '@backstage/frontend-defaults'}}",
"@backstage/frontend-plugin-api": "^{{ version '@backstage/frontend-plugin-api'}}",
"@backstage/integration-react": "^{{ version '@backstage/integration-react'}}",
"@backstage/plugin-api-docs": "^{{ version '@backstage/plugin-api-docs'}}",
"@backstage/plugin-app-react": "^{{ version '@backstage/plugin-app-react'}}",
"@backstage/plugin-app-visualizer": "^{{ version '@backstage/plugin-app-visualizer'}}",
"@backstage/plugin-auth": "^{{ version '@backstage/plugin-auth'}}",
"@backstage/plugin-catalog": "^{{ version '@backstage/plugin-catalog'}}",
"@backstage/plugin-catalog-graph": "^{{ version '@backstage/plugin-catalog-graph'}}",
"@backstage/plugin-catalog-import": "^{{ version '@backstage/plugin-catalog-import'}}",
"@backstage/plugin-kubernetes": "^{{ version '@backstage/plugin-kubernetes'}}",
"@backstage/plugin-notifications": "^{{ version '@backstage/plugin-notifications'}}",
"@backstage/plugin-org": "^{{version '@backstage/plugin-org'}}",
"@backstage/plugin-permission-react": "^{{version '@backstage/plugin-permission-react'}}",
"@backstage/plugin-scaffolder": "^{{version '@backstage/plugin-scaffolder'}}",
"@backstage/plugin-search": "^{{version '@backstage/plugin-search'}}",
"@backstage/plugin-search-react": "^{{version '@backstage/plugin-search-react'}}",
"@backstage/plugin-org": "^{{ version '@backstage/plugin-org'}}",
"@backstage/plugin-scaffolder": "^{{ version '@backstage/plugin-scaffolder'}}",
"@backstage/plugin-search": "^{{ version '@backstage/plugin-search'}}",
"@backstage/plugin-signals": "^{{ version '@backstage/plugin-signals'}}",
"@backstage/plugin-techdocs": "^{{version '@backstage/plugin-techdocs'}}",
"@backstage/plugin-techdocs-module-addons-contrib": "^{{version '@backstage/plugin-techdocs-module-addons-contrib'}}",
"@backstage/plugin-techdocs-react": "^{{version '@backstage/plugin-techdocs-react'}}",
"@backstage/plugin-user-settings": "^{{version '@backstage/plugin-user-settings'}}",
"@backstage/theme": "^{{version '@backstage/theme'}}",
"@backstage/ui": "^{{version '@backstage/ui'}}",
"@backstage/plugin-techdocs": "^{{ version '@backstage/plugin-techdocs'}}",
"@backstage/plugin-techdocs-module-addons-contrib": "^{{ version '@backstage/plugin-techdocs-module-addons-contrib'}}",
"@backstage/plugin-user-settings": "^{{ version '@backstage/plugin-user-settings'}}",
"@backstage/ui": "^{{ version '@backstage/ui'}}",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"react": "^18.0.2",
@@ -49,7 +45,7 @@
"react-router-dom": "^6.30.2"
},
"devDependencies": {
"@backstage/test-utils": "^{{version '@backstage/test-utils'}}",
"@backstage/frontend-test-utils": "^{{ version '@backstage/frontend-test-utils'}}",
"@playwright/test": "^1.32.3",
"@testing-library/dom": "^9.0.0",
"@testing-library/jest-dom": "^6.0.0",
@@ -19,7 +19,7 @@ describe('App', () => {
] as any,
};
const rendered = render(<App />);
const rendered = render(App.createRoot());
await waitFor(() => {
expect(rendered.baseElement).toBeInTheDocument();
@@ -1,112 +1,7 @@
import { Navigate, Route } from 'react-router-dom';
import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs';
import {
CatalogEntityPage,
CatalogIndexPage,
catalogPlugin,
} from '@backstage/plugin-catalog';
import {
CatalogImportPage,
catalogImportPlugin,
} from '@backstage/plugin-catalog-import';
import { ScaffolderPage, scaffolderPlugin } from '@backstage/plugin-scaffolder';
import { orgPlugin } from '@backstage/plugin-org';
import { SearchPage } from '@backstage/plugin-search';
import {
TechDocsIndexPage,
techdocsPlugin,
TechDocsReaderPage,
} from '@backstage/plugin-techdocs';
import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
import { UserSettingsPage } from '@backstage/plugin-user-settings';
import { apis } from './apis';
import { entityPage } from './components/catalog/EntityPage';
import { searchPage } from './components/search/SearchPage';
import { Root } from './components/Root';
import { createApp } from '@backstage/frontend-defaults';
import catalogPlugin from '@backstage/plugin-catalog/alpha';
import { navModule } from './modules/nav';
import {
AlertDisplay,
OAuthRequestDialog,
SignInPage,
} from '@backstage/core-components';
import { createApp } from '@backstage/app-defaults';
import { AppRouter, FlatRoutes } from '@backstage/core-app-api';
import { CatalogGraphPage } from '@backstage/plugin-catalog-graph';
import { RequirePermission } from '@backstage/plugin-permission-react';
import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha';
import { NotificationsPage } from '@backstage/plugin-notifications';
import { SignalsDisplay } from '@backstage/plugin-signals';
const app = createApp({
apis,
bindRoutes({ bind }) {
bind(catalogPlugin.externalRoutes, {
createComponent: scaffolderPlugin.routes.root,
viewTechDoc: techdocsPlugin.routes.docRoot,
createFromTemplate: scaffolderPlugin.routes.selectedTemplate,
});
bind(apiDocsPlugin.externalRoutes, {
registerApi: catalogImportPlugin.routes.importPage,
});
bind(scaffolderPlugin.externalRoutes, {
registerComponent: catalogImportPlugin.routes.importPage,
viewTechDoc: techdocsPlugin.routes.docRoot,
});
bind(orgPlugin.externalRoutes, {
catalogIndex: catalogPlugin.routes.catalogIndex,
});
},
components: {
SignInPage: props => <SignInPage {...props} auto providers={['guest']} />,
},
export default createApp({
features: [catalogPlugin, navModule],
});
const routes = (
<FlatRoutes>
<Route path="/" element={<Navigate to="catalog" />} />
<Route path="/catalog" element={<CatalogIndexPage />} />
<Route
path="/catalog/:namespace/:kind/:name"
element={<CatalogEntityPage />}
>
{entityPage}
</Route>
<Route path="/docs" element={<TechDocsIndexPage />} />
<Route
path="/docs/:namespace/:kind/:name/*"
element={<TechDocsReaderPage />}
>
<TechDocsAddons>
<ReportIssue />
</TechDocsAddons>
</Route>
<Route path="/create" element={<ScaffolderPage />} />
<Route path="/api-docs" element={<ApiExplorerPage />} />
<Route
path="/catalog-import"
element={
<RequirePermission permission={catalogEntityCreatePermission}>
<CatalogImportPage />
</RequirePermission>
}
/>
<Route path="/search" element={<SearchPage />}>
{searchPage}
</Route>
<Route path="/settings" element={<UserSettingsPage />} />
<Route path="/catalog-graph" element={<CatalogGraphPage />} />
<Route path="/notifications" element={<NotificationsPage />} />
</FlatRoutes>
);
export default app.createRoot(
<>
<AlertDisplay />
<OAuthRequestDialog />
<SignalsDisplay />
<AppRouter>
<Root>{routes}</Root>
</AppRouter>
</>,
);
@@ -3,4 +3,4 @@ import ReactDOM from 'react-dom/client';
import App from './App';
import '@backstage/ui/css/styles.css';
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
ReactDOM.createRoot(document.getElementById('root')!).render(App.createRoot());
@@ -24,7 +24,7 @@ export default defineConfig({
timeout: 60_000,
expect: {
timeout: 5_000,
timeout: 30_000,
},
// Run your local dev server before starting the tests
@@ -33,9 +33,9 @@ export default defineConfig({
: [
{
command: 'yarn start app',
port: 3000,
url: 'http://localhost:3000',
reuseExistingServer: true,
timeout: 60_000,
timeout: 120_000,
},
{
command: 'yarn start backend',
@@ -1 +1,12 @@
# intentionally left empty
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
__metadata:
version: 8
cacheKey: 10
"root-workspace-0b6124@workspace:.":
version: 0.0.0-use.local
resolution: "root-workspace-0b6124@workspace:."
languageName: unknown
linkType: soft
@@ -2,15 +2,6 @@ app:
title: Scaffolded Backstage App
baseUrl: http://localhost:3000
# Enable all packages by default, this will discover packages from packages/app/package.json
packages: all
extensions:
# Configure the catalog index page to be the root page, this is normally mounted on /catalog
- page:catalog:
config:
path: /
organization:
name: My Company
@@ -27,7 +27,20 @@
"new": {
"globals": {
"license": "UNLICENSED"
}
},
"templates": [
"@backstage/cli-module-new/templates/legacy-frontend-plugin",
"@backstage/cli-module-new/templates/backend-plugin",
"@backstage/cli-module-new/templates/backend-plugin-module",
"@backstage/cli-module-new/templates/plugin-web-library",
"@backstage/cli-module-new/templates/plugin-node-library",
"@backstage/cli-module-new/templates/plugin-common-library",
"@backstage/cli-module-new/templates/web-library",
"@backstage/cli-module-new/templates/node-library",
"@backstage/cli-module-new/templates/cli-module",
"@backstage/cli-module-new/templates/catalog-provider-module",
"@backstage/cli-module-new/templates/scaffolder-backend-module"
]
}
}
},
@@ -23,11 +23,5 @@ test('App should render the welcome page', async ({ page }) => {
await expect(enterButton).toBeVisible();
await enterButton.click();
const nav = page.getByRole('navigation');
await expect(
nav.getByRole('link', { name: 'Catalog', exact: true }),
).toBeVisible();
await expect(
page.getByRole('link', { name: 'APIs', exact: true }),
).toBeVisible();
await expect(page.getByText('My Company Catalog')).toBeVisible();
});
@@ -0,0 +1,76 @@
{
"name": "app",
"version": "0.0.0",
"private": true,
"bundled": true,
"backstage": {
"role": "frontend"
},
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"clean": "backstage-cli package clean",
"test": "backstage-cli package test",
"lint": "backstage-cli package lint"
},
"dependencies": {
"@backstage/app-defaults": "^{{version '@backstage/app-defaults'}}",
"@backstage/catalog-model": "^{{version '@backstage/catalog-model'}}",
"@backstage/cli": "^{{version '@backstage/cli'}}",
"@backstage/core-app-api": "^{{version '@backstage/core-app-api'}}",
"@backstage/core-components": "^{{version '@backstage/core-components'}}",
"@backstage/core-plugin-api": "^{{version '@backstage/core-plugin-api'}}",
"@backstage/integration-react": "^{{version '@backstage/integration-react'}}",
"@backstage/plugin-api-docs": "^{{version '@backstage/plugin-api-docs'}}",
"@backstage/plugin-catalog": "^{{version '@backstage/plugin-catalog'}}",
"@backstage/plugin-catalog-common": "^{{version '@backstage/plugin-catalog-common'}}",
"@backstage/plugin-catalog-graph": "^{{version '@backstage/plugin-catalog-graph'}}",
"@backstage/plugin-catalog-import": "^{{version '@backstage/plugin-catalog-import'}}",
"@backstage/plugin-catalog-react": "^{{version '@backstage/plugin-catalog-react'}}",
"@backstage/plugin-kubernetes": "^{{version '@backstage/plugin-kubernetes'}}",
"@backstage/plugin-notifications": "^{{ version '@backstage/plugin-notifications'}}",
"@backstage/plugin-org": "^{{version '@backstage/plugin-org'}}",
"@backstage/plugin-permission-react": "^{{version '@backstage/plugin-permission-react'}}",
"@backstage/plugin-scaffolder": "^{{version '@backstage/plugin-scaffolder'}}",
"@backstage/plugin-search": "^{{version '@backstage/plugin-search'}}",
"@backstage/plugin-search-react": "^{{version '@backstage/plugin-search-react'}}",
"@backstage/plugin-signals": "^{{ version '@backstage/plugin-signals'}}",
"@backstage/plugin-techdocs": "^{{version '@backstage/plugin-techdocs'}}",
"@backstage/plugin-techdocs-module-addons-contrib": "^{{version '@backstage/plugin-techdocs-module-addons-contrib'}}",
"@backstage/plugin-techdocs-react": "^{{version '@backstage/plugin-techdocs-react'}}",
"@backstage/plugin-user-settings": "^{{version '@backstage/plugin-user-settings'}}",
"@backstage/theme": "^{{version '@backstage/theme'}}",
"@backstage/ui": "^{{version '@backstage/ui'}}",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"react": "^18.0.2",
"react-dom": "^18.0.2",
"react-router": "^6.30.2",
"react-router-dom": "^6.30.2"
},
"devDependencies": {
"@backstage/test-utils": "^{{version '@backstage/test-utils'}}",
"@playwright/test": "^1.32.3",
"@testing-library/dom": "^9.0.0",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.0.0",
"@types/react-dom": "*",
"cross-env": "^7.0.0"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"files": [
"dist"
]
}

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

@@ -19,7 +19,7 @@ describe('App', () => {
] as any,
};
const rendered = render(App.createRoot());
const rendered = render(<App />);
await waitFor(() => {
expect(rendered.baseElement).toBeInTheDocument();
@@ -0,0 +1,112 @@
import { Navigate, Route } from 'react-router-dom';
import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs';
import {
CatalogEntityPage,
CatalogIndexPage,
catalogPlugin,
} from '@backstage/plugin-catalog';
import {
CatalogImportPage,
catalogImportPlugin,
} from '@backstage/plugin-catalog-import';
import { ScaffolderPage, scaffolderPlugin } from '@backstage/plugin-scaffolder';
import { orgPlugin } from '@backstage/plugin-org';
import { SearchPage } from '@backstage/plugin-search';
import {
TechDocsIndexPage,
techdocsPlugin,
TechDocsReaderPage,
} from '@backstage/plugin-techdocs';
import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
import { UserSettingsPage } from '@backstage/plugin-user-settings';
import { apis } from './apis';
import { entityPage } from './components/catalog/EntityPage';
import { searchPage } from './components/search/SearchPage';
import { Root } from './components/Root';
import {
AlertDisplay,
OAuthRequestDialog,
SignInPage,
} from '@backstage/core-components';
import { createApp } from '@backstage/app-defaults';
import { AppRouter, FlatRoutes } from '@backstage/core-app-api';
import { CatalogGraphPage } from '@backstage/plugin-catalog-graph';
import { RequirePermission } from '@backstage/plugin-permission-react';
import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha';
import { NotificationsPage } from '@backstage/plugin-notifications';
import { SignalsDisplay } from '@backstage/plugin-signals';
const app = createApp({
apis,
bindRoutes({ bind }) {
bind(catalogPlugin.externalRoutes, {
createComponent: scaffolderPlugin.routes.root,
viewTechDoc: techdocsPlugin.routes.docRoot,
createFromTemplate: scaffolderPlugin.routes.selectedTemplate,
});
bind(apiDocsPlugin.externalRoutes, {
registerApi: catalogImportPlugin.routes.importPage,
});
bind(scaffolderPlugin.externalRoutes, {
registerComponent: catalogImportPlugin.routes.importPage,
viewTechDoc: techdocsPlugin.routes.docRoot,
});
bind(orgPlugin.externalRoutes, {
catalogIndex: catalogPlugin.routes.catalogIndex,
});
},
components: {
SignInPage: props => <SignInPage {...props} auto providers={['guest']} />,
},
});
const routes = (
<FlatRoutes>
<Route path="/" element={<Navigate to="catalog" />} />
<Route path="/catalog" element={<CatalogIndexPage />} />
<Route
path="/catalog/:namespace/:kind/:name"
element={<CatalogEntityPage />}
>
{entityPage}
</Route>
<Route path="/docs" element={<TechDocsIndexPage />} />
<Route
path="/docs/:namespace/:kind/:name/*"
element={<TechDocsReaderPage />}
>
<TechDocsAddons>
<ReportIssue />
</TechDocsAddons>
</Route>
<Route path="/create" element={<ScaffolderPage />} />
<Route path="/api-docs" element={<ApiExplorerPage />} />
<Route
path="/catalog-import"
element={
<RequirePermission permission={catalogEntityCreatePermission}>
<CatalogImportPage />
</RequirePermission>
}
/>
<Route path="/search" element={<SearchPage />}>
{searchPage}
</Route>
<Route path="/settings" element={<UserSettingsPage />} />
<Route path="/catalog-graph" element={<CatalogGraphPage />} />
<Route path="/notifications" element={<NotificationsPage />} />
</FlatRoutes>
);
export default app.createRoot(
<>
<AlertDisplay />
<OAuthRequestDialog />
<SignalsDisplay />
<AppRouter>
<Root>{routes}</Root>
</AppRouter>
</>,
);
@@ -3,4 +3,4 @@ import ReactDOM from 'react-dom/client';
import App from './App';
import '@backstage/ui/css/styles.css';
ReactDOM.createRoot(document.getElementById('root')!).render(App.createRoot());
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
@@ -1,72 +0,0 @@
{
"name": "app",
"version": "0.0.0",
"private": true,
"bundled": true,
"backstage": {
"role": "frontend"
},
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"clean": "backstage-cli package clean",
"test": "backstage-cli package test",
"lint": "backstage-cli package lint"
},
"dependencies": {
"@backstage/cli": "^{{ version '@backstage/cli'}}",
"@backstage/core-components": "^{{ version '@backstage/core-components'}}",
"@backstage/core-plugin-api": "^{{ version '@backstage/core-plugin-api'}}",
"@backstage/frontend-defaults": "^{{ version '@backstage/frontend-defaults'}}",
"@backstage/frontend-plugin-api": "^{{ version '@backstage/frontend-plugin-api'}}",
"@backstage/integration-react": "^{{ version '@backstage/integration-react'}}",
"@backstage/plugin-api-docs": "^{{ version '@backstage/plugin-api-docs'}}",
"@backstage/plugin-app-react": "^{{ version '@backstage/plugin-app-react'}}",
"@backstage/plugin-app-visualizer": "^{{ version '@backstage/plugin-app-visualizer'}}",
"@backstage/plugin-auth": "^{{ version '@backstage/plugin-auth'}}",
"@backstage/plugin-catalog": "^{{ version '@backstage/plugin-catalog'}}",
"@backstage/plugin-catalog-graph": "^{{ version '@backstage/plugin-catalog-graph'}}",
"@backstage/plugin-catalog-import": "^{{ version '@backstage/plugin-catalog-import'}}",
"@backstage/plugin-kubernetes": "^{{ version '@backstage/plugin-kubernetes'}}",
"@backstage/plugin-notifications": "^{{ version '@backstage/plugin-notifications'}}",
"@backstage/plugin-org": "^{{ version '@backstage/plugin-org'}}",
"@backstage/plugin-scaffolder": "^{{ version '@backstage/plugin-scaffolder'}}",
"@backstage/plugin-search": "^{{ version '@backstage/plugin-search'}}",
"@backstage/plugin-signals": "^{{ version '@backstage/plugin-signals'}}",
"@backstage/plugin-techdocs": "^{{ version '@backstage/plugin-techdocs'}}",
"@backstage/plugin-techdocs-module-addons-contrib": "^{{ version '@backstage/plugin-techdocs-module-addons-contrib'}}",
"@backstage/plugin-user-settings": "^{{ version '@backstage/plugin-user-settings'}}",
"@backstage/ui": "^{{ version '@backstage/ui'}}",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"react": "^18.0.2",
"react-dom": "^18.0.2",
"react-router": "^6.30.2",
"react-router-dom": "^6.30.2"
},
"devDependencies": {
"@backstage/frontend-test-utils": "^{{ version '@backstage/frontend-test-utils'}}",
"@playwright/test": "^1.32.3",
"@testing-library/dom": "^9.0.0",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.0.0",
"@types/react-dom": "*",
"cross-env": "^7.0.0"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"files": [
"dist"
]
}
@@ -1,7 +0,0 @@
import { createApp } from '@backstage/frontend-defaults';
import catalogPlugin from '@backstage/plugin-catalog/alpha';
import { navModule } from './modules/nav';
export default createApp({
features: [catalogPlugin, navModule],
});
+6 -4
View File
@@ -35,9 +35,9 @@ const ownPaths = findOwnPaths(__dirname);
const templatePackagePaths = [
'packages/cli-module-new/templates/frontend-plugin/package.json.hbs',
'packages/create-app/templates/next-app/package.json.hbs',
'packages/create-app/templates/next-app/packages/app/package.json.hbs',
'packages/create-app/templates/next-app/packages/backend/package.json.hbs',
'packages/create-app/templates/default-app/package.json.hbs',
'packages/create-app/templates/default-app/packages/app/package.json.hbs',
'packages/create-app/templates/default-app/packages/backend/package.json.hbs',
];
export async function runCommand(opts: OptionValues) {
@@ -344,7 +344,9 @@ async function overrideModuleResolutions(appDir: string, workspaceDir: string) {
pkgJson.dependencies[name] = `file:${pkgPath}`;
pkgJson.resolutions[name] = `file:${pkgPath}`;
delete pkgJson.devDependencies[name];
if (pkgJson.devDependencies) {
delete pkgJson.devDependencies[name];
}
}
}
fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });