@zap-js/client 0.0.1 → 0.0.4
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 +310 -24
- package/bin/zap +0 -0
- package/bin/zap-codegen +0 -0
- package/dist/cli/commands/build.d.ts +11 -0
- package/dist/cli/commands/build.js +282 -0
- package/dist/cli/commands/codegen.d.ts +8 -0
- package/dist/cli/commands/codegen.js +95 -0
- package/dist/cli/commands/dev.d.ts +20 -0
- package/dist/cli/commands/dev.js +78 -0
- package/dist/cli/commands/new.d.ts +9 -0
- package/dist/cli/commands/new.js +307 -0
- package/dist/cli/commands/routes-old.d.ts +9 -0
- package/dist/cli/commands/routes-old.js +106 -0
- package/dist/cli/commands/routes.d.ts +11 -0
- package/dist/cli/commands/routes.js +280 -0
- package/dist/cli/commands/serve.d.ts +17 -0
- package/dist/cli/commands/serve.js +386 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +76 -0
- package/dist/cli/utils/index.d.ts +2 -0
- package/dist/cli/utils/index.js +2 -0
- package/dist/cli/utils/logger.d.ts +84 -0
- package/dist/cli/utils/logger.js +181 -0
- package/dist/cli/utils/port-finder.d.ts +8 -0
- package/dist/cli/utils/port-finder.js +48 -0
- package/dist/dev-server/codegen-runner.d.ts +41 -0
- package/dist/dev-server/codegen-runner.js +172 -0
- package/dist/dev-server/hot-reload.d.ts +72 -0
- package/dist/dev-server/hot-reload.js +280 -0
- package/dist/dev-server/index.d.ts +8 -0
- package/dist/dev-server/index.js +8 -0
- package/dist/dev-server/route-scanner.d.ts +71 -0
- package/dist/dev-server/route-scanner.js +114 -0
- package/dist/dev-server/rust-builder.d.ts +66 -0
- package/dist/dev-server/rust-builder.js +286 -0
- package/dist/dev-server/server.d.ts +147 -0
- package/dist/dev-server/server.js +658 -0
- package/dist/dev-server/vite-proxy.d.ts +56 -0
- package/dist/dev-server/vite-proxy.js +212 -0
- package/dist/dev-server/watcher.d.ts +48 -0
- package/dist/dev-server/watcher.js +127 -0
- package/dist/router/codegen-enhanced.d.ts +5 -0
- package/dist/router/codegen-enhanced.js +275 -0
- package/dist/router/codegen.d.ts +17 -0
- package/dist/router/codegen.js +654 -0
- package/dist/router/index.d.ts +16 -0
- package/dist/router/index.js +19 -0
- package/dist/router/scanner.d.ts +86 -0
- package/dist/router/scanner.js +689 -0
- package/dist/router/ssg.d.ts +115 -0
- package/dist/router/ssg.js +202 -0
- package/dist/router/types.d.ts +124 -0
- package/dist/router/types.js +9 -0
- package/dist/router/watch.d.ts +38 -0
- package/dist/router/watch.js +135 -0
- package/dist/runtime/csrf.d.ts +146 -0
- package/dist/runtime/csrf.js +166 -0
- package/dist/runtime/error-boundary.d.ts +129 -0
- package/dist/runtime/error-boundary.js +287 -0
- package/dist/runtime/hooks.d.ts +83 -0
- package/dist/runtime/hooks.js +96 -0
- package/dist/runtime/index.d.ts +229 -0
- package/dist/runtime/index.js +449 -0
- package/dist/runtime/ipc-client.d.ts +144 -0
- package/dist/runtime/ipc-client.js +621 -0
- package/dist/runtime/logger.d.ts +71 -0
- package/dist/runtime/logger.js +164 -0
- package/dist/runtime/middleware.d.ts +66 -0
- package/dist/runtime/middleware.js +114 -0
- package/dist/runtime/process-manager.d.ts +51 -0
- package/dist/runtime/process-manager.js +207 -0
- package/dist/runtime/router-simple.d.ts +98 -0
- package/dist/runtime/router-simple.js +330 -0
- package/dist/runtime/router.d.ts +103 -0
- package/dist/runtime/router.js +435 -0
- package/dist/runtime/rpc-client.d.ts +35 -0
- package/dist/runtime/rpc-client.js +140 -0
- package/dist/runtime/streaming-utils.d.ts +86 -0
- package/dist/runtime/streaming-utils.js +150 -0
- package/dist/runtime/types.d.ts +465 -0
- package/dist/runtime/types.js +60 -0
- package/dist/runtime/websockets-utils.d.ts +50 -0
- package/dist/runtime/websockets-utils.js +92 -0
- package/package.json +31 -21
- package/index.js +0 -29
- package/internal/cli/package.json +0 -46
- package/internal/cli/tsconfig.tsbuildinfo +0 -1
- package/internal/dev-server/node_modules/ora/index.d.ts +0 -332
- package/internal/dev-server/node_modules/ora/index.js +0 -416
- package/internal/dev-server/node_modules/ora/license +0 -9
- package/internal/dev-server/node_modules/ora/node_modules/string-width/index.d.ts +0 -36
- package/internal/dev-server/node_modules/ora/node_modules/string-width/index.js +0 -65
- package/internal/dev-server/node_modules/ora/node_modules/string-width/license +0 -9
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/LICENSE-MIT.txt +0 -20
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/README.md +0 -107
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/index.d.ts +0 -3
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/index.js +0 -4
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/index.mjs +0 -4
- package/internal/dev-server/node_modules/ora/node_modules/string-width/node_modules/emoji-regex/package.json +0 -46
- package/internal/dev-server/node_modules/ora/node_modules/string-width/package.json +0 -60
- package/internal/dev-server/node_modules/ora/node_modules/string-width/readme.md +0 -62
- package/internal/dev-server/node_modules/ora/package.json +0 -66
- package/internal/dev-server/node_modules/ora/readme.md +0 -325
- package/internal/dev-server/package.json +0 -41
- package/internal/router/package.json +0 -28
- package/internal/runtime/package.json +0 -41
- package/internal/runtime/src/error-boundary.tsx +0 -476
- package/internal/runtime/src/router-simple.tsx +0 -640
- package/internal/runtime/src/router.tsx +0 -771
- package/internal/runtime/tsconfig.tsbuildinfo +0 -1
- package/src/errors.js +0 -33
- package/src/logger.js +0 -10
- package/src/middleware.js +0 -32
- package/src/router.js +0 -41
- package/src/types.js +0 -39
package/README.md
CHANGED
|
@@ -1,44 +1,330 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @zapjs/client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Official CLI and development tools for ZapJS - the fullstack Rust + React framework.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
+
### Global Installation (Recommended)
|
|
8
|
+
|
|
9
|
+
Install the ZapJS CLI globally to use the `zap` command anywhere:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @zapjs/client
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
After installation, verify it works:
|
|
16
|
+
|
|
7
17
|
```bash
|
|
8
|
-
|
|
18
|
+
zap --version
|
|
19
|
+
zap --help
|
|
9
20
|
```
|
|
10
21
|
|
|
11
|
-
|
|
22
|
+
### Local Project Installation
|
|
12
23
|
|
|
13
|
-
|
|
14
|
-
import { router, middleware, errors, logger } from '@zap-js/client'
|
|
24
|
+
For project-specific installation:
|
|
15
25
|
|
|
16
|
-
|
|
17
|
-
|
|
26
|
+
```bash
|
|
27
|
+
npm install --save-dev @zapjs/client
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Then use via npm scripts in your `package.json`:
|
|
18
31
|
|
|
19
|
-
|
|
20
|
-
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"scripts": {
|
|
35
|
+
"dev": "zap dev",
|
|
36
|
+
"build": "zap build",
|
|
37
|
+
"serve": "zap serve"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Local Development (Contributors)
|
|
21
43
|
|
|
22
|
-
|
|
23
|
-
const { ErrorBoundary } = errors
|
|
44
|
+
If you're contributing to ZapJS:
|
|
24
45
|
|
|
25
|
-
|
|
26
|
-
|
|
46
|
+
```bash
|
|
47
|
+
cd packages/client
|
|
48
|
+
npm install
|
|
49
|
+
npm run build
|
|
50
|
+
npm link
|
|
27
51
|
```
|
|
28
52
|
|
|
29
|
-
|
|
53
|
+
This creates a global symlink for local testing. The `zap` command will now use your local development version.
|
|
54
|
+
|
|
55
|
+
## Available Commands
|
|
56
|
+
|
|
57
|
+
### `zap new <name>`
|
|
58
|
+
|
|
59
|
+
Create a new ZapJS project with the specified template.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
zap new my-app
|
|
63
|
+
zap new my-app --template fullstack
|
|
64
|
+
zap new my-app --no-install --no-git
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Options:**
|
|
68
|
+
- `-t, --template <template>` - Template to use: `basic` or `fullstack` (default: `basic`)
|
|
69
|
+
- `--no-install` - Skip npm install
|
|
70
|
+
- `--no-git` - Skip git initialization
|
|
71
|
+
|
|
72
|
+
### `zap dev`
|
|
73
|
+
|
|
74
|
+
Start the development server with hot reload for both Rust and TypeScript.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
zap dev
|
|
78
|
+
zap dev --port 3000 --vite-port 5173
|
|
79
|
+
zap dev --release --skip-build
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Options:**
|
|
83
|
+
- `-p, --port <port>` - API server port (default: `3000`)
|
|
84
|
+
- `--vite-port <port>` - Vite dev server port (default: `5173`)
|
|
85
|
+
- `--no-open` - Don't open browser automatically
|
|
86
|
+
- `-l, --log-level <level>` - Log level: `debug`, `info`, `warn`, `error` (default: `info`)
|
|
87
|
+
- `--release` - Build Rust in release mode (slower build, faster runtime)
|
|
88
|
+
- `--skip-build` - Skip initial Rust build (use existing binary)
|
|
89
|
+
- `--binary-path <path>` - Path to pre-built zap binary
|
|
90
|
+
- `--codegen-binary-path <path>` - Path to pre-built zap-codegen binary
|
|
91
|
+
|
|
92
|
+
**Keyboard Shortcuts (during dev):**
|
|
93
|
+
- `r` - Manually trigger Rust rebuild
|
|
94
|
+
- `c` - Regenerate TypeScript bindings
|
|
95
|
+
- `q` - Quit dev server
|
|
96
|
+
- `Ctrl+C` - Stop server
|
|
97
|
+
|
|
98
|
+
### `zap build`
|
|
99
|
+
|
|
100
|
+
Build your application for production.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
zap build
|
|
104
|
+
zap build --output ./dist --target x86_64-unknown-linux-gnu
|
|
105
|
+
zap build --skip-frontend
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Options:**
|
|
109
|
+
- `--release` - Build optimized release (default: `true`)
|
|
110
|
+
- `-o, --output <dir>` - Output directory (default: `./dist`)
|
|
111
|
+
- `--target <target>` - Cross-compile target (e.g., `x86_64-unknown-linux-gnu`)
|
|
112
|
+
- `--skip-frontend` - Skip Vite frontend build
|
|
113
|
+
- `--skip-codegen` - Skip TypeScript binding generation
|
|
114
|
+
|
|
115
|
+
**Output Structure:**
|
|
116
|
+
```
|
|
117
|
+
dist/
|
|
118
|
+
├── bin/
|
|
119
|
+
│ └── zap # Rust binary
|
|
120
|
+
├── static/ # Frontend assets (if built)
|
|
121
|
+
├── config.json # Production config
|
|
122
|
+
└── manifest.json # Build metadata
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `zap serve`
|
|
126
|
+
|
|
127
|
+
Run the production server.
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
zap serve
|
|
131
|
+
zap serve --port 8080 --host 0.0.0.0
|
|
132
|
+
zap serve --config ./custom-config.json
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Options:**
|
|
136
|
+
- `-p, --port <port>` - Server port
|
|
137
|
+
- `--host <host>` - Host to bind to (default: `0.0.0.0`)
|
|
138
|
+
- `-c, --config <path>` - Path to config file
|
|
139
|
+
- `-w, --workers <count>` - Number of worker threads
|
|
140
|
+
|
|
141
|
+
### `zap codegen`
|
|
142
|
+
|
|
143
|
+
Generate TypeScript bindings from Rust exports.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
zap codegen
|
|
147
|
+
zap codegen --output ./src/api
|
|
148
|
+
zap codegen --input ./metadata.json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Options:**
|
|
152
|
+
- `-i, --input <file>` - Input metadata JSON file
|
|
153
|
+
- `-o, --output <dir>` - Output directory (default: `./src/api`)
|
|
154
|
+
|
|
155
|
+
### `zap routes`
|
|
156
|
+
|
|
157
|
+
Scan routes directory and display/generate route tree.
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
zap routes
|
|
161
|
+
zap routes --routes-dir ./routes --output ./src/generated
|
|
162
|
+
zap routes --json
|
|
163
|
+
zap routes --verbose
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Options:**
|
|
167
|
+
- `-d, --routes-dir <dir>` - Routes directory path
|
|
168
|
+
- `-o, --output <dir>` - Output directory for generated files
|
|
169
|
+
- `--json` - Output routes as JSON
|
|
170
|
+
- `--verbose` - Show full handler code
|
|
171
|
+
|
|
172
|
+
## Project Structure
|
|
173
|
+
|
|
174
|
+
A typical ZapJS project structure:
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
my-app/
|
|
178
|
+
├── routes/ # File-based routing
|
|
179
|
+
│ ├── __root.tsx # Root layout
|
|
180
|
+
│ ├── index.tsx # Home page (/)
|
|
181
|
+
│ ├── about.tsx # About page (/about)
|
|
182
|
+
│ ├── posts/
|
|
183
|
+
│ │ ├── [id].tsx # Dynamic route (/posts/:id)
|
|
184
|
+
│ │ └── index.tsx # Posts index (/posts)
|
|
185
|
+
│ └── api/
|
|
186
|
+
│ ├── hello.ts # API route (/api/hello)
|
|
187
|
+
│ └── users.$id.ts # Dynamic API route (/api/users/:id)
|
|
188
|
+
├── server/
|
|
189
|
+
│ ├── src/
|
|
190
|
+
│ │ └── main.rs # Rust server entry point
|
|
191
|
+
│ └── Cargo.toml # Rust dependencies
|
|
192
|
+
├── src/
|
|
193
|
+
│ └── generated/ # Auto-generated files
|
|
194
|
+
│ ├── routes.tsx # Generated route tree
|
|
195
|
+
│ └── routeManifest.json
|
|
196
|
+
├── package.json
|
|
197
|
+
├── Cargo.toml # Workspace Cargo.toml
|
|
198
|
+
├── tsconfig.json
|
|
199
|
+
└── zap.config.ts # Optional config
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Configuration
|
|
203
|
+
|
|
204
|
+
### `zap.config.ts` (Optional)
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import { defineConfig } from 'zap';
|
|
208
|
+
|
|
209
|
+
export default defineConfig({
|
|
210
|
+
server: {
|
|
211
|
+
port: 3000,
|
|
212
|
+
hostname: '127.0.0.1',
|
|
213
|
+
},
|
|
214
|
+
dev: {
|
|
215
|
+
apiPort: 3000,
|
|
216
|
+
clientPort: 5173,
|
|
217
|
+
watchRust: true,
|
|
218
|
+
watchTypeScript: true,
|
|
219
|
+
open: true,
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Routing Conventions
|
|
225
|
+
|
|
226
|
+
### File-Based Routing
|
|
227
|
+
|
|
228
|
+
ZapJS uses Next.js-style file-based routing:
|
|
229
|
+
|
|
230
|
+
- `routes/index.tsx` → `/`
|
|
231
|
+
- `routes/about.tsx` → `/about`
|
|
232
|
+
- `routes/posts/[id].tsx` → `/posts/:id`
|
|
233
|
+
- `routes/posts/[...slug].tsx` → `/posts/*` (catch-all)
|
|
234
|
+
- `routes/__root.tsx` → Root layout wrapper
|
|
235
|
+
- `routes/_layout.tsx` → Layout wrapper
|
|
236
|
+
|
|
237
|
+
### API Routes
|
|
238
|
+
|
|
239
|
+
TypeScript files in `routes/api/` become API endpoints:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
// routes/api/users.ts
|
|
243
|
+
export const GET = async () => {
|
|
244
|
+
return { users: [] };
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
export const POST = async ({ request }: { request: Request }) => {
|
|
248
|
+
const body = await request.json();
|
|
249
|
+
return { created: body };
|
|
250
|
+
};
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Supported Methods:** `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`
|
|
254
|
+
|
|
255
|
+
### Dynamic Routes
|
|
256
|
+
|
|
257
|
+
Use `[param]` or `$param` syntax for dynamic segments:
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
// routes/api/users.$id.ts or routes/api/users/[id].ts
|
|
261
|
+
export const GET = async ({ params }: { params: { id: string } }) => {
|
|
262
|
+
return { id: params.id };
|
|
263
|
+
};
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Troubleshooting
|
|
267
|
+
|
|
268
|
+
### `zap` command not found
|
|
269
|
+
|
|
270
|
+
After installing globally, if `zap` is not found:
|
|
271
|
+
|
|
272
|
+
1. Check npm global bin directory is in PATH:
|
|
273
|
+
```bash
|
|
274
|
+
npm config get prefix
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
2. Add npm global bin to your PATH:
|
|
278
|
+
```bash
|
|
279
|
+
# Add to ~/.bashrc or ~/.zshrc
|
|
280
|
+
export PATH="$(npm config get prefix)/bin:$PATH"
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
3. Verify installation:
|
|
284
|
+
```bash
|
|
285
|
+
which zap
|
|
286
|
+
zap --version
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Development server won't start
|
|
290
|
+
|
|
291
|
+
1. Ensure Rust is installed:
|
|
292
|
+
```bash
|
|
293
|
+
rustc --version
|
|
294
|
+
cargo --version
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
2. Clean and rebuild:
|
|
298
|
+
```bash
|
|
299
|
+
cargo clean
|
|
300
|
+
npm run build
|
|
301
|
+
zap dev
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
3. Check port availability:
|
|
305
|
+
```bash
|
|
306
|
+
lsof -i :3000 # Check if port 3000 is in use
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### TypeScript bindings not generating
|
|
310
|
+
|
|
311
|
+
1. Ensure `zap-codegen` binary exists:
|
|
312
|
+
```bash
|
|
313
|
+
cargo build --release --bin zap-codegen
|
|
314
|
+
```
|
|
30
315
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- **TypeScript support** out of the box
|
|
36
|
-
- **Production optimizations** built-in
|
|
316
|
+
2. Manually run codegen:
|
|
317
|
+
```bash
|
|
318
|
+
zap codegen
|
|
319
|
+
```
|
|
37
320
|
|
|
38
|
-
##
|
|
321
|
+
## Links
|
|
39
322
|
|
|
40
|
-
|
|
323
|
+
- [Documentation](https://github.com/yourusername/zapjs)
|
|
324
|
+
- [Examples](https://github.com/yourusername/zapjs/tree/main/examples)
|
|
325
|
+
- [Discord Community](https://discord.gg/zapjs)
|
|
326
|
+
- [GitHub Issues](https://github.com/yourusername/zapjs/issues)
|
|
41
327
|
|
|
42
328
|
## License
|
|
43
329
|
|
|
44
|
-
MIT
|
|
330
|
+
MIT
|
package/bin/zap
ADDED
|
Binary file
|
package/bin/zap-codegen
ADDED
|
Binary file
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface BuildOptions {
|
|
2
|
+
release?: boolean;
|
|
3
|
+
output?: string;
|
|
4
|
+
target?: string;
|
|
5
|
+
skipFrontend?: boolean;
|
|
6
|
+
skipCodegen?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Build for production
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildCommand(options: BuildOptions): Promise<void>;
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { join, resolve } from 'path';
|
|
3
|
+
import { existsSync, mkdirSync, copyFileSync, readdirSync, statSync, rmSync, writeFileSync } from 'fs';
|
|
4
|
+
import { cliLogger } from '../utils/logger.js';
|
|
5
|
+
/**
|
|
6
|
+
* Build for production
|
|
7
|
+
*/
|
|
8
|
+
export async function buildCommand(options) {
|
|
9
|
+
const outputDir = resolve(options.output || './dist');
|
|
10
|
+
const startTime = Date.now();
|
|
11
|
+
try {
|
|
12
|
+
cliLogger.header('ZapJS Production Build');
|
|
13
|
+
// Step 1: Generate TypeScript bindings first (needed for type checking)
|
|
14
|
+
if (!options.skipCodegen) {
|
|
15
|
+
await runCodegen();
|
|
16
|
+
}
|
|
17
|
+
// Step 2: TypeScript type checking (optional but recommended)
|
|
18
|
+
await typeCheck();
|
|
19
|
+
// Clean output directory
|
|
20
|
+
if (existsSync(outputDir)) {
|
|
21
|
+
cliLogger.spinner('clean', 'Cleaning output directory...');
|
|
22
|
+
rmSync(outputDir, { recursive: true, force: true });
|
|
23
|
+
cliLogger.succeedSpinner('clean', 'Output directory cleaned');
|
|
24
|
+
}
|
|
25
|
+
// Step 3: Build frontend first (if not skipped)
|
|
26
|
+
// Vite will create the dist/ directory and populate it
|
|
27
|
+
let staticDir = null;
|
|
28
|
+
if (!options.skipFrontend) {
|
|
29
|
+
staticDir = await buildFrontend(outputDir);
|
|
30
|
+
}
|
|
31
|
+
// Step 4: Create bin directory and build Rust binary
|
|
32
|
+
// This happens AFTER frontend build so Vite doesn't overwrite it
|
|
33
|
+
mkdirSync(join(outputDir, 'bin'), { recursive: true });
|
|
34
|
+
await buildRust(outputDir, options);
|
|
35
|
+
// Step 5: Create production config
|
|
36
|
+
await createProductionConfig(outputDir, staticDir);
|
|
37
|
+
// Step 6: Create build manifest
|
|
38
|
+
const manifest = createBuildManifest(outputDir, staticDir);
|
|
39
|
+
writeFileSync(join(outputDir, 'manifest.json'), JSON.stringify(manifest, null, 2));
|
|
40
|
+
// Summary
|
|
41
|
+
const elapsed = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
42
|
+
const binSize = getBinarySize(join(outputDir, 'bin', 'zap'));
|
|
43
|
+
cliLogger.newline();
|
|
44
|
+
cliLogger.success(`Build complete in ${elapsed}s`);
|
|
45
|
+
cliLogger.newline();
|
|
46
|
+
cliLogger.keyValue('Directory', outputDir);
|
|
47
|
+
cliLogger.keyValue('Binary', binSize);
|
|
48
|
+
if (staticDir) {
|
|
49
|
+
cliLogger.keyValue('Static', join(outputDir, 'static'));
|
|
50
|
+
}
|
|
51
|
+
cliLogger.newline();
|
|
52
|
+
cliLogger.command(`cd ${outputDir} && ./bin/zap`, 'Run in production');
|
|
53
|
+
cliLogger.newline();
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (error instanceof Error) {
|
|
57
|
+
cliLogger.error('Build failed', error);
|
|
58
|
+
}
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function buildRust(outputDir, options) {
|
|
63
|
+
cliLogger.spinner('rust', 'Building Rust backend (release mode)...');
|
|
64
|
+
const args = ['build', '--release', '--bin', 'zap'];
|
|
65
|
+
if (options.target) {
|
|
66
|
+
args.push('--target', options.target);
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
execSync(`cargo ${args.join(' ')}`, {
|
|
70
|
+
cwd: process.cwd(),
|
|
71
|
+
stdio: 'inherit', // Show cargo output
|
|
72
|
+
});
|
|
73
|
+
const targetDir = options.target
|
|
74
|
+
? join('target', options.target, 'release')
|
|
75
|
+
: join('target', 'release');
|
|
76
|
+
// Get the default Rust target for this platform
|
|
77
|
+
let defaultTarget = '';
|
|
78
|
+
try {
|
|
79
|
+
defaultTarget = execSync('rustc -vV | grep host | cut -d: -f2', {
|
|
80
|
+
stdio: 'pipe',
|
|
81
|
+
encoding: 'utf-8'
|
|
82
|
+
}).trim();
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Fallback targets based on platform
|
|
86
|
+
if (process.platform === 'darwin' && process.arch === 'arm64') {
|
|
87
|
+
defaultTarget = 'aarch64-apple-darwin';
|
|
88
|
+
}
|
|
89
|
+
else if (process.platform === 'darwin') {
|
|
90
|
+
defaultTarget = 'x86_64-apple-darwin';
|
|
91
|
+
}
|
|
92
|
+
else if (process.platform === 'linux') {
|
|
93
|
+
defaultTarget = 'x86_64-unknown-linux-gnu';
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const platformTargetDir = defaultTarget
|
|
97
|
+
? join('target', defaultTarget, 'release')
|
|
98
|
+
: targetDir;
|
|
99
|
+
// Try multiple locations for the binary (workspace vs local)
|
|
100
|
+
const possibleBinaryPaths = [
|
|
101
|
+
// Workspace target with platform-specific dir
|
|
102
|
+
join(process.cwd(), '..', platformTargetDir, 'zap'),
|
|
103
|
+
join(process.cwd(), '..', '..', platformTargetDir, 'zap'),
|
|
104
|
+
// Workspace target standard
|
|
105
|
+
join(process.cwd(), '..', targetDir, 'zap'),
|
|
106
|
+
join(process.cwd(), '..', '..', targetDir, 'zap'),
|
|
107
|
+
// Local target with platform-specific dir
|
|
108
|
+
join(process.cwd(), platformTargetDir, 'zap'),
|
|
109
|
+
// Local target standard
|
|
110
|
+
join(process.cwd(), targetDir, 'zap'),
|
|
111
|
+
];
|
|
112
|
+
let srcBinary = null;
|
|
113
|
+
for (const path of possibleBinaryPaths) {
|
|
114
|
+
if (existsSync(path)) {
|
|
115
|
+
srcBinary = path;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (!srcBinary) {
|
|
120
|
+
throw new Error(`Binary not found. Checked:\n${possibleBinaryPaths.join('\n')}`);
|
|
121
|
+
}
|
|
122
|
+
const destBinary = join(outputDir, 'bin', 'zap');
|
|
123
|
+
copyFileSync(srcBinary, destBinary);
|
|
124
|
+
execSync(`chmod +x "${destBinary}"`, { stdio: 'pipe' });
|
|
125
|
+
cliLogger.succeedSpinner('rust', 'Rust backend built (release + LTO)');
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
cliLogger.failSpinner('rust', 'Rust build failed');
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function buildFrontend(outputDir) {
|
|
133
|
+
const viteConfig = ['vite.config.ts', 'vite.config.js', 'vite.config.mjs']
|
|
134
|
+
.find(f => existsSync(join(process.cwd(), f)));
|
|
135
|
+
if (!viteConfig) {
|
|
136
|
+
cliLogger.info('No Vite config found, skipping frontend build');
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
cliLogger.spinner('vite', 'Building frontend (Vite)...');
|
|
140
|
+
try {
|
|
141
|
+
// Build to a temporary directory to avoid conflicts
|
|
142
|
+
const tempDist = join(process.cwd(), '.dist-temp');
|
|
143
|
+
// Clean temp directory if it exists
|
|
144
|
+
if (existsSync(tempDist)) {
|
|
145
|
+
rmSync(tempDist, { recursive: true, force: true });
|
|
146
|
+
}
|
|
147
|
+
execSync(`npx vite build --outDir ${tempDist}`, {
|
|
148
|
+
cwd: process.cwd(),
|
|
149
|
+
stdio: 'pipe',
|
|
150
|
+
});
|
|
151
|
+
const staticDir = join(outputDir, 'static');
|
|
152
|
+
if (existsSync(tempDist)) {
|
|
153
|
+
copyDirectory(tempDist, staticDir);
|
|
154
|
+
// Clean up temp directory
|
|
155
|
+
rmSync(tempDist, { recursive: true, force: true });
|
|
156
|
+
cliLogger.succeedSpinner('vite', 'Frontend built and bundled');
|
|
157
|
+
return staticDir;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
cliLogger.warn('Vite build completed but no output found');
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
cliLogger.failSpinner('vite', 'Frontend build failed');
|
|
166
|
+
cliLogger.warn('Continuing without frontend');
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async function typeCheck() {
|
|
171
|
+
// Check if tsconfig exists
|
|
172
|
+
const tsconfigPath = join(process.cwd(), 'tsconfig.json');
|
|
173
|
+
if (!existsSync(tsconfigPath)) {
|
|
174
|
+
return; // Skip if no tsconfig
|
|
175
|
+
}
|
|
176
|
+
cliLogger.spinner('typecheck', 'Type checking TypeScript...');
|
|
177
|
+
try {
|
|
178
|
+
execSync('npx tsc --noEmit', {
|
|
179
|
+
cwd: process.cwd(),
|
|
180
|
+
stdio: 'pipe',
|
|
181
|
+
});
|
|
182
|
+
cliLogger.succeedSpinner('typecheck', 'TypeScript types OK');
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
cliLogger.warn('TypeScript type check failed (continuing build)');
|
|
186
|
+
// Don't fail the build, just warn
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async function runCodegen() {
|
|
190
|
+
const projectDir = process.cwd();
|
|
191
|
+
// Find codegen binary using same logic as codegen command
|
|
192
|
+
const possiblePaths = [
|
|
193
|
+
join(projectDir, 'bin', 'zap-codegen'),
|
|
194
|
+
join(projectDir, '../../target/release/zap-codegen'),
|
|
195
|
+
join(projectDir, '../../target/aarch64-apple-darwin/release/zap-codegen'),
|
|
196
|
+
join(projectDir, '../../target/x86_64-unknown-linux-gnu/release/zap-codegen'),
|
|
197
|
+
join(projectDir, 'target/release/zap-codegen'),
|
|
198
|
+
];
|
|
199
|
+
let codegenBinary = null;
|
|
200
|
+
for (const path of possiblePaths) {
|
|
201
|
+
if (existsSync(path)) {
|
|
202
|
+
codegenBinary = path;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Try global zap-codegen as fallback
|
|
207
|
+
if (!codegenBinary) {
|
|
208
|
+
try {
|
|
209
|
+
execSync('which zap-codegen', { stdio: 'pipe' });
|
|
210
|
+
codegenBinary = 'zap-codegen';
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
cliLogger.info('Codegen skipped (binary not found)');
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
cliLogger.spinner('codegen', 'Generating TypeScript bindings...');
|
|
218
|
+
try {
|
|
219
|
+
execSync(`"${codegenBinary}" --output-dir ./src/api`, {
|
|
220
|
+
cwd: process.cwd(),
|
|
221
|
+
stdio: 'pipe',
|
|
222
|
+
});
|
|
223
|
+
cliLogger.succeedSpinner('codegen', 'TypeScript bindings generated');
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
cliLogger.warn('Codegen failed (continuing build)');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async function createProductionConfig(outputDir, staticDir) {
|
|
230
|
+
const config = {
|
|
231
|
+
server: {
|
|
232
|
+
host: '0.0.0.0',
|
|
233
|
+
port: 3000,
|
|
234
|
+
},
|
|
235
|
+
static: staticDir ? {
|
|
236
|
+
prefix: '/',
|
|
237
|
+
directory: './static',
|
|
238
|
+
} : null,
|
|
239
|
+
logging: {
|
|
240
|
+
level: 'info',
|
|
241
|
+
format: 'json',
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
writeFileSync(join(outputDir, 'config.json'), JSON.stringify(config, null, 2));
|
|
245
|
+
}
|
|
246
|
+
function createBuildManifest(outputDir, staticDir) {
|
|
247
|
+
return {
|
|
248
|
+
version: '1.0.0',
|
|
249
|
+
buildTime: new Date().toISOString(),
|
|
250
|
+
rustBinary: './bin/zap',
|
|
251
|
+
staticDir: staticDir ? './static' : null,
|
|
252
|
+
env: 'production',
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function copyDirectory(src, dest) {
|
|
256
|
+
mkdirSync(dest, { recursive: true });
|
|
257
|
+
const entries = readdirSync(src, { withFileTypes: true });
|
|
258
|
+
for (const entry of entries) {
|
|
259
|
+
const srcPath = join(src, entry.name);
|
|
260
|
+
const destPath = join(dest, entry.name);
|
|
261
|
+
if (entry.isDirectory()) {
|
|
262
|
+
copyDirectory(srcPath, destPath);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
copyFileSync(srcPath, destPath);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function getBinarySize(path) {
|
|
270
|
+
try {
|
|
271
|
+
const stats = statSync(path);
|
|
272
|
+
const bytes = stats.size;
|
|
273
|
+
if (bytes < 1024)
|
|
274
|
+
return `${bytes} B`;
|
|
275
|
+
if (bytes < 1024 * 1024)
|
|
276
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
277
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
return 'unknown';
|
|
281
|
+
}
|
|
282
|
+
}
|