Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat rsc #6516

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions examples/with-rsc/src/components/Content.server.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use server';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

文件名规范直接修改过来吧


export default function InnerServer() {
return (
<div>
inner server
</div>
);
}
5 changes: 4 additions & 1 deletion examples/with-rsc/src/components/Counter.client.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use client';
import { useState } from 'react';
import styles from './index.module.css';
import { clientPrint } from './EditButton.client';

export default function Counter() {
export default function Counter({ children }) {
const [count, setCount] = useState(0);

function updateCount() {
Expand All @@ -12,6 +13,8 @@ export default function Counter() {
return (
<button className={styles.button} type="button" onClick={updateCount}>
👍🏻 {count}
<div> {clientPrint('clientPrint call')} </div>
{children}
</button>
);
}
4 changes: 4 additions & 0 deletions examples/with-rsc/src/components/EditButton.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ export default function EditButton({ noteId, children }) {
</button>
);
}

export function clientPrint(sentence) {
return sentence;
}
23 changes: 23 additions & 0 deletions examples/with-rsc/src/components/Server.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use server';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Server.tsx 这个文件名可以和其他组件一样,改个语义化的名称吧,不然会以为是段 Server 端的通用逻辑


import EditButton from '@/components/EditButton.client';
import Counter from '@/components/Counter.client';
import InnerServer from '@/components/Content.server';

export default function ServerComp() {
return (
<>
<Counter>
<InnerServer />
</Counter>
<EditButton noteId="editButton">
hello world
</EditButton>
<div> {serverPrint('serverPrint call')} </div>
</>
);
}

export function serverPrint(sentence) {
return sentence;
}
8 changes: 2 additions & 6 deletions examples/with-rsc/src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import { useState } from 'react';
import styles from './index.module.css';
import ServerComp from '@/components/Server';
// import Comments from '@/components/Comments';
// import Footer from '@/components/Footer';
import EditButton from '@/components/EditButton.client';
import Counter from '@/components/Counter.client';

export default function Home() {
console.log('Render: Index');

return (
<div>
<h2>Home Page</h2>
<Counter />
<EditButton noteId="editButton">
hello world
</EditButton>
<ServerComp />
</div>
);
}
3 changes: 3 additions & 0 deletions packages/ice/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
"@swc/helpers": "0.5.1",
"@types/express": "^4.17.14",
"address": "^1.1.2",
"acorn": "^8.10.0",
"acorn-jsx": "^5.3.2",
"recast": "^0.23.4",
"build-scripts": "^2.1.2-0",
"chalk": "^4.0.0",
"commander": "^9.0.0",
Expand Down
86 changes: 81 additions & 5 deletions packages/ice/src/esbuild/rscServerRegister.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,92 @@
import fs from 'fs';
import url from 'url';
import type { Plugin, PluginBuild } from 'esbuild';
import { Parser } from 'acorn';
import jsx from 'acorn-jsx';
import recast from 'recast';

const rscServerRegister = (): Plugin => {
return {
name: 'rsc-server-register',
setup: async (build: PluginBuild) => {
build.onLoad({ filter: /\/src\/.*\.client\.(js|ts|jsx|tsx)$/ }, async (args) => { // /src\/.*\
build.onLoad({ filter: /\/src\/.*\.(js|ts|jsx|tsx)$/ }, async (args) => {
const { path } = args;
const loader = path.endsWith('.tsx') || path.endsWith('.ts') ? 'tsx' : 'jsx';
const content: string = await fs.promises.readFile(path, 'utf-8');

if (content.indexOf('use client') === -1 && content.indexOf('use server') === -1) {
return { contents: content, loader };
}

let body;
try {
// get AST of source code.
body = (Parser.extend(jsx()).parse(content, {
ecmaVersion: 2024,
sourceType: 'module',
}) as any).body;
} catch (x) {
console.error('Error parsing %s %s %s', url, x.message, path);
return { contents: content, loader };
}

let useClient = false;
let useServer = false;
for (let i = 0; i < body.length; i++) {
const node = body[i];
if (node.type !== 'ExpressionStatement' || !node.directive) {
break;
}
if (node.directive === 'use client') {
useClient = true;
}
if (node.directive === 'use server') {
useServer = true;
}
}

if (!useClient && !useServer) {
return { contents: content, loader };
}

if (useClient && useServer) {
throw new Error(
'Cannot have both "use client" and "use server" directives in the same file.',
);
}

let source: string = content;
const moduleId: string = url.pathToFileURL(path).href;
let source = 'const Server: any = require(\'react-server-dom-webpack/server.node\');const createClientModuleProxy = Server.createClientModuleProxy;';
source += transformContent(moduleId);

if (useClient) {
source = `\
const Server: any = require('react-server-dom-webpack/server.node');\n
const createClientModuleProxy = Server.createClientModuleProxy;\n`;
source += transformContent(moduleId);
} else if (useServer) {
source = `\
const Server = require('react-server-dom-webpack/server.node');\n
const registerServerReference = Server.registerServerReference;\n`;
for (let i = 0; i < body.length; i++) {
const node = body[i];
if (node.type === 'ImportDeclaration') {
// concat the 'import' statements.
const { start, end } = node;
source += content.substring(start, end);
} else if (node.type === 'ExportNamedDeclaration' || node.type === 'ExportDefaultDeclaration') {
const { declaration } = node;
// Handling the case where the export is a function.
if (declaration.type === 'FunctionDeclaration') {
source += `var ${declaration.id.name} = registerServerReference(${recast.print(declaration).code}, '${moduleId}', null);\n`;
if (node.type === 'ExportNamedDeclaration') {
source += `module.exports.${declaration.id.name} = ${declaration.id.name};\n`;
} else {
source += `module.exports = ${declaration.id.name};\n`;
}
}
}
}
}
return { contents: source, loader };
});
},
Expand All @@ -19,8 +95,8 @@ const rscServerRegister = (): Plugin => {

function transformContent(moduleId: string) {
const content = `\
const comp = createClientModuleProxy('${moduleId}');
module.exports = comp`;
const comp = createClientModuleProxy('${moduleId}');\n\
module.exports = comp;`;
return content;
}

Expand Down
Loading