react-client 1.0.6 ā 1.0.8
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 +163 -84
- package/dist/cli/commands/build.js +30 -47
- package/dist/cli/commands/build.ssr.js +31 -54
- package/dist/cli/commands/dev.js +205 -6
- package/dist/cli/commands/init.js +80 -30
- package/dist/cli/commands/preview.js +44 -16
- package/dist/cli/index.js +104 -18
- package/dist/utils/loadConfig.js +111 -0
- package/package.json +68 -34
- package/templates/react/index.html +1 -0
- package/templates/react/package.json +14 -0
- package/templates/react/src/App.jsx +1 -0
- package/templates/react/src/main.jsx +1 -0
- package/templates/react-ssr/index.html +1 -0
- package/templates/react-ssr/package.json +14 -0
- package/templates/react-ssr/src/entry-client.jsx +1 -0
- package/templates/react-ssr/src/entry-server.jsx +1 -0
- package/templates/react-ssr/src/pages/index.jsx +1 -0
- package/templates/react-ssr-ts/index.html +1 -0
- package/templates/react-ssr-ts/package.json +21 -0
- package/templates/react-ssr-ts/src/entry-client.js +5 -0
- package/templates/react-ssr-ts/src/entry-client.tsx +6 -0
- package/templates/react-ssr-ts/src/entry-server.js +4 -0
- package/templates/react-ssr-ts/src/entry-server.tsx +5 -0
- package/templates/react-ssr-ts/src/pages/index.js +2 -0
- package/templates/react-ssr-ts/src/pages/index.tsx +3 -0
- package/templates/react-ssr-ts/tsconfig.json +15 -0
- package/templates/react-tailwind/index.html +1 -0
- package/templates/react-tailwind/package.json +20 -0
- package/templates/react-tailwind/postcss.config.cjs +1 -0
- package/templates/react-tailwind/src/App.jsx +1 -0
- package/templates/react-tailwind/src/main.jsx +1 -0
- package/templates/react-tailwind/tailwind.config.cjs +1 -0
- package/templates/react-tailwind-ts/index.html +1 -0
- package/templates/react-tailwind-ts/package.json +24 -0
- package/templates/react-tailwind-ts/postcss.config.cjs +1 -0
- package/templates/react-tailwind-ts/src/App.js +2 -0
- package/templates/react-tailwind-ts/src/App.tsx +2 -0
- package/templates/react-tailwind-ts/src/index.css +1 -0
- package/templates/react-tailwind-ts/src/main.js +7 -0
- package/templates/react-tailwind-ts/src/main.tsx +8 -0
- package/templates/react-tailwind-ts/tailwind.config.cjs +1 -0
- package/templates/react-tailwind-ts/tsconfig.json +14 -0
- package/templates/react-ts/index.html +1 -0
- package/templates/react-ts/package.json +20 -0
- package/templates/react-ts/src/App.tsx +1 -0
- package/templates/react-ts/src/main.tsx +1 -0
package/README.md
CHANGED
|
@@ -2,140 +2,219 @@
|
|
|
2
2
|
[](https://npm-stat.com/charts.html?package=react-client)
|
|
3
3
|
[](https://github.com/venkateshsundaram/react-client/issues)
|
|
4
4
|
|
|
5
|
-
react-client is a
|
|
5
|
+
**react-client** is a next-generation CLI and runtime for building React apps with instant feedback, fast iteration, and a beautiful developer experience.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Built for simplicity, designed for speed ā”
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## š Table of Contents
|
|
8
12
|
- [Installation](#installation)
|
|
9
13
|
- [With Config](#with-config)
|
|
10
|
-
- [
|
|
11
|
-
- [
|
|
12
|
-
- [
|
|
13
|
-
- [
|
|
14
|
-
* [react-ssr-ts](#react-ssr-ts)
|
|
15
|
-
* [react-tailwind-ts](#react-tailwind-ts)
|
|
16
|
-
- [How the CLI wires features](#how-the-cli-wires-features)
|
|
17
|
-
- [Local testing checklist](#local-testing-checklist)
|
|
14
|
+
- [Available Templates](#available-templates)
|
|
15
|
+
- [Core Features](#core-features)
|
|
16
|
+
- [How It Works](#how-it-works)
|
|
17
|
+
- [Local Development](#local-development)
|
|
18
18
|
- [Troubleshooting](#troubleshooting)
|
|
19
|
-
- [Extending & Contributing](#extending-contributing)
|
|
20
19
|
- [Contributing](#contributing)
|
|
21
20
|
- [Publishing](#publishing)
|
|
22
|
-
- [
|
|
21
|
+
- [Feedback](#feedback)
|
|
23
22
|
- [License](#license)
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
---
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
## š§© Installation
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
Install globally and scaffold your first app:
|
|
30
29
|
|
|
31
30
|
```bash
|
|
32
31
|
npm install -g react-client
|
|
33
32
|
react-client init myapp --template react-ts
|
|
34
33
|
cd myapp
|
|
34
|
+
npm install
|
|
35
35
|
npm run dev
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
This launches the **custom dev server** ā not Vite ā built on **Connect + WebSocket + esbuild**, featuring:
|
|
39
|
+
- Instant rebuilds
|
|
40
|
+
- React Fast Refresh (HMR)
|
|
41
|
+
- Auto port detection & confirmation prompt
|
|
42
|
+
- In-browser overlay with syntax-highlighted code frames
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
---
|
|
41
45
|
|
|
42
|
-
|
|
43
|
-
- `--with-config`: also create a `react-client.config.ts` file in the generated project with a minimal `defineConfig({})` stub.
|
|
46
|
+
## āļø With Config
|
|
44
47
|
|
|
45
|
-
|
|
48
|
+
You can generate a project-level configuration file using `--with-config`.
|
|
46
49
|
|
|
47
50
|
```bash
|
|
48
51
|
react-client init myapp --template react-ts --with-config
|
|
49
|
-
# creates `myapp/` and writes `myapp/react-client.config.ts`
|
|
50
52
|
```
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
Creates:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
// react-client.config.js
|
|
58
|
+
import { defineConfig } from 'react-client/config';
|
|
59
|
+
|
|
60
|
+
export default defineConfig({
|
|
61
|
+
root: './src',
|
|
62
|
+
server: { port: 5173 },
|
|
63
|
+
build: { outDir: '.react-client/build' }
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
ā
Loaded automatically by the CLI
|
|
68
|
+
ā
Type-safe with IntelliSense via `defineConfig()`
|
|
69
|
+
ā
Supports `.js`, `.mjs`, `.ts` (auto-compiled)
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## š§° Available Templates
|
|
74
|
+
|
|
75
|
+
| Template | Description |
|
|
76
|
+
|-----------|-------------|
|
|
77
|
+
| `react` | JavaScript SPA |
|
|
78
|
+
| `react-ts` | TypeScript SPA |
|
|
79
|
+
| `react-ssr` | JavaScript SSR |
|
|
80
|
+
| `react-ssr-ts` | TypeScript SSR |
|
|
81
|
+
| `react-tailwind` | JS + Tailwind |
|
|
82
|
+
| `react-tailwind-ts` | TS + Tailwind |
|
|
83
|
+
|
|
84
|
+
Each template is pre-configured for esbuild, HMR, and fast bootstrapping.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## š Core Features
|
|
89
|
+
|
|
90
|
+
- ā” **Custom Dev Server (no Vite)** ā Connect + WebSocket + esbuild
|
|
91
|
+
- š **React Fast Refresh (HMR)** ā State-preserving reloads
|
|
92
|
+
- š„ **Vite-style Overlay** ā Syntax-highlighted stack frames, clickable file links (`vscode://file`)
|
|
93
|
+
- š **Source Map Stack Mapping** ā Maps runtime errors to original TS/JS source lines
|
|
94
|
+
- š¬ **Auto Port Detection** ā Prompts when default port 5173 is occupied
|
|
95
|
+
- š§ **Smart Config Loader** ā Detects project root, compiles `.ts` configs dynamically
|
|
96
|
+
- šØ **PrismJS Highlighting** ā For pretty overlay code frames
|
|
97
|
+
- š§± **SSR Runtime Support** ā For server-side templates (`react-ssr*`)
|
|
98
|
+
- š§© **Generators** ā Create components, routes, and tests instantly
|
|
99
|
+
- š **Plugin Hook System** ā Extendable with `configResolved`, `transform`, `buildEnd`
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 𧬠How It Works
|
|
104
|
+
|
|
105
|
+
**Under the hood:**
|
|
106
|
+
|
|
107
|
+
1. **esbuild** handles bundling, incremental rebuilds, and sourcemaps.
|
|
108
|
+
2. **Connect** serves files and APIs (React Refresh runtime, overlay, source-map).
|
|
109
|
+
3. **WebSocket** pushes HMR updates and overlay messages.
|
|
110
|
+
4. **Chokidar** watches `/src` for changes and triggers rebuilds.
|
|
111
|
+
5. **Overlay UI** (via PrismJS) displays mapped stack frames with syntax highlighting.
|
|
112
|
+
|
|
113
|
+
---
|
|
53
114
|
|
|
54
|
-
|
|
115
|
+
## š§Ŗ Local Development
|
|
55
116
|
|
|
56
|
-
|
|
117
|
+
To test `react-client` locally:
|
|
57
118
|
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
119
|
+
```bash
|
|
120
|
+
cd ~/Desktop/Workspace/Hoppy-projects/react-client
|
|
121
|
+
npm run build
|
|
122
|
+
npm link
|
|
123
|
+
cd myapp
|
|
124
|
+
react-client dev
|
|
125
|
+
```
|
|
64
126
|
|
|
65
|
-
|
|
127
|
+
If you run it from inside the CLI repo, it auto-detects and switches to `myapp/` as the root.
|
|
66
128
|
|
|
67
|
-
|
|
68
|
-
- PostCSS/Tailwind auto-injection and setup for Tailwind templates
|
|
69
|
-
- CSS Modules support with hashing and AST-based import rewriting
|
|
70
|
-
- Asset hashing and manifest generation for SSR runtime lookups
|
|
71
|
-
- Plugin hook system (configResolved, transform, buildEnd)
|
|
72
|
-
- Generators: component/route/test scaffolding
|
|
73
|
-
- SSR runtime with server that consults manifest.json
|
|
129
|
+
---
|
|
74
130
|
|
|
75
|
-
##
|
|
131
|
+
## š§© Troubleshooting
|
|
76
132
|
|
|
77
|
-
###
|
|
133
|
+
### ā Config not loading
|
|
134
|
+
Make sure `react-client.config.js` exists in your project root (not `.ts`).
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
/Users/<you>/myapp/react-client.config.js
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### ā `react-refresh/runtime` not found
|
|
141
|
+
Install in the CLI or the project:
|
|
142
|
+
```bash
|
|
143
|
+
npm install react-refresh
|
|
144
|
+
```
|
|
78
145
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
146
|
+
### ā ļø Port already in use
|
|
147
|
+
CLI will auto-detect and prompt:
|
|
148
|
+
```
|
|
149
|
+
Port 5173 is occupied. Use 5174 instead? (Y/n)
|
|
150
|
+
```
|
|
84
151
|
|
|
85
|
-
###
|
|
152
|
+
### ā ļø Permission denied
|
|
153
|
+
Ensure your CLI entry file is executable:
|
|
154
|
+
```bash
|
|
155
|
+
chmod +x dist/cli/index.js
|
|
156
|
+
npm link
|
|
157
|
+
```
|
|
86
158
|
|
|
87
|
-
|
|
88
|
-
- `src/index.css` with Tailwind directives
|
|
89
|
-
- `package.json` includes tailwind/postcss devDependencies (install locally in the app)
|
|
159
|
+
---
|
|
90
160
|
|
|
91
|
-
##
|
|
161
|
+
## š§āš» Contributing
|
|
92
162
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
3. **Manifest**: after building, the CLI hashes outputs and writes `client/manifest.json` mapping original -> hashed filenames. The SSR server uses this manifest to serve correct hashed assets and update HTML script tags dynamically.
|
|
96
|
-
4. **PostCSS/Tailwind**: if `postcss.config.cjs` or `tailwind.config.cjs` exists in the project, the build process runs `npx postcss` to process CSS and includes the output in the final client bundle.
|
|
163
|
+
We welcome contributions!
|
|
164
|
+
Read the [Contributing Guide](./CONTRIBUTING.md) for setup instructions.
|
|
97
165
|
|
|
98
|
-
|
|
166
|
+
```bash
|
|
167
|
+
npm run lint
|
|
168
|
+
npm run test
|
|
169
|
+
npm run build
|
|
170
|
+
```
|
|
99
171
|
|
|
100
|
-
|
|
101
|
-
2. Scaffold a new app using the CLI `init` command pointing to these templates.
|
|
102
|
-
3. `npm install` in the new app to get dependencies.
|
|
103
|
-
4. Run `node /path/to/react-client/dist/cli/index.js dev` for HMR-enabled dev server, or `build:ssr` to produce SSR build and run `node dist/server/server.js` to preview.
|
|
172
|
+
---
|
|
104
173
|
|
|
105
|
-
##
|
|
174
|
+
## š Publishing
|
|
106
175
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
- For PostCSS/Tailwind errors, ensure tailwind & postcss installed in the target app:
|
|
112
|
-
```bash
|
|
113
|
-
npm install --save-dev tailwindcss postcss autoprefixer
|
|
114
|
-
```
|
|
115
|
-
- For AST rewriting, ensure `es-module-lexer` and `magic-string` are installed where the CLI runs:
|
|
116
|
-
```bash
|
|
117
|
-
npm install --save-dev es-module-lexer magic-string
|
|
118
|
-
```
|
|
176
|
+
Before publishing:
|
|
177
|
+
1. Update version in `package.json`
|
|
178
|
+
2. Run a full build
|
|
179
|
+
3. Ensure the entry file has execute permission
|
|
119
180
|
|
|
120
|
-
|
|
181
|
+
```bash
|
|
182
|
+
npm run build
|
|
183
|
+
npm publish
|
|
184
|
+
```
|
|
121
185
|
|
|
122
|
-
|
|
123
|
-
-
|
|
186
|
+
Your package now includes:
|
|
187
|
+
- `#!/usr/bin/env node` shebang
|
|
188
|
+
- Auto-detecting config loader
|
|
189
|
+
- Built-in React Refresh runtime
|
|
124
190
|
|
|
125
|
-
|
|
191
|
+
---
|
|
126
192
|
|
|
127
|
-
|
|
193
|
+
## š¬ Feedback
|
|
128
194
|
|
|
129
|
-
|
|
195
|
+
Found an issue or have a feature request?
|
|
196
|
+
š [Open an issue](https://github.com/venkateshsundaram/react-client/issues)
|
|
130
197
|
|
|
131
|
-
|
|
198
|
+
---
|
|
132
199
|
|
|
133
|
-
|
|
200
|
+
## šŖŖ License
|
|
134
201
|
|
|
135
|
-
|
|
202
|
+
**MIT Licensed** Ā© [Venkatesh Sundaram](https://github.com/venkateshsundaram)
|
|
136
203
|
|
|
137
|
-
|
|
204
|
+
---
|
|
138
205
|
|
|
139
|
-
##
|
|
206
|
+
## šŗļø Architecture Overview (Bonus)
|
|
140
207
|
|
|
141
|
-
|
|
208
|
+
```text
|
|
209
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
210
|
+
ā react-client (CLI) ā
|
|
211
|
+
ā āāā esbuild (watch/bundle)
|
|
212
|
+
ā āāā connect (dev server)
|
|
213
|
+
ā āāā websocket (HMR)
|
|
214
|
+
ā āāā prismjs overlay
|
|
215
|
+
ā āāā chokidar (file watch)
|
|
216
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
217
|
+
ā
|
|
218
|
+
ā¼
|
|
219
|
+
Browser ā Live HMR + Overlay
|
|
220
|
+
```
|
|
@@ -7,55 +7,38 @@ exports.default = build;
|
|
|
7
7
|
const esbuild_1 = __importDefault(require("esbuild"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
function hash(buf) {
|
|
13
|
-
return crypto_1.default.createHash('sha256').update(buf).digest('hex').slice(0, 8);
|
|
14
|
-
}
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const loadConfig_1 = require("../../utils/loadConfig");
|
|
15
12
|
async function build() {
|
|
16
13
|
const root = process.cwd();
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
catch (e) {
|
|
42
|
-
console.warn('PostCSS failed', e && e.message);
|
|
43
|
-
}
|
|
14
|
+
const config = await (0, loadConfig_1.loadReactClientConfig)(root);
|
|
15
|
+
const appRoot = path_1.default.resolve(root, config.root || '.');
|
|
16
|
+
const outDir = path_1.default.join(appRoot, config.build?.outDir || '.react-client/build');
|
|
17
|
+
console.log(chalk_1.default.cyan(`\nšļø Building project...`));
|
|
18
|
+
console.log(chalk_1.default.gray(`Root: ${appRoot}`));
|
|
19
|
+
console.log(chalk_1.default.gray(`Output: ${outDir}\n`));
|
|
20
|
+
const entry = path_1.default.join(appRoot, 'src', 'main.tsx');
|
|
21
|
+
if (!fs_extra_1.default.existsSync(entry)) {
|
|
22
|
+
console.error(chalk_1.default.red('ā Entry not found: src/main.tsx'));
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
await fs_extra_1.default.ensureDir(outDir);
|
|
26
|
+
try {
|
|
27
|
+
await esbuild_1.default.build({
|
|
28
|
+
entryPoints: [entry],
|
|
29
|
+
bundle: true,
|
|
30
|
+
minify: true,
|
|
31
|
+
sourcemap: true,
|
|
32
|
+
outdir: outDir,
|
|
33
|
+
define: { 'process.env.NODE_ENV': '"production"' },
|
|
34
|
+
loader: { '.ts': 'ts', '.tsx': 'tsx', '.js': 'jsx', '.jsx': 'jsx' },
|
|
35
|
+
});
|
|
36
|
+
console.log(chalk_1.default.green(`ā
Build completed successfully!`));
|
|
37
|
+
console.log(chalk_1.default.gray(`Output directory: ${outDir}`));
|
|
44
38
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
continue;
|
|
50
|
-
const data = await fs_extra_1.default.readFile(abs);
|
|
51
|
-
const h = hash(data);
|
|
52
|
-
const rel = outPath.replace(/^client\//, '');
|
|
53
|
-
const ext = path_1.default.extname(rel);
|
|
54
|
-
const hashed = rel.replace(ext, '.' + h + ext);
|
|
55
|
-
await fs_extra_1.default.ensureDir(path_1.default.join(out, 'client', path_1.default.dirname(hashed)));
|
|
56
|
-
await fs_extra_1.default.move(abs, path_1.default.join(out, 'client', hashed));
|
|
57
|
-
manifest[rel] = hashed;
|
|
39
|
+
catch (err) {
|
|
40
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
41
|
+
console.error('ā Build failed:', msg);
|
|
42
|
+
process.exit(1);
|
|
58
43
|
}
|
|
59
|
-
await fs_extra_1.default.writeFile(path_1.default.join(out, 'client', 'manifest.json'), JSON.stringify(manifest, null, 2), 'utf8');
|
|
60
|
-
console.log('Built client to', path_1.default.join(out, 'client'));
|
|
61
44
|
}
|
|
@@ -7,63 +7,40 @@ exports.default = buildSsr;
|
|
|
7
7
|
const esbuild_1 = __importDefault(require("esbuild"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
return crypto_1.default.createHash('sha256').update(buf).digest('hex').slice(0, 8);
|
|
13
|
-
}
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const loadConfig_1 = require("../../utils/loadConfig");
|
|
14
12
|
async function buildSsr() {
|
|
15
13
|
const root = process.cwd();
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
const config = await (0, loadConfig_1.loadReactClientConfig)(root);
|
|
15
|
+
const appRoot = path_1.default.resolve(root, config.root || '.');
|
|
16
|
+
const outDir = path_1.default.join(appRoot, config.build?.outDir || '.react-client/ssr');
|
|
17
|
+
console.log(chalk_1.default.cyan(`\nš§± Building SSR bundle...`));
|
|
18
|
+
console.log(chalk_1.default.gray(`Root: ${appRoot}`));
|
|
19
|
+
console.log(chalk_1.default.gray(`Output: ${outDir}\n`));
|
|
20
|
+
const entry = path_1.default.join(appRoot, 'src', 'server.tsx');
|
|
21
|
+
if (!fs_extra_1.default.existsSync(entry)) {
|
|
22
|
+
console.error(chalk_1.default.red('ā SSR entry not found: src/server.tsx'));
|
|
20
23
|
process.exit(1);
|
|
21
24
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
'.
|
|
33
|
-
'.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
bundle: true,
|
|
43
|
-
platform: 'node',
|
|
44
|
-
format: 'cjs',
|
|
45
|
-
outfile: path_1.default.join(out, 'server', 'entry-server.js'),
|
|
46
|
-
external: ['react', 'react-dom'],
|
|
47
|
-
});
|
|
48
|
-
const manifest = {};
|
|
49
|
-
for (const outPath in clientResult.metafile.outputs) {
|
|
50
|
-
if (!outPath.startsWith('client/'))
|
|
51
|
-
continue;
|
|
52
|
-
const abs = path_1.default.join(out, outPath);
|
|
53
|
-
if (!fs_extra_1.default.existsSync(abs))
|
|
54
|
-
continue;
|
|
55
|
-
const data = await fs_extra_1.default.readFile(abs);
|
|
56
|
-
const h = hash(data);
|
|
57
|
-
const rel = outPath.replace(/^client\//, '');
|
|
58
|
-
const ext = path_1.default.extname(rel);
|
|
59
|
-
const hashed = rel.replace(ext, '.' + h + ext);
|
|
60
|
-
await fs_extra_1.default.ensureDir(path_1.default.join(out, 'client', path_1.default.dirname(hashed)));
|
|
61
|
-
await fs_extra_1.default.move(abs, path_1.default.join(out, 'client', hashed));
|
|
62
|
-
manifest[rel] = hashed;
|
|
25
|
+
await fs_extra_1.default.ensureDir(outDir);
|
|
26
|
+
try {
|
|
27
|
+
await esbuild_1.default.build({
|
|
28
|
+
entryPoints: [entry],
|
|
29
|
+
bundle: true,
|
|
30
|
+
platform: 'node',
|
|
31
|
+
format: 'esm',
|
|
32
|
+
target: 'node18',
|
|
33
|
+
external: ['react', 'react-dom'],
|
|
34
|
+
outdir: outDir,
|
|
35
|
+
define: { 'process.env.NODE_ENV': '"production"' },
|
|
36
|
+
loader: { '.ts': 'ts', '.tsx': 'tsx' },
|
|
37
|
+
});
|
|
38
|
+
console.log(chalk_1.default.green(`ā
SSR build completed successfully!`));
|
|
39
|
+
console.log(chalk_1.default.gray(`Output directory: ${outDir}`));
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
43
|
+
console.error('ā SSR build failed:', msg);
|
|
44
|
+
process.exit(1);
|
|
63
45
|
}
|
|
64
|
-
await fs_extra_1.default.writeFile(path_1.default.join(out, 'client', 'manifest.json'), JSON.stringify(manifest, null, 2), 'utf8');
|
|
65
|
-
const runtime = `const http = require('http');const fs = require('fs');const path=require('path');const { render } = require('./entry-server.js');const clientDir = path.join(__dirname,'..','client');const manifest = JSON.parse(fs.readFileSync(path.join(clientDir,'manifest.json'),'utf8'));const PORT = process.env.PORT||3000;const server = http.createServer(async (req,res)=>{ try{ if(req.url && req.url.startsWith('/client/')){ const rel = req.url.replace('/client/',''); const mapped = manifest[rel] || rel; const p = path.join(clientDir, mapped); if(fs.existsSync(p)) { res.writeHead(200); res.end(fs.readFileSync(p)); return; } } const htmlPath = path.join(clientDir,'index.html'); let html = '<!doctype html><html><head></head><body><div id="root"></div><script type="module" src="/client/bundle.js"></script></body></html>'; if(fs.existsSync(htmlPath)) html = fs.readFileSync(htmlPath,'utf8'); const content = await render(req.url||'/'); const outHtml = html.replace('<div id="root"></div>','<div id="root">'+content+'</div>'); res.writeHead(200,{'Content-Type':'text/html'}); res.end(outHtml);}catch(e){ console.error(e); res.writeHead(500); res.end('SSR error');}});server.listen(PORT,()=>console.log('SSR server running on port '+PORT));`;
|
|
66
|
-
await fs_extra_1.default.ensureDir(path_1.default.join(out, 'server'));
|
|
67
|
-
await fs_extra_1.default.writeFile(path_1.default.join(out, 'server', 'server.js'), runtime, 'utf8');
|
|
68
|
-
console.log('SSR build complete at', out);
|
|
69
46
|
}
|