frontend-defaults: new display for app startup errors
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { AppError } from '@backstage/frontend-app-api';
|
||||
import { AppErrorTypes } from '@backstage/frontend-app-api';
|
||||
import { Config } from '@backstage/config';
|
||||
import { ConfigApi } from '@backstage/frontend-plugin-api';
|
||||
import { CreateAppRouteBinder } from '@backstage/frontend-app-api';
|
||||
@@ -45,6 +47,16 @@ export function discoverAvailableFeatures(config: Config): {
|
||||
features: (FrontendFeature | FrontendFeatureLoader)[];
|
||||
};
|
||||
|
||||
// @public
|
||||
export function maybeCreateErrorPage(
|
||||
app: {
|
||||
errors?: AppError[];
|
||||
},
|
||||
options?: {
|
||||
warningCodes?: Array<keyof AppErrorTypes>;
|
||||
},
|
||||
): JSX_2.Element | undefined;
|
||||
|
||||
// @public (undocumented)
|
||||
export function resolveAsyncFeatures(options: {
|
||||
config: Config;
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
import appPlugin from '@backstage/plugin-app';
|
||||
import { discoverAvailableFeatures } from './discovery';
|
||||
import { resolveAsyncFeatures } from './resolution';
|
||||
import { maybeCreateErrorPage } from './maybeCreateErrorPage';
|
||||
|
||||
/**
|
||||
* Options for {@link createApp}.
|
||||
@@ -136,6 +137,11 @@ export function createApp(options?: CreateAppOptions): {
|
||||
advanced: options?.advanced,
|
||||
});
|
||||
|
||||
const errorPage = maybeCreateErrorPage(app);
|
||||
if (errorPage) {
|
||||
return { default: () => errorPage };
|
||||
}
|
||||
|
||||
const rootEl = app.tree.root.instance!.getData(
|
||||
coreExtensionData.reactElement,
|
||||
);
|
||||
|
||||
@@ -24,3 +24,4 @@ export { createApp, type CreateAppOptions } from './createApp';
|
||||
export { createPublicSignInApp } from './createPublicSignInApp';
|
||||
export { discoverAvailableFeatures } from './discovery';
|
||||
export { resolveAsyncFeatures } from './resolution';
|
||||
export { maybeCreateErrorPage } from './maybeCreateErrorPage';
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2024 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 { FrontendPluginInfo } from '@backstage/frontend-plugin-api';
|
||||
import { JSX, useEffect, useState } from 'react';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { AppError, AppErrorTypes } from '@backstage/frontend-app-api';
|
||||
|
||||
const DEFAULT_WARNING_CODES: Array<keyof AppErrorTypes> = [
|
||||
'EXTENSION_IGNORED',
|
||||
'INVALID_EXTENSION_CONFIG_KEY',
|
||||
'EXTENSION_INPUT_DATA_IGNORED',
|
||||
'EXTENSION_OUTPUT_IGNORED',
|
||||
];
|
||||
|
||||
function AppErrorItem(props: { error: AppError }): JSX.Element {
|
||||
const { context } = props.error;
|
||||
|
||||
const extensionId =
|
||||
'extensionId' in context ? context.extensionId : context.node.spec.id;
|
||||
const node = 'node' in context ? context.node : undefined;
|
||||
const plugin = 'plugin' in context ? context.plugin : node?.spec.plugin;
|
||||
const pluginId = plugin?.id ?? 'N/A';
|
||||
|
||||
const [info, setInfo] = useState<FrontendPluginInfo | undefined>(undefined);
|
||||
useEffect(() => {
|
||||
plugin?.info().then(setInfo, error => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Failed to load info for plugin ${plugin.id}: ${error}`);
|
||||
});
|
||||
}, [plugin]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<b>{props.error.code}</b>: {props.error.message}
|
||||
<pre style={{ marginLeft: '1rem' }}>
|
||||
<div>extensionId: {extensionId}</div>
|
||||
{pluginId && <div>pluginId: {pluginId}</div>}
|
||||
{info && (
|
||||
<div>
|
||||
package: {info.packageName}@{info.version}
|
||||
</div>
|
||||
)}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AppErrorPage(props: { errors: AppError[] }): JSX.Element {
|
||||
return (
|
||||
<div style={{ margin: '1rem' }}>
|
||||
<h2>App startup failed</h2>
|
||||
{props.errors.map((error, index) => (
|
||||
<AppErrorItem error={error} key={index} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* If there are any unrecoverable errors in the app, this will return an error page in the form of a JSX element.
|
||||
*
|
||||
* If there are any recoverable errors, they will always be logged as warnings in the console.
|
||||
* @public
|
||||
*/
|
||||
export function maybeCreateErrorPage(
|
||||
app: { errors?: AppError[] },
|
||||
options?: {
|
||||
warningCodes?: Array<keyof AppErrorTypes>;
|
||||
},
|
||||
): JSX.Element | undefined {
|
||||
if (!app.errors) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const errors = new Array<AppError>();
|
||||
const warnings = new Array<AppError>();
|
||||
|
||||
const warningCodes = new Set(options?.warningCodes ?? DEFAULT_WARNING_CODES);
|
||||
for (const error of app.errors) {
|
||||
if (warningCodes.has(error.code)) {
|
||||
warnings.push(error);
|
||||
} else {
|
||||
errors.push(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (warnings.length > 0) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('App startup encountered warnings:');
|
||||
for (const warning of warnings) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(`${warning.code}: ${warning.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return <AppErrorPage errors={errors} />;
|
||||
}
|
||||
Reference in New Issue
Block a user