react-bun-ssr 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -132
- package/framework/cli/commands.ts +59 -223
- package/framework/cli/dev-client-watch.ts +281 -0
- package/framework/cli/dev-route-table.ts +71 -0
- package/framework/cli/dev-runtime.ts +382 -0
- package/framework/cli/internal.ts +138 -0
- package/framework/cli/main.ts +27 -31
- package/framework/runtime/build-tools.ts +280 -57
- package/framework/runtime/bun-route-adapter.ts +20 -7
- package/framework/runtime/client-runtime.tsx +1218 -15
- package/framework/runtime/client-transition-core.ts +159 -0
- package/framework/runtime/config.ts +4 -1
- package/framework/runtime/index.ts +6 -0
- package/framework/runtime/io.ts +1 -1
- package/framework/runtime/link.tsx +205 -0
- package/framework/runtime/markdown-headings.ts +54 -0
- package/framework/runtime/markdown-routes.ts +9 -40
- package/framework/runtime/matcher.ts +11 -11
- package/framework/runtime/module-loader.ts +215 -52
- package/framework/runtime/navigation-api.ts +223 -0
- package/framework/runtime/render.tsx +105 -105
- package/framework/runtime/route-api.ts +6 -0
- package/framework/runtime/route-errors.ts +166 -0
- package/framework/runtime/router.ts +80 -0
- package/framework/runtime/runtime-constants.ts +4 -0
- package/framework/runtime/server.ts +713 -145
- package/framework/runtime/tree.tsx +171 -3
- package/framework/runtime/types.ts +81 -3
- package/framework/runtime/utils.ts +9 -5
- package/package.json +19 -5
package/README.md
CHANGED
|
@@ -1,48 +1,45 @@
|
|
|
1
1
|
# react-bun-ssr
|
|
2
2
|
|
|
3
|
-
`react-bun-ssr` is a
|
|
3
|
+
`react-bun-ssr` is a Bun-native SSR React framework with file-based routing, loaders, actions, middleware, streaming, soft navigation, and first-class markdown routes.
|
|
4
4
|
|
|
5
|
-
Documentation: https://react-bun-ssr.fly.dev/docs
|
|
5
|
+
- Documentation: https://react-bun-ssr.fly.dev/docs
|
|
6
|
+
- API reference: https://react-bun-ssr.fly.dev/docs/api/overview
|
|
7
|
+
- Blog: https://react-bun-ssr.fly.dev/blog
|
|
8
|
+
- Repository: https://github.com/react-formation/react-bun-ssr
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
- The framework implementation (`framework/**`, `bin/**`).
|
|
9
|
-
- The official documentation site built with the framework itself (`app/**`).
|
|
10
|
+
## Why react-bun-ssr?
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
`react-bun-ssr` exists for teams that want server-rendered React without starting from Node-first assumptions.
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
- `bin/rbssr.ts`: CLI entrypoint.
|
|
15
|
-
- `app/`: docs web app routes, layouts, middleware, and styles.
|
|
16
|
-
- `app/routes/docs/**/*.md`: hand-authored markdown docs routes.
|
|
17
|
-
- `app/routes/docs/_sidebar.ts`: docs navigation source of truth.
|
|
18
|
-
- `app/routes/docs/api/*.md`: generated API docs.
|
|
19
|
-
- `app/routes/docs/search-index.json`: generated search index.
|
|
20
|
-
- `tests/`: unit + integration tests.
|
|
21
|
-
- `e2e/`: Playwright end-to-end tests.
|
|
14
|
+
It is designed around Bun's runtime, server, bundler, markdown support, and file APIs instead of treating Bun as a compatibility layer. The goal is to stay small enough to understand, but complete enough to use seriously for real SSR applications. The documentation site in this repository is built with the framework itself, so the framework is continuously exercised by its own product surface.
|
|
22
15
|
|
|
23
|
-
##
|
|
16
|
+
## What it includes
|
|
24
17
|
|
|
25
|
-
-
|
|
26
|
-
-
|
|
18
|
+
- [File-based routing](/docs/routing/file-based-routing) for pages, APIs, dynamic params, and markdown routes
|
|
19
|
+
- [Layouts, route groups, and middleware](/docs/routing/layouts-and-groups) with a dedicated [middleware pipeline](/docs/routing/middleware)
|
|
20
|
+
- [Loaders and actions](/docs/data/loaders) for explicit data fetching and mutation flow
|
|
21
|
+
- [Streaming SSR and deferred data](/docs/rendering/streaming-deferred)
|
|
22
|
+
- Soft client transitions with [`Link` and `useRouter`](/docs/routing/navigation)
|
|
23
|
+
- [Bun-first runtime, build, and deployment model](/docs/deployment/bun-deployment)
|
|
24
|
+
- [CSS Modules and static asset support](/docs/styling/css-modules)
|
|
25
|
+
- [Response header config and static caching defaults](/docs/deployment/configuration)
|
|
27
26
|
|
|
28
|
-
##
|
|
27
|
+
## Installation
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
- `node:path` is intentionally retained for robust path resolution behavior.
|
|
32
|
-
- `node:fs` is retained only for `watch` usage in dev mode.
|
|
29
|
+
Prerequisites:
|
|
33
30
|
|
|
34
|
-
|
|
31
|
+
- Bun `>= 1.3.10`
|
|
32
|
+
- `rbssr` available on PATH in the workflow you use to start a new app
|
|
35
33
|
|
|
36
|
-
|
|
34
|
+
Minimal setup:
|
|
37
35
|
|
|
38
36
|
```bash
|
|
37
|
+
bun --version
|
|
38
|
+
mkdir my-app
|
|
39
|
+
cd my-app
|
|
40
|
+
rbssr init
|
|
39
41
|
bun install
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Run docs site in development:
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
bun run docs:dev
|
|
42
|
+
bun run dev
|
|
46
43
|
```
|
|
47
44
|
|
|
48
45
|
Open:
|
|
@@ -51,155 +48,142 @@ Open:
|
|
|
51
48
|
http://127.0.0.1:3000
|
|
52
49
|
```
|
|
53
50
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
bun run docs:build
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Start production server:
|
|
51
|
+
For the full setup walkthrough, read the installation guide:
|
|
61
52
|
|
|
62
|
-
|
|
63
|
-
bun run docs:preview
|
|
64
|
-
```
|
|
53
|
+
- https://react-bun-ssr.fly.dev/docs/start/installation
|
|
65
54
|
|
|
66
|
-
##
|
|
55
|
+
## What `rbssr init` gives you
|
|
67
56
|
|
|
68
|
-
|
|
57
|
+
`rbssr init` scaffolds a small Bun-first SSR app:
|
|
69
58
|
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
59
|
+
```text
|
|
60
|
+
app/
|
|
61
|
+
root.tsx
|
|
62
|
+
middleware.ts
|
|
63
|
+
routes/
|
|
64
|
+
index.tsx
|
|
65
|
+
api/
|
|
66
|
+
health.ts
|
|
67
|
+
rbssr.config.ts
|
|
76
68
|
```
|
|
77
69
|
|
|
78
|
-
|
|
70
|
+
- `app/root.tsx`: document shell and top-level layout
|
|
71
|
+
- `app/middleware.ts`: global request pipeline hook
|
|
72
|
+
- `app/routes/index.tsx`: first SSR page route
|
|
73
|
+
- `app/routes/api/health.ts`: first API route
|
|
74
|
+
- `rbssr.config.ts`: runtime configuration entrypoint
|
|
79
75
|
|
|
80
|
-
|
|
81
|
-
bun run docs:dev
|
|
82
|
-
bun run docs:check
|
|
83
|
-
bun run docs:build
|
|
84
|
-
bun run docs:preview
|
|
85
|
-
```
|
|
76
|
+
The quickest follow-up is:
|
|
86
77
|
|
|
87
|
-
|
|
78
|
+
- https://react-bun-ssr.fly.dev/docs/start/quick-start
|
|
88
79
|
|
|
89
|
-
|
|
90
|
-
- `app/routes/docs/api/*.md`
|
|
91
|
-
- `app/routes/docs/search-index.json`
|
|
80
|
+
## How it works
|
|
92
81
|
|
|
93
|
-
|
|
94
|
-
- `bun run scripts/generate-api-docs.ts`
|
|
95
|
-
- `bun run scripts/build-search-index.ts`
|
|
96
|
-
- or automatically via `bun run docs:build`
|
|
82
|
+
### File-based routing
|
|
97
83
|
|
|
98
|
-
|
|
84
|
+
Routes live under `app/routes`. Page routes, API routes, dynamic params, and markdown routes all share one route tree. Files like `_layout` and `_middleware` participate in routing and request flow without becoming public URL segments.
|
|
99
85
|
|
|
100
|
-
|
|
86
|
+
Read more:
|
|
101
87
|
|
|
102
|
-
|
|
103
|
-
git checkout -b feat/<short-description>
|
|
104
|
-
```
|
|
88
|
+
- https://react-bun-ssr.fly.dev/docs/routing/file-based-routing
|
|
105
89
|
|
|
106
|
-
###
|
|
90
|
+
### Request pipeline
|
|
107
91
|
|
|
108
|
-
-
|
|
109
|
-
- Docs content: update `app/routes/docs/**/*.md`.
|
|
110
|
-
- Nav changes: update `app/routes/docs/_sidebar.ts`.
|
|
111
|
-
- If exports change, regenerate API docs/search index.
|
|
92
|
+
For a page request, the framework resolves the matching route, runs global and nested middleware, executes the matched loader or action, and then renders an HTML response or returns a direct `Response` when the route short-circuits. API routes use the same route tree and middleware model, but return handler responses instead of page HTML.
|
|
112
93
|
|
|
113
|
-
|
|
94
|
+
Read more:
|
|
114
95
|
|
|
115
|
-
|
|
116
|
-
bun
|
|
117
|
-
bun run docs:check
|
|
118
|
-
bun run docs:build
|
|
119
|
-
```
|
|
96
|
+
- https://react-bun-ssr.fly.dev/docs/routing/middleware
|
|
97
|
+
- https://react-bun-ssr.fly.dev/docs/data/loaders
|
|
120
98
|
|
|
121
|
-
###
|
|
99
|
+
### Rendering model
|
|
122
100
|
|
|
123
|
-
|
|
101
|
+
SSR is the default model. HTML responses stream, deferred loader data is supported, and soft client transitions are handled through `Link` and `useRouter`. The docs site in this repository uses the same routing, rendering, markdown, and transition model that framework users get.
|
|
124
102
|
|
|
125
|
-
|
|
103
|
+
Read more:
|
|
126
104
|
|
|
127
|
-
|
|
128
|
-
-
|
|
129
|
-
- Approach and tradeoffs.
|
|
130
|
-
- Testing performed.
|
|
131
|
-
- Screenshots/GIF for docs UI changes (if relevant).
|
|
105
|
+
- https://react-bun-ssr.fly.dev/docs/rendering/streaming-deferred
|
|
106
|
+
- https://react-bun-ssr.fly.dev/docs/routing/navigation
|
|
132
107
|
|
|
133
|
-
|
|
108
|
+
### Bun-first runtime
|
|
134
109
|
|
|
135
|
-
|
|
136
|
-
- `bun run test:unit`
|
|
137
|
-
- `bun run test:integration`
|
|
138
|
-
- `bun run docs:check`
|
|
139
|
-
- `bun run docs:build`
|
|
110
|
+
Bun provides the runtime, server, bundler, markdown support, and file APIs that the framework is built around. `react-bun-ssr` is designed to use those primitives directly instead of layering itself on top of a Node-first base.
|
|
140
111
|
|
|
141
|
-
|
|
112
|
+
Read more:
|
|
142
113
|
|
|
143
|
-
|
|
114
|
+
- https://react-bun-ssr.fly.dev/docs/api/bun-runtime-apis
|
|
144
115
|
|
|
145
|
-
##
|
|
116
|
+
## Core commands
|
|
146
117
|
|
|
147
|
-
|
|
118
|
+
Framework commands:
|
|
148
119
|
|
|
149
|
-
-
|
|
150
|
-
-
|
|
120
|
+
- `rbssr init`: scaffold a new app in the current directory
|
|
121
|
+
- `rbssr dev`: start the Bun dev server
|
|
122
|
+
- `rbssr build`: create production output in `dist/`
|
|
123
|
+
- `rbssr start`: run the built app in production mode
|
|
151
124
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
125
|
+
Repository maintenance commands:
|
|
126
|
+
|
|
127
|
+
- `bun run docs:dev`
|
|
128
|
+
- `bun run docs:check`
|
|
129
|
+
- `bun run docs:build`
|
|
130
|
+
- `bun run test`
|
|
131
|
+
- `bun run typecheck`
|
|
155
132
|
|
|
156
|
-
|
|
133
|
+
CLI reference:
|
|
157
134
|
|
|
158
|
-
|
|
135
|
+
- https://react-bun-ssr.fly.dev/docs/tooling/cli
|
|
159
136
|
|
|
160
|
-
|
|
137
|
+
## Working on this repository
|
|
161
138
|
|
|
162
|
-
|
|
139
|
+
This repository contains both the framework and the official docs site built with it.
|
|
163
140
|
|
|
164
141
|
```bash
|
|
165
|
-
|
|
142
|
+
git clone git@github.com:react-formation/react-bun-ssr.git
|
|
143
|
+
cd react-bun-ssr
|
|
144
|
+
bun install
|
|
145
|
+
bun run docs:dev
|
|
166
146
|
```
|
|
167
147
|
|
|
168
|
-
|
|
148
|
+
That starts the docs site locally using the framework itself.
|
|
169
149
|
|
|
170
|
-
|
|
171
|
-
fly deploy
|
|
172
|
-
```
|
|
150
|
+
## Project layout
|
|
173
151
|
|
|
174
|
-
|
|
152
|
+
- `framework/`: runtime, renderer, route handling, build tooling, and CLI internals
|
|
153
|
+
- `bin/rbssr.ts`: CLI entrypoint
|
|
154
|
+
- `app/`: docs site routes, layouts, middleware, blog, and styles
|
|
155
|
+
- `app/routes/docs/**/*.md`: authored documentation pages
|
|
156
|
+
- `app/routes/blog/*.md`: authored blog posts
|
|
157
|
+
- `scripts/`: generators and validation scripts
|
|
158
|
+
- `tests/`: unit and integration tests
|
|
159
|
+
- `e2e/`: Playwright end-to-end tests
|
|
175
160
|
|
|
176
|
-
|
|
177
|
-
fly status
|
|
178
|
-
fly logs
|
|
179
|
-
```
|
|
161
|
+
## Contributing
|
|
180
162
|
|
|
181
|
-
|
|
163
|
+
Contributions should keep framework behavior, docs, tests, and generated artifacts aligned. For local setup, workflow, validation requirements, and generated-file policy, read [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
182
164
|
|
|
183
|
-
|
|
184
|
-
https://<your-fly-app>.fly.dev/docs/getting-started/introduction
|
|
185
|
-
```
|
|
165
|
+
## Release and deploy
|
|
186
166
|
|
|
187
|
-
|
|
167
|
+
- Pushes to `main` run the main-branch CI gate and deploy automatically to Fly.io.
|
|
168
|
+
- Tags like `v0.1.1-rc.0` publish prereleases to npm under `rc`.
|
|
169
|
+
- Tags like `v0.1.1` publish stable releases to npm under `latest`.
|
|
170
|
+
- The release workflow derives the published package version from the Git tag and rewrites `package.json` in the release job before publishing.
|
|
171
|
+
- npm publishing uses trusted publishing with GitHub OIDC instead of an `NPM_TOKEN`.
|
|
172
|
+
- npm package settings must have a trusted publisher configured for `react-formation / react-bun-ssr / release.yml`.
|
|
188
173
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
```
|
|
174
|
+
## Deploying
|
|
175
|
+
|
|
176
|
+
Fly.io deployment support is already documented and used by this project.
|
|
193
177
|
|
|
194
|
-
|
|
178
|
+
Happy path:
|
|
195
179
|
|
|
196
180
|
```bash
|
|
197
|
-
fly
|
|
181
|
+
fly auth login
|
|
182
|
+
fly deploy
|
|
198
183
|
```
|
|
199
184
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
The workflow includes an optional `deploy-fly` job on `main` push.
|
|
203
|
-
Set this repository secret before enabling production deploys:
|
|
185
|
+
Full deployment docs:
|
|
204
186
|
|
|
205
|
-
-
|
|
187
|
+
- https://react-bun-ssr.fly.dev/docs/deployment/bun-deployment
|
|
188
|
+
- https://react-bun-ssr.fly.dev/docs/deployment/configuration
|
|
189
|
+
- https://react-bun-ssr.fly.dev/docs/deployment/troubleshooting
|
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
import { watch, type FSWatcher } from "node:fs";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import {
|
|
4
3
|
buildRouteManifest,
|
|
5
4
|
bundleClientEntries,
|
|
6
5
|
copyDirRecursive,
|
|
7
6
|
createBuildManifest,
|
|
8
|
-
discoverFileSignature,
|
|
9
7
|
ensureCleanDirectory,
|
|
10
8
|
generateClientEntries,
|
|
11
9
|
} from "../runtime/build-tools";
|
|
12
10
|
import { loadUserConfig, resolveConfig } from "../runtime/config";
|
|
13
|
-
import { ensureDir, existsPath,
|
|
14
|
-
import {
|
|
15
|
-
import
|
|
11
|
+
import { ensureDir, existsPath, writeText, writeTextIfChanged } from "../runtime/io";
|
|
12
|
+
import type { FrameworkConfig, ResolvedConfig } from "../runtime/types";
|
|
13
|
+
import {
|
|
14
|
+
createDevHotEntrypointSource,
|
|
15
|
+
createProductionServerEntrypointSource,
|
|
16
|
+
createTestCommands,
|
|
17
|
+
createTypecheckCommand,
|
|
18
|
+
parseFlags,
|
|
19
|
+
RBSSR_DEV_RESTART_EXIT_CODE,
|
|
20
|
+
} from "./internal";
|
|
16
21
|
import { scaffoldApp } from "./scaffold";
|
|
17
22
|
|
|
18
23
|
function log(message: string): void {
|
|
@@ -20,12 +25,6 @@ function log(message: string): void {
|
|
|
20
25
|
console.log(`[rbssr] ${message}`);
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
function parseFlags(args: string[]): { force: boolean } {
|
|
24
|
-
return {
|
|
25
|
-
force: args.includes("--force"),
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
28
|
async function getConfig(cwd: string): Promise<{ userConfig: FrameworkConfig; resolved: ResolvedConfig }> {
|
|
30
29
|
const userConfig = await loadUserConfig(cwd);
|
|
31
30
|
const resolved = resolveConfig(userConfig, cwd);
|
|
@@ -37,29 +36,7 @@ async function writeProductionServerEntrypoint(options: { distDir: string }): Pr
|
|
|
37
36
|
await ensureDir(serverDir);
|
|
38
37
|
|
|
39
38
|
const serverEntryPath = path.join(serverDir, "server.mjs");
|
|
40
|
-
|
|
41
|
-
import config from "../../rbssr.config.ts";
|
|
42
|
-
import { startHttpServer } from "../../framework/runtime/index.ts";
|
|
43
|
-
|
|
44
|
-
const rootDir = path.resolve(path.dirname(Bun.fileURLToPath(import.meta.url)), "../..");
|
|
45
|
-
process.chdir(rootDir);
|
|
46
|
-
|
|
47
|
-
const manifestPath = path.resolve(rootDir, "dist/manifest.json");
|
|
48
|
-
const manifest = await Bun.file(manifestPath).json();
|
|
49
|
-
|
|
50
|
-
startHttpServer({
|
|
51
|
-
config: {
|
|
52
|
-
...(config ?? {}),
|
|
53
|
-
mode: "production",
|
|
54
|
-
},
|
|
55
|
-
runtimeOptions: {
|
|
56
|
-
dev: false,
|
|
57
|
-
buildManifest: manifest,
|
|
58
|
-
},
|
|
59
|
-
});
|
|
60
|
-
`;
|
|
61
|
-
|
|
62
|
-
await writeText(serverEntryPath, content);
|
|
39
|
+
await writeText(serverEntryPath, createProductionServerEntrypointSource());
|
|
63
40
|
}
|
|
64
41
|
|
|
65
42
|
export async function runInit(args: string[], cwd = process.cwd()): Promise<void> {
|
|
@@ -109,200 +86,64 @@ export async function runBuild(cwd = process.cwd()): Promise<void> {
|
|
|
109
86
|
}
|
|
110
87
|
|
|
111
88
|
export async function runDev(cwd = process.cwd()): Promise<void> {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
let currentServerSnapshotDir = resolved.appDir;
|
|
129
|
-
const reloadListeners = new Set<(nextVersion: number) => void>();
|
|
130
|
-
const docsDir = path.resolve(cwd, "docs");
|
|
131
|
-
|
|
132
|
-
const watchedRoots = [resolved.appDir];
|
|
133
|
-
if (await existsPath(docsDir)) {
|
|
134
|
-
watchedRoots.push(docsDir);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const getSourceSignature = async (): Promise<string> => {
|
|
138
|
-
const signatures = await Promise.all(
|
|
139
|
-
watchedRoots.map(root => discoverFileSignature(root)),
|
|
140
|
-
);
|
|
141
|
-
return signatures.join(":");
|
|
89
|
+
await getConfig(cwd);
|
|
90
|
+
|
|
91
|
+
const generatedDevDir = path.resolve(cwd, ".rbssr/generated/dev");
|
|
92
|
+
const generatedEntryPath = path.join(generatedDevDir, "entry.ts");
|
|
93
|
+
await ensureDir(generatedDevDir);
|
|
94
|
+
await writeTextIfChanged(generatedEntryPath, createDevHotEntrypointSource({
|
|
95
|
+
cwd,
|
|
96
|
+
runtimeModulePath: path.resolve(import.meta.dir, "dev-runtime.ts"),
|
|
97
|
+
}));
|
|
98
|
+
|
|
99
|
+
let activeChild: Bun.Subprocess<"inherit", "inherit", "inherit"> | null = null;
|
|
100
|
+
let shuttingDown = false;
|
|
101
|
+
|
|
102
|
+
const forwardSignal = (signal: NodeJS.Signals): void => {
|
|
103
|
+
shuttingDown = true;
|
|
104
|
+
activeChild?.kill(signal);
|
|
142
105
|
};
|
|
143
106
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const rebuildIfNeeded = async (force = false): Promise<void> => {
|
|
151
|
-
const nextSignature = await getSourceSignature();
|
|
152
|
-
if (!force && nextSignature === signature) {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
signature = nextSignature;
|
|
157
|
-
|
|
158
|
-
const snapshotDir = path.join(serverSnapshotsRoot, `v${version + 1}`);
|
|
159
|
-
const hasDocsSourceDir = await existsPath(docsSourceDir);
|
|
160
|
-
await Promise.all([
|
|
161
|
-
(async () => {
|
|
162
|
-
await ensureCleanDirectory(snapshotDir);
|
|
163
|
-
await copyDirRecursive(resolved.appDir, snapshotDir);
|
|
164
|
-
})(),
|
|
165
|
-
hasDocsSourceDir
|
|
166
|
-
? (async () => {
|
|
167
|
-
await ensureCleanDirectory(docsSnapshotDir);
|
|
168
|
-
await copyDirRecursive(docsSourceDir, docsSnapshotDir);
|
|
169
|
-
})()
|
|
170
|
-
: removePath(docsSnapshotDir),
|
|
171
|
-
]);
|
|
172
|
-
|
|
173
|
-
const snapshotConfig: ResolvedConfig = {
|
|
174
|
-
...resolved,
|
|
175
|
-
appDir: snapshotDir,
|
|
176
|
-
routesDir: path.join(snapshotDir, "routes"),
|
|
177
|
-
rootModule: path.join(snapshotDir, "root.tsx"),
|
|
178
|
-
middlewareFile: path.join(snapshotDir, "middleware.ts"),
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
const manifest = await buildRouteManifest(snapshotConfig);
|
|
182
|
-
const entries = await generateClientEntries({
|
|
183
|
-
config: snapshotConfig,
|
|
184
|
-
manifest,
|
|
185
|
-
generatedDir,
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
await ensureCleanDirectory(devClientDir);
|
|
189
|
-
|
|
190
|
-
routeAssets = await bundleClientEntries({
|
|
191
|
-
entries,
|
|
192
|
-
outDir: devClientDir,
|
|
193
|
-
dev: true,
|
|
194
|
-
publicPrefix: "/__rbssr/client/",
|
|
195
|
-
});
|
|
107
|
+
process.once("SIGINT", () => {
|
|
108
|
+
forwardSignal("SIGINT");
|
|
109
|
+
});
|
|
196
110
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
.filter(entry => entry.isDirectory && /^v\d+$/.test(entry.name))
|
|
201
|
-
.map(entry => entry.name)
|
|
202
|
-
.sort((a, b) => {
|
|
203
|
-
const aNum = Number(a.slice(1));
|
|
204
|
-
const bNum = Number(b.slice(1));
|
|
205
|
-
return bNum - aNum;
|
|
206
|
-
})
|
|
207
|
-
.slice(3);
|
|
208
|
-
await Promise.all(staleVersions.map(stale => removePath(path.join(serverSnapshotsRoot, stale))));
|
|
209
|
-
|
|
210
|
-
version += 1;
|
|
211
|
-
notifyReload();
|
|
212
|
-
log(`rebuilt client assets (version ${version})`);
|
|
213
|
-
};
|
|
111
|
+
process.once("SIGTERM", () => {
|
|
112
|
+
forwardSignal("SIGTERM");
|
|
113
|
+
});
|
|
214
114
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
115
|
+
while (true) {
|
|
116
|
+
activeChild = Bun.spawn({
|
|
117
|
+
cmd: ["bun", "--hot", generatedEntryPath],
|
|
118
|
+
cwd,
|
|
119
|
+
stdin: "inherit",
|
|
120
|
+
stdout: "inherit",
|
|
121
|
+
stderr: "inherit",
|
|
122
|
+
env: {
|
|
123
|
+
...process.env,
|
|
124
|
+
RBSSR_DEV_LAUNCHER: "1",
|
|
125
|
+
RBSSR_DEV_CHILD: "1",
|
|
126
|
+
},
|
|
221
127
|
});
|
|
222
|
-
return task;
|
|
223
|
-
};
|
|
224
128
|
|
|
225
|
-
|
|
129
|
+
const exitCode = await activeChild.exited;
|
|
130
|
+
activeChild = null;
|
|
226
131
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
if (rebuildTimer) {
|
|
230
|
-
clearTimeout(rebuildTimer);
|
|
132
|
+
if (shuttingDown) {
|
|
133
|
+
return;
|
|
231
134
|
}
|
|
232
135
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}, 75);
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
const watchers: FSWatcher[] = [];
|
|
240
|
-
for (const root of watchedRoots) {
|
|
241
|
-
try {
|
|
242
|
-
const watcher = watch(root, { recursive: true }, () => {
|
|
243
|
-
scheduleRebuild();
|
|
244
|
-
});
|
|
245
|
-
watchers.push(watcher);
|
|
246
|
-
} catch {
|
|
247
|
-
log(`recursive file watching unavailable for ${root}; relying on request-time rebuild checks`);
|
|
136
|
+
if (exitCode === RBSSR_DEV_RESTART_EXIT_CODE) {
|
|
137
|
+
log("restarting dev child after config change");
|
|
138
|
+
continue;
|
|
248
139
|
}
|
|
249
|
-
}
|
|
250
140
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
clearTimeout(rebuildTimer);
|
|
254
|
-
rebuildTimer = undefined;
|
|
255
|
-
}
|
|
256
|
-
for (const watcher of watchers) {
|
|
257
|
-
watcher.close();
|
|
141
|
+
if (exitCode !== 0) {
|
|
142
|
+
process.exit(exitCode);
|
|
258
143
|
}
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
process.once("SIGINT", () => {
|
|
262
|
-
cleanup();
|
|
263
|
-
process.exit(0);
|
|
264
|
-
});
|
|
265
144
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
process.exit(0);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
const server = createServer(
|
|
272
|
-
{
|
|
273
|
-
...userConfig,
|
|
274
|
-
mode: "development",
|
|
275
|
-
},
|
|
276
|
-
{
|
|
277
|
-
dev: true,
|
|
278
|
-
getDevAssets: () => routeAssets,
|
|
279
|
-
reloadVersion: () => version,
|
|
280
|
-
subscribeReload: listener => {
|
|
281
|
-
reloadListeners.add(listener);
|
|
282
|
-
return () => {
|
|
283
|
-
reloadListeners.delete(listener);
|
|
284
|
-
};
|
|
285
|
-
},
|
|
286
|
-
resolvePaths: () => ({
|
|
287
|
-
appDir: currentServerSnapshotDir,
|
|
288
|
-
routesDir: path.join(currentServerSnapshotDir, "routes"),
|
|
289
|
-
rootModule: path.join(currentServerSnapshotDir, "root.tsx"),
|
|
290
|
-
middlewareFile: path.join(currentServerSnapshotDir, "middleware.ts"),
|
|
291
|
-
}),
|
|
292
|
-
onBeforeRequest: () => {
|
|
293
|
-
return enqueueRebuild(false);
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
);
|
|
297
|
-
|
|
298
|
-
const bunServer = Bun.serve({
|
|
299
|
-
hostname: resolved.host,
|
|
300
|
-
port: resolved.port,
|
|
301
|
-
fetch: server.fetch,
|
|
302
|
-
development: true,
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
log(`dev server listening on ${bunServer.url}`);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
306
147
|
}
|
|
307
148
|
|
|
308
149
|
export async function runStart(cwd = process.cwd()): Promise<void> {
|
|
@@ -328,16 +169,11 @@ function runSubprocess(cmd: string[]): void {
|
|
|
328
169
|
}
|
|
329
170
|
|
|
330
171
|
export async function runTypecheck(): Promise<void> {
|
|
331
|
-
runSubprocess(
|
|
172
|
+
runSubprocess(createTypecheckCommand());
|
|
332
173
|
}
|
|
333
174
|
|
|
334
175
|
export async function runTest(extraArgs: string[]): Promise<void> {
|
|
335
|
-
|
|
336
|
-
runSubprocess(
|
|
337
|
-
return;
|
|
176
|
+
for (const cmd of createTestCommands(extraArgs)) {
|
|
177
|
+
runSubprocess(cmd);
|
|
338
178
|
}
|
|
339
|
-
|
|
340
|
-
runSubprocess(["bun", "test", "./tests/unit"]);
|
|
341
|
-
runSubprocess(["bun", "test", "./tests/integration"]);
|
|
342
|
-
runSubprocess(["bun", "x", "playwright", "test"]);
|
|
343
179
|
}
|