Skip to content

Commit fd79487

Browse files
committed
initial commit
0 parents  commit fd79487

25 files changed

+994
-0
lines changed

.gitignore

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
2+
3+
# Logs
4+
5+
logs
6+
_.log
7+
npm-debug.log_
8+
yarn-debug.log*
9+
yarn-error.log*
10+
lerna-debug.log*
11+
.pnpm-debug.log*
12+
13+
# Diagnostic reports (https://nodejs.org/api/report.html)
14+
15+
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
16+
17+
# Runtime data
18+
19+
pids
20+
_.pid
21+
_.seed
22+
\*.pid.lock
23+
24+
# Directory for instrumented libs generated by jscoverage/JSCover
25+
26+
lib-cov
27+
28+
# Coverage directory used by tools like istanbul
29+
30+
coverage
31+
\*.lcov
32+
33+
# nyc test coverage
34+
35+
.nyc_output
36+
37+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
38+
39+
.grunt
40+
41+
# Bower dependency directory (https://bower.io/)
42+
43+
bower_components
44+
45+
# node-waf configuration
46+
47+
.lock-wscript
48+
49+
# Compiled binary addons (https://nodejs.org/api/addons.html)
50+
51+
build/Release
52+
53+
# Dependency directories
54+
55+
node_modules/
56+
jspm_packages/
57+
58+
# Snowpack dependency directory (https://snowpack.dev/)
59+
60+
web_modules/
61+
62+
# TypeScript cache
63+
64+
\*.tsbuildinfo
65+
66+
# Optional npm cache directory
67+
68+
.npm
69+
70+
# Optional eslint cache
71+
72+
.eslintcache
73+
74+
# Optional stylelint cache
75+
76+
.stylelintcache
77+
78+
# Microbundle cache
79+
80+
.rpt2_cache/
81+
.rts2_cache_cjs/
82+
.rts2_cache_es/
83+
.rts2_cache_umd/
84+
85+
# Optional REPL history
86+
87+
.node_repl_history
88+
89+
# Output of 'npm pack'
90+
91+
\*.tgz
92+
93+
# Yarn Integrity file
94+
95+
.yarn-integrity
96+
97+
# dotenv environment variable files
98+
99+
.env
100+
.env.development.local
101+
.env.test.local
102+
.env.production.local
103+
.env.local
104+
105+
# parcel-bundler cache (https://parceljs.org/)
106+
107+
.cache
108+
.parcel-cache
109+
110+
# Next.js build output
111+
112+
.next
113+
out
114+
115+
# Nuxt.js build / generate output
116+
117+
.nuxt
118+
dist
119+
120+
# Gatsby files
121+
122+
.cache/
123+
124+
# Comment in the public line in if your project uses Gatsby and not Next.js
125+
126+
# https://nextjs.org/blog/next-9-1#public-directory-support
127+
128+
# public
129+
130+
# vuepress build output
131+
132+
.vuepress/dist
133+
134+
# vuepress v2.x temp and cache directory
135+
136+
.temp
137+
.cache
138+
139+
# Docusaurus cache and generated files
140+
141+
.docusaurus
142+
143+
# Serverless directories
144+
145+
.serverless/
146+
147+
# FuseBox cache
148+
149+
.fusebox/
150+
151+
# DynamoDB Local files
152+
153+
.dynamodb/
154+
155+
# TernJS port file
156+
157+
.tern-port
158+
159+
# Stores VSCode versions used for testing VSCode extensions
160+
161+
.vscode-test
162+
163+
# yarn v2
164+
165+
.yarn/cache
166+
.yarn/unplugged
167+
.yarn/build-state.yml
168+
.yarn/install-state.gz
169+
.pnp.\*
170+
171+
# IntelliJ based IDEs
172+
.idea
173+
174+
# Finder (MacOS) folder config
175+
.DS_Store
176+
177+
178+
example/.build

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
example

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# bun-react-ssr
2+
3+
WIP!
4+
5+
## Features
6+
7+
1. Use bun's native http server / FileSystemRouter / build system
8+
2. React 18 Server Side Rendering
9+
3. Client hydrate support
10+
4. `getServerSideProps`! (note: I did some special processing so that it can be excluded when the client bundle is built, so you don't have to worry about mixing server and client logic.)
11+
12+
## TODO
13+
14+
* app dir mode?
15+
* `"use server"`
16+
17+
## Usage
18+
19+
No docs yet, sorry.
20+
21+
See example folder.
22+
23+
Run example/build.ts to build
24+
Run example/index.ts to start a server

build.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { BunPlugin, fileURLToPath, pathToFileURL } from "bun";
2+
import { readdir, unlink } from "node:fs/promises";
3+
import { basename, join } from "node:path";
4+
5+
function escapeRegExp(string: string) {
6+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
7+
}
8+
9+
// Some bug in bun cause use FileSystemRouter with bun.build got Error: Unexpected
10+
async function* walkDir(path: string): AsyncGenerator<string> {
11+
const dirents = await readdir(path, { withFileTypes: true });
12+
for (const dirent of dirents) {
13+
const finalPath = join(path, dirent.name);
14+
if (dirent.isDirectory()) {
15+
yield* walkDir(finalPath);
16+
} else {
17+
yield finalPath;
18+
}
19+
}
20+
}
21+
22+
export async function build({
23+
baseDir,
24+
buildDir = ".build",
25+
pageDir = "pages",
26+
hydrate,
27+
sourcemap,
28+
minify = Bun.env.NODE_ENV === "production",
29+
define,
30+
plugins,
31+
}: {
32+
baseDir: string;
33+
buildDir?: string;
34+
pageDir?: string;
35+
hydrate: string;
36+
sourcemap?: "external" | "none" | "inline";
37+
minify?: boolean;
38+
define?: Record<string, string>;
39+
plugins?: BunPlugin[];
40+
}) {
41+
const entrypoints = [join(baseDir, hydrate)];
42+
const absPageDir = join(baseDir, pageDir);
43+
for await (const path of walkDir(absPageDir)) {
44+
entrypoints.push(path);
45+
}
46+
const result = await Bun.build({
47+
entrypoints,
48+
sourcemap,
49+
target: "browser",
50+
outdir: join(baseDir, buildDir),
51+
splitting: true,
52+
minify,
53+
define: {
54+
"process.env.NODE_ENV": JSON.stringify(Bun.env.NODE_ENV || "development"),
55+
...define,
56+
},
57+
plugins: [
58+
...(plugins ?? []),
59+
{
60+
name: "bun-react-ssr",
61+
target: "browser",
62+
setup(build) {
63+
build.onLoad(
64+
{
65+
filter: new RegExp(
66+
"^" + escapeRegExp(absPageDir) + "/.*" + "\\.ts[x]$"
67+
),
68+
},
69+
async ({ path, loader }) => {
70+
const search = new URLSearchParams();
71+
search.append("client", "1");
72+
search.append("loader", loader);
73+
return {
74+
contents:
75+
"export { default } from " +
76+
JSON.stringify("./" + basename(path) + "?client"),
77+
loader: "ts",
78+
};
79+
}
80+
);
81+
build.onResolve(
82+
{ filter: /\.ts[x]\?client$/ },
83+
async ({ importer, path }) => {
84+
const url = pathToFileURL(importer);
85+
return {
86+
path: fileURLToPath(new URL(path, url)),
87+
namespace: "client",
88+
};
89+
}
90+
);
91+
build.onLoad(
92+
{ namespace: "client", filter: /\.ts[x]$/ },
93+
async ({ path, loader }) => {
94+
return { contents: await Bun.file(path).text(), loader };
95+
}
96+
);
97+
},
98+
},
99+
],
100+
});
101+
if (result.success) {
102+
for await (const path of walkDir(join(baseDir, buildDir))) {
103+
if (result.outputs.every((x) => x.path !== path)) {
104+
await unlink(path);
105+
}
106+
}
107+
}
108+
return result;
109+
}

bun.lockb

5.96 KB
Binary file not shown.

example/build-watch.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { watchBuild } from "bun-react-ssr/watch";
2+
import { doBuild } from "./build";
3+
4+
watchBuild(doBuild, ["./hydrate.ts", "./pages"]);

example/build.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { build } from "bun-react-ssr/build";
2+
3+
export async function doBuild() {
4+
const result = await build({
5+
baseDir: import.meta.dir,
6+
hydrate: "./hydrate.ts",
7+
});
8+
if (result.logs.length) {
9+
console.log(...result.logs);
10+
} else if (result.success) {
11+
console.log("built", new Date());
12+
}
13+
}
14+
15+
if (import.meta.main) {
16+
doBuild();
17+
}

example/hydrate.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { hydrate } from "bun-react-ssr/hydrate";
2+
import { ExampleShell } from "./shell";
3+
4+
await hydrate(ExampleShell);

example/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { router } from "./routes";
2+
import { ExampleShell } from "./shell";
3+
4+
Bun.serve({
5+
port: "4480",
6+
async fetch(request, server) {
7+
console.log(request.url);
8+
const response = await router.serve(request, {
9+
Shell: ExampleShell,
10+
bootstrapModules: ["/hydrate.js"],
11+
});
12+
if (response) return response;
13+
return new Response("Not found", {
14+
status: 404,
15+
});
16+
},
17+
});

example/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"private": true,
3+
"scripts": {
4+
"build": "bun run build.ts",
5+
"watch": "bun run build-watch.ts",
6+
"serve": "bun run index.ts",
7+
"dev": "bun run --hot index.ts"
8+
}
9+
}

0 commit comments

Comments
 (0)