Error Boundaries Handler (get-error-jsx.js)
Examine the layout-aware error bounds resolver, dynamic boundary cascades, and parallel slot crash isolations.
Key File Location: ./dinou/core/get-error-jsx.js💡 Overview
When a Server Component crashes during runtime, displaying a blank screen or a default server exception is a bad user experience. Dinou implements a directory-crawling error boundary system: if a page crashes, the framework searches for the nearest error.tsx file to display a fallback interface.
The get-error-jsx.js utility resolves these fallbacks. It locates error boundaries, wraps them inside parent layouts, and isolates component failures.
📊 Error Resolution Pipeline
The flowchart below traces layout wrapping and slot-level exception handling:
⚛️ Parallel Slots Isolation
A key feature of the handler is Parallel Slot Crash Isolation. In complex dashboards, a page might render multiple slots in parallel (e.g. @sidebar and @feed):
- Slot-Level Try/Catch: The handler renders each slot in a separate try/catch block. If the feed component crashes (e.g. due to an API timeout), the sidebar and layout render uninterrupted.
- Locating Slot Boundaries: By reading
slotElement.props.__modulePath, the handler resolves the slot's directory and searches for a slot-specificerror.tsx. - Targeted Fallbacks: Replaces the crashed slot with its local error fallback component, preserving the rest of the layout.
⚙️ Code Implementation Details
Below is the core implementation of the slot-level boundary handler inside get-error-jsx.js:
const path = require("path");
const { existsSync } = require("./vfs");
const React = require("react");
const { getFilePathAndDynamicParams } = require("./get-file-path-and-dynamic-params");
const importModule = require("./import-module");
const { asyncRenderJSXToClientJSX } = require("./render-jsx-to-client-jsx");
async function getErrorJSX(reqPath, query, error, isDevelopment = false) {
const srcFolder = path.resolve(process.cwd(), "src");
const reqSegments = reqPath.split("/").filter(Boolean);
let pagePath;
// 1. Search for error.tsx in directory segments
const [filePath, dParams] = getFilePathAndDynamicParams(
reqSegments,
query,
srcFolder,
"error"
);
pagePath = filePath;
let dynamicParams = dParams ?? {};
if (pagePath) {
const pageModule = await importModule(pagePath);
const Page = pageModule.default ?? pageModule;
let jsx = React.createElement(Page, {
params: dynamicParams ?? {},
error,
});
// 2. Crawl parent layouts to wrap the error page
const layouts = getFilePathAndDynamicParams(
reqSegments,
query,
srcFolder,
"layout",
true,
false,
undefined,
0,
{},
true
);
if (layouts && Array.isArray(layouts)) {
for (const [layoutPath, dParams, slots] of layouts.reverse()) {
const layoutModule = await importModule(layoutPath);
const Layout = layoutModule.default ?? layoutModule;
const updatedSlots = {};
// 3. Parallel Slot Error Isolation
for (const [slotName, slotElement] of Object.entries(slots)) {
let updatedSlotElement;
try {
// Test render slot element
await asyncRenderJSXToClientJSX(slotElement);
updatedSlotElement = slotElement;
} catch (e) {
// If slot element rendering fails, resolve its path via __modulePath
const slotFilePath = slotElement.props?.__modulePath;
if (slotFilePath) {
const realSlotFolder = path.dirname(slotFilePath);
// Locate error.tsx inside the slot folder
const [slotErrorPath, slotErrorParams] = getFilePathAndDynamicParams(
reqSegments,
query,
realSlotFolder,
"error",
true,
true,
undefined,
reqSegments.length
);
if (slotErrorPath) {
const slotErrorModule = await importModule(slotErrorPath);
const SlotError = slotErrorModule.default ?? slotErrorModule;
updatedSlotElement = React.createElement(SlotError, {
params: slotErrorParams,
key: slotName,
error: { message: e.message || "Unknown Slot Error" },
});
} else {
updatedSlotElement = null; // Fallback if no slot error boundary
}
} else {
updatedSlotElement = null;
}
} finally {
updatedSlots[slotName] = updatedSlotElement;
}
}
jsx = React.createElement(Layout, { params: dParams, ...updatedSlots }, jsx);
}
}
return jsx;
}
return null;
}
module.exports = { getErrorJSX };