Handle chunk loading errors

Signed-off-by: Oliver Sand <oliver.sand@sda-se.com>
This commit is contained in:
Oliver Sand
2021-04-06 15:23:52 +02:00
parent 78c6e0d90f
commit 1279a33259
4 changed files with 67 additions and 32 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/core': patch
'@backstage/core-api': patch
---
Introduce a `load-chunk` step in the `BootErrorPage` to show make chunk loading
errors visible to the user.
+1 -1
View File
@@ -24,7 +24,7 @@ import { AppConfig } from '@backstage/config';
import { SubRouteRef } from '../routing/types';
export type BootErrorPageProps = {
step: 'load-config';
step: 'load-config' | 'load-chunk';
error: Error;
};
+34 -22
View File
@@ -15,9 +15,10 @@
*/
import React, { lazy, Suspense } from 'react';
import { useApp } from '../app';
import { BackstagePlugin, Extension } from '../plugin/types';
import { RouteRef, useRouteRef } from '../routing';
import { attachComponentData } from './componentData';
import { Extension, BackstagePlugin } from '../plugin/types';
type ComponentLoader<T> =
| {
@@ -40,30 +41,41 @@ export function createRoutableExtension<
return createReactExtension({
component: {
lazy: () =>
component().then(InnerComponent => {
const RoutableExtensionWrapper: any = (props: any) => {
// Validate that the routing is wired up correctly in the App.tsx
try {
useRouteRef(mountPoint);
} catch {
throw new Error(
`Routable extension component with mount point ${mountPoint} was not discovered in the app element tree. ` +
'Routable extension components may not be rendered by other components and must be ' +
'directly available as an element within the App provider component.',
);
}
return <InnerComponent {...props} />;
};
component().then(
InnerComponent => {
const RoutableExtensionWrapper: any = (props: any) => {
// Validate that the routing is wired up correctly in the App.tsx
try {
useRouteRef(mountPoint);
} catch {
throw new Error(
`Routable extension component with mount point ${mountPoint} was not discovered in the app element tree. ` +
'Routable extension components may not be rendered by other components and must be ' +
'directly available as an element within the App provider component.',
);
}
return <InnerComponent {...props} />;
};
const componentName =
(InnerComponent as { displayName?: string }).displayName ||
InnerComponent.name ||
'LazyComponent';
const componentName =
(InnerComponent as { displayName?: string }).displayName ||
InnerComponent.name ||
'LazyComponent';
RoutableExtensionWrapper.displayName = `RoutableExtension(${componentName})`;
RoutableExtensionWrapper.displayName = `RoutableExtension(${componentName})`;
return RoutableExtensionWrapper as T;
}),
return RoutableExtensionWrapper as T;
},
error => {
const RoutableExtensionWrapper: any = (_: any) => {
const app = useApp();
const { BootErrorPage } = app.getComponents();
return <BootErrorPage step="load-chunk" error={error} />;
};
return RoutableExtensionWrapper;
},
),
},
data: {
'core.mountPoint': mountPoint,
+25 -9
View File
@@ -97,16 +97,32 @@ export function createApp(options?: AppOptions) {
<ErrorPage status="404" statusMessage="PAGE NOT FOUND" />
);
const DefaultBootErrorPage = ({ step, error }: BootErrorPageProps) => {
let message = '';
if (step === 'load-config') {
message = `The configuration failed to load, someone should have a look at this error: ${error.message}`;
switch (step) {
case 'load-config':
// TODO: figure out a nicer way to handle routing on the error page, when it can be done.
return (
<MemoryRouter>
<ErrorPage
status="501"
statusMessage={`The configuration failed to load, someone should have a look at this error: ${error.message}`}
/>
</MemoryRouter>
);
case 'load-chunk':
return (
<ErrorPage
status="501"
statusMessage={`Lazy loaded chunk failed to load, try to reload the page: ${error.message}`}
/>
);
default:
// TODO: figure out a nicer way to handle routing on the error page, when it can be done.
return (
<MemoryRouter>
<ErrorPage status="501" statusMessage="" />
</MemoryRouter>
);
}
// TODO: figure out a nicer way to handle routing on the error page, when it can be done.
return (
<MemoryRouter>
<ErrorPage status="501" statusMessage={message} />
</MemoryRouter>
);
};
const apis = options?.apis ?? [];