Skip to content

Commit

Permalink
Merge pull request #4398 from easyops-cn/steve/v3-error-display
Browse files Browse the repository at this point in the history
fix(): refine error display
  • Loading branch information
qiaofengxi authored Aug 12, 2024
2 parents 2c7aeef + 9409196 commit 5622e80
Show file tree
Hide file tree
Showing 19 changed files with 703 additions and 97 deletions.
2 changes: 1 addition & 1 deletion cypress/e2e/sub-routes.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ for (const port of Cypress.env("ports")) {
cy.contains("SyntaxError");
cy.expectMainContents([
...fixedContents,
'SyntaxError: Unexpected token (1:4), in "<% CTX. %>"',
'Oops! Something went wrong: SyntaxError: Unexpected token (1:4), in "<% CTX. %>"',
]);

cy.get("@console.error").should("be.called");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ function getE2eSettings() {
presetBricks: {
notification: false,
dialog: false,
error: false,
},
};
}
33 changes: 33 additions & 0 deletions packages/brick-container/src/BootstrapError.shadow.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
:host {
display: flex;
flex-direction: column;
align-items: center;
padding: 48px 32px;
}

:host([hidden]) {
display: none;
}

.icon {
color: var(--color-error);
font-size: 72px;
margin-bottom: 24px;
}

.icon svg {
display: block;
}

.title {
color: var(--antd-heading-color);
font-size: 24px;
line-height: 1.8;
}

.description {
color: var(--antd-text-color-secondary);
font-size: 14px;
line-height: 1.6;
text-align: center;
}
19 changes: 19 additions & 0 deletions packages/brick-container/src/BootstrapError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// istanbul ignore file
import styleText from "./BootstrapError.shadow.css";

const icon = `<svg viewBox="64 64 896 896" focusable="false" data-icon="close-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></svg>`;

export class BootstrapError extends HTMLElement {
connectedCallback() {
if (this.shadowRoot) {
return;
}
const shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.innerHTML = [
`<style>${styleText}</style>`,
`<div class="icon">${icon}</div>`,
'<div class="title">启动错误</div>',
'<div class="description"><slot></slot></div>',
].join("");
}
}
9 changes: 7 additions & 2 deletions packages/brick-container/src/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { getSpanId } from "./utils.js";
import { listen } from "./preview/listen.js";
import { getMock } from "./mocks.js";
import { NS, locales } from "./i18n.js";
import { BootstrapError } from "./BootstrapError.js";

analytics.initialize(
`${getBasePath()}api/gateway/data_exchange.store.ClickHouseInsertData/api/v1/data_exchange/frontend_stat`
Expand Down Expand Up @@ -140,9 +141,13 @@ async function main() {
// `.bootstrap-error` makes loading-bar invisible.
document.body.classList.add("bootstrap-error");

customElements.define("easyops-bootstrap-error", BootstrapError);
const errorElement = document.createElement("easyops-bootstrap-error");
errorElement.textContent = httpErrorToString(error);

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
document.querySelector("#main-mount-point")!.textContent =
`bootstrap failed: ${httpErrorToString(error)}`;
document.querySelector("#main-mount-point")!.replaceChildren(errorElement);

return "failed";
}
}
Expand Down
4 changes: 4 additions & 0 deletions packages/brick-container/src/styles/default.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ body.launchpad-open {
.bootstrap-error #global-loading-bar {
display: none;
}

#main-mount-point > illustrations\.error-message:only-child {
min-height: 100vh;
}
4 changes: 2 additions & 2 deletions packages/loader/src/stableLoadBricks.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ describe("loadBricksImperatively", () => {
]
)
).rejects.toMatchInlineSnapshot(
`[Error: Load bricks of "unsure.not-existed" failed: oops]`
`[BrickLoadError: Load bricks of "unsure.not-existed" failed: oops]`
);
expect(requestsCount).toBe(1);
await promise;
Expand Down Expand Up @@ -425,7 +425,7 @@ describe("loadBricksImperatively", () => {
]
)
).rejects.toMatchInlineSnapshot(
`[Error: Load bricks of "eo-will-fail" failed: oops]`
`[BrickLoadError: Load bricks of "eo-will-fail" failed: oops]`
);
expect(requestsCount).toBe(1);
await promise;
Expand Down
17 changes: 16 additions & 1 deletion packages/loader/src/stableLoadBricks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,21 @@ export function loadEditorsImperatively(
return dispatchRequestStatus(promise);
}

export class BrickLoadError extends Error {
constructor(message: string) {
// Pass remaining arguments (including vendor specific ones) to parent constructor
super(message);

this.name = "BrickLoadError";

// Maintains proper stack trace for where our error was thrown (only available on V8)
// istanbul ignore else
if (Error.captureStackTrace) {
Error.captureStackTrace(this, BrickLoadError);
}
}
}

interface V2AdapterBrick {
resolve(
adapterPkgFilePath: string,
Expand Down Expand Up @@ -186,7 +201,7 @@ async function loadBrickModule(
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
throw new Error(
throw new BrickLoadError(
`Load ${type} of "${item.fullName}" failed: ${(error as Error)?.message}`
);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/createRoot.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ describe("preview", () => {
]);

expect(container.innerHTML).toBe(
'<div data-error-boundary="">ReferenceError: QUERY is not defined, in "&lt;% QUERY.q %&gt;"</div>'
'<div data-error-boundary="">UNKNOWN_ERROR: ReferenceError: QUERY is not defined, in "&lt;% QUERY.q %&gt;"</div>'
);
expect(portal.innerHTML).toBe("");
expect(applyTheme).not.toBeCalled();
Expand Down
18 changes: 2 additions & 16 deletions packages/runtime/src/createRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ import { RendererContext } from "./internal/RendererContext.js";
import { DataStore } from "./internal/data/DataStore.js";
import type { RenderRoot, RuntimeContext } from "./internal/interfaces.js";
import { mountTree, unmountTree } from "./internal/mount.js";
import { httpErrorToString } from "./handleHttpError.js";
import { applyMode, applyTheme, setMode, setTheme } from "./themeAndMode.js";
import { RenderTag } from "./internal/enums.js";
import { registerStoryboardFunctions } from "./internal/compute/StoryboardFunctions.js";
import { registerAppI18n } from "./internal/registerAppI18n.js";
import { registerCustomTemplates } from "./internal/registerCustomTemplates.js";
import { setUIVersion } from "./setUIVersion.js";
import { ErrorNode } from "./internal/ErrorNode.js";

export interface CreateRootOptions {
portal?: HTMLElement;
Expand Down Expand Up @@ -182,21 +182,7 @@ export function unstable_createRoot(
} catch (error) {
failed = true;
output = {
node: {
tag: RenderTag.BRICK,
type: "div",
properties: {
textContent: httpErrorToString(error),
dataset: {
errorBoundary: "",
},
style: {
color: "var(--color-error)",
},
},
return: renderRoot,
runtimeContext: null!,
},
node: await ErrorNode(error, renderRoot, scope === "page"),
blockingList: [],
};
}
Expand Down
Loading

0 comments on commit 5622e80

Please sign in to comment.