Skip to content

Commit

Permalink
Emit client component chunk scripts during SSR
Browse files Browse the repository at this point in the history
Based on facebook/react#27314

This feature requires `AsyncLocalStorage` to be available in the edge
environment via the `node:async_hooks` module. This is the case for
[Vercel](https://vercel.com/docs/functions/edge-functions/edge-runtime#compatible-node.js-modules)
as well as
[Cloudflare](https://developers.cloudflare.com/workers/runtime-apis/nodejs/asynclocalstorage/).

fixes #17
  • Loading branch information
unstubbable committed Oct 30, 2023
1 parent 941ab30 commit f3b68ab
Show file tree
Hide file tree
Showing 22 changed files with 742 additions and 625 deletions.
10 changes: 5 additions & 5 deletions apps/cloudflare-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,24 @@
"@cloudflare/kv-asset-handler": "^0.3.0",
"@mfng/core": "*",
"@mfng/shared-app": "*",
"react": "0.0.0-experimental-c8369527e-20230420",
"react-dom": "0.0.0-experimental-c8369527e-20230420"
"react": "0.0.0-experimental-8039e6d0b-20231026",
"react-dom": "0.0.0-experimental-8039e6d0b-20231026"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20230221.0",
"@jest/globals": "^29.5.0",
"@mfng/webpack-rsc": "*",
"@swc/core": "^1.3.22",
"@types/react": "^18.0.38",
"@types/react-dom": "^18.0.11",
"@types/react": "^18.2.33",
"@types/react-dom": "^18.2.14",
"autoprefixer": "^10.4.14",
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.3",
"cssnano": "^5.1.15",
"mini-css-extract-plugin": "^2.7.5",
"postcss": "^8.4.21",
"postcss-loader": "^7.0.2",
"react-server-dom-webpack": "0.0.0-experimental-c8369527e-20230420",
"react-server-dom-webpack": "0.0.0-experimental-8039e6d0b-20231026",
"resolve-typescript-plugin": "^2.0.0",
"source-map-loader": "^4.0.1",
"swc-loader": "^0.2.3",
Expand Down
1 change: 1 addition & 0 deletions apps/cloudflare-app/src/worker/wrangler.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name = "mfng"
main = "../../dist/worker.js"
compatibility_date = "2023-02-21"
compatibility_flags = ["nodejs_compat"]

[dev]
port = 3000
Expand Down
2 changes: 1 addition & 1 deletion apps/cloudflare-app/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export default function createConfigs(_env, argv) {
],
experiments: {outputModule: true, layers: true},
performance: {maxAssetSize: 1_000_000, maxEntrypointSize: 1_000_000},
externals: [`__STATIC_CONTENT_MANIFEST`],
externals: [`__STATIC_CONTENT_MANIFEST`, `node:async_hooks`],
devtool: `source-map`,
mode,
stats,
Expand Down
4 changes: 2 additions & 2 deletions apps/shared-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
"clsx": "^1.2.1",
"countries-list": "^2.6.1",
"fuse.js": "^6.6.2",
"react": "0.0.0-experimental-c8369527e-20230420",
"react": "0.0.0-experimental-8039e6d0b-20231026",
"react-markdown": "^8.0.5",
"server-only": "^0.0.1",
"zod": "^3.21.4"
},
"devDependencies": {
"@types/react": "^18.0.38",
"@types/react": "^18.2.33",
"tailwindcss": "^3.2.7"
}
}
2 changes: 0 additions & 2 deletions apps/shared-app/src/server/home-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@ import {Suspended} from './suspended.js';
export function HomePage(): JSX.Element {
return (
<Main>
{/* @ts-expect-error (async component) */}
<Hello />
<React.Suspense fallback={<p className="my-3">Loading...</p>}>
{/* @ts-expect-error (async component) */}
<Suspended />
</React.Suspense>
<React.Suspense>
Expand Down
1 change: 0 additions & 1 deletion apps/shared-app/src/server/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export function Routes(): JSX.Element {

switch (pathname) {
case `/slow-page`:
// @ts-expect-error (async component)
return <SlowPage />;
case `/fast-page`:
return <FastPage />;
Expand Down
3 changes: 3 additions & 0 deletions apps/vercel-app/dev-server/global-async-local-storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const AsyncLocalStorage = globalThis.AsyncLocalStorage;

export {AsyncLocalStorage};
18 changes: 17 additions & 1 deletion apps/vercel-app/dev-server/run.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fs from 'fs/promises';
import {AsyncLocalStorage} from 'node:async_hooks';
import path from 'path';
import url from 'url';
import chokidar from 'chokidar';
Expand All @@ -13,7 +14,12 @@ const outfile = path.join(currentDirname, `../dist/dev-server.js`);
const vercelOutputDirname = path.join(currentDirname, `../.vercel/output`);
const staticDirname = path.join(vercelOutputDirname, `static`);
const functionDirname = path.join(vercelOutputDirname, `functions/index.func`);
const runtime = new EdgeRuntime();

const runtime = new EdgeRuntime({
// Define AsyncLocalStorage as a global. Works in conjunction with the
// AsyncLocalStorage plugin below.
extend: (context) => Object.assign(context, {AsyncLocalStorage}),
});

const buildContext = await esbuild.context({
bundle: true,
Expand All @@ -22,6 +28,16 @@ const buildContext = await esbuild.context({
outfile,
format: `esm`,
logLevel: `info`,
plugins: [
{
name: `AsyncLocalStorage plugin`,
setup({onResolve}) {
onResolve({filter: /node:async_hooks/}, () => ({
path: path.join(currentDirname, `global-async-local-storage.js`),
}));
},
},
],
});

const rebuild = debounce(async () => {
Expand Down
12 changes: 6 additions & 6 deletions apps/vercel-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,23 @@
"@mfng/core": "*",
"@mfng/shared-app": "*",
"@vercel/analytics": "^0.1.11",
"react": "0.0.0-experimental-c8369527e-20230420",
"react-dom": "0.0.0-experimental-c8369527e-20230420",
"react": "0.0.0-experimental-8039e6d0b-20231026",
"react-dom": "0.0.0-experimental-8039e6d0b-20231026",
"web-vitals": "^3.3.1"
},
"devDependencies": {
"@edge-runtime/types": "^2.0.8",
"@edge-runtime/types": "^2.2.6",
"@mfng/webpack-rsc": "*",
"@swc/core": "^1.3.22",
"@types/express": "^4.17.17",
"@types/react": "^18.0.38",
"@types/react-dom": "^18.0.11",
"@types/react": "^18.2.33",
"@types/react-dom": "^18.2.14",
"autoprefixer": "^10.4.14",
"chokidar": "^3.5.3",
"copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.3",
"cssnano": "^5.1.15",
"edge-runtime": "^2.1.2",
"edge-runtime": "^2.5.6",
"esbuild": "^0.17.15",
"express": "^4.18.2",
"mini-css-extract-plugin": "^2.7.5",
Expand Down
1 change: 1 addition & 0 deletions apps/vercel-app/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export default function createConfigs(_env, argv) {
],
experiments: {outputModule: true, layers: true},
performance: {maxAssetSize: 1_000_000, maxEntrypointSize: 1_000_000},
externals: [`node:async_hooks`],
devtool: `source-map`,
mode,
stats,
Expand Down
Loading

0 comments on commit f3b68ab

Please sign in to comment.