react-bun-ssr 0.3.0 → 0.3.1
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 +11 -0
- package/framework/cli/scaffold.ts +183 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,9 +57,15 @@ For the full setup walkthrough, read the installation guide:
|
|
|
57
57
|
`rbssr init` scaffolds a small Bun-first SSR app:
|
|
58
58
|
|
|
59
59
|
```text
|
|
60
|
+
package.json
|
|
61
|
+
tsconfig.json
|
|
62
|
+
.gitignore
|
|
60
63
|
app/
|
|
61
64
|
root.tsx
|
|
65
|
+
root.module.css
|
|
62
66
|
middleware.ts
|
|
67
|
+
public/
|
|
68
|
+
favicon.svg
|
|
63
69
|
routes/
|
|
64
70
|
index.tsx
|
|
65
71
|
api/
|
|
@@ -67,8 +73,13 @@ app/
|
|
|
67
73
|
rbssr.config.ts
|
|
68
74
|
```
|
|
69
75
|
|
|
76
|
+
- `package.json`: Bun scripts and framework/runtime dependencies
|
|
77
|
+
- `tsconfig.json`: starter TypeScript config for Bun + JSX
|
|
78
|
+
- `.gitignore`: minimal app-level ignore rules
|
|
70
79
|
- `app/root.tsx`: document shell and top-level layout
|
|
80
|
+
- `app/root.module.css`: starter CSS Module for layout and base presentation
|
|
71
81
|
- `app/middleware.ts`: global request pipeline hook
|
|
82
|
+
- `app/public/favicon.svg`: starter public asset
|
|
72
83
|
- `app/routes/index.tsx`: first SSR page route
|
|
73
84
|
- `app/routes/api/health.ts`: first API route
|
|
74
85
|
- `rbssr.config.ts`: runtime configuration entrypoint
|
|
@@ -6,6 +6,23 @@ interface ScaffoldFile {
|
|
|
6
6
|
content: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
interface FrameworkPackageManifest {
|
|
10
|
+
version?: string;
|
|
11
|
+
devDependencies?: Record<string, string>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const DEFAULT_FRAMEWORK_VERSION = "0.0.0";
|
|
15
|
+
const DEFAULT_TYPESCRIPT_VERSION = "^5";
|
|
16
|
+
const DEFAULT_BUN_TYPES_VERSION = "latest";
|
|
17
|
+
const DEFAULT_REACT_TYPES_VERSION = "^19";
|
|
18
|
+
const FAVICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96">
|
|
19
|
+
<rect width="96" height="96" rx="20" fill="#111827"/>
|
|
20
|
+
<path d="M25 28h20c13.807 0 25 11.193 25 25S58.807 78 45 78H25V28Zm18 40c8.837 0 16-7.163 16-16s-7.163-16-16-16h-8v32h8Z" fill="#f9fafb"/>
|
|
21
|
+
</svg>
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
let frameworkPackageManifestPromise: Promise<FrameworkPackageManifest> | null = null;
|
|
25
|
+
|
|
9
26
|
async function writeIfMissing(filePath: string, content: string, force: boolean): Promise<void> {
|
|
10
27
|
if (!force && await existsPath(filePath)) {
|
|
11
28
|
return;
|
|
@@ -14,8 +31,115 @@ async function writeIfMissing(filePath: string, content: string, force: boolean)
|
|
|
14
31
|
await writeText(filePath, content);
|
|
15
32
|
}
|
|
16
33
|
|
|
17
|
-
function
|
|
34
|
+
function getFrameworkPackageManifest(): Promise<FrameworkPackageManifest> {
|
|
35
|
+
if (!frameworkPackageManifestPromise) {
|
|
36
|
+
const packageJsonPath = path.resolve(import.meta.dir, "../../package.json");
|
|
37
|
+
frameworkPackageManifestPromise = Bun.file(packageJsonPath).json() as Promise<FrameworkPackageManifest>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return frameworkPackageManifestPromise;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function normalizePackageName(cwd: string): string {
|
|
44
|
+
const baseName = path.basename(path.resolve(cwd));
|
|
45
|
+
const normalized = baseName
|
|
46
|
+
.toLowerCase()
|
|
47
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
48
|
+
.replace(/[._-]{2,}/g, "-")
|
|
49
|
+
.replace(/^[._-]+|[._-]+$/g, "");
|
|
50
|
+
|
|
51
|
+
return normalized || "rbssr-app";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function createPackageJsonContent(options: {
|
|
55
|
+
cwd: string;
|
|
56
|
+
frameworkVersion: string;
|
|
57
|
+
typescriptVersion: string;
|
|
58
|
+
bunTypesVersion: string;
|
|
59
|
+
reactTypesVersion: string;
|
|
60
|
+
reactDomTypesVersion: string;
|
|
61
|
+
}): string {
|
|
62
|
+
const packageJson = {
|
|
63
|
+
name: normalizePackageName(options.cwd),
|
|
64
|
+
version: "0.0.0",
|
|
65
|
+
private: true,
|
|
66
|
+
type: "module",
|
|
67
|
+
scripts: {
|
|
68
|
+
dev: "rbssr dev",
|
|
69
|
+
build: "rbssr build",
|
|
70
|
+
start: "rbssr start",
|
|
71
|
+
typecheck: "bunx tsc --noEmit",
|
|
72
|
+
},
|
|
73
|
+
dependencies: {
|
|
74
|
+
"react-bun-ssr": options.frameworkVersion,
|
|
75
|
+
react: "^19",
|
|
76
|
+
"react-dom": "^19",
|
|
77
|
+
},
|
|
78
|
+
devDependencies: {
|
|
79
|
+
"@types/react": options.reactTypesVersion,
|
|
80
|
+
"@types/react-dom": options.reactDomTypesVersion,
|
|
81
|
+
"bun-types": options.bunTypesVersion,
|
|
82
|
+
typescript: options.typescriptVersion,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return `${JSON.stringify(packageJson, null, 2)}\n`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function createTsconfigContent(): string {
|
|
90
|
+
const tsconfig = {
|
|
91
|
+
compilerOptions: {
|
|
92
|
+
target: "ESNext",
|
|
93
|
+
module: "Preserve",
|
|
94
|
+
moduleResolution: "Bundler",
|
|
95
|
+
jsx: "react-jsx",
|
|
96
|
+
strict: true,
|
|
97
|
+
types: ["bun-types"],
|
|
98
|
+
},
|
|
99
|
+
include: ["app", "rbssr.config.ts"],
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return `${JSON.stringify(tsconfig, null, 2)}\n`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function createGitignoreContent(): string {
|
|
106
|
+
return `node_modules
|
|
107
|
+
dist
|
|
108
|
+
.rbssr
|
|
109
|
+
.env
|
|
110
|
+
.env.local
|
|
111
|
+
.DS_Store
|
|
112
|
+
`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function templateFiles(cwd: string): Promise<ScaffoldFile[]> {
|
|
116
|
+
const frameworkPackage = await getFrameworkPackageManifest();
|
|
117
|
+
const frameworkVersion = frameworkPackage.version ?? DEFAULT_FRAMEWORK_VERSION;
|
|
118
|
+
const typescriptVersion = frameworkPackage.devDependencies?.typescript ?? DEFAULT_TYPESCRIPT_VERSION;
|
|
119
|
+
const bunTypesVersion = frameworkPackage.devDependencies?.["bun-types"] ?? DEFAULT_BUN_TYPES_VERSION;
|
|
120
|
+
const reactTypesVersion = frameworkPackage.devDependencies?.["@types/react"] ?? DEFAULT_REACT_TYPES_VERSION;
|
|
121
|
+
const reactDomTypesVersion = frameworkPackage.devDependencies?.["@types/react-dom"] ?? DEFAULT_REACT_TYPES_VERSION;
|
|
122
|
+
|
|
18
123
|
return [
|
|
124
|
+
{
|
|
125
|
+
filePath: path.join(cwd, "package.json"),
|
|
126
|
+
content: createPackageJsonContent({
|
|
127
|
+
cwd,
|
|
128
|
+
frameworkVersion,
|
|
129
|
+
typescriptVersion,
|
|
130
|
+
bunTypesVersion,
|
|
131
|
+
reactTypesVersion,
|
|
132
|
+
reactDomTypesVersion,
|
|
133
|
+
}),
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
filePath: path.join(cwd, "tsconfig.json"),
|
|
137
|
+
content: createTsconfigContent(),
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
filePath: path.join(cwd, ".gitignore"),
|
|
141
|
+
content: createGitignoreContent(),
|
|
142
|
+
},
|
|
19
143
|
{
|
|
20
144
|
filePath: path.join(cwd, "rbssr.config.ts"),
|
|
21
145
|
content: `import { defineConfig } from "react-bun-ssr";
|
|
@@ -29,14 +153,17 @@ export default defineConfig({
|
|
|
29
153
|
{
|
|
30
154
|
filePath: path.join(cwd, "app/root.tsx"),
|
|
31
155
|
content: `import { Outlet } from "react-bun-ssr/route";
|
|
156
|
+
import styles from "./root.module.css";
|
|
32
157
|
|
|
33
158
|
export default function RootLayout() {
|
|
34
159
|
return (
|
|
35
|
-
<main className=
|
|
36
|
-
<header className=
|
|
160
|
+
<main className={styles.shell}>
|
|
161
|
+
<header className={styles.top}>
|
|
37
162
|
<h1>react-bun-ssr</h1>
|
|
38
163
|
</header>
|
|
39
|
-
<
|
|
164
|
+
<section className={styles.content}>
|
|
165
|
+
<Outlet />
|
|
166
|
+
</section>
|
|
40
167
|
</main>
|
|
41
168
|
);
|
|
42
169
|
}
|
|
@@ -44,6 +171,53 @@ export default function RootLayout() {
|
|
|
44
171
|
export function head() {
|
|
45
172
|
return <title>react-bun-ssr app</title>;
|
|
46
173
|
}
|
|
174
|
+
`,
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
filePath: path.join(cwd, "app/root.module.css"),
|
|
178
|
+
content: `:global(*) {
|
|
179
|
+
box-sizing: border-box;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
:global(html) {
|
|
183
|
+
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
184
|
+
background: linear-gradient(180deg, #f8fafc 0%, #e2e8f0 100%);
|
|
185
|
+
color: #0f172a;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
:global(body) {
|
|
189
|
+
margin: 0;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.shell {
|
|
193
|
+
min-height: 100vh;
|
|
194
|
+
width: min(100%, 72rem);
|
|
195
|
+
margin: 0 auto;
|
|
196
|
+
padding: 3rem 1.5rem 4rem;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.top {
|
|
200
|
+
display: flex;
|
|
201
|
+
align-items: center;
|
|
202
|
+
justify-content: space-between;
|
|
203
|
+
margin-bottom: 2rem;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.top h1 {
|
|
207
|
+
margin: 0;
|
|
208
|
+
font-size: clamp(2rem, 5vw, 3.5rem);
|
|
209
|
+
line-height: 1;
|
|
210
|
+
letter-spacing: -0.04em;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.content {
|
|
214
|
+
padding: 1.5rem;
|
|
215
|
+
border: 1px solid rgba(15, 23, 42, 0.08);
|
|
216
|
+
border-radius: 1.5rem;
|
|
217
|
+
background: rgba(255, 255, 255, 0.84);
|
|
218
|
+
box-shadow: 0 24px 60px rgba(15, 23, 42, 0.12);
|
|
219
|
+
backdrop-filter: blur(18px);
|
|
220
|
+
}
|
|
47
221
|
`,
|
|
48
222
|
},
|
|
49
223
|
{
|
|
@@ -87,11 +261,15 @@ export const middleware: Middleware = async (ctx, next) => {
|
|
|
87
261
|
};
|
|
88
262
|
`,
|
|
89
263
|
},
|
|
264
|
+
{
|
|
265
|
+
filePath: path.join(cwd, "app/public/favicon.svg"),
|
|
266
|
+
content: FAVICON_SVG,
|
|
267
|
+
},
|
|
90
268
|
];
|
|
91
269
|
}
|
|
92
270
|
|
|
93
271
|
export async function scaffoldApp(cwd: string, options: { force: boolean }): Promise<void> {
|
|
94
|
-
for (const file of templateFiles(cwd)) {
|
|
272
|
+
for (const file of await templateFiles(cwd)) {
|
|
95
273
|
await writeIfMissing(file.filePath, file.content, options.force);
|
|
96
274
|
}
|
|
97
275
|
}
|