diff --git a/packages/error-overlay/CHANGELOG.md b/packages/error-overlay/CHANGELOG.md new file mode 100644 index 000000000..895d068dc --- /dev/null +++ b/packages/error-overlay/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +## v0.0.1 + +- feat: collect errors from `console.error` ([#273](https://github.com/hi-ogawa/vite-plugins/pull/293)) + +## v0.0.0 + +- feat: create `vite-plugin-error-overlay` ([#272](https://github.com/hi-ogawa/vite-plugins/pull/272)) diff --git a/packages/error-overlay/package.json b/packages/error-overlay/package.json index 75dac86cc..bf87334d0 100644 --- a/packages/error-overlay/package.json +++ b/packages/error-overlay/package.json @@ -1,6 +1,6 @@ { "name": "@hiogawa/vite-plugin-error-overlay", - "version": "0.0.0", + "version": "0.0.1", "homepage": "https://github.com/hi-ogawa/vite-plugins/tree/main/packages/error-overlay", "repository": { "type": "git", diff --git a/packages/error-overlay/src/index.ts b/packages/error-overlay/src/index.ts index 1ae6fc05c..9ccecd8cc 100644 --- a/packages/error-overlay/src/index.ts +++ b/packages/error-overlay/src/index.ts @@ -3,9 +3,12 @@ import { name as packageName } from "../package.json"; const virtualName = "virtual:runtime-error-overlay"; -export function vitePluginErrorOverlay(options?: { - filter?: (error: Error) => boolean; -}): Plugin { +export function vitePluginErrorOverlay( + options: { + filter?: (error: Error) => boolean; + patchConsoleError?: boolean; + } = {}, +): Plugin { return { name: packageName, apply: "serve", @@ -23,7 +26,7 @@ export function vitePluginErrorOverlay(options?: { }, load(id, _options) { if (id === "\0" + virtualName) { - return `(${clientScriptFn.toString()})()`; + return `(${clientScriptFn.toString()})(${JSON.stringify(options)})`; } return; }, @@ -45,7 +48,7 @@ export function vitePluginErrorOverlay(options?: { }; } -function clientScriptFn() { +function clientScriptFn(options: { patchConsoleError?: boolean }) { if (import.meta.hot) { window.addEventListener("error", (evt) => { sendError(evt.error); @@ -55,6 +58,21 @@ function clientScriptFn() { sendError(evt.reason); }); + // monkey-patch console.error to collect errors handled by error boundaries + // https://github.com/facebook/react/blob/9defcd56bc3cd53ac2901ed93f29218007010434/packages/react-reconciler/src/ReactFiberErrorLogger.js#L24-L31 + // https://github.com/vercel/next.js/blob/904908cf33bda1dfc50d81a19f3fc60c2c20f8da/packages/next/src/client/components/react-dev-overlay/internal/helpers/hydration-error-info.ts#L56 + if (options.patchConsoleError) { + const oldFn = console.error; + console.error = function (...args) { + for (const arg of args) { + if (arg instanceof Error) { + sendError(arg); + } + } + oldFn.apply(this, args); + }; + } + function sendError(e: unknown) { const error = e instanceof Error ? e : new Error("(unknown error)", { cause: e }); diff --git a/packages/react-server/examples/basic/package.json b/packages/react-server/examples/basic/package.json index ca0f3fe0b..7400f03a3 100644 --- a/packages/react-server/examples/basic/package.json +++ b/packages/react-server/examples/basic/package.json @@ -30,6 +30,7 @@ "@hattip/adapter-node": "^0.0.44", "@hiogawa/unocss-preset-antd": "2.2.1-pre.7", "@hiogawa/utils": "^1.6.3", + "@hiogawa/vite-plugin-error-overlay": "latest", "@hiogawa/vite-plugin-ssr-middleware": "latest", "@iconify-json/ri": "^1.1.20", "@playwright/test": "^1.42.1", diff --git a/packages/react-server/examples/basic/vite.config.ts b/packages/react-server/examples/basic/vite.config.ts index d42616e07..2c5ae1b08 100644 --- a/packages/react-server/examples/basic/vite.config.ts +++ b/packages/react-server/examples/basic/vite.config.ts @@ -1,5 +1,6 @@ import path from "node:path"; import { vitePluginReactServer } from "@hiogawa/react-server/plugin"; +import { vitePluginErrorOverlay } from "@hiogawa/vite-plugin-error-overlay"; import { vitePluginLogger, vitePluginSsrMiddleware, @@ -13,6 +14,10 @@ export default defineConfig({ plugins: [ react(), unocss(), + !process.env["CI"] && + vitePluginErrorOverlay({ + patchConsoleError: true, + }), vitePluginReactServer({ plugins: [ testVitePluginVirtual(), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ca121a11..76d3970df 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -166,6 +166,9 @@ importers: '@hiogawa/utils': specifier: ^1.6.3 version: 1.6.3 + '@hiogawa/vite-plugin-error-overlay': + specifier: latest + version: link:../../../error-overlay '@hiogawa/vite-plugin-ssr-middleware': specifier: latest version: link:../../../vite-plugin-ssr-middleware