Skip to content

Commit

Permalink
feat: refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
infodusha committed May 15, 2024
1 parent 910def3 commit 5a8091e
Show file tree
Hide file tree
Showing 10 changed files with 67 additions and 70 deletions.
3 changes: 2 additions & 1 deletion examples/general/global-server-error.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export default function (err, ctx) {
export default function ServerError(err, ctx) {
console.dir({ err, ctx });
return <h1>Unexpected error</h1>;
}
3 changes: 0 additions & 3 deletions examples/general/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"type": "module",
"private": true,
"dependencies": {
Expand Down
1 change: 1 addition & 0 deletions examples/general/src/app/layout.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export default function Layout({ children }) {
// throw new Error("This is a Layout error");
return (
<html lang="en">
<body>{children}</body>
Expand Down
2 changes: 1 addition & 1 deletion examples/general/src/app/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Comp } from "./Comp";
export const dynamic = 'force-dynamic';

export default async function Page() {
// throw new Error("This is a Page error");
throw new Error("This is a Page error");

function renderTest() {
return <span>Should not be wrapped</span>
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"name": "root",
"version": "0.0.0",
"scripts": {
"dev": "cd examples/general && next dev",
"test": "echo \"Error: no test specified\" && exit 1"
Expand Down
23 changes: 14 additions & 9 deletions packages/next-rsc-error-handler/inserted/capture.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { PHASE_PRODUCTION_BUILD } from "next/constants";
import {
PHASE_PRODUCTION_BUILD,
PHASE_DEVELOPMENT_SERVER,
} from "next/constants";
import { default as globalHandler } from "/global-server-error";
import { isRedirectError } from "next/dist/client/components/redirect";
import { isNotFoundError } from "next/dist/client/components/not-found";

export async function capture(error, ctx) {
export async function capture(error, { options, ...ctx }) {
if (
isNotFoundError(error) ||
isRedirectError(error) ||
Expand All @@ -12,12 +15,14 @@ export async function capture(error, ctx) {
throw error;
}

globalHandler(error, ctx);
const result = globalHandler(error, ctx);

// TODO return a valid response?
return (
<html>
<body>{error.message}</body>
</html>
);
if (
result !== undefined &&
process.env.NEXT_PHASE !== PHASE_DEVELOPMENT_SERVER
) {
return result;
}

throw error;
}
23 changes: 2 additions & 21 deletions packages/next-rsc-error-handler/inserted/wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,12 @@ export function __rscWrapper(fn, ctx) {
const result = originalFunction.apply(self, args);

if (result instanceof Promise) {
Promise.resolve(result).catch((err) => {
void capture(err, ctx);
});

return result.catch(() => {
return (
// FIXME handle error.ts and make it optional
// That helps not to log errors in the console
<html>
<body></body>
</html>
);
});
return result.catch((err) => capture(err, ctx));
}

return result;
} catch (err) {
void capture(err, ctx);

return (
<html>
<body></body>
</html>
);
throw err;
return capture(err, ctx);
}
},
});
Expand Down
3 changes: 0 additions & 3 deletions packages/next-rsc-error-handler/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
"version": "0.0.1",
"description": "Webpack plugin that allow to handle RSC errors on the server side",
"main": "./src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"type": "module",
"repository": {
"type": "git",
Expand Down
3 changes: 2 additions & 1 deletion packages/next-rsc-error-handler/src/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export default function (source) {
const ctx = {
filePath: getRelativePath(resourcePath),
functionName: getFunctionName(p),
options,
};

wasWrapped = true;
Expand Down Expand Up @@ -78,7 +79,7 @@ function getFunctionName(p) {
}
}

return "unknown";
return "(anonymous)";
}

function addImport(ast) {
Expand Down
75 changes: 45 additions & 30 deletions packages/next-rsc-error-handler/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,35 +24,6 @@ export function isReactElement(p) {
return isReactElement;
}

/**
* Wraps FunctionDeclaration or ArrowFunctionExpression with a function call with context.
*/
export function wrapWithFunction(p, wrapFunctionName, context) {
// create a node from the options object
const optionsExpression = t.objectExpression(
Object.entries(context).map(([key, value]) => {
const literalValue =
typeof value === "string"
? t.stringLiteral(value)
: typeof value === "boolean"
? t.booleanLiteral(value)
: typeof value === "number"
? t.numericLiteral(value)
: t.nullLiteral();

return t.objectProperty(t.identifier(key), literalValue);
})
);

if (p.isArrowFunctionExpression()) {
return wrapArrowFunction(p, wrapFunctionName, optionsExpression);
} else if (p.isFunctionDeclaration()) {
return wrapFunctionDeclaration(p, wrapFunctionName, optionsExpression);
} else {
throw new Error("Only arrow functions are supported.");
}
}

const JSX_FN_NAMES =
process.env.NODE_ENV === "production"
? ["_jsx", "_jsxs"]
Expand Down Expand Up @@ -86,6 +57,49 @@ function isReturningJSXElement(p) {
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]) =>
t.objectProperty(t.identifier(key), getOptionsExpressionLiteral(value))
)
);
}

function getOptionsExpressionLiteral(value) {
if (value === null) {
return t.nullLiteral();
}
switch (typeof value) {
case "undefined":
return t.identifier("undefined");
case "string":
return t.stringLiteral(value);
case "boolean":
return t.booleanLiteral(value);
case "number":
return t.numericLiteral(value);
case "object":
return getOptionsExpression(value);
default:
throw new Error(`Unsupported type of value: ${typeof value}`);
}
}

function wrapArrowFunction(p, wrapFunctionName, optionsNode) {
return p.replaceWith(
t.callExpression(t.identifier(wrapFunctionName), [p.node, optionsNode])
Expand All @@ -102,6 +116,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.");
}

Expand All @@ -116,7 +131,7 @@ function wrapFunctionDeclaration(p, wrapFunctionName, argumentsExpression) {
),
]);

if (p?.parentPath?.isExportDefaultDeclaration()) {
if (p.parentPath?.isExportDefaultDeclaration()) {
p.parentPath.replaceWithMultiple([
wrappedFunction,
t.exportDefaultDeclaration(originalFunctionIdentifier),
Expand Down

0 comments on commit 5a8091e

Please sign in to comment.