Skip to content

Commit

Permalink
feat!: do not require use client
Browse files Browse the repository at this point in the history
  • Loading branch information
infodusha committed May 21, 2024
1 parent e3aa4af commit 1ddd254
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 13 deletions.
8 changes: 6 additions & 2 deletions examples/general/src/app/Comp.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
'use client';

import { Inner } from './Inner';

export function Comp() {
throw new Error('Comp error thrown');
return Math.random() > 0.5 ? <div>Comp content</div> : 'Comp content';
// throw new Error('Comp error thrown');
return <div>Comp content <Inner /></div>;
}
5 changes: 5 additions & 0 deletions examples/general/src/app/Inner.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use client';

export function Inner() {
return <div>Inner</div>;
}
2 changes: 2 additions & 0 deletions examples/general/src/app/page.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Comp } from "./Comp";
import { Comp as Comp2 } from "./Comp";
import { Inner } from "./Inner";

export const dynamic = 'force-dynamic';

Expand All @@ -23,6 +24,7 @@ export default async function Page() {
<Comp2 />
{renderTest()}
<ABC />
<Inner />
</div>
);
}
2 changes: 1 addition & 1 deletion packages/next-rsc-error-handler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Webpack plugin that allow to handle RSC errors on the server side.

**This plugin requires all the client components to be marked with `'use client;'`**
**This plugin does not allow dual client and server components**

## Get started

Expand Down
60 changes: 50 additions & 10 deletions packages/next-rsc-error-handler/src/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import parser from "@babel/parser";
import traverse from "@babel/traverse";
import generate from "@babel/generator";
import * as t from "@babel/types";
import path from "node:path";

import {
getRelativePath,
Expand All @@ -14,15 +15,36 @@ import {
const WRAPPER_NAME = "__rscWrapper";
const WRAPPER_PATH = "next-rsc-error-handler/inserted/wrapper";

const clientComponents = new Set();
const serverComponents = new Set();

export default function (source) {
if (isClientComponent(source)) {
const resourcePath = this.resourcePath;
const relativePath = getRelativePath(resourcePath);

if (isRoute(relativePath)) {
return source;
}

const resourcePath = this.resourcePath;
const filePath = getRelativePath(resourcePath);
const noExtRelativePath = dropExtension(relativePath);
const isTrulyClientComponent = isClientComponent(source);

if (isTrulyClientComponent || clientComponents.has(noExtRelativePath)) {
if (!isTrulyClientComponent && serverComponents.has(noExtRelativePath)) {
throw new Error(`${relativePath} is used on both client and server`);
}

const ast = parser.parse(source, {
sourceType: "module",
plugins: ["typescript", "jsx"],
});

traverse.default(ast, {
ImportDeclaration(p) {
clientComponents.add(getImportRelativePath(resourcePath, p));
},
});

if (isRoute(filePath)) {
return source;
}

Expand All @@ -41,17 +63,21 @@ export default function (source) {
}

const ctx = {
filePath,
filePath: relativePath,
componentName: functionName,
};
const optionsExpression = getOptionsExpressionLiteral(ctx);

wasWrapped = true;

wrapFn(p, WRAPPER_NAME, optionsExpression);
}

const innerServerComponents = new Set();

traverse.default(ast, {
ImportDeclaration(p) {
innerServerComponents.add(getImportRelativePath(resourcePath, p));
},
// TODO add FunctionExpression
FunctionDeclaration(p) {
const functionName = p.node.id?.name ?? "";
Expand All @@ -67,19 +93,21 @@ export default function (source) {
return source;
}

innerServerComponents.forEach((c) => serverComponents.add(c));

addImport(ast);
const output = generate.default(ast);

return output.code;
}

function isInApp(resourcePath) {
return /^(src(\/|\\))?app(\/|\\)/.test(resourcePath);
function isInApp(relativePath) {
return /^(src(\/|\\))?app(\/|\\)/.test(relativePath);
}

function isRoute(resourcePath) {
function isRoute(relativePath) {
return (
isInApp(resourcePath) && /(\/|\\)route\.(c|m)?(t|j)s$/.test(resourcePath)
isInApp(relativePath) && /(\/|\\)route\.(c|m)?(t|j)s$/.test(relativePath)
);
}

Expand All @@ -101,3 +129,15 @@ function addImport(ast) {

ast.program.body.unshift(wrapperImport);
}

function dropExtension(relativePath) {
return relativePath.replace(/\.[^/.]+$/, "");
}

function getImportRelativePath(resourcePath, p) {
return dropExtension(
getRelativePath(
path.resolve(path.dirname(resourcePath), p.node.source.value)
)
);
}

0 comments on commit 1ddd254

Please sign in to comment.