diff --git a/examples/general/src/app/page.jsx b/examples/general/src/app/page.jsx
index 4b51e57..459db49 100644
--- a/examples/general/src/app/page.jsx
+++ b/examples/general/src/app/page.jsx
@@ -1,4 +1,5 @@
import { Comp } from "./Comp";
+import { Comp as Comp2 } from "./Comp";
export const dynamic = 'force-dynamic';
@@ -6,15 +7,22 @@ export default async function Page() {
// throw new Error("This is a Page error");
function renderTest() {
- throw new Error("This is a render method error");
- // return Should not be wrapped
+ // throw new Error("This is a render method error");
+ return Should not be wrapped
+ }
+
+ function ABC() {
+ // throw new Error("This is a ABC error");
+ return
ABC
}
return (
);
}
diff --git a/packages/next-rsc-error-handler/src/index.d.ts b/packages/next-rsc-error-handler/src/index.d.ts
index 3833acc..1b59daa 100644
--- a/packages/next-rsc-error-handler/src/index.d.ts
+++ b/packages/next-rsc-error-handler/src/index.d.ts
@@ -1,6 +1,8 @@
import { NextConfig } from "next";
-export interface RscErrorHandlerOptions {}
+export interface RscErrorHandlerOptions {
+ componentName: RegExp;
+}
export function rscErrorHandler(
options: RscErrorHandlerOptions
@@ -8,8 +10,7 @@ export function rscErrorHandler(
export interface GlobalServerErrorContext {
filePath: string;
- functionName: string;
- options: RscErrorHandlerOptions;
+ componentName: string;
}
export type GlobalServerError = (
diff --git a/packages/next-rsc-error-handler/src/index.js b/packages/next-rsc-error-handler/src/index.js
index 636f586..26365a8 100644
--- a/packages/next-rsc-error-handler/src/index.js
+++ b/packages/next-rsc-error-handler/src/index.js
@@ -1,4 +1,6 @@
-const defaultOptions = {};
+const defaultOptions = {
+ componentName: /^[A-Z]/,
+};
export function rscErrorHandler(options = {}) {
return function withLoader(nextConfig = {}) {
diff --git a/packages/next-rsc-error-handler/src/loader.js b/packages/next-rsc-error-handler/src/loader.js
index 4ae0b4f..71708ac 100644
--- a/packages/next-rsc-error-handler/src/loader.js
+++ b/packages/next-rsc-error-handler/src/loader.js
@@ -6,8 +6,9 @@ import * as t from "@babel/types";
import {
getRelativePath,
isClientComponent,
- isReactElement,
- wrapWithFunction,
+ wrapFunctionDeclaration,
+ wrapArrowFunction,
+ getOptionsExpressionLiteral,
} from "./utils.js";
const WRAPPER_NAME = "__rscWrapper";
@@ -28,26 +29,31 @@ export default function (source) {
let wasWrapped = false;
- traverse.default(ast, {
- enter(p) {
- if (!p.isFunctionDeclaration() && !p.isArrowFunctionExpression()) {
- return;
- }
+ function wrapIfComponent(functionName, p, wrapFn) {
+ if (!options.componentName.test(functionName)) {
+ return;
+ }
- if (!isReactElement(p)) {
- return;
- }
+ const ctx = {
+ filePath: getRelativePath(resourcePath),
+ componentName: functionName,
+ };
+ const optionsExpression = getOptionsExpressionLiteral(ctx);
- const ctx = {
- filePath: getRelativePath(resourcePath),
- functionName: getFunctionName(p),
- options,
- };
+ wasWrapped = true;
- wasWrapped = true;
- wrapWithFunction(p, WRAPPER_NAME, ctx);
+ wrapFn(p, WRAPPER_NAME, optionsExpression);
+ }
- p.skip();
+ traverse.default(ast, {
+ // TODO add FunctionExpression
+ FunctionDeclaration(p) {
+ const functionName = p.node.id?.name ?? "";
+ wrapIfComponent(functionName, p, wrapFunctionDeclaration);
+ },
+ ArrowFunctionExpression(p) {
+ const functionName = getArrowFunctionName(p);
+ wrapIfComponent(functionName, p, wrapArrowFunction);
},
});
@@ -59,19 +65,14 @@ export default function (source) {
return output.code;
}
-function getFunctionName(p) {
- if (p.isFunctionDeclaration()) {
- return p.node.id?.name;
- }
-
+function getArrowFunctionName(p) {
if (p.isArrowFunctionExpression()) {
const parent = p.parentPath;
if (parent.isVariableDeclarator() && parent.node.id.type === "Identifier") {
return parent.node.id.name;
}
}
-
- return "(anonymous)";
+ return "";
}
function addImport(ast) {
diff --git a/packages/next-rsc-error-handler/src/utils.js b/packages/next-rsc-error-handler/src/utils.js
index 019ddf0..42ec8be 100644
--- a/packages/next-rsc-error-handler/src/utils.js
+++ b/packages/next-rsc-error-handler/src/utils.js
@@ -10,64 +10,6 @@ export function isClientComponent(source) {
return source.includes("__next_internal_client_entry_do_not_use__");
}
-/**
- * Return true if node is a function and returns jsx.
- * @param {Path} p
- * @returns {boolean}
- */
-export function isReactElement(p) {
- let isReactElement = false;
- if (p.isFunctionDeclaration() || p.isArrowFunctionExpression()) {
- isReactElement = isReturningJSXElement(p);
- }
-
- return isReactElement;
-}
-
-const JSX_FN_NAMES =
- process.env.NODE_ENV === "production"
- ? ["_jsx", "_jsxs"]
- : ["_jsxDEV", "_jsxsDEV"];
-
-/**
- * Helper function that returns true if node returns a JSX element.
- * @param {Path} p
- * @returns {boolean}
- */
-function isReturningJSXElement(p) {
- let foundJSX = false;
-
- p.traverse({
- CallExpression(innerP) {
- const calleePath = innerP.get("callee");
- if (
- t.isIdentifier(calleePath.node) &&
- JSX_FN_NAMES.includes(calleePath.node.name)
- ) {
- foundJSX = true;
- innerP.stop();
- }
- },
- });
-
- return foundJSX;
-}
-
-/**
- * Wraps FunctionDeclaration or ArrowFunctionExpression with a function call with context.
- */
-export function wrapWithFunction(p, wrapFunctionName, context) {
- const optionsExpression = getOptionsExpressionLiteral(context);
-
- if (p.isArrowFunctionExpression()) {
- return wrapArrowFunction(p, wrapFunctionName, optionsExpression);
- } else if (p.isFunctionDeclaration()) {
- return wrapFunctionDeclaration(p, wrapFunctionName, optionsExpression);
- } else {
- throw new Error("Unsupported type of function");
- }
-}
-
function getOptionsExpression(obj) {
return t.objectExpression(
Object.entries(obj).map(([key, value]) =>
@@ -76,7 +18,7 @@ function getOptionsExpression(obj) {
);
}
-function getOptionsExpressionLiteral(value) {
+export function getOptionsExpressionLiteral(value) {
if (value === null) {
return t.nullLiteral();
}
@@ -96,13 +38,17 @@ function getOptionsExpressionLiteral(value) {
}
}
-function wrapArrowFunction(p, wrapFunctionName, optionsNode) {
+export function wrapArrowFunction(p, wrapFunctionName, optionsNode) {
return p.replaceWith(
t.callExpression(t.identifier(wrapFunctionName), [p.node, optionsNode])
);
}
-function wrapFunctionDeclaration(p, wrapFunctionName, argumentsExpression) {
+export function wrapFunctionDeclaration(
+ p,
+ wrapFunctionName,
+ argumentsExpression
+) {
const expression = t.functionExpression(
null,
p.node.params,
@@ -112,8 +58,7 @@ function wrapFunctionDeclaration(p, wrapFunctionName, argumentsExpression) {
);
if (p.node.id == null) {
- // FIXME should work no matter if there is no function name
- throw new Error("FunctionDeclaration has no id.");
+ throw new Error("FunctionDeclaration has no name");
}
const originalFunctionIdentifier = t.identifier(p.node.id.name);