Fix crash in program viewer when trying to display new value type (#3585)
fix https://github.com/Azure/typespec-azure/issues/1016 ### Fix crash in the program viewer When we were trying to display a value(only case right now is using a default value as property) it would blow up ### Add error boundary to only crash the output view and ability to retry so the entire UI is not lost ![image](https://github.com/microsoft/typespec/assets/1031227/d856c8c9-41d9-4c86-9c6b-432e60a12d53)
This commit is contained in:
Родитель
d319e3324a
Коммит
216f423a8c
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
|
||||||
|
changeKind: feature
|
||||||
|
packages:
|
||||||
|
- "@typespec/playground"
|
||||||
|
---
|
||||||
|
|
||||||
|
Add error recovery for viewer that crash
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
|
||||||
|
changeKind: fix
|
||||||
|
packages:
|
||||||
|
- "@typespec/html-program-viewer"
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix crash in program viewer when trying to display new value type
|
|
@ -1,5 +1,6 @@
|
||||||
import { css } from "@emotion/react";
|
import { css } from "@emotion/react";
|
||||||
import {
|
import {
|
||||||
|
Entity,
|
||||||
Enum,
|
Enum,
|
||||||
EnumMember,
|
EnumMember,
|
||||||
getNamespaceFullName,
|
getNamespaceFullName,
|
||||||
|
@ -132,12 +133,12 @@ const NamedTypeUI = <T extends NamedType>({ type, name, properties }: NamedTypeU
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const render = (x: any) =>
|
const render = (x: Entity) =>
|
||||||
action === "ref" ? <TypeReference type={value} /> : <TypeUI type={x} />;
|
action === "ref" ? <TypeReference type={value} /> : <EntityUI entity={x} />;
|
||||||
let valueUI;
|
let valueUI;
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
valueUI = value;
|
valueUI = value;
|
||||||
} else if (value.kind) {
|
} else if (value.entityKind) {
|
||||||
valueUI = render(value);
|
valueUI = render(value);
|
||||||
} else if (
|
} else if (
|
||||||
typeof value === "object" &&
|
typeof value === "object" &&
|
||||||
|
@ -160,10 +161,19 @@ const NamedTypeUI = <T extends NamedType>({ type, name, properties }: NamedTypeU
|
||||||
};
|
};
|
||||||
|
|
||||||
interface TypeUIProps {
|
interface TypeUIProps {
|
||||||
type: Type;
|
readonly entity: Entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TypeUI: FunctionComponent<TypeUIProps> = ({ type }) => {
|
const EntityUI: FunctionComponent<TypeUIProps> = ({ entity }) => {
|
||||||
|
switch (entity.entityKind) {
|
||||||
|
case "Type":
|
||||||
|
return <TypeUI type={entity} />;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const TypeUI: FunctionComponent<{ type: Type }> = ({ type }) => {
|
||||||
switch (type.kind) {
|
switch (type.kind) {
|
||||||
case "Namespace":
|
case "Namespace":
|
||||||
return <NamespaceUI type={type} />;
|
return <NamespaceUI type={type} />;
|
||||||
|
@ -379,7 +389,7 @@ const TypeReference: FunctionComponent<{ type: Type }> = ({ type }) => {
|
||||||
if (type.name === "") {
|
if (type.name === "") {
|
||||||
return (
|
return (
|
||||||
<KeyValueSection>
|
<KeyValueSection>
|
||||||
<TypeUI type={type} />
|
<EntityUI entity={type} />
|
||||||
</KeyValueSection>
|
</KeyValueSection>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -85,6 +85,7 @@
|
||||||
"monaco-editor": "~0.46.0",
|
"monaco-editor": "~0.46.0",
|
||||||
"react": "~18.3.1",
|
"react": "~18.3.1",
|
||||||
"react-dom": "~18.3.1",
|
"react-dom": "~18.3.1",
|
||||||
|
"react-error-boundary": "^4.0.13",
|
||||||
"swagger-ui-dist": "^5.17.10",
|
"swagger-ui-dist": "^5.17.10",
|
||||||
"vscode-languageserver": "~9.0.1",
|
"vscode-languageserver": "~9.0.1",
|
||||||
"vscode-languageserver-textdocument": "~1.0.11"
|
"vscode-languageserver-textdocument": "~1.0.11"
|
||||||
|
|
|
@ -26,3 +26,7 @@
|
||||||
.viewer-tabs-container {
|
.viewer-tabs-container {
|
||||||
background-color: var(--colorNeutralBackground3);
|
background-color: var(--colorNeutralBackground3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.viewer-error {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Tab, TabList, type SelectTabEventHandler } from "@fluentui/react-components";
|
import { Button, Tab, TabList, type SelectTabEventHandler } from "@fluentui/react-components";
|
||||||
import { useCallback, useMemo, useState, type FunctionComponent } from "react";
|
import { useCallback, useMemo, useState, type FunctionComponent } from "react";
|
||||||
|
import { ErrorBoundary, type FallbackProps } from "react-error-boundary";
|
||||||
import type { PlaygroundEditorsOptions } from "../playground.js";
|
import type { PlaygroundEditorsOptions } from "../playground.js";
|
||||||
import type { CompilationState, CompileResult, FileOutputViewer, ProgramViewer } from "../types.js";
|
import type { CompilationState, CompileResult, FileOutputViewer, ProgramViewer } from "../types.js";
|
||||||
import { createFileViewer } from "./file-viewer.js";
|
import { createFileViewer } from "./file-viewer.js";
|
||||||
|
@ -81,10 +82,12 @@ const OutputViewInternal: FunctionComponent<{
|
||||||
return (
|
return (
|
||||||
<div className={style["output-view"]}>
|
<div className={style["output-view"]}>
|
||||||
<div className={style["output-content"]}>
|
<div className={style["output-content"]}>
|
||||||
<viewer.render
|
<ErrorBoundary fallbackRender={fallbackRender}>
|
||||||
program={compilationResult.program}
|
<viewer.render
|
||||||
outputFiles={compilationResult.outputFiles}
|
program={compilationResult.program}
|
||||||
/>
|
outputFiles={compilationResult.outputFiles}
|
||||||
|
/>
|
||||||
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
<div className={style["viewer-tabs-container"]}>
|
<div className={style["viewer-tabs-container"]}>
|
||||||
<TabList
|
<TabList
|
||||||
|
@ -106,3 +109,13 @@ const OutputViewInternal: FunctionComponent<{
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function fallbackRender({ error, resetErrorBoundary }: FallbackProps) {
|
||||||
|
return (
|
||||||
|
<div role="alert" className={style["viewer-error"]}>
|
||||||
|
<h2>Something went wrong:</h2>
|
||||||
|
<div style={{ color: "red" }}>{error.toString()}</div>
|
||||||
|
<Button onClick={resetErrorBoundary}>Try again</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -754,6 +754,9 @@ importers:
|
||||||
react-dom:
|
react-dom:
|
||||||
specifier: ~18.3.1
|
specifier: ~18.3.1
|
||||||
version: 18.3.1(react@18.3.1)
|
version: 18.3.1(react@18.3.1)
|
||||||
|
react-error-boundary:
|
||||||
|
specifier: ^4.0.13
|
||||||
|
version: 4.0.13(react@18.3.1)
|
||||||
swagger-ui-dist:
|
swagger-ui-dist:
|
||||||
specifier: ^5.17.10
|
specifier: ^5.17.10
|
||||||
version: 5.17.10
|
version: 5.17.10
|
||||||
|
@ -14203,6 +14206,7 @@ packages:
|
||||||
|
|
||||||
/ieee754@1.2.1:
|
/ieee754@1.2.1:
|
||||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||||
|
requiresBuild: true
|
||||||
|
|
||||||
/ignore-walk@6.0.4:
|
/ignore-walk@6.0.4:
|
||||||
resolution: {integrity: sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw==}
|
resolution: {integrity: sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw==}
|
||||||
|
@ -18161,6 +18165,15 @@ packages:
|
||||||
react-is: 18.1.0
|
react-is: 18.1.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/react-error-boundary@4.0.13(react@18.3.1):
|
||||||
|
resolution: {integrity: sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.13.1'
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.24.1
|
||||||
|
react: 18.3.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/react-error-overlay@6.0.11:
|
/react-error-overlay@6.0.11:
|
||||||
resolution: {integrity: sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==}
|
resolution: {integrity: sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
Загрузка…
Ссылка в новой задаче