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

[WIP] Support RSC #6460

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
5 changes: 3 additions & 2 deletions examples/basic-project/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@
"@types/react-dom": "^18.0.2",
"speed-measure-webpack-plugin": "^1.5.0",
"webpack": "^5.86.0"
}
}
},
"repository": "[email protected]:alibaba/ice.git"
}
2 changes: 2 additions & 0 deletions examples/rsc-project/.browserslistrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
defaults
ios_saf 9
21 changes: 21 additions & 0 deletions examples/rsc-project/ice.config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { defineConfig } from '@ice/app';
import auth from '@ice/plugin-auth';
import SpeedMeasurePlugin from 'speed-measure-webpack-plugin';

export default defineConfig(() => ({
publicPath: '/',
webpack: (webpackConfig) => {
if (process.env.NODE_ENV !== 'test') {
webpackConfig.plugins?.push(new SpeedMeasurePlugin());
}
return webpackConfig;
},
dropLogLevel: 'warn',
plugins: [
auth(),
],
eslint: true,
ssr: false,
ssg: false,
rsc: true,
}));
32 changes: 32 additions & 0 deletions examples/rsc-project/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@examples/basic-project",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "ice start",
"build": "ice build",
"serve": "tsx server.mts"
},
"description": "",
"author": "",
"license": "MIT",
"dependencies": {
"@ice/app": "workspace:*",
"@ice/plugin-auth": "workspace:*",
"@ice/runtime": "workspace:*",
"antd-mobile": "^5.12.6",
"compression": "^1.7.4",
"react": "^0.0.0-experimental-1cea38448-20230530",
"react-dom": "^0.0.0-experimental-1cea38448-20230530",
"react-router-dom": "6.11.2",
"react-server-dom-webpack": "18.3.0-canary-1cea38448-20230530"
},
"devDependencies": {
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"speed-measure-webpack-plugin": "^1.5.0",
"tslib": "^2.5.0",
"tsx": "^3.12.1",
"webpack": "^5.86.0"
}
}
12 changes: 12 additions & 0 deletions examples/rsc-project/src/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineAppConfig } from 'ice';
import { defineAuthConfig } from '@ice/plugin-auth/types';

export const authConfig = defineAuthConfig(() => {
return {
initialAuth: {
admin: true,
},
};
});

export default defineAppConfig(() => ({}));
8 changes: 8 additions & 0 deletions examples/rsc-project/src/components/bar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use client';
export default function Bar() {
return (
<div>
bar
</div>
);
}
6 changes: 6 additions & 0 deletions examples/rsc-project/src/components/testClient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
'use client';
export default function TestClient(props) {
return (
<div>client test {props.name}</div>
);
}
32 changes: 32 additions & 0 deletions examples/rsc-project/src/document.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// import { DocumentClientBody, DocumentClientHead } from './documentClient';
import { Meta, Title, Links, Main, Scripts } from 'ice';
// import RscServerRouter from './rscServerRouter';

// import { Meta, Title, Links, Main, Scripts } from 'ice';

function Document() {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="description" content="ICE Demo" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
{/* <DocumentClientHead /> */}
<Meta />
<Title />
<Links />
</head>
<body>
{/* <DocumentClientBody /> */}
<Main />
<Scripts />
</body>
</html>
// <>
// <DocumentClientHead />
// <DocumentClientBody />
// </>
);
}

export default Document;
21 changes: 21 additions & 0 deletions examples/rsc-project/src/documentClient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// import { Meta, Title, Links, Main, Scripts } from 'ice';

// export function DocumentClientHead() {
// return (
// <>
// <Meta />
// <Title />
// <Links />
// </>
// );
// }

// export function DocumentClientBody() {
// return (
// <>
// <Main />
// <Scripts />
// </>
// );
// }

41 changes: 41 additions & 0 deletions examples/rsc-project/src/pages/about.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// @ts-expect-error
import { Link, useData, useConfig, definePageConfig, defineDataLoader } from 'ice';
import url from './ice.png';

export default function About() {
const data = useData();
const config = useConfig();

console.log('render About', 'data', data, 'config', config);

return (
<>
<h2>About Page</h2>
<Link to="/">home</Link><br />
<Link to="/blog">blog</Link>
<img src={url} height="40" width="40" />
<span className="mark">new</span>
</>
);
}

export const pageConfig = definePageConfig(() => {
return {
title: 'About',
meta: [
{
name: 'theme-color',
content: '#eee',
},
],
links: [{
href: 'https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css',
rel: 'stylesheet',
}],
scripts: [{
src: 'https://cdn.jsdelivr.net/npm/[email protected]/dist/lodash.min.js',
}],
auth: ['admin'],
};
});

18 changes: 18 additions & 0 deletions examples/rsc-project/src/pages/blog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Link, definePageConfig } from 'ice';


export default function Blog() {
return (
<>
<h2>Blog Page</h2>
<Link to="/">home</Link>
</>
);
}

export const pageConfig = definePageConfig(() => {
return {
title: 'Blog',
auth: ['guest'],
};
});
Binary file added examples/rsc-project/src/pages/ice.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions examples/rsc-project/src/pages/index.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.title {
color: red;
}
32 changes: 32 additions & 0 deletions examples/rsc-project/src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Suspense } from 'react';
import { Link, useData, useConfig, definePageConfig, defineDataLoader } from 'ice';
// Not recommended but works.
import styles from './index.module.css';

export default function Home() {
return (
<>
<h2 className={styles.title}>Home Page</h2>
<Link to="/about">about</Link>
<Suspense fallback={<div>hello</div>} />
</>
);
}

export const pageConfig = definePageConfig(() => {
return {
title: 'Home',
meta: [
{
name: 'theme-color',
content: '#000',
},
{
name: 'title-color',
content: '#f00',
},
],
auth: ['admin'],
};
});

28 changes: 28 additions & 0 deletions examples/rsc-project/src/pages/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Outlet, useData, useConfig, definePageConfig, defineDataLoader } from 'ice';

export default () => {
const data = useData();
const config = useConfig();

console.log('render Layout', 'data', data, 'config', config);

return (
<div>
<h1>Layout</h1>
<Outlet />
</div>
);
};

export const pageConfig = definePageConfig(() => {
return {
title: 'Layout',
meta: [
{
name: 'layout-color',
content: '#f00',
},
],
auth: ['admin'],
};
});
84 changes: 84 additions & 0 deletions examples/rsc-project/src/rscServerRouter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
'use client';
import { RouteErrorComponent, WrapRouteComponent, createRouteLoader } from '@ice/runtime';
import { createStaticRouter } from 'react-router-dom/server.mjs';
import { createServerRoutes } from '@ice/runtime';
import { useAppContext } from '@ice/runtime';
import React from 'react';
import { AppErrorBoundary } from '@ice/runtime';
import TestClient from './components/testClient';

async function generateRoutesDefinition(nestRouteManifest, renderMode) {
const routeDefinitionObj: any[] = [];
console.log('generateRoutesDefinition', nestRouteManifest);

for (const route of nestRouteManifest) {
const { children, path: routePath, index, componentName, file, id, layout, exports } = route;
const componentModule = await import('@/pages/layout');
console.log('componentModule', componentModule);

const currentRouteDefinition = {
path: routePath,
async lazy() {
return {
...componentModule,
Component: () => WrapRouteComponent({
routeId: id,
isLayout: layout,
routeExports: componentModule,
}),
loader: createRouteLoader({
routeId: id,
renderMode,
module: componentModule,
}),
};
},
errorElement: <RouteErrorComponent />,
componentName,
index,
id,
exact: true,
exports: JSON.stringify(exports),
layout,
};

if (children) {
const res = generateRoutesDefinition(children, renderMode);
currentRouteDefinition['children'] = res;
}
routeDefinitionObj.push(currentRouteDefinition);
console.log('after push', routeDefinitionObj);
}

return routeDefinitionObj;
}

export default async function RscServerRouter(props) {
// the routes object here need to be a sierilazable object
const { routerContext, routes: routeManifest, requestContext, renderMode } = props;
const routes = await generateRoutesDefinition(routeManifest, renderMode);
// Server router only be called once.
const router = createStaticRouter(createServerRoutes(routes), routerContext);

return (
<>
<TestClient name={'RCC props'} />
<div>Server component: rscServerRouter</div>
</>
);
}

function App({ children }) {
const { appConfig } = useAppContext();
const { strict, errorBoundary } = appConfig.app;
const StrictMode = strict ? React.StrictMode : React.Fragment;
const ErrorBoundary = errorBoundary ? AppErrorBoundary : React.Fragment;

return (
<StrictMode>
<ErrorBoundary>
{children}
</ErrorBoundary>
</StrictMode>
);
}
6 changes: 6 additions & 0 deletions examples/rsc-project/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface AppData {
title: string;
auth: {
[key: string]: boolean;
};
}
1 change: 1 addition & 0 deletions examples/rsc-project/src/typings.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="@ice/app/types" />
Loading