frontend-plugin-api: move app blueprints to new app-react package

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2026-01-16 10:58:04 +01:00
parent d15524f895
commit 9ccf84e219
33 changed files with 653 additions and 34 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-app-backend': patch
'@backstage/plugin-app-node': patch
---
Updated plugin metadata.
+13
View File
@@ -0,0 +1,13 @@
---
'@backstage/plugin-app-react': patch
---
Moved the following blueprints from `@backstage/frontend-plugin-api`:
- `IconBundleBlueprint`
- `NavContentBlueprint`
- `RouterBlueprint`
- `SignInPageBlueprint`
- `SwappableComponentBlueprint`
- `ThemeBlueprint`
- `TranslationBlueprint`
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-app-react': minor
---
Initial release of this web library for `@backstage/plugin-app`.
+13
View File
@@ -0,0 +1,13 @@
---
'@backstage/frontend-plugin-api': patch
---
The following blueprints are being restricted to only be used in app plugin overrides and modules. They are being moved to the `@backstage/plugin-app-react` package and have been deprecated:
- `IconBundleBlueprint`
- `NavContentBlueprint`
- `RouterBlueprint`
- `SignInPageBlueprint`
- `SwappableComponentBlueprint`
- `ThemeBlueprint`
- `TranslationBlueprint`
+13
View File
@@ -0,0 +1,13 @@
---
'@backstage/plugin-app': patch
---
The following blueprints are being restricted to only be used in app plugin overrides and modules. They will now produce a deprecation warning when used outside of the app plugin:
- `IconBundleBlueprint`
- `NavContentBlueprint`
- `RouterBlueprint`
- `SignInPageBlueprint`
- `SwappableComponentBlueprint`
- `ThemeBlueprint`
- `TranslationBlueprint`
@@ -444,7 +444,7 @@ const app = createApp({
Can be converted to the following extension:
```tsx
import { SignInPageBlueprint } from '@backstage/frontend-plugin-api';
import { SignInPageBlueprint } from '@backstage/plugin-app-react';
const signInPage = SignInPageBlueprint.make({
params: {
@@ -492,7 +492,7 @@ const app = createApp({
Can be converted to the following extension:
```tsx
import { ThemeBlueprint } from '@backstage/frontend-plugin-api';
import { ThemeBlueprint } from '@backstage/plugin-app-react';
const customLightThemeExtension = ThemeBlueprint.make({
name: 'custom-light',
@@ -535,7 +535,7 @@ const app = createApp({
Icons are now installed as extensions, using the `IconBundleBlueprint` to make new instances which can be added to the app.
```ts
import { IconBundleBlueprint } from '@backstage/frontend-plugin-api';
import { IconBundleBlueprint } from '@backstage/plugin-app-react';
const exampleIconBundle = IconBundleBlueprint.make({
name: 'example-bundle',
@@ -586,10 +586,8 @@ Can be converted to the following extension:
```tsx
import { catalogTranslationRef } from '@backstage/plugin-catalog/alpha';
import {
createTranslationMessages,
TranslationBlueprint,
} from '@backstage/frontend-plugin-api';
import { createTranslationMessages } from '@backstage/frontend-plugin-api';
import { TranslationBlueprint } from '@backstage/plugin-app-react';
const catalogTranslations = TranslationBlueprint.make({
name: 'catalog-overrides',
@@ -705,7 +703,7 @@ export const navModule = createFrontendModule({
Then in the actual implementation for the `SidebarContent` extension, you can provide something like the following, where you implement the entire `Sidebar` component.
```tsx title="in packages/app/src/modules/nav/Sidebar.tsx"
import { NavContentBlueprint } from '@backstage/frontend-plugin-api';
import { NavContentBlueprint } from '@backstage/plugin-app-react';
export const SidebarContent = NavContentBlueprint.make({
params: {
@@ -23,6 +23,8 @@ Navigation item extensions are used to provide menu items that link to different
Page extensions provide content for a particular route in the app. By default pages are attached to the app routes extensions, which renders the root routes.
## Extension blueprints in `@backstage/plugin-app-react`
### SignInPage - [Reference](https://backstage.io/api/stable/variables/_backstage_frontend-plugin-api.SignInPageBlueprint.html)
Sign-in page extension have a single purpose - to implement a custom sign-in page. They are always attached to the app root extension and are rendered before the rest of the app until the user is signed in.
@@ -43,6 +45,14 @@ Icon bundle extensions provide the ability to replace or provide new icons to th
Translation extension provide custom translation messages for the app. They can be used both to override the default english messages to custom ones, as well as provide translations for additional languages.
### NavContent - [Reference](https://backstage.io/api/stable/variables/_backstage_frontend-plugin-api.NavContentBlueprint.html)
Nav content extensions allow you to replace the entire navbar with your own component. They are always attached to the app nav extension.
### Router - [Reference](https://backstage.io/api/stable/variables/_backstage_frontend-plugin-api.RouterBlueprint.html)
Router extensions allow you to replace the router component used by the app. They are always attached to the app root extension.
## Extension blueprints in `@backstage/plugin-catalog-react/alpha`
These are the [extension blueprints](../architecture/23-extension-blueprints.md) provided by the Catalog plugin.
@@ -52,11 +52,8 @@ In order to override a Swappable Component, you need to create a `SwappableCompo
There are two different ways to add extensions to the `app` plugin, both are documented below in an example of overriding the `Progress` Swappable Component.
```tsx title="in packages/app/src/App.tsx"
import {
Progress,
SwappableComponentBlueprint,
createFrontendModule,
} from '@backstage/frontend-plugin-api';
import { Progress, createFrontendModule } from '@backstage/frontend-plugin-api';
import { SwappableComponentBlueprint } from '@backstage/plugin-app-react';
import { MyCustomProgress } from './CustomProgress';
import { createApp } from '@backstage/frontend-defaults';
import appPlugin from '@backstage/plugin-app';
+7 -7
View File
@@ -1457,7 +1457,7 @@ export const googleAuthApiRef: ApiRef<
SessionApi
>;
// @public (undocumented)
// @public @deprecated (undocumented)
export const IconBundleBlueprint: ExtensionBlueprint_2<{
kind: 'icon-bundle';
params: {
@@ -1522,7 +1522,7 @@ export const microsoftAuthApiRef: ApiRef<
SessionApi
>;
// @public
// @public @deprecated
export const NavContentBlueprint: ExtensionBlueprint_2<{
kind: 'nav-content';
params: {
@@ -1923,7 +1923,7 @@ export type RouteFunc<TParams extends AnyRouteRefParams> = (
: readonly [params: TParams]
) => string;
// @public (undocumented)
// @public @deprecated (undocumented)
export const RouterBlueprint: ExtensionBlueprint_2<{
kind: 'app-router-component';
params: {
@@ -1998,7 +1998,7 @@ export namespace SessionState {
export type SignedOut = typeof SessionState.SignedOut;
}
// @public
// @public @deprecated
export const SignInPageBlueprint: ExtensionBlueprint_2<{
kind: 'sign-in-page';
params: {
@@ -2066,7 +2066,7 @@ export interface SubRouteRef<
readonly T: TParams;
}
// @public
// @public @deprecated
export const SwappableComponentBlueprint: ExtensionBlueprint_2<{
kind: 'component';
params: <Ref extends SwappableComponentRef<any>>(params: {
@@ -2150,7 +2150,7 @@ export interface SwappableComponentsApi {
// @public
export const swappableComponentsApiRef: ApiRef_2<SwappableComponentsApi>;
// @public
// @public @deprecated
export const ThemeBlueprint: ExtensionBlueprint_2<{
kind: 'theme';
params: {
@@ -2186,7 +2186,7 @@ export type TranslationApi = {
// @public (undocumented)
export const translationApiRef: ApiRef<TranslationApi>;
// @public
// @public @deprecated
export const TranslationBlueprint: ExtensionBlueprint_2<{
kind: 'translation';
params: {
@@ -21,7 +21,10 @@ const iconsDataRef = createExtensionDataRef<{
[key in string]: IconComponent;
}>().with({ id: 'core.icons' });
/** @public */
/**
* @public
* @deprecated Use {@link @backstage/plugin-app-react#IconBundleBlueprint} instead.
*/
export const IconBundleBlueprint = createExtensionBlueprint({
kind: 'icon-bundle',
attachTo: { id: 'api:app/icons', input: 'icons' },
@@ -60,6 +60,7 @@ const componentDataRef = createExtensionDataRef<NavContentComponent>().with({
* Creates an extension that replaces the entire nav bar with your own component.
*
* @public
* @deprecated Use {@link @backstage/plugin-app-react#NavContentBlueprint} instead.
*/
export const NavContentBlueprint = createExtensionBlueprint({
kind: 'nav-content',
@@ -21,7 +21,10 @@ const componentDataRef = createExtensionDataRef<
(props: { children: ReactNode }) => JSX.Element | null
>().with({ id: 'app.router.wrapper' });
/** @public */
/**
* @public
* @deprecated Use {@link @backstage/plugin-app-react#RouterBlueprint} instead.
*/
export const RouterBlueprint = createExtensionBlueprint({
kind: 'app-router-component',
attachTo: { id: 'app/root', input: 'router' },
@@ -44,6 +44,7 @@ const componentDataRef = createExtensionDataRef<
* Creates an extension that replaces the sign in page.
*
* @public
* @deprecated Use {@link @backstage/plugin-app-react#SignInPageBlueprint} instead.
*/
export const SignInPageBlueprint = createExtensionBlueprint({
kind: 'sign-in-page',
@@ -31,6 +31,7 @@ export const componentDataRef = createExtensionDataRef<{
* Blueprint for creating swappable components from a SwappableComponentRef and a loader
*
* @public
* @deprecated Use {@link @backstage/plugin-app-react#SwappableComponentBlueprint} instead.
*/
export const SwappableComponentBlueprint = createExtensionBlueprint({
kind: 'component',
@@ -25,6 +25,7 @@ const themeDataRef = createExtensionDataRef<AppTheme>().with({
* Creates an extension that adds/replaces an app theme.
*
* @public
* @deprecated Use {@link @backstage/plugin-app-react#ThemeBlueprint} instead.
*/
export const ThemeBlueprint = createExtensionBlueprint({
kind: 'theme',
@@ -25,6 +25,7 @@ const translationDataRef = createExtensionDataRef<
* Creates an extension that adds translations to your app.
*
* @public
* @deprecated Use {@link @backstage/plugin-app-react#TranslationBlueprint} instead.
*/
export const TranslationBlueprint = createExtensionBlueprint({
kind: 'translation',
+2 -1
View File
@@ -8,7 +8,8 @@
"pluginPackages": [
"@backstage/plugin-app",
"@backstage/plugin-app-backend",
"@backstage/plugin-app-node"
"@backstage/plugin-app-node",
"@backstage/plugin-app-react"
]
},
"publishConfig": {
+2 -1
View File
@@ -8,7 +8,8 @@
"pluginPackages": [
"@backstage/plugin-app",
"@backstage/plugin-app-backend",
"@backstage/plugin-app-node"
"@backstage/plugin-app-node",
"@backstage/plugin-app-react"
]
},
"publishConfig": {
+1
View File
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
+5
View File
@@ -0,0 +1,5 @@
# @backstage/plugin-app-react
Welcome to the web library package for the app plugin!
_This plugin was created through the Backstage CLI_
+10
View File
@@ -0,0 +1,10 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: backstage-plugin-app-react
title: '@backstage/plugin-app-react'
description: Web library for the app plugin
spec:
lifecycle: experimental
type: backstage-web-library
owner: framework-maintainers
+68
View File
@@ -0,0 +1,68 @@
{
"name": "@backstage/plugin-app-react",
"version": "0.0.0",
"description": "Web library for the app plugin",
"backstage": {
"role": "web-library",
"pluginId": "app",
"pluginPackages": [
"@backstage/plugin-app",
"@backstage/plugin-app-backend",
"@backstage/plugin-app-node",
"@backstage/plugin-app-react"
]
},
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
"types": "dist/index.d.ts"
},
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
"directory": "plugins/app-react"
},
"license": "Apache-2.0",
"sideEffects": false,
"main": "src/index.ts",
"types": "src/index.ts",
"files": [
"dist"
],
"scripts": {
"build": "backstage-cli package build",
"clean": "backstage-cli package clean",
"lint": "backstage-cli package lint",
"prepack": "backstage-cli package prepack",
"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"
},
"devDependencies": {
"@backstage/cli": "workspace:^",
"@backstage/frontend-test-utils": "workspace:^",
"@backstage/test-utils": "workspace:^",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^16.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
}
}
}
+234
View File
@@ -0,0 +1,234 @@
## API Report File for "@backstage/plugin-app-react"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { AppTheme } from '@backstage/frontend-plugin-api';
import { ComponentType } from 'react';
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionBlueprint } from '@backstage/frontend-plugin-api';
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/frontend-plugin-api';
import { NavContentComponent } from '@backstage/frontend-plugin-api';
import { NavContentComponentProps } from '@backstage/frontend-plugin-api';
import { ReactNode } from 'react';
import { SignInPageProps } from '@backstage/frontend-plugin-api';
import { SwappableComponentRef } from '@backstage/frontend-plugin-api';
import { TranslationMessages } from '@backstage/frontend-plugin-api';
import { TranslationResource } from '@backstage/frontend-plugin-api';
// @public
export const IconBundleBlueprint: ExtensionBlueprint<{
kind: 'icon-bundle';
params: {
icons: { [key in string]: IconComponent };
};
output: ExtensionDataRef<
{
[x: string]: IconComponent;
},
'core.icons',
{}
>;
inputs: {};
config: {};
configInput: {};
dataRefs: {
icons: ConfigurableExtensionDataRef<
{
[x: string]: IconComponent;
},
'core.icons',
{}
>;
};
}>;
// @public
export const NavContentBlueprint: ExtensionBlueprint<{
kind: 'nav-content';
params: {
component: NavContentComponent;
};
output: ExtensionDataRef<
NavContentComponent,
'core.nav-content.component',
{}
>;
inputs: {};
config: {};
configInput: {};
dataRefs: {
component: ConfigurableExtensionDataRef<
NavContentComponent,
'core.nav-content.component',
{}
>;
};
}>;
export { NavContentComponent };
export { NavContentComponentProps };
// @public
export const RouterBlueprint: ExtensionBlueprint<{
kind: 'app-router-component';
params: {
Component?: [error: 'Use the `component` parameter instead'];
component: (props: { children: ReactNode }) => JSX.Element | null;
};
output: ExtensionDataRef<
(props: { children: ReactNode }) => JSX.Element | null,
'app.router.wrapper',
{}
>;
inputs: {};
config: {};
configInput: {};
dataRefs: {
component: ConfigurableExtensionDataRef<
(props: { children: ReactNode }) => JSX.Element | null,
'app.router.wrapper',
{}
>;
};
}>;
// @public
export const SignInPageBlueprint: ExtensionBlueprint<{
kind: 'sign-in-page';
params: {
loader: () => Promise<ComponentType<SignInPageProps>>;
};
output: ExtensionDataRef<
ComponentType<SignInPageProps>,
'core.sign-in-page.component',
{}
>;
inputs: {};
config: {};
configInput: {};
dataRefs: {
component: ConfigurableExtensionDataRef<
ComponentType<SignInPageProps>,
'core.sign-in-page.component',
{}
>;
};
}>;
export { SignInPageProps };
// @public
export const SwappableComponentBlueprint: ExtensionBlueprint<{
kind: 'component';
params: <Ref extends SwappableComponentRef<any>>(params: {
component: Ref extends SwappableComponentRef<
any,
infer IExternalComponentProps
>
? {
ref: Ref;
} & ((props: IExternalComponentProps) => JSX.Element | null)
: never;
loader: Ref extends SwappableComponentRef<infer IInnerComponentProps, any>
?
| (() => (props: IInnerComponentProps) => JSX.Element | null)
| (() => Promise<(props: IInnerComponentProps) => JSX.Element | null>)
: never;
}) => ExtensionBlueprintParams<{
component: Ref extends SwappableComponentRef<
any,
infer IExternalComponentProps
>
? {
ref: Ref;
} & ((props: IExternalComponentProps) => JSX.Element | null)
: never;
loader: Ref extends SwappableComponentRef<infer IInnerComponentProps, any>
?
| (() => (props: IInnerComponentProps) => JSX.Element | null)
| (() => Promise<(props: IInnerComponentProps) => JSX.Element | null>)
: never;
}>;
output: ExtensionDataRef<
{
ref: SwappableComponentRef;
loader:
| (() => (props: {}) => JSX.Element | null)
| (() => Promise<(props: {}) => JSX.Element | null>);
},
'core.swappableComponent',
{}
>;
inputs: {};
config: {};
configInput: {};
dataRefs: {
component: ConfigurableExtensionDataRef<
{
ref: SwappableComponentRef;
loader:
| (() => (props: {}) => JSX.Element | null)
| (() => Promise<(props: {}) => JSX.Element | null>);
},
'core.swappableComponent',
{}
>;
};
}>;
// @public
export const ThemeBlueprint: ExtensionBlueprint<{
kind: 'theme';
params: {
theme: AppTheme;
};
output: ExtensionDataRef<AppTheme, 'core.theme.theme', {}>;
inputs: {};
config: {};
configInput: {};
dataRefs: {
theme: ConfigurableExtensionDataRef<AppTheme, 'core.theme.theme', {}>;
};
}>;
// @public
export const TranslationBlueprint: ExtensionBlueprint<{
kind: 'translation';
params: {
resource: TranslationResource | TranslationMessages;
};
output: ExtensionDataRef<
| TranslationResource<string>
| TranslationMessages<
string,
{
[x: string]: string;
},
boolean
>,
'core.translation.translation',
{}
>;
inputs: {};
config: {};
configInput: {};
dataRefs: {
translation: ConfigurableExtensionDataRef<
| TranslationResource<string>
| TranslationMessages<
string,
{
[x: string]: string;
},
boolean
>,
'core.translation.translation',
{}
>;
};
}>;
```
+98
View File
@@ -0,0 +1,98 @@
/*
* 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 {
IconBundleBlueprint as _IconBundleBlueprint,
NavContentBlueprint as _NavContentBlueprint,
type NavContentComponent,
type NavContentComponentProps,
RouterBlueprint as _RouterBlueprint,
SignInPageBlueprint as _SignInPageBlueprint,
type SignInPageProps,
SwappableComponentBlueprint as _SwappableComponentBlueprint,
ThemeBlueprint as _ThemeBlueprint,
TranslationBlueprint as _TranslationBlueprint,
} from '@backstage/frontend-plugin-api';
/**
* Creates an extension that adds/replaces an app theme. This blueprint is limited to use by the app plugin.
*
* @public
*/
export const ThemeBlueprint = _ThemeBlueprint;
/**
* Blueprint for creating swappable components from a SwappableComponentRef and a loader. This blueprint is limited to use by the app plugin.
*
* @public
*/
export const SwappableComponentBlueprint = _SwappableComponentBlueprint;
/**
* Creates an extension that replaces the sign in page. This blueprint is limited to use by the app plugin.
*
* @public
*/
export const SignInPageBlueprint = _SignInPageBlueprint;
/**
* Creates an extension that replaces the router component. This blueprint is limited to use by the app plugin.
*
* @public
*/
export const RouterBlueprint = _RouterBlueprint;
/**
* Creates an extension that replaces the entire nav bar with your own component. This blueprint is limited to use by the app plugin.
*
* @public
*/
export const NavContentBlueprint = _NavContentBlueprint;
/**
* Creates an extension that adds icon bundles to your app. This blueprint is limited to use by the app plugin.
*
* @public
*/
export const IconBundleBlueprint = _IconBundleBlueprint;
/**
* Props for the `SignInPage` component.
*
* @public
*/
export type { SignInPageProps };
/**
* The props for the {@link NavContentComponent}.
*
* @public
*/
export type { NavContentComponentProps };
/**
* A component that renders the nav bar content, to be passed to the {@link NavContentBlueprint}.
*
* @public
*/
export type { NavContentComponent };
/**
* Creates an extension that adds translations to your app. This blueprint is limited to use by the app plugin.
*
* @public
*/
export const TranslationBlueprint = _TranslationBlueprint;
+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.
*/
/**
* Web library for the app plugin.
*
* @packageDocumentation
*/
export * from './blueprints';
+16
View File
@@ -0,0 +1,16 @@
/*
* 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 '@testing-library/jest-dom';
+3 -1
View File
@@ -7,7 +7,8 @@
"pluginPackages": [
"@backstage/plugin-app",
"@backstage/plugin-app-backend",
"@backstage/plugin-app-node"
"@backstage/plugin-app-node",
"@backstage/plugin-app-react"
]
},
"publishConfig": {
@@ -54,6 +55,7 @@
"@backstage/core-plugin-api": "workspace:^",
"@backstage/frontend-plugin-api": "workspace:^",
"@backstage/integration-react": "workspace:^",
"@backstage/plugin-app-react": "workspace:^",
"@backstage/plugin-permission-react": "workspace:^",
"@backstage/theme": "workspace:^",
"@backstage/types": "workspace:^",
+8
View File
@@ -94,6 +94,14 @@ export const AppNav = createExtension({
},
output: [coreExtensionData.reactElement],
*factory({ inputs }) {
if (inputs.content && inputs.content.node.spec.plugin?.id !== 'app') {
// eslint-disable-next-line no-console
console.warn(
`DEPRECATION WARNING: NavContent should only be installed as an extension in the app plugin. ` +
`You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${inputs.content.node.spec.id}`,
);
}
const Content =
inputs.content?.get(NavContentBlueprint.dataRefs.component) ??
DefaultNavContent;
+16
View File
@@ -74,6 +74,22 @@ export const AppRoot = createExtension({
},
output: [coreExtensionData.reactElement],
factory({ inputs, apis }) {
if (inputs.router && inputs.router.node.spec.plugin?.id !== 'app') {
// eslint-disable-next-line no-console
console.warn(
`DEPRECATION WARNING: Router should only be installed as an extension in the app plugin. ` +
`You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${inputs.router.node.spec.id}`,
);
}
if (inputs.signInPage && inputs.signInPage.node.spec.plugin?.id !== 'app') {
// eslint-disable-next-line no-console
console.warn(
`DEPRECATION WARNING: SignInPage should only be installed as an extension in the app plugin. ` +
`You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${inputs.signInPage.node.spec.id}`,
);
}
if (isProtectedApp()) {
const identityApi = apis.get(identityApiRef);
if (!identityApi) {
+17 -3
View File
@@ -44,10 +44,24 @@ export const AppThemeApi = ApiBlueprint.makeWithOverrides({
defineParams({
api: appThemeApiRef,
deps: {},
factory: () =>
AppThemeSelector.createWithStorage(
factory: () => {
const nonAppExtensions = inputs.themes.filter(
i => i.node.spec.plugin?.id !== 'app',
);
if (nonAppExtensions.length > 0) {
const list = nonAppExtensions.map(i => i.node.spec.id).join(', ');
// eslint-disable-next-line no-console
console.warn(
`DEPRECATION WARNING: Theme should only be installed as an extension in the app plugin. ` +
`You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${list}`,
);
}
return AppThemeSelector.createWithStorage(
inputs.themes.map(i => i.get(ThemeBlueprint.dataRefs.theme)),
),
);
},
}),
);
},
+17 -3
View File
@@ -40,12 +40,26 @@ export const IconsApi = ApiBlueprint.makeWithOverrides({
defineParams({
api: iconsApiRef,
deps: {},
factory: () =>
new DefaultIconsApi(
factory: () => {
const nonAppExtensions = inputs.icons.filter(
i => i.node.spec.plugin?.id !== 'app',
);
if (nonAppExtensions.length > 0) {
const list = nonAppExtensions.map(i => i.node.spec.id).join(', ');
// eslint-disable-next-line no-console
console.warn(
`DEPRECATION WARNING: IconBundle should only be installed as an extension in the app plugin. ` +
`You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${list}`,
);
}
return new DefaultIconsApi(
inputs.icons
.map(i => i.get(IconBundleBlueprint.dataRefs.icons))
.reduce((acc, bundle) => ({ ...acc, ...bundle }), defaultIcons),
),
);
},
}),
);
},
+17 -3
View File
@@ -42,13 +42,27 @@ export const TranslationsApi = ApiBlueprint.makeWithOverrides({
defineParams({
api: translationApiRef,
deps: { languageApi: appLanguageApiRef },
factory: ({ languageApi }) =>
I18nextTranslationApi.create({
factory: ({ languageApi }) => {
const nonAppExtensions = inputs.translations.filter(
i => i.node.spec.plugin?.id !== 'app',
);
if (nonAppExtensions.length > 0) {
const list = nonAppExtensions.map(i => i.node.spec.id).join(', ');
// eslint-disable-next-line no-console
console.warn(
`DEPRECATION WARNING: Translations should only be installed as an extension in the app plugin. ` +
`You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${list}`,
);
}
return I18nextTranslationApi.create({
languageApi,
resources: inputs.translations.map(i =>
i.get(TranslationBlueprint.dataRefs.translation),
),
}),
});
},
}),
);
},
+28
View File
@@ -4155,6 +4155,33 @@ __metadata:
languageName: unknown
linkType: soft
"@backstage/plugin-app-react@workspace:^, @backstage/plugin-app-react@workspace:plugins/app-react":
version: 0.0.0-use.local
resolution: "@backstage/plugin-app-react@workspace:plugins/app-react"
dependencies:
"@backstage/cli": "workspace:^"
"@backstage/core-plugin-api": "workspace:^"
"@backstage/frontend-plugin-api": "workspace:^"
"@backstage/frontend-test-utils": "workspace:^"
"@backstage/test-utils": "workspace:^"
"@material-ui/core": "npm:^4.9.13"
"@testing-library/jest-dom": "npm:^6.0.0"
"@testing-library/react": "npm:^16.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
"@backstage/plugin-app-visualizer@workspace:^, @backstage/plugin-app-visualizer@workspace:plugins/app-visualizer":
version: 0.0.0-use.local
resolution: "@backstage/plugin-app-visualizer@workspace:plugins/app-visualizer"
@@ -4194,6 +4221,7 @@ __metadata:
"@backstage/frontend-plugin-api": "workspace:^"
"@backstage/frontend-test-utils": "workspace:^"
"@backstage/integration-react": "workspace:^"
"@backstage/plugin-app-react": "workspace:^"
"@backstage/plugin-permission-react": "workspace:^"
"@backstage/test-utils": "workspace:^"
"@backstage/theme": "workspace:^"