From dc7504c9dd59a580ae40cfbb93e1699b7a5f759b Mon Sep 17 00:00:00 2001 From: Endre Vegh Date: Thu, 15 Aug 2024 17:31:26 +0200 Subject: [PATCH 1/7] fix: wrap preview in boundary --- .../src/components/Live/LivePreview.tsx | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/packages/react-live/src/components/Live/LivePreview.tsx b/packages/react-live/src/components/Live/LivePreview.tsx index 034fcc4..70be343 100644 --- a/packages/react-live/src/components/Live/LivePreview.tsx +++ b/packages/react-live/src/components/Live/LivePreview.tsx @@ -1,17 +1,55 @@ -import React, { useContext } from "react"; +import React, { useContext, Component } from "react"; import LiveContext from "./LiveContext"; type Props = { Component?: T; } & React.ComponentPropsWithoutRef; +class ErrorBoundary extends Component< + { + children: React.ReactNode; + onError?: (error: Error) => void; + }, + { hasError: boolean } +> { + static getDerivedStateFromError() { + return { hasError: true }; + } + + constructor(props: { + children: React.ReactNode; + onError: (error: Error) => void; + }) { + super(props); + this.state = { hasError: false }; + } + + componentDidCatch(err: Error): void { + this.props.onError?.(err); + } + + render() { + return this.props.children; + } +} + function LivePreview( props: Props ): JSX.Element; function LivePreview(props: Props): JSX.Element; function LivePreview({ Component = "div", ...rest }: Props): JSX.Element { - const { element: Element } = useContext(LiveContext); - return {Element ? : null}; + const { element: Element, onError, code } = useContext(LiveContext); + + return ( + { + onError(err); + }} + > + {Element ? : null} + + ); } export default LivePreview; From 0c02f4b1695c5525a1bcff109d0c79bc95783d7a Mon Sep 17 00:00:00 2001 From: Endre Vegh Date: Mon, 19 Aug 2024 12:27:27 +0200 Subject: [PATCH 2/7] fix: throw away Boundary on new code --- .../react-live/src/components/Live/LiveContext.ts | 1 + .../src/components/Live/LivePreview.tsx | 8 ++++++-- .../src/components/Live/LiveProvider.tsx | 15 ++++++++++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/react-live/src/components/Live/LiveContext.ts b/packages/react-live/src/components/Live/LiveContext.ts index 37eae39..e230023 100644 --- a/packages/react-live/src/components/Live/LiveContext.ts +++ b/packages/react-live/src/components/Live/LiveContext.ts @@ -5,6 +5,7 @@ type ContextValue = { error?: string; element?: ComponentType | null; code: string; + newCode?: string; disabled: boolean; language: string; theme?: typeof themes.nightOwl; diff --git a/packages/react-live/src/components/Live/LivePreview.tsx b/packages/react-live/src/components/Live/LivePreview.tsx index 70be343..a1d30f4 100644 --- a/packages/react-live/src/components/Live/LivePreview.tsx +++ b/packages/react-live/src/components/Live/LivePreview.tsx @@ -29,6 +29,10 @@ class ErrorBoundary extends Component< } render() { + if (this.state.hasError) { + return null; + } + return this.props.children; } } @@ -39,11 +43,11 @@ function LivePreview( function LivePreview(props: Props): JSX.Element; function LivePreview({ Component = "div", ...rest }: Props): JSX.Element { - const { element: Element, onError, code } = useContext(LiveContext); + const { element: Element, onError, newCode } = useContext(LiveContext); return ( { onError(err); }} diff --git a/packages/react-live/src/components/Live/LiveProvider.tsx b/packages/react-live/src/components/Live/LiveProvider.tsx index b34b661..030dcbb 100644 --- a/packages/react-live/src/components/Live/LiveProvider.tsx +++ b/packages/react-live/src/components/Live/LiveProvider.tsx @@ -6,6 +6,7 @@ import { themes } from "prism-react-renderer"; type ProviderState = { element?: ComponentType | null; error?: string; + newCode?: string; }; type Props = { @@ -37,7 +38,11 @@ function LiveProvider({ async function transpileAsync(newCode: string) { const errorCallback = (error: Error) => { - setState({ error: error.toString(), element: undefined }); + setState((previousState) => ({ + ...previousState, + error: error.toString(), + element: undefined, + })); }; // - transformCode may be synchronous or asynchronous. @@ -51,7 +56,7 @@ function LiveProvider({ try { const transformedCode = await Promise.resolve(transformResult); const renderElement = (element: ComponentType) => - setState({ error: undefined, element }); + setState({ error: undefined, element, newCode }); if (typeof transformedCode !== "string") { throw new Error("Code failed to transform"); @@ -65,7 +70,11 @@ function LiveProvider({ }; if (noInline) { - setState({ error: undefined, element: null }); // Reset output for async (no inline) evaluation + setState((previousState) => ({ + ...previousState, + error: undefined, + element: null, + })); // Reset output for async (no inline) evaluation renderElementAsync(input, renderElement, errorCallback); } else { renderElement(generateElement(input, errorCallback)); From b7ba3e8177b37fee3a8d26b2affd12efbd5019d5 Mon Sep 17 00:00:00 2001 From: Charles Brown Date: Tue, 19 Nov 2024 07:28:46 -0600 Subject: [PATCH 3/7] Refactor boundary into component file --- .../src/components/Live/ErrorBoundary.tsx | 33 +++++++++++++++++ .../src/components/Live/LivePreview.tsx | 36 ++----------------- 2 files changed, 36 insertions(+), 33 deletions(-) create mode 100644 packages/react-live/src/components/Live/ErrorBoundary.tsx diff --git a/packages/react-live/src/components/Live/ErrorBoundary.tsx b/packages/react-live/src/components/Live/ErrorBoundary.tsx new file mode 100644 index 0000000..b949d05 --- /dev/null +++ b/packages/react-live/src/components/Live/ErrorBoundary.tsx @@ -0,0 +1,33 @@ +import { Component, ReactNode } from "react"; + +type Props = { + children: ReactNode; + onError?: (error: Error) => void; +}; + +type State = { + hasError: boolean; +}; + +export class ErrorBoundary extends Component { + static getDerivedStateFromError() { + return { hasError: true }; + } + + constructor(props: Props) { + super(props); + this.state = { hasError: false }; + } + + componentDidCatch(err: Error): void { + this.props.onError?.(err); + } + + render() { + if (this.state.hasError) { + return null; + } + + return this.props.children; + } +} diff --git a/packages/react-live/src/components/Live/LivePreview.tsx b/packages/react-live/src/components/Live/LivePreview.tsx index a1d30f4..c8105dc 100644 --- a/packages/react-live/src/components/Live/LivePreview.tsx +++ b/packages/react-live/src/components/Live/LivePreview.tsx @@ -1,42 +1,12 @@ -import React, { useContext, Component } from "react"; +import React, { useContext } from "react"; + +import { ErrorBoundary } from "./ErrorBoundary"; import LiveContext from "./LiveContext"; type Props = { Component?: T; } & React.ComponentPropsWithoutRef; -class ErrorBoundary extends Component< - { - children: React.ReactNode; - onError?: (error: Error) => void; - }, - { hasError: boolean } -> { - static getDerivedStateFromError() { - return { hasError: true }; - } - - constructor(props: { - children: React.ReactNode; - onError: (error: Error) => void; - }) { - super(props); - this.state = { hasError: false }; - } - - componentDidCatch(err: Error): void { - this.props.onError?.(err); - } - - render() { - if (this.state.hasError) { - return null; - } - - return this.props.children; - } -} - function LivePreview( props: Props ): JSX.Element; From 7766b3b714c51b1884ab5f23ae4b92ef5c2098a8 Mon Sep 17 00:00:00 2001 From: Charles Brown Date: Tue, 19 Nov 2024 07:28:59 -0600 Subject: [PATCH 4/7] Add test to CI runs --- .github/workflows/code-check.yml | 10 +++++----- .github/workflows/release.yml | 8 +++++++- package.json | 1 + 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/code-check.yml b/.github/workflows/code-check.yml index 5d3590c..48ab814 100644 --- a/.github/workflows/code-check.yml +++ b/.github/workflows/code-check.yml @@ -1,9 +1,6 @@ name: Code Check on: - push: - branches: - - master pull_request: branches: - master @@ -16,8 +13,11 @@ jobs: - uses: actions/checkout@v4 - uses: ./.github/actions/setup - - name: Check Code ${{ matrix.node-version }} + - name: Check Code run: pnpm lint - - name: Build ${{ matrix.node-version }} + - name: Test + run: pnpm test + + - name: Build run: pnpm build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a2f8745..2b2f224 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: react-live Release Workflow +name: Release Workflow on: push: @@ -21,6 +21,12 @@ jobs: - uses: actions/checkout@v4 - uses: ./.github/actions/setup + - name: Check Code + run: pnpm lint + + - name: Test + run: pnpm test + - name: Build packages run: pnpm run build diff --git a/package.json b/package.json index 3a16da7..07c23c0 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "build:lib": "pnpm run --filter react-live build", "lint": "pnpm run --parallel lint", "lint:fix": "pnpm run --parallel lint --fix", + "test": "pnpm run --filter react-live test", "changeset": "changeset", "version": "pnpm changeset version && pnpm install --no-frozen-lockfile" }, From 02e6b214e00516e3cebdb820cd88f736534089d4 Mon Sep 17 00:00:00 2001 From: Charles Brown Date: Tue, 19 Nov 2024 07:29:03 -0600 Subject: [PATCH 5/7] Add changeset --- .changeset/silent-ads-sell.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/silent-ads-sell.md diff --git a/.changeset/silent-ads-sell.md b/.changeset/silent-ads-sell.md new file mode 100644 index 0000000..672f7a6 --- /dev/null +++ b/.changeset/silent-ads-sell.md @@ -0,0 +1,5 @@ +--- +"react-live": patch +--- + +Wrap preview in error boundary From 3aa8bb53ba977b232b7f1e1f309de5984356ccf4 Mon Sep 17 00:00:00 2001 From: Charlie Brown Date: Tue, 19 Nov 2024 09:36:54 -0600 Subject: [PATCH 6/7] Fix serve command --- website/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/package.json b/website/package.json index 64de88d..5e20cb8 100644 --- a/website/package.json +++ b/website/package.json @@ -11,7 +11,7 @@ "lint": "eslint --ext .js,.ts,.tsx src", "deploy": "docusaurus deploy", "clear": "docusaurus clear", - "serve": "docusaurus serve", + "serve": "docusaurus serve --config ./docusaurus.config.js --dir ./build/open-source/react-live --port 3565 --no-open", "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", "typecheck": "tsc" From 4761252664122536a917236c858684c5207bf44b Mon Sep 17 00:00:00 2001 From: Charlie Brown Date: Tue, 19 Nov 2024 09:38:12 -0600 Subject: [PATCH 7/7] Minor cleanup --- packages/react-live/src/components/Live/LivePreview.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/react-live/src/components/Live/LivePreview.tsx b/packages/react-live/src/components/Live/LivePreview.tsx index c8105dc..e41b98f 100644 --- a/packages/react-live/src/components/Live/LivePreview.tsx +++ b/packages/react-live/src/components/Live/LivePreview.tsx @@ -16,12 +16,7 @@ function LivePreview({ Component = "div", ...rest }: Props): JSX.Element { const { element: Element, onError, newCode } = useContext(LiveContext); return ( - { - onError(err); - }} - > + {Element ? : null} );