Skip to content

Commit 92b238a

Browse files
committed
add reload support
1 parent 6fe3a25 commit 92b238a

File tree

5 files changed

+71
-35
lines changed

5 files changed

+71
-35
lines changed

example/build-watch.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import { watchBuild } from "bun-react-ssr/watch";
22
import { doBuild } from "./build";
33

4-
watchBuild(doBuild, ["./hydrate.ts", "./pages"]);
4+
watchBuild(doBuild, ["./hydrate.ts", "./pages", "./components"]);

example/components/Clock.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function Clock({ time }: { time: Date }) {
2+
return <div>Server time: {time.toISOString()}</div>;
3+
}

example/pages/index.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Link, ReloadContext, useLoadingEffect } from "bun-react-ssr/router";
22
import { useContext } from "react";
3+
import { Clock } from "../components/Clock";
34

45
export default function Index({ time }: { time: Date }) {
56
const reload = useContext(ReloadContext);
@@ -8,7 +9,7 @@ export default function Index({ time }: { time: Date }) {
89
});
910
return (
1011
<div>
11-
<div>time {time.toISOString()}</div>
12+
<Clock time={time} />
1213
<Link href="/test?test">index</Link>
1314
<div onClick={() => reload()}>reload</div>
1415
</div>

example/routes.ts

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
11
import { StaticRouters } from "bun-react-ssr";
2+
import { watch } from "node:fs";
23

34
export const router = new StaticRouters(import.meta.dir);
5+
6+
if (Bun.env.NODE_ENV !== "production") {
7+
const watcher = watch("./.build/.meta.json");
8+
watcher.on("change", () => {
9+
console.log("reload");
10+
router.reload();
11+
});
12+
}

index.tsx

+56-33
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,43 @@
11
import { FileSystemRouter } from "bun";
22
import { NJSON } from "next-json";
3-
import { statSync } from "node:fs";
3+
import { readFileSync, statSync } from "node:fs";
44
import { join, relative } from "node:path";
5-
import { preloadModule } from "react-dom";
65
import { renderToReadableStream } from "react-dom/server";
76
import { ClientOnlyError } from "./client";
87
import { MetaContext, PreloadModule } from "./preload";
98

109
export class StaticRouters {
11-
readonly server: FileSystemRouter;
12-
readonly client: FileSystemRouter;
13-
readonly #routes: Map<string, string>;
14-
readonly #routes_dump: string;
15-
readonly #dependencies: Record<string, string[]>;
16-
readonly #hashed: Record<string, string>;
10+
server!: FileSystemRouter;
11+
client!: FileSystemRouter;
12+
#routes!: Map<string, string>;
13+
#routes_dump!: string;
14+
#dependencies!: Record<string, string[]>;
15+
#hashed!: Record<string, string>;
16+
#cached = new Set<string>();
1717

1818
constructor(
1919
public baseDir: string,
2020
public buildDir = ".build",
2121
public pageDir = "pages"
2222
) {
23+
this.reload();
24+
}
25+
26+
reload(excludes: RegExp[] = []) {
27+
const { baseDir, pageDir, buildDir } = this;
28+
const metafile = Bun.fileURLToPath(
29+
import.meta.resolve(join(baseDir, buildDir, ".meta.json"))
30+
);
31+
delete require.cache[metafile];
32+
if (this.#cached.size) {
33+
for (const cached of this.#cached) {
34+
delete require.cache[cached];
35+
for (const dep of scanCacheDependencies(cached, excludes)) {
36+
delete require.cache[dep];
37+
}
38+
}
39+
this.#cached.clear();
40+
}
2341
this.server = new FileSystemRouter({
2442
dir: join(baseDir, pageDir),
2543
style: "nextjs",
@@ -28,7 +46,7 @@ export class StaticRouters {
2846
dir: join(baseDir, buildDir, pageDir),
2947
style: "nextjs",
3048
});
31-
const parsed = require(join(baseDir, buildDir, ".meta.json"));
49+
const parsed = require(metafile);
3250
this.#hashed = parsed.hashed;
3351
this.#dependencies = parsed.dependencies;
3452
this.#routes = new Map(
@@ -83,7 +101,8 @@ export class StaticRouters {
83101
"No client-side script found for server-side component: " +
84102
serverSide.filePath
85103
);
86-
const module = await import(serverSide.filePath);
104+
const module = require(serverSide.filePath);
105+
this.#cached.add(serverSide.filePath);
87106
const result = await module.getServerSideProps?.({
88107
params: serverSide.params,
89108
req: request,
@@ -156,32 +175,36 @@ export class StaticRouters {
156175
}
157176
}
158177

159-
function DirectPreloadModule({
160-
target,
161-
dependencies,
162-
}: {
163-
target: string;
164-
dependencies: Record<string, string[]>;
165-
}) {
166-
preloadModule(target, { as: "script" });
167-
preloadModule(target, { as: "script" });
168-
for (const dep of walkDependencies(target, dependencies)) {
169-
preloadModule(dep, { as: "script" });
170-
preloadModule(dep, { as: "script" });
171-
}
172-
return null;
173-
}
174-
175-
function* walkDependencies(
178+
function* scanCacheDependencies(
176179
target: string,
177-
dependencies: Record<string, string[]>
180+
excludes: RegExp[] = []
178181
): Generator<string> {
179-
if (dependencies[target]) {
180-
for (const dep of dependencies[target]) {
181-
yield dep;
182-
yield* walkDependencies(dep, dependencies);
182+
try {
183+
const imports = new Bun.Transpiler({
184+
loader: target.endsWith(".tsx")
185+
? "tsx"
186+
: target.endsWith(".ts")
187+
? "ts"
188+
: "jsx",
189+
}).scanImports(readFileSync(target));
190+
for (const imp of imports) {
191+
if (imp.kind === "import-statement") {
192+
const path = Bun.fileURLToPath(import.meta.resolve(imp.path, target));
193+
if (
194+
path.includes("/node_modules/") ||
195+
excludes.some((x) => path.match(x))
196+
)
197+
continue;
198+
const resolved = Object.keys(require.cache).find((x) =>
199+
x.startsWith(path)
200+
);
201+
if (resolved) {
202+
yield resolved;
203+
yield* scanCacheDependencies(resolved, excludes);
204+
}
205+
}
183206
}
184-
}
207+
} catch {}
185208
}
186209

187210
export async function serveFromDir(config: {

0 commit comments

Comments
 (0)