rasengan 1.2.1-beta.4 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/lib/esm/core/plugins/index.js +10 -13
- package/lib/esm/entries/client/render.js +24 -7
- package/lib/esm/entries/server/entry.server.js +1 -1
- package/lib/esm/entries/server/error-template.js +14 -0
- package/lib/esm/entries/server/index.js +2 -1
- package/lib/esm/routing/components/index.js +12 -69
- package/lib/esm/routing/error-overlay/ErrorBoundaryFallback.js +42 -0
- package/lib/esm/routing/error-overlay/ErrorOverlay.js +60 -0
- package/lib/esm/routing/error-overlay/ErrorOverlayProvider.js +39 -0
- package/lib/esm/routing/error-overlay/error-overlay.css +472 -0
- package/lib/esm/routing/error-overlay/error-store.js +44 -0
- package/lib/esm/routing/error-overlay/index.js +2 -0
- package/lib/esm/routing/error-overlay/stack-utils.js +72 -0
- package/lib/esm/routing/utils/define-router.js +10 -4
- package/lib/esm/routing/utils/flat-routes.js +10 -5
- package/lib/esm/routing/utils/generate-routes.js +3 -2
- package/lib/esm/server/dev/handlers.js +9 -5
- package/lib/esm/server/dev/server.js +8 -1
- package/lib/esm/server/node/index.js +2 -4
- package/lib/esm/server/node/rendering.js +6 -4
- package/lib/tsconfig.esm.tsbuildinfo +1 -1
- package/lib/tsconfig.types.tsbuildinfo +1 -1
- package/lib/types/entries/server/entry.server.d.ts +2 -0
- package/lib/types/entries/server/error-template.d.ts +1 -0
- package/lib/types/routing/error-overlay/ErrorBoundaryFallback.d.ts +14 -0
- package/lib/types/routing/error-overlay/ErrorOverlay.d.ts +2 -0
- package/lib/types/routing/error-overlay/ErrorOverlayProvider.d.ts +7 -0
- package/lib/types/routing/error-overlay/error-store.d.ts +24 -0
- package/lib/types/routing/error-overlay/index.d.ts +3 -0
- package/lib/types/routing/error-overlay/stack-utils.d.ts +31 -0
- package/lib/types/routing/utils/define-router.d.ts +6 -0
- package/lib/types/server/node/rendering.d.ts +7 -1
- package/package.json +13 -12
- package/types/client.d.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
## Unreleased
|
|
2
2
|
|
|
3
|
+
## 1.2.2 (2026-06-01)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- add custom ErrorBoundary d419e67
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
- add error templates and fix flat-routes error regarding the default layout 333414d
|
|
12
|
+
- catch error occuring in component definition 62d6d51
|
|
13
|
+
|
|
14
|
+
## 1.2.1 (2026-04-03)
|
|
15
|
+
|
|
3
16
|
## 1.2.1-beta.4 (2026-01-11)
|
|
4
17
|
|
|
5
18
|
## 1.2.1-beta.3 (2026-01-11)
|
|
@@ -9,29 +9,26 @@ import { preRenderApp } from '../../server/node/index.js';
|
|
|
9
9
|
function loadRasenganGlobal() {
|
|
10
10
|
return {
|
|
11
11
|
name: 'vite-plugin-rasengan-config',
|
|
12
|
-
async config(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
async config() {
|
|
13
|
+
let version = '';
|
|
14
|
+
try {
|
|
15
|
+
const rasenganPkgPath = resolve(process.cwd(), 'node_modules/rasengan/package.json');
|
|
16
|
+
if (fs.existsSync(rasenganPkgPath)) {
|
|
17
|
+
const raw = fs.readFileSync(rasenganPkgPath, { encoding: 'utf-8' });
|
|
18
|
+
version = JSON.parse(raw).version || '';
|
|
19
|
+
}
|
|
18
20
|
}
|
|
19
|
-
|
|
20
|
-
encoding: 'utf-8',
|
|
21
|
-
});
|
|
22
|
-
const packageJson = JSON.parse(packageJsonRaw);
|
|
21
|
+
catch { }
|
|
23
22
|
const rasenganConfig = {
|
|
24
|
-
version
|
|
23
|
+
version,
|
|
25
24
|
ssr: true,
|
|
26
25
|
};
|
|
27
|
-
// Inject the configuration as a global constant
|
|
28
26
|
return {
|
|
29
27
|
define: {
|
|
30
28
|
['Rasengan']: JSON.stringify(rasenganConfig),
|
|
31
29
|
},
|
|
32
30
|
};
|
|
33
31
|
},
|
|
34
|
-
apply: 'build',
|
|
35
32
|
};
|
|
36
33
|
}
|
|
37
34
|
function rasenganConfigPlugin() {
|
|
@@ -4,11 +4,18 @@ import { StrictMode } from 'react';
|
|
|
4
4
|
import { RootComponent } from '../../routing/components/template.js';
|
|
5
5
|
import { generateRoutes, preloadMatches, } from '../../routing/utils/generate-routes.js';
|
|
6
6
|
import { createBrowserRouter, RouterProvider } from 'react-router';
|
|
7
|
+
import { ErrorOverlayProvider, errorStore, } from '../../routing/error-overlay/index.js';
|
|
7
8
|
const isSpaMode = Boolean(window.__RASENGAN_SPA_MODE__);
|
|
8
9
|
export default async function renderApp(App, AppRouter, options) {
|
|
10
|
+
// Forward SSR errors to the error overlay
|
|
11
|
+
const ssrError = window.__RASENGAN_SSR_ERROR__;
|
|
12
|
+
if (ssrError) {
|
|
13
|
+
const err = new Error(ssrError.message);
|
|
14
|
+
err.name = ssrError.name;
|
|
15
|
+
err.stack = ssrError.stack;
|
|
16
|
+
errorStore.addError(err, 'global');
|
|
17
|
+
}
|
|
9
18
|
// Get root element
|
|
10
|
-
// We need to get the root element to render the app - (SPA mode)
|
|
11
|
-
// or to hydrate the app - (SSR mode)
|
|
12
19
|
const root = document.getElementById('root');
|
|
13
20
|
if (!root) {
|
|
14
21
|
throw new Error('#root element not found in the DOM');
|
|
@@ -17,18 +24,24 @@ export default async function renderApp(App, AppRouter, options) {
|
|
|
17
24
|
const RasenganRouter = await AppRouter;
|
|
18
25
|
// Generate routes for the browser routing
|
|
19
26
|
const routes = generateRoutes(RasenganRouter);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
if (!ssrError) {
|
|
28
|
+
// Preload lazy routes
|
|
29
|
+
// We have to only preload the routes that are matched for the current URL
|
|
30
|
+
// The remaining routing will be lazy loaded on the client after routes change
|
|
31
|
+
await preloadMatches(window.location, routes);
|
|
32
|
+
}
|
|
24
33
|
// Create router
|
|
25
34
|
let router = createBrowserRouter(routes, {
|
|
26
35
|
hydrationData: window.__staticRouterHydrationData,
|
|
27
36
|
});
|
|
28
37
|
// Generate client router
|
|
29
38
|
const ClientRouter = () => _jsx(RouterProvider, { router: router });
|
|
39
|
+
// Detect dev mode
|
|
40
|
+
const devMode = typeof import.meta !== 'undefined' &&
|
|
41
|
+
import.meta.env?.DEV === true;
|
|
30
42
|
// Generate app tree
|
|
31
|
-
const
|
|
43
|
+
const appContent = options.reactStrictMode ? (_jsx(StrictMode, { children: _jsx(App, { Component: (props) => (_jsx(RootComponent, { ...props, Router: ClientRouter })) }) })) : (_jsx(App, { Component: (props) => _jsx(RootComponent, { ...props, Router: ClientRouter }) }));
|
|
44
|
+
const appTree = (_jsx(ErrorOverlayProvider, { devMode: devMode, children: appContent }));
|
|
32
45
|
// Render app
|
|
33
46
|
if (isSpaMode) {
|
|
34
47
|
// No SSR markup, so start fresh
|
|
@@ -36,6 +49,10 @@ export default async function renderApp(App, AppRouter, options) {
|
|
|
36
49
|
onCaughtError: (error) => {
|
|
37
50
|
console.error(error);
|
|
38
51
|
},
|
|
52
|
+
onUncaughtError(error, errorInfo) {
|
|
53
|
+
console.error(error);
|
|
54
|
+
console.error(errorInfo);
|
|
55
|
+
},
|
|
39
56
|
onRecoverableError(error, errorInfo) {
|
|
40
57
|
console.error(error);
|
|
41
58
|
console.error(errorInfo);
|
|
@@ -28,7 +28,7 @@ export const render = async (StaticRouterComponent, res, options, stream = true)
|
|
|
28
28
|
Template = (await loadModuleSSR(`${rootPath}/src/template`)).default;
|
|
29
29
|
}
|
|
30
30
|
if (stream) {
|
|
31
|
-
await renderToStream(_jsx(TemplateLayout, { StaticRouterComponent: StaticRouterComponent, metadata: metadata, assets: assets, App: App, Template: Template }), res);
|
|
31
|
+
await renderToStream(_jsx(TemplateLayout, { StaticRouterComponent: StaticRouterComponent, metadata: metadata, assets: assets, App: App, Template: Template }), res, { statusCode: options.statusCode, responseHeaders: options.responseHeaders });
|
|
32
32
|
}
|
|
33
33
|
else {
|
|
34
34
|
const html = renderToString(_jsx(TemplateLayout, { StaticRouterComponent: StaticRouterComponent, metadata: metadata, assets: assets, App: App, Template: Template }));
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { TemplateLayout } from './index.js';
|
|
3
|
+
import { renderToString } from '../../server/node/rendering.js';
|
|
4
|
+
export function renderErrorPage(error) {
|
|
5
|
+
const serialized = JSON.stringify({
|
|
6
|
+
message: error instanceof Error ? error.message : String(error),
|
|
7
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
8
|
+
name: error instanceof Error ? error.name : 'Error',
|
|
9
|
+
});
|
|
10
|
+
const ErrorTemplate = ({ Head, Body, Script, }) => (_jsxs("html", { lang: "en", children: [_jsx(Head, { children: _jsx("script", { type: "module", dangerouslySetInnerHTML: {
|
|
11
|
+
__html: `window.__RASENGAN_SSR_ERROR__ = ${serialized};`,
|
|
12
|
+
} }) }), _jsx(Body, { children: _jsx(Script, { children: _jsx("script", { type: "module", src: "/src/index" }) }) })] }));
|
|
13
|
+
return renderToString(_jsx(TemplateLayout, { Template: ErrorTemplate, isSpaMode: false }));
|
|
14
|
+
}
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { BodyComponent, HeadComponent, RootComponent, ScriptComponent, } from '../../routing/components/template.js';
|
|
4
4
|
import { isServerMode, ServerMode } from '../../server/runtime/mode.js';
|
|
5
|
+
import { ErrorBoundaryFallback } from '../../routing/error-overlay/ErrorBoundaryFallback.js';
|
|
5
6
|
export const TemplateLayout = ({ StaticRouterComponent, metadata, assets, App, Template, isSpaMode = false, }) => {
|
|
6
7
|
// inject vite refresh script to avoid "React refresh preamble was not loaded"
|
|
7
8
|
let viteScripts = _jsx(React.Fragment, {});
|
|
@@ -27,5 +28,5 @@ export const TemplateLayout = ({ StaticRouterComponent, metadata, assets, App, T
|
|
|
27
28
|
__html: `window.__RASENGAN_SPA_MODE__=false;`,
|
|
28
29
|
} }) }));
|
|
29
30
|
}
|
|
30
|
-
return (_jsx(Template, { Head: ({ children }) => (_jsxs(HeadComponent, { metadata: metadata, assets: assets, children: [viteScripts, otherScripts, children] })), Body: ({ children }) => (_jsx(BodyComponent, { asChild: App ? true : false, AppContent: App && (_jsx(App, { Component: (props) => _jsx(RootComponent, { ...props }), children: StaticRouterComponent })), children: children })), Script: ({ children }) => _jsx(ScriptComponent, { children: children }) }));
|
|
31
|
+
return (_jsx(Template, { Head: ({ children }) => (_jsxs(HeadComponent, { metadata: metadata, assets: assets, children: [viteScripts, otherScripts, children] })), Body: ({ children }) => (_jsx(BodyComponent, { asChild: App ? true : false, AppContent: App && (_jsx(ErrorBoundaryFallback, { children: _jsx(App, { Component: (props) => _jsx(RootComponent, { ...props }), children: StaticRouterComponent }) })), children: children })), Script: ({ children }) => _jsx(ScriptComponent, { children: children }) }));
|
|
31
32
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { isRouteErrorResponse, Link, useLocation, useParams, useRouteError, } from 'react-router';
|
|
3
3
|
import { useEffect, useRef } from 'react';
|
|
4
|
+
import { errorStore } from '../error-overlay/error-store.js';
|
|
4
5
|
// Extract the environment variables
|
|
5
6
|
const extractEnv = () => {
|
|
6
7
|
try {
|
|
@@ -55,75 +56,17 @@ export function ErrorBoundary() {
|
|
|
55
56
|
}, children: _jsx("p", { style: {
|
|
56
57
|
fontSize: '18px',
|
|
57
58
|
}, children: "Application Error" }) }));
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
alignItems: 'center',
|
|
70
|
-
height: '100vh',
|
|
71
|
-
width: '100vw',
|
|
72
|
-
gap: 10,
|
|
73
|
-
backgroundColor: '#fff',
|
|
74
|
-
}, children: [_jsx("p", { style: {
|
|
75
|
-
fontSize: '18px',
|
|
76
|
-
}, children: "Application Error" }), _jsxs("h1", { style: {
|
|
77
|
-
fontSize: '18px',
|
|
78
|
-
}, children: [error.status, " ", error.statusText] }), _jsx("p", { style: {
|
|
79
|
-
fontSize: '18px',
|
|
80
|
-
}, children: error.data })] }));
|
|
81
|
-
}
|
|
82
|
-
else if (error instanceof Error) {
|
|
83
|
-
return (_jsxs("section", { style: {
|
|
84
|
-
position: 'fixed',
|
|
85
|
-
top: 0,
|
|
86
|
-
left: 0,
|
|
87
|
-
right: 0,
|
|
88
|
-
bottom: 0,
|
|
89
|
-
zIndex: 100,
|
|
90
|
-
display: 'flex',
|
|
91
|
-
flexDirection: 'column',
|
|
92
|
-
justifyContent: 'start',
|
|
93
|
-
alignItems: 'start',
|
|
94
|
-
height: '100vh',
|
|
95
|
-
width: '100vw',
|
|
96
|
-
padding: 20,
|
|
97
|
-
backgroundColor: '#fff',
|
|
98
|
-
}, children: [_jsx("h1", { style: {
|
|
99
|
-
fontSize: '1.4rem',
|
|
100
|
-
fontWeight: 'bold',
|
|
101
|
-
marginBottom: 5,
|
|
102
|
-
}, children: "Application Error" }), _jsx("p", { style: {
|
|
103
|
-
fontSize: '1rem',
|
|
104
|
-
marginBottom: 10,
|
|
105
|
-
}, children: error.message }), _jsxs("div", { style: {
|
|
106
|
-
marginTop: 20,
|
|
107
|
-
overflow: 'auto',
|
|
108
|
-
width: '100%',
|
|
109
|
-
maxHeight: '100vh',
|
|
110
|
-
padding: '10px 20px',
|
|
111
|
-
borderRadius: 10,
|
|
112
|
-
backgroundColor: '#f5f5f5',
|
|
113
|
-
}, children: [_jsx("p", { style: {
|
|
114
|
-
fontWeight: 'bold',
|
|
115
|
-
fontSize: '1.2rem',
|
|
116
|
-
color: '#000',
|
|
117
|
-
}, children: "The stack trace is:" }), _jsx("pre", { style: {
|
|
118
|
-
whiteSpace: 'pre-wrap',
|
|
119
|
-
wordWrap: 'break-word',
|
|
120
|
-
fontSize: '1rem',
|
|
121
|
-
color: '#ff0000aa',
|
|
122
|
-
}, children: error.stack })] })] }));
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
return _jsx("h1", { children: "Unknown Error" });
|
|
126
|
-
}
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (!DEV)
|
|
61
|
+
return;
|
|
62
|
+
const err = error instanceof Error
|
|
63
|
+
? error
|
|
64
|
+
: isRouteErrorResponse(error)
|
|
65
|
+
? new Error(`${error.status} ${error.statusText}: ${error.data}`)
|
|
66
|
+
: new Error(String(error));
|
|
67
|
+
errorStore.addError(err, 'route');
|
|
68
|
+
}, [error, DEV]);
|
|
69
|
+
return null;
|
|
127
70
|
}
|
|
128
71
|
/**
|
|
129
72
|
* Page component that defines title and description to a page
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Component } from 'react';
|
|
3
|
+
import { errorStore } from './error-store.js';
|
|
4
|
+
export class ErrorBoundaryFallback extends Component {
|
|
5
|
+
constructor(props) {
|
|
6
|
+
super(props);
|
|
7
|
+
this.state = { hasError: false };
|
|
8
|
+
}
|
|
9
|
+
static getDerivedStateFromError() {
|
|
10
|
+
return { hasError: true };
|
|
11
|
+
}
|
|
12
|
+
componentDidCatch(error, info) {
|
|
13
|
+
errorStore.addError(error, 'react', info.componentStack);
|
|
14
|
+
}
|
|
15
|
+
render() {
|
|
16
|
+
if (this.state.hasError) {
|
|
17
|
+
if (typeof process !== 'undefined' &&
|
|
18
|
+
process.env?.NODE_ENV === 'production') {
|
|
19
|
+
return (_jsx("section", { style: {
|
|
20
|
+
position: 'fixed',
|
|
21
|
+
top: 0,
|
|
22
|
+
left: 0,
|
|
23
|
+
right: 0,
|
|
24
|
+
bottom: 0,
|
|
25
|
+
zIndex: 100,
|
|
26
|
+
display: 'flex',
|
|
27
|
+
flexDirection: 'row',
|
|
28
|
+
justifyContent: 'center',
|
|
29
|
+
alignItems: 'center',
|
|
30
|
+
height: '100vh',
|
|
31
|
+
width: '100vw',
|
|
32
|
+
gap: 10,
|
|
33
|
+
backgroundColor: '#fff',
|
|
34
|
+
}, children: _jsx("p", { style: {
|
|
35
|
+
fontSize: '18px',
|
|
36
|
+
}, children: "Application Error" }) }));
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return this.props.children;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState, useSyncExternalStore } from 'react';
|
|
3
|
+
import { createPortal } from 'react-dom';
|
|
4
|
+
import { errorStore } from './error-store.js';
|
|
5
|
+
import { parseStackFrame, fetchSourceSnippet } from './stack-utils.js';
|
|
6
|
+
import './error-overlay.css';
|
|
7
|
+
export function ErrorOverlay() {
|
|
8
|
+
const errors = useSyncExternalStore(errorStore.subscribe.bind(errorStore), () => errorStore.getErrors(), () => errorStore.getErrors());
|
|
9
|
+
const minimized = useSyncExternalStore(errorStore.subscribe.bind(errorStore), () => errorStore.isMinimized(), () => errorStore.isMinimized());
|
|
10
|
+
const [activeTab, setActiveTab] = useState(0);
|
|
11
|
+
const [codePreview, setCodePreview] = useState(null);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (errors.length > 0 && activeTab >= errors.length) {
|
|
14
|
+
setActiveTab(errors.length - 1);
|
|
15
|
+
}
|
|
16
|
+
}, [errors.length, activeTab]);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const error = errors[activeTab];
|
|
19
|
+
if (!error || !error.error.stack) {
|
|
20
|
+
setCodePreview(null);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const frame = parseStackFrame(error.error.stack);
|
|
24
|
+
if (!frame) {
|
|
25
|
+
setCodePreview(null);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
fetchSourceSnippet(frame.file, frame.line).then((result) => {
|
|
29
|
+
if (result) {
|
|
30
|
+
setCodePreview({
|
|
31
|
+
snippet: result.snippet,
|
|
32
|
+
errorLineIndex: result.errorLineIndex,
|
|
33
|
+
file: frame.file,
|
|
34
|
+
line: frame.line,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
setCodePreview(null);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}, [activeTab, errors]);
|
|
42
|
+
if (errors.length === 0)
|
|
43
|
+
return null;
|
|
44
|
+
if (minimized)
|
|
45
|
+
return createPortal(_jsxs("button", { className: "rasengan-error-fab", onClick: () => errorStore.toggleMinimize(), title: `${errors.length} error${errors.length > 1 ? 's' : ''} — click to view`, children: [_jsx("svg", { className: "rasengan-error-fab-icon", viewBox: "0 0 16 16", width: "18", height: "18", fill: "currentColor", children: _jsx("path", { d: "M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l5.782 10.832c.659 1.234-.165 2.771-1.543 2.771H2.218c-1.378 0-2.202-1.537-1.543-2.771L6.457 1.047zM8 4a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 018 4zm0 7a1 1 0 100-2 1 1 0 000 2z" }) }), _jsx("span", { className: "rasengan-error-fab-count", children: errors.length })] }), document.body);
|
|
46
|
+
const activeError = errors[activeTab];
|
|
47
|
+
if (!activeError)
|
|
48
|
+
return null;
|
|
49
|
+
const totalErrors = errors.length;
|
|
50
|
+
const isFirst = activeTab === 0;
|
|
51
|
+
const isLast = activeTab === totalErrors - 1;
|
|
52
|
+
const snippetLines = codePreview ? codePreview.snippet.split('\n') : [];
|
|
53
|
+
const relativeLineStart = codePreview
|
|
54
|
+
? codePreview.line - codePreview.errorLineIndex
|
|
55
|
+
: 0;
|
|
56
|
+
return createPortal(_jsxs(_Fragment, { children: [_jsx("div", { className: "rasengan-error-backdrop" }), _jsxs("div", { className: "rasengan-error-overlay", children: [_jsxs("div", { className: "rasengan-error-overlay-header", children: [_jsxs("div", { className: "rasengan-error-pagination", children: [_jsx("button", { className: "rasengan-error-pagination-btn", disabled: isFirst, onClick: () => setActiveTab(activeTab - 1), title: "Previous error", children: "\u2039" }), _jsxs("span", { className: "rasengan-error-pagination-text", children: [activeTab + 1, " of ", totalErrors] }), _jsx("button", { className: "rasengan-error-pagination-btn", disabled: isLast, onClick: () => setActiveTab(activeTab + 1), title: "Next error", children: "\u203A" })] }), _jsx("div", { className: "rasengan-error-overlay-actions", children: _jsx("button", { className: "rasengan-error-overlay-close", onClick: () => errorStore.toggleMinimize(), title: "Minimize", children: "\u00D7" }) })] }), _jsxs("div", { className: "rasengan-error-overlay-body", children: [_jsxs("div", { className: "rasengan-error-overlay-source", children: [_jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "currentColor", style: { marginRight: 4, verticalAlign: -2 }, children: _jsx("path", { d: "M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l5.782 10.832c.659 1.234-.165 2.771-1.543 2.771H2.218c-1.378 0-2.202-1.537-1.543-2.771L6.457 1.047zM8 4a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 018 4zm0 7a1 1 0 100-2 1 1 0 000 2z" }) }), _jsx("p", { children: activeError.source })] }), _jsx("div", { className: "rasengan-error-overlay-message", children: activeError.error.message || 'Unknown error' }), codePreview && (_jsxs("div", { className: "rasengan-error-code-preview", children: [_jsxs("div", { className: "rasengan-error-code-preview-header", children: [_jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "currentColor", style: { marginRight: 4, flexShrink: 0 }, children: _jsx("path", { d: "M2 1.75C2 .784 2.784 0 3.75 0h5.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0112.25 16h-8.5A1.75 1.75 0 012 14.25V1.75zM3.75 1.5a.25.25 0 00-.25.25v12.5c0 .138.112.25.25.25h8.5a.25.25 0 00.25-.25V5.5h-2.75A1.75 1.75 0 018 3.75V1.5H3.75zm5.75.56v2.19c0 .138.112.25.25.25h2.19L9.5 2.06z" }) }), _jsx("span", { className: "rasengan-error-code-preview-file", children: codePreview.file }), _jsxs("span", { className: "rasengan-error-code-preview-line", children: ["line ", codePreview.line] })] }), _jsx("div", { className: "rasengan-error-code-preview-content", children: snippetLines.map((lineContent, idx) => {
|
|
57
|
+
const isErrorLine = idx === codePreview.errorLineIndex;
|
|
58
|
+
return (_jsxs("div", { className: `rasengan-error-code-line${isErrorLine ? ' error' : ''}`, children: [_jsx("span", { className: "rasengan-error-code-line-number", children: relativeLineStart + idx }), isErrorLine && (_jsx("span", { className: "rasengan-error-code-line-arrow", children: "\u25B8" })), !isErrorLine && (_jsx("span", { className: "rasengan-error-code-line-arrow" })), _jsx("span", { className: "rasengan-error-code-line-content", children: lineContent })] }, idx));
|
|
59
|
+
}) })] })), activeError.componentStack && (_jsxs("details", { className: "rasengan-error-overlay-section", open: true, children: [_jsx("summary", { className: "rasengan-error-overlay-section-title", children: "Component Stack" }), _jsx("pre", { className: "rasengan-error-overlay-stack", children: activeError.componentStack })] })), activeError.error.stack && (_jsxs("details", { className: "rasengan-error-overlay-section", open: true, children: [_jsx("summary", { className: "rasengan-error-overlay-section-title", children: "Stack Trace" }), _jsx("pre", { className: "rasengan-error-overlay-stack", children: activeError.error.stack })] }))] })] })] }), document.body);
|
|
60
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
import { ErrorBoundaryFallback } from './ErrorBoundaryFallback.js';
|
|
4
|
+
import { errorStore } from './error-store.js';
|
|
5
|
+
import { ErrorOverlay } from './ErrorOverlay.js';
|
|
6
|
+
export function ErrorOverlayProvider({ children, devMode }) {
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (!devMode)
|
|
9
|
+
return;
|
|
10
|
+
const onError = (event) => {
|
|
11
|
+
errorStore.addError(event.error || new Error(event.message), 'global');
|
|
12
|
+
};
|
|
13
|
+
const onRejection = (event) => {
|
|
14
|
+
const error = event.reason instanceof Error
|
|
15
|
+
? event.reason
|
|
16
|
+
: new Error(String(event.reason));
|
|
17
|
+
errorStore.addError(error, 'global');
|
|
18
|
+
};
|
|
19
|
+
window.addEventListener('error', onError);
|
|
20
|
+
window.addEventListener('unhandledrejection', onRejection);
|
|
21
|
+
const hot = import.meta.hot;
|
|
22
|
+
const handleViteError = (data) => {
|
|
23
|
+
if (data?.err) {
|
|
24
|
+
errorStore.addError(data.err, 'vite');
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
if (typeof hot?.on === 'function') {
|
|
28
|
+
hot.on('vite:error', handleViteError);
|
|
29
|
+
}
|
|
30
|
+
return () => {
|
|
31
|
+
window.removeEventListener('error', onError);
|
|
32
|
+
window.removeEventListener('unhandledrejection', onRejection);
|
|
33
|
+
if (typeof hot?.off === 'function') {
|
|
34
|
+
hot.off('vite:error', handleViteError);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}, [devMode]);
|
|
38
|
+
return (_jsxs(_Fragment, { children: [_jsx(ErrorBoundaryFallback, { children: children }), devMode && _jsx(ErrorOverlay, {})] }));
|
|
39
|
+
}
|